V2 integration (#293)

* feat: integrations and lifecycles

* refactor: prevent issues with start order
This commit is contained in:
Carlos Valente
2023-02-23 20:22:38 +01:00
committed by GitHub
parent d8a84ac88d
commit 70cc071425
61 changed files with 1510 additions and 687 deletions
+1
View File
@@ -25,6 +25,7 @@
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-fast-compare": "^3.2.0",
"react-hook-form": "^7.43.1",
"react-qr-code": "^2.0.11",
"react-router-dom": "^6.3.0",
"react-table": "^7.7.0",
+3 -3
View File
@@ -3,7 +3,7 @@ import axios from 'axios';
import { URLAliasType } from '../models/Alias.type';
import { InfoType } from '../models/Info.types';
import { OntimeSettingsType } from '../models/OntimeSettings.type';
import { OscSettingsType } from '../models/OscSettings.type';
import { OSCSettings } from '../models/OscSettings.type';
import { UserFieldsType } from '../models/UserFields.type';
import { ViewSettingsType } from '../models/ViewSettings.type';
@@ -90,7 +90,7 @@ export async function postUserFields(data: UserFieldsType) {
* @description HTTP request to retrieve osc settings
* @return {Promise}
*/
export async function getOSC(): Promise<OscSettingsType> {
export async function getOSC(): Promise<OSCSettings> {
const res = await axios.get(`${ontimeURL}/osc`);
return res.data;
}
@@ -99,7 +99,7 @@ export async function getOSC(): Promise<OscSettingsType> {
* @description HTTP request to mutate osc settings
* @return {Promise}
*/
export async function postOSC(data: OscSettingsType) {
export async function postOSC(data: OSCSettings) {
return axios.post(`${ontimeURL}/osc`, data);
}
@@ -1,13 +1,16 @@
export type OscSettingsType = {
port: string;
import { OSCSettings } from 'ontime-types';
// in the placeholder, we pass strings to satisfy input type
export interface PlaceholderSettings extends Omit<OSCSettings, 'portIn' | 'portOut'> {
portIn: string;
portOut: string;
targetIP: string;
enabled: boolean;
}
export const oscPlaceholderSettings: OscSettingsType = {
port: '',
export const oscPlaceholderSettings: PlaceholderSettings = {
portIn: '',
portOut: '',
targetIP: '',
enabled: false,
enabledIn: false,
enabledOut: false,
subscriptions: {},
};
@@ -0,0 +1,27 @@
import { isIPAddress, isOnlyNumbers } from '../regex';
describe('simple tests for regex', () => {
test('isOnlyNumbers', () => {
const right = ['1231', '1'];
const wrong = ['a', 'asdas1asdas', '11as', '1_', '1.1'];
right.forEach((t) => {
expect(isOnlyNumbers.test(t)).toBe(true);
});
wrong.forEach((t) => {
expect(isOnlyNumbers.test(t)).toBe(false);
});
});
test('isIPAddress', () => {
const right = ['0.0.0.0', '127.0.0.1'];
const wrong = ['0', 'testing', '123.0.1'];
right.forEach((t) => {
expect(isIPAddress.test(t)).toBe(true);
});
wrong.forEach((t) => {
expect(isIPAddress.test(t)).toBe(false);
});
});
});
+2
View File
@@ -0,0 +1,2 @@
export const isOnlyNumbers = /^\d+$/;
export const isIPAddress = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
+13 -11
View File
@@ -1,10 +1,11 @@
import { lazy, useEffect } from 'react';
import { Box, useDisclosure } from '@chakra-ui/react';
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
import UploadModal from '../../common/components/upload-modal/UploadModal';
import ModalManager from '../../features/modals/ModalManager';
import ModalManager from '../modals/ModalManager';
import MenuBar from '../menu/MenuBar';
import IntegrationModal from '../modals/integration-modal/IntegrationModal';
import styles from './Editor.module.scss';
@@ -15,16 +16,14 @@ const Info = lazy(() => import('../../features/info/InfoExport'));
const EventEditor = lazy(() => import('../../features/event-editor/EventEditorExport'));
export default function Editor() {
const {
isOpen: isSettingsOpen,
onOpen: onSettingsOpen,
onClose: onSettingsClose,
} = useDisclosure();
const { isOpen: isSettingsOpen, onOpen: onSettingsOpen, onClose: onSettingsClose } = useDisclosure();
const { isOpen: isUploadModalOpen, onOpen: onUploadModalOpen, onClose: onUploadModalClose } = useDisclosure();
const {
isOpen: isUploadModalOpen,
onOpen: onUploadModalOpen,
onClose: onUploadModalClose,
isOpen: isIntegrationModalOpen,
onOpen: onIntegrationModalOpen,
onClose: onIntegrationModalClose,
} = useDisclosure();
// Set window title
@@ -35,10 +34,11 @@ export default function Editor() {
return (
<>
<UploadModal onClose={onUploadModalClose} isOpen={isUploadModalOpen} />
<IntegrationModal onClose={onIntegrationModalClose} isOpen={isIntegrationModalOpen} />
<ErrorBoundary>
<ModalManager isOpen={isSettingsOpen} onClose={onSettingsClose} />
</ErrorBoundary>
<div className={styles.mainContainer} data-testid="event-editor">
<div className={styles.mainContainer} data-testid='event-editor'>
<Box id='settings' className={styles.settings}>
<ErrorBoundary>
<MenuBar
@@ -47,6 +47,8 @@ export default function Editor() {
onSettingsClose={onSettingsClose}
isUploadOpen={isUploadModalOpen}
onUploadOpen={onUploadModalOpen}
isIntegrationOpen={isIntegrationModalOpen}
onIntegrationOpen={onIntegrationModalOpen}
/>
</ErrorBoundary>
</Box>
+52 -28
View File
@@ -4,6 +4,8 @@ import { FiHelpCircle } from '@react-icons/all-files/fi/FiHelpCircle';
import { FiMinimize } from '@react-icons/all-files/fi/FiMinimize';
import { FiSave } from '@react-icons/all-files/fi/FiSave';
import { FiUpload } from '@react-icons/all-files/fi/FiUpload';
import { IoExtensionPuzzle } from '@react-icons/all-files/io5/IoExtensionPuzzle';
import { IoExtensionPuzzleOutline } from '@react-icons/all-files/io5/IoExtensionPuzzleOutline';
import { IoScan } from '@react-icons/all-files/io5/IoScan';
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
import { downloadRundown } from '../../common/api/ontimeApi';
@@ -20,6 +22,8 @@ interface MenuBarProps {
onSettingsClose: () => void;
isUploadOpen: boolean;
onUploadOpen: () => void;
isIntegrationOpen: boolean;
onIntegrationOpen: () => void;
}
type Actions = 'min' | 'max' | 'shutdown' | 'help';
@@ -29,42 +33,53 @@ const buttonStyle = {
size: 'lg',
colorScheme: 'white',
_hover: {
background: 'rgba(255, 255, 255, 0.10)' // $white-10
background: 'rgba(255, 255, 255, 0.10)', // $white-10
},
_active: {
background: 'rgba(255, 255, 255, 0.13)' // $white-13
}
background: 'rgba(255, 255, 255, 0.13)', // $white-13
},
};
export default function MenuBar(props: MenuBarProps) {
const { isSettingsOpen, onSettingsOpen, onSettingsClose, isUploadOpen, onUploadOpen } = props;
const {
isSettingsOpen,
onSettingsOpen,
onSettingsClose,
isUploadOpen,
onUploadOpen,
isIntegrationOpen,
onIntegrationOpen,
} = props;
const { isElectron, sendToElectron } = useElectronEvent();
const actionHandler = useCallback((action: Actions) => {
// Stop crashes when testing locally
if (!isElectron) {
if (action === 'help') {
window.open('https://cpvalente.gitbook.io/ontime/');
const actionHandler = useCallback(
(action: Actions) => {
// Stop crashes when testing locally
if (!isElectron) {
if (action === 'help') {
window.open('https://cpvalente.gitbook.io/ontime/');
}
} else {
switch (action) {
case 'min':
sendToElectron('set-window', 'to-tray');
break;
case 'max':
sendToElectron('set-window', 'to-max');
break;
case 'shutdown':
sendToElectron('shutdown', 'now');
break;
case 'help':
sendToElectron('send-to-link', 'help');
break;
default:
break;
}
}
} else {
switch (action) {
case 'min':
sendToElectron('set-window', 'to-tray');
break;
case 'max':
sendToElectron('set-window', 'to-max');
break;
case 'shutdown':
sendToElectron('shutdown', 'now');
break;
case 'help':
sendToElectron('send-to-link', 'help');
break;
default:
break;
}
}
}, [sendToElectron, isElectron]);
},
[sendToElectron, isElectron],
);
// Handle keyboard shortcuts
const handleKeyPress = useCallback(
@@ -129,6 +144,15 @@ export default function MenuBar(props: MenuBarProps) {
aria-label='Settings'
/>
<div className={style.gap} />
<TooltipActionBtn
{...buttonStyle}
icon={isIntegrationOpen ? <IoExtensionPuzzle /> : <IoExtensionPuzzleOutline />}
className={isIntegrationOpen ? style.open : ''}
clickHandler={onIntegrationOpen}
tooltip='Integrations'
aria-label='Integrations'
/>
<div className={style.gap} />
<TooltipActionBtn
{...buttonStyle}
icon={<FiUpload />}
@@ -0,0 +1,77 @@
@use '../../theme/v2Styles' as *;
@use '../../theme/ontimeColours' as *;
.headerNotes {
font-size: $text-body-size;
width: 100%;
padding: 0 16px;
color: $modal-note-color;
margin-bottom: $section-spacing;
}
.divider {
margin: 8px 0;
border: 0;
border-top: 1px solid $gray-100;
}
.sectionContainer {
display: flex;
flex-direction: column;
height: 100%
}
.splitSection {
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 3px;
padding: 8px;
&:hover {
background-color: $blue-50;
}
}
.sectionTitle {
font-size: 14px;
display: block;
&.main {
font-weight: 600;
}
}
@mixin subsection {
font-size: 14px;
display: block;
}
.sectionSubtitle {
@include subsection;
color: $modal-note-color;
}
.error {
@include subsection;
color: $error-red;
}
.buttonSection {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid $gray-100;
display: flex;
gap: 16px;
}
.buttonSection {
button:first-of-type {
margin-right: auto;
}
}
.spacer {
flex-grow: 1;
}
@@ -10,17 +10,19 @@ import {
TabPanels,
Tabs,
} from '@chakra-ui/react';
import PropTypes from 'prop-types';
import AliasesModal from './AliasesModal';
import AppSettingsModal from './AppSettingsModal';
import EventSettingsModal from './EventSettingsModal';
import IntegrationSettingsModal from './IntegrationSettingsModal';
import OscSettingsModal from './OscSettingsModal';
import TableOptionsModal from './TableOptionsModal';
import ViewsSettingsModal from './ViewsSettingsModal';
export default function ModalManager(props) {
interface ModalManagerProps {
isOpen: boolean;
onClose: () => void;
}
export default function ModalManager(props: ModalManagerProps) {
const { isOpen, onClose } = props;
return (
<Modal
@@ -38,13 +40,11 @@ export default function ModalManager(props) {
<Tabs size='sm' isLazy>
<TabList>
<Tab style={{ fontSize: '0.9em' }}>App Settings</Tab>
<Tab style={{ fontSize: '0.9em' }}>Viewers</Tab>
<Tab style={{ fontSize: '0.9em' }}>Event Data</Tab>
<Tab style={{ fontSize: '0.9em' }}>URL Aliases</Tab>
<Tab style={{ fontSize: '0.9em' }}>Cuesheet</Tab>
<Tab style={{ fontSize: '0.9em' }}>OSC</Tab>
{/*<Tab style={{ fontSize: '0.9em' }}>Integration</Tab>*/}
<Tab>App Settings</Tab>
<Tab>Viewers</Tab>
<Tab>Event Data</Tab>
<Tab>URL Aliases</Tab>
<Tab>Cuesheet</Tab>
</TabList>
<TabPanels>
<TabPanel>
@@ -62,20 +62,9 @@ export default function ModalManager(props) {
<TabPanel>
<TableOptionsModal />
</TabPanel>
<TabPanel>
<OscSettingsModal />
</TabPanel>
{/*<TabPanel>*/}
{/* <IntegrationSettingsModal />*/}
{/*</TabPanel>*/}
</TabPanels>
</Tabs>
</ModalContent>
</Modal>
);
}
ModalManager.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
};
@@ -0,0 +1,23 @@
import { Button, ModalFooter } from '@chakra-ui/react';
import styles from './Modal.module.scss';
export default function ModalSubmitFooter() {
return (
<ModalFooter className={styles.buttonSection}>
<Button variant='ghosted' paddingLeft={0} color='#6c6c6c'>
Revert to saved
</Button>
<Button colorScheme='gray'>Cancel</Button>
<Button
variant='ontime-filled'
type='submit'
// disabled={isSubmitting}
isLoading={false}
padding='0 2.5em'
>
Save
</Button>
</ModalFooter>
);
}
@@ -0,0 +1,41 @@
import { PropsWithChildren } from 'react';
import {
Button,
Modal,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
import styles from './Modal.module.scss';
interface ModalWrapperProps {
isOpen: boolean;
onClose: () => void;
title: string;
}
export default function ModalWrapper(props: PropsWithChildren<ModalWrapperProps>) {
const { isOpen, onClose, title, children } = props;
return (
<Modal
onClose={onClose}
isOpen={isOpen}
closeOnOverlayClick={false}
motionPreset='slideInBottom'
size='xl'
scrollBehavior='inside'
preserveScrollBarGap
variant='ontime'
>
<ModalOverlay />
<ModalContent>
<ModalHeader>{title}</ModalHeader>
<ModalCloseButton />
{children}
</ModalContent>
</Modal>
);
}
@@ -1,4 +1,5 @@
@use '../../theme/main' as *;
@use '../../theme/ontimeColours' as *;
@use '../../theme/v2Styles' as *;
//////////////////////////////////// main
@@ -7,7 +8,7 @@
.notes {
font-weight: 400;
color: $light-bg;
color: $action-blue;
display: grid;
place-items: center;
height: 4em;
@@ -16,7 +17,7 @@
.modalFields {
max-height: 45vh;
overflow-y: auto;
scrollbar-color: rgba($light-bg, 0.35) rgba($light-bg, 0.15);
scrollbar-color: rgba($action-blue, 0.35) rgba($action-blue, 0.15);
padding-right: 6px;
label {
@@ -42,7 +43,7 @@
grid-template-columns: 20% 1fr 4em;
.placeholder {
background: $light-text;
background: black;
width: 100%;
height: 24px;
}
@@ -51,19 +52,19 @@
/* Track */
::-webkit-scrollbar-track {
background: rgba($light-bg, 0.15);
background: rgba($gray-50, 0.15);
border-radius: 3px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: rgba($light-bg, 0.35);
background: rgba($gray-100, 0.35);
border-radius: 3px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: rgba($light-bg, 0.45);
background: rgba($gray-200, 0.45);
}
.modalInline {
@@ -99,9 +100,8 @@
padding-top: 2em;
display: flex;
justify-content: flex-end;
gap: 1em;
gap: 16px;
}
}
.modalBody > * {
@@ -115,7 +115,7 @@ ul.featureList {
display: flex;
align-items: center;
svg {
color: $ontime-accent-text;
color: black;
margin-right: 4px;
}
}
@@ -124,7 +124,7 @@ ul.featureList {
p {
&.notes {
text-align: center;
border-color: $light-bg-transparent;
border-color: black;
border-width: 0 2px;
font-size: 0.9em;
margin-bottom: 1em;
@@ -139,7 +139,7 @@ span {
}
.blockNotes {
background-color: $bg-gray;
background-color: $gray-1100;
margin: 1em 0;
padding: 0.5em;
font-size: 0.8em;
@@ -147,7 +147,7 @@ span {
table {
background-color: #fff;
border-left: 4px solid lighten($ontime-pink, 5%);
border-left: 4px solid lighten($ontime-color, 5%);
width: 100%;
margin: 0.5em 0;
border-radius: 2px;
@@ -181,12 +181,12 @@ span {
}
.labelNote {
color: $light-bg;
color: $action-blue;
padding-right: 1em;
}
.labelNoteInline {
color: $light-bg;
color: $action-blue;
}
.inlineFlex {
@@ -0,0 +1,40 @@
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
import ModalWrapper from '../ModalWrapper';
import OscIntegrationSettings from './OscIntegrationSettings';
import OscSettingsModal from './OscSettingsModal';
import styles from '../Modal.module.scss';
interface IntegrationModalProps {
isOpen: boolean;
onClose: () => void;
}
export default function IntegrationModal(props: IntegrationModalProps) {
const { isOpen, onClose } = props;
return (
<ModalWrapper title='Integration Settings' isOpen={isOpen} onClose={onClose}>
<div className={styles.headerNotes}>
Manage settings related to protocol integrations. <br />
Changes take effect on app restart.
</div>
<Tabs variant='ontime' size='sm' isLazy>
<TabList>
<Tab>OSC</Tab>
<Tab>Old OSC</Tab>
</TabList>
<TabPanels>
<TabPanel>
<OscIntegrationSettings />
</TabPanel>
<TabPanel>
<OscSettingsModal />
</TabPanel>
</TabPanels>
</Tabs>
</ModalWrapper>
);
}
@@ -0,0 +1,181 @@
import { useContext } from 'react';
import { useForm } from 'react-hook-form';
import { Button, FormControl, Input, ModalBody, ModalFooter, Switch } from '@chakra-ui/react';
import { postOSC } from '../../../common/api/ontimeApi';
import { LoggingContext } from '../../../common/context/LoggingContext';
import useOscSettings from '../../../common/hooks-query/useOscSettings';
import { PlaceholderSettings } from '../../../common/models/OscSettings.type';
import { isIPAddress, isOnlyNumbers } from '../../../common/utils/regex';
import styles from '../Modal.module.scss';
export default function OscIntegrationSettings() {
const { data } = useOscSettings();
const { emitError } = useContext(LoggingContext);
const {
handleSubmit,
register,
reset,
setError,
formState: { errors, isSubmitting, isDirty, isValid },
} = useForm<PlaceholderSettings>({
defaultValues: data,
values: data,
});
const disableSubmit = isSubmitting || !isDirty || !isValid;
const onSubmit = async (values: PlaceholderSettings) => {
const numericPortIn = Number(values.portIn);
const numericPortOut = Number(values.portOut);
if (numericPortIn === numericPortOut) {
setError('portIn', { message: 'OSC IN and OUT Ports cant be the same' });
return;
}
const parsedValues = {
...values,
portIn: numericPortIn,
portOut: numericPortOut,
};
try {
await postOSC(parsedValues);
} catch (error) {
emitError(`Error setting OSC: ${error}`);
}
};
const resetForm = () => reset(data);
return (
<>
<form onSubmit={handleSubmit(onSubmit)} className={styles.sectionContainer} id='test'>
<ModalBody>
<div className={styles.splitSection}>
<div>
<span className={`${styles.sectionTitle} ${styles.main}`}>OSC Input</span>
<span className={styles.sectionSubtitle}>Control Ontime with OSC</span>
</div>
<Switch {...register('enabledIn')} variant='ontime-on-light' />
</div>
<FormControl isInvalid={!!errors.portIn} className={styles.splitSection}>
<label htmlFor='portIn'>
<span className={styles.sectionTitle}>Listen on Port</span>
{errors.portIn ? (
<span className={styles.error}>{errors.portIn.message}</span>
) : (
<span className={styles.sectionSubtitle}>Default 8888</span>
)}
</label>
<Input
id='portIn'
placeholder='8888'
width='75px'
size='sm'
textAlign='right'
maxLength={5}
{...register('portIn', {
required: { value: true, message: 'Required field' },
max: { value: 65535, message: 'Port in incorrect range (1024 - 65535)' },
min: { value: 1024, message: 'Port in incorrect range (1024 - 65535)' },
pattern: {
value: isOnlyNumbers,
message: 'Value should be numeric',
},
})}
/>
</FormControl>
<hr className={styles.divider} />
<div className={styles.splitSection}>
<div>
<span className={styles.sectionTitle} style={{ fontWeight: 600 }}>
OSC Output
</span>
<span className={styles.sectionSubtitle}>Ontime data feedback</span>
</div>
<Switch {...register('enabledOut')} variant='ontime-on-light' />
</div>
<FormControl isInvalid={!!errors.targetIP} className={styles.splitSection}>
<label htmlFor='targetIP'>
<span className={styles.sectionTitle}>OSC target IP</span>
{errors.targetIP ? (
<span className={styles.error}>{errors.targetIP.message}</span>
) : (
<span className={styles.sectionSubtitle}>Default 127.0.0.1</span>
)}
</label>
<Input
id='targetIP'
placeholder='127.0.0.1'
width='140px'
size='sm'
textAlign='right'
{...register('targetIP', {
required: { value: true, message: 'Required field' },
pattern: {
value: isIPAddress,
message: 'Invalid IP address',
},
})}
/>
</FormControl>
<FormControl className={styles.splitSection}>
<label htmlFor='portOut'>
<span className={styles.sectionTitle}>OSC target Port</span>
{errors.portOut ? (
<span className={styles.error}>{errors.portOut.message}</span>
) : (
<span className={styles.sectionSubtitle}>Default 9999</span>
)}
</label>
<Input
id='portOut'
placeholder='9999'
width='75px'
size='sm'
textAlign='right'
maxLength={5}
{...register('portOut', {
required: { value: true, message: 'Required field' },
max: { value: 65535, message: 'Port in incorrect range (1024 - 65535)' },
min: { value: 1024, message: 'Port in incorrect range (1024 - 65535)' },
pattern: {
value: isOnlyNumbers,
message: 'Value should be numeric',
},
})}
/>
</FormControl>
</ModalBody>
{/*<ModalSubmitFooter />*/}
</form>
<ModalFooter className={styles.buttonSection}>
<Button variant='ontime-ghost-on-light' size='sm' onClick={resetForm}>
Revert to saved
</Button>
<Button variant='ontime-subtle-on-light' size='sm'>
Cancel
</Button>
<Button
variant='ontime-filled'
type='submit'
form='test'
disabled={disableSubmit}
isLoading={isSubmitting}
padding='0 2em'
size='sm'
>
Save
</Button>
</ModalFooter>
</>
);
}
@@ -2,16 +2,15 @@ import { useCallback, useContext, useEffect, useState } from 'react';
import { FormControl, FormLabel, Input, ModalBody } from '@chakra-ui/react';
import { IoInformationCircleOutline } from '@react-icons/all-files/io5/IoInformationCircleOutline';
import { postOSC } from '../../common/api/ontimeApi';
import EnableBtn from '../../common/components/buttons/EnableBtn';
import { LoggingContext } from '../../common/context/LoggingContext';
import useOscSettings from '../../common/hooks-query/useOscSettings';
import { oscPlaceholderSettings } from '../../common/models/OscSettings.type';
import { postOSC } from '../../../common/api/ontimeApi';
import EnableBtn from '../../../common/components/buttons/EnableBtn';
import { LoggingContext } from '../../../common/context/LoggingContext';
import useOscSettings from '../../../common/hooks-query/useOscSettings';
import { oscPlaceholderSettings } from '../../../common/models/OscSettings.type';
import { inputProps, portInputProps } from '../modalHelper';
import SubmitContainer from '../SubmitContainer';
import { inputProps, portInputProps } from './modalHelper';
import SubmitContainer from './SubmitContainer';
import style from './Modals.module.scss';
import style from '../Modals.module.scss';
// currently defined endpoints
// temporary
@@ -9,4 +9,4 @@ export const portInputProps = {
type: 'number',
min: '1024',
max: '65535',
};
};
+7
View File
@@ -7,6 +7,13 @@
scrollbar-width: thin;
}
// reset box-sizing
*,
*:before,
*:after {
box-sizing: inherit;
}
body,
html,
.App {
-5
View File
@@ -2,7 +2,6 @@ import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import { TimerType } from 'ontime-types';
import App from './App';
import { ONTIME_VERSION } from './ONTIME_VERSION';
@@ -20,10 +19,6 @@ Sentry.init({
enabled: import.meta.env.PROD,
});
// TODO: apply code and remove, refs PR #290
const sharedType: TimerType = TimerType.CountDown;
console.log('WIP', sharedType);
root.render(
<StrictMode>
<App />
+2
View File
@@ -35,6 +35,8 @@ $bg-container-onlight: $gray-100;
$box-shadow-l1: rgba(0, 0, 0, 0.15) 0 3px 3px 0;
$box-shadow-l2: rgba(0, 0, 0, 0.15) 0 3px 3px 0;
$modal-note-color: $gray-700;
// interface elements
$border-color-ondark: $white-10;
$element-inner-spacing: 4px;
+29 -9
View File
@@ -1,17 +1,13 @@
const commonStyles = {
letterSpacing: '0.3px',
fontWeight: '400',
borderRadius: '3px',
};
export const ontimeButtonFilled = {
...commonStyles,
background: '#2B5ABC', // $blue-700
color: '#fff', // pure-white
border: '1px solid #2B5ABC', // $blue-700
_hover: {
backgroundColor: '#0A43B9', // $blue-800
border: '1px solid #0A43B9', // $blue-800
_disabled: {
background: '#2B5ABC', // $blue-700
},
},
_active: {
backgroundColor: '#0036A6', // blue-900
@@ -20,7 +16,6 @@ export const ontimeButtonFilled = {
};
export const ontimeButtonOutlined = {
...commonStyles,
backgroundColor: '#2d2d2d', // $gray-1100
color: '#e2e2e2', // $blue-400
border: '1px solid rgba(255, 255, 255, 0.10)', // white-10
@@ -34,7 +29,6 @@ export const ontimeButtonOutlined = {
};
export const ontimeButtonSubtle = {
...commonStyles,
backgroundColor: '#303030', // $gray-1050
color: '#779BE7', // $blue-400
border: '1px solid transparent',
@@ -47,6 +41,32 @@ export const ontimeButtonSubtle = {
},
};
export const ontimeButtonSubtleOnLight = {
backgroundColor: '#ececec', // $gray-100
color: '#595959', // $gray-800
border: '1px solid transparent',
_hover: {
backgroundColor: '#cfcfcf', // $gray-200
},
_active: {
backgroundColor: '#ececec', // $gray-200
borderColor: '#ececec', // $gray-300
},
};
export const ontimeGhostOnLight = {
backgroundColor: 'transparent',
color: '#595959', // $gray-800
_hover: {
color: '#595959', // $gray-800
backgroundColor: '#ececec', // $gray-200
},
_active: {
backgroundColor: 'transparent',
borderColor: '#595959', // $gray-800
},
};
export const ontimeButtonSubtleWhite = {
...ontimeButtonSubtle,
color: '#f6f6f6', // $gray-50
+20
View File
@@ -0,0 +1,20 @@
export const ontimeModal = {
header: {
fontWeight: 400,
letterSpacing: '0.3px',
padding: '8px 16px',
fontSize: '20px',
color: '#202020', // $gray-50
},
dialog: {
borderRadius: '3px',
padding: 0,
minHeight: 'min(500px, 75vh)',
},
body: {
padding: 0,
},
closeButton: {
color: '#202020', // $gray-50
},
};
+14 -3
View File
@@ -1,5 +1,4 @@
export const ontimeSwitch = {
container: { },
track: {
background: '#2d2d2d', // $gray-1100
border: '1px solid transparent',
@@ -8,7 +7,19 @@ export const ontimeSwitch = {
},
_focus: {
border: '1px solid #578AF4', // $blue-500
}
},
},
};
export const lightSwitch = {
track: {
border: '2px solid transparent',
background: '#cfcfcf', // $gray-300
_checked: {
background: `#578AF4`, // $blue-500
},
_focus: {
border: '2px solid #D2DDFF', // $blue-200
},
},
thumb: {},
};
+16
View File
@@ -0,0 +1,16 @@
export const ontimeTab = {
tab: {
fontWeight: 600,
borderBottom: '2px solid transparent',
color: '#9d9d9d', // $gray-500
marginBottom: '-2px',
_selected: {
color: '#101010', // $ui-black
border: 'none',
borderBottom: '2px solid #779BE7', // $blue-400
},
},
tablist: {
borderBottom: '2px solid #ececec', // $gray-100
},
};
+25 -10
View File
@@ -4,24 +4,26 @@ import {
ontimeButtonFilled,
ontimeButtonOutlined,
ontimeButtonSubtle,
ontimeButtonSubtleOnLight,
ontimeButtonSubtleWhite,
ontimeGhostOnLight,
} from './ontimeButton';
import { ontimeCheckboxOnDark } from './ontimeCheckbox';
import { ontimeEditable } from './ontimeEditable';
import { ontimeMenuOnDark } from './ontimeMenu';
import { ontimeModal } from './ontimeModal';
import { ontimeSelect } from './ontimeSelect';
import { ontimeSwitch } from './ontimeSwitch';
import {
ontimeInputFilled,
ontimeTextAreaFilled,
ontimeTextAreaFilledOnLight,
} from './ontimeTextInputs';
import { lightSwitch, ontimeSwitch } from './ontimeSwitch';
import { ontimeTab } from './ontimeTab';
import { ontimeInputFilled, ontimeTextAreaFilled, ontimeTextAreaFilledOnLight } from './ontimeTextInputs';
import { ontimeTooltip } from './ontimeTooltip';
const theme = extendTheme({
components: {
Button: {
baseStyle: {
letterSpacing: '0.3px',
fontWeight: '400',
borderRadius: '3px',
},
variants: {
@@ -29,6 +31,8 @@ const theme = extendTheme({
'ontime-outlined': { ...ontimeButtonOutlined },
'ontime-subtle': { ...ontimeButtonSubtle },
'ontime-subtle-white': { ...ontimeButtonSubtleWhite },
'ontime-subtle-on-light': { ...ontimeButtonSubtleOnLight },
'ontime-ghost-on-light': { ...ontimeGhostOnLight },
},
},
Checkbox: {
@@ -38,7 +42,7 @@ const theme = extendTheme({
},
Editable: {
variants: {
'ontime': { ...ontimeEditable },
ontime: { ...ontimeEditable },
},
},
Input: {
@@ -50,6 +54,16 @@ const theme = extendTheme({
'ontime-filled': { ...ontimeInputFilled },
},
},
Modal: {
variants: {
ontime: { ...ontimeModal },
},
},
Tabs: {
variants: {
ontime: { ...ontimeTab },
},
},
Textarea: {
baseStyle: {
borderRadius: '3px',
@@ -60,16 +74,17 @@ const theme = extendTheme({
},
},
Tooltip: {
baseStyle: { ...ontimeTooltip},
baseStyle: { ...ontimeTooltip },
},
Switch: {
variants: {
'ontime': { ...ontimeSwitch },
ontime: { ...ontimeSwitch },
'ontime-on-light': { ...lightSwitch },
},
},
Select: {
variants: {
'ontime': { ...ontimeSelect },
ontime: { ...ontimeSelect },
},
},
Menu: {