diff --git a/apps/client/src/common/components/input/colour-input/SwatchSelect.tsx b/apps/client/src/common/components/input/colour-input/SwatchSelect.tsx index 07682b11c..84c6f9a57 100644 --- a/apps/client/src/common/components/input/colour-input/SwatchSelect.tsx +++ b/apps/client/src/common/components/input/colour-input/SwatchSelect.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react'; -import { EventEditorSubmitActions } from '../../../../features/event-editor/EventEditor'; +import { TitleActions } from '../../../../features/event-editor/composite/EventEditorTitles'; import Swatch from './Swatch'; @@ -8,8 +8,8 @@ import style from './SwatchSelect.module.scss'; interface ColourInputProps { value: string; - name: EventEditorSubmitActions; - handleChange: (newValue: EventEditorSubmitActions, name: string) => void; + name: TitleActions; + handleChange: (newValue: TitleActions, name: string) => void; } const colours = [ diff --git a/apps/client/src/features/editors/Editor.module.scss b/apps/client/src/features/editors/Editor.module.scss index b538001de..bd427b07e 100644 --- a/apps/client/src/features/editors/Editor.module.scss +++ b/apps/client/src/features/editors/Editor.module.scss @@ -167,6 +167,7 @@ $playback-width: 450px; background-color: $gray-1200; padding: 8px; border-left: 1px solid $white-10; + border-radius: 0 8px 0 0; } } diff --git a/apps/client/src/features/event-editor/EventEditor.tsx b/apps/client/src/features/event-editor/EventEditor.tsx index 1fe8a70ed..d0ac458b6 100644 --- a/apps/client/src/features/event-editor/EventEditor.tsx +++ b/apps/client/src/features/event-editor/EventEditor.tsx @@ -1,35 +1,23 @@ -import { useCallback, useEffect, useState } from 'react'; -import { Select, Switch } from '@chakra-ui/react'; -import { EndAction, OntimeEvent, TimerType } from 'ontime-types'; -import { millisToString } from 'ontime-utils'; +import { useEffect, useState } from 'react'; +import { OntimeEvent } from 'ontime-types'; import CopyTag from '../../common/components/copy-tag/CopyTag'; -import SwatchSelect from '../../common/components/input/colour-input/SwatchSelect'; -import TimeInput from '../../common/components/input/time-input/TimeInput'; -import { useEventAction } from '../../common/hooks/useEventAction'; import useRundown from '../../common/hooks-query/useRundown'; import { useEventEditorStore } from '../../common/stores/eventEditor'; -import { useEmitLog } from '../../common/stores/logger'; -import { millisToMinutes } from '../../common/utils/dateConfig'; import getDelayTo from '../../common/utils/getDelayTo'; -import { calculateDuration, TimeEntryField, validateEntry } from '../../common/utils/timesManager'; -import CountedTextArea from './composite/CountedTextArea'; -import CountedTextInput from './composite/CountedTextInput'; +import EventEditorTimes from './composite/EventEditorTimes'; +import EventEditorTitles from './composite/EventEditorTitles'; import style from './EventEditor.module.scss'; -export type EventEditorSubmitActions = keyof OntimeEvent | 'durationOverride'; +export type EventEditorSubmitActions = keyof OntimeEvent; -// Todo: add previous end to TimeInput fields export default function EventEditor() { const { openId } = useEventEditorStore(); const { data } = useRundown(); - const { emitError } = useEmitLog(); - const { updateEvent } = useEventAction(); const [event, setEvent] = useState(null); const [delay, setDelay] = useState(0); - const [warning, setWarnings] = useState({ start: '', end: '', duration: '' }); useEffect(() => { if (!data || !openId) { @@ -46,83 +34,10 @@ export default function EventEditor() { } }, [data, event, openId]); - const handleSubmit = useCallback( - (field: EventEditorSubmitActions, value: string | number) => { - if (event === null) { - return; - } - const newEventData: Partial = { id: event.id }; - switch (field) { - case 'durationOverride': { - // duration defines timeEnd - newEventData.duration = value as number; - newEventData.timeEnd = event.timeStart + (value as number); - break; - } - case 'timeStart': { - newEventData.duration = calculateDuration(value as number, event.timeEnd); - newEventData.timeStart = value as number; - break; - } - case 'timeEnd': { - newEventData.duration = calculateDuration(event.timeStart, value as number); - newEventData.timeEnd = value as number; - break; - } - default: { - if (field in event) { - // create object with new field - newEventData[field] = value; - break; - } else { - emitError(`Unknown field: ${field}`); - return; - } - } - } - updateEvent(newEventData); - }, - [emitError, event, updateEvent], - ); - - const timerValidationHandler = useCallback( - (entry: TimeEntryField, val: number) => { - if (!event?.timeStart) { - return true; - } - const valid = validateEntry(entry, val, event.timeStart, event.timeEnd); - setWarnings((prev) => ({ ...prev, ...valid.warnings })); - return valid.value; - }, - [event?.timeStart, event?.timeEnd], - ); - - const handleChange = useCallback( - (field: string, value: string) => { - updateEvent({ id: event.id, [field]: value }); - }, - [event, updateEvent], - ); - - const togglePublic = useCallback( - (currentValue: boolean) => { - if (!event) { - return; - } - updateEvent({ id: event.id, isPublic: !currentValue }); - }, - [event, updateEvent], - ); - if (!event) { return Loading...; } - const delayed = delay !== 0; - const addedTime = delayed ? `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))} minutes` : null; - const newStart = delayed ? `New start ${millisToString(event.timeStart + delay)}` : null; - const newEnd = delayed ? `New end ${millisToString(event.timeEnd + delay)}` : null; - return (
@@ -132,103 +47,24 @@ export default function EventEditor() {
{`/ontime/gotoid/${event.id}`}
-
-
- - - - - - -
-
- - - - - - -
-
-
-
- - - -
-
-
- -
- -
-
- -
-
+ +
); } diff --git a/apps/client/src/features/event-editor/EventEditorExport.tsx b/apps/client/src/features/event-editor/EventEditorExport.tsx index ec875278a..304315872 100644 --- a/apps/client/src/features/event-editor/EventEditorExport.tsx +++ b/apps/client/src/features/event-editor/EventEditorExport.tsx @@ -35,4 +35,3 @@ const EventEditorExport = () => { }; export default memo(EventEditorExport); - diff --git a/apps/client/src/features/event-editor/composite/CountedTextArea.tsx b/apps/client/src/features/event-editor/composite/CountedTextArea.tsx index 5e6fd99e9..935b304f9 100644 --- a/apps/client/src/features/event-editor/composite/CountedTextArea.tsx +++ b/apps/client/src/features/event-editor/composite/CountedTextArea.tsx @@ -2,15 +2,16 @@ import { useCallback } from 'react'; import { Textarea } from '@chakra-ui/react'; import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput'; -import { EventEditorSubmitActions } from '../EventEditor'; + +import { TitleActions } from './EventEditorTitles'; import style from '../EventEditor.module.scss'; interface CountedTextAreaProps { - field: EventEditorSubmitActions; + field: TitleActions; label: string; initialValue: string; - submitHandler: (field: EventEditorSubmitActions, value: string) => void; + submitHandler: (field: TitleActions, value: string) => void; } export default function CountedTextArea(props: CountedTextAreaProps) { diff --git a/apps/client/src/features/event-editor/composite/CountedTextInput.tsx b/apps/client/src/features/event-editor/composite/CountedTextInput.tsx index 3046e5a0b..1014e10c3 100644 --- a/apps/client/src/features/event-editor/composite/CountedTextInput.tsx +++ b/apps/client/src/features/event-editor/composite/CountedTextInput.tsx @@ -2,15 +2,16 @@ import { useCallback } from 'react'; import { Input } from '@chakra-ui/react'; import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput'; -import { EventEditorSubmitActions } from '../EventEditor'; + +import { TitleActions } from './EventEditorTitles'; import style from '../EventEditor.module.scss'; interface CountedTextInputProps { - field: EventEditorSubmitActions; + field: TitleActions; label: string; initialValue: string; - submitHandler: (field: EventEditorSubmitActions, value: string) => void; + submitHandler: (field: TitleActions, value: string) => void; } export default function CountedTextInput(props: CountedTextInputProps) { diff --git a/apps/client/src/features/event-editor/composite/EventEditorTimes.tsx b/apps/client/src/features/event-editor/composite/EventEditorTimes.tsx new file mode 100644 index 000000000..b2f8dd834 --- /dev/null +++ b/apps/client/src/features/event-editor/composite/EventEditorTimes.tsx @@ -0,0 +1,154 @@ +import { memo, useState } from 'react'; +import { Select, Switch } from '@chakra-ui/react'; +import { EndAction, OntimeEvent, TimerType } from 'ontime-types'; +import { millisToString } from 'ontime-utils'; + +import TimeInput from '../../../common/components/input/time-input/TimeInput'; +import { useEventAction } from '../../../common/hooks/useEventAction'; +import { millisToMinutes } from '../../../common/utils/dateConfig'; +import { calculateDuration, TimeEntryField, validateEntry } from '../../../common/utils/timesManager'; + +import style from '../EventEditor.module.scss'; + +interface EventEditorTimesProps { + eventId: string; + timeStart: number; + timeEnd: number; + duration: number; + delay: number; + isPublic: boolean; + endAction: EndAction; + timerType: TimerType; +} + +type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride' | 'timerType' | 'endAction' | 'isPublic'; + +// Todo: add previous end to TimeInput fields +const EventEditorTimes = (props: EventEditorTimesProps) => { + const { eventId, timeStart, timeEnd, duration, delay, isPublic, endAction, timerType } = props; + const { updateEvent } = useEventAction(); + + const [warning, setWarnings] = useState({ start: '', end: '', duration: '' }); + + const timerValidationHandler = (entry: TimeEntryField, val: number) => { + const valid = validateEntry(entry, val, timeStart, timeEnd); + setWarnings((prev) => ({ ...prev, ...valid.warnings })); + return valid.value; + }; + + const handleSubmit = (field: TimeActions, value: number | string | boolean) => { + const newEventData: Partial = { id: eventId }; + switch (field) { + case 'durationOverride': { + // duration defines timeEnd + newEventData.duration = value as number; + newEventData.timeEnd = timeStart + (value as number); + break; + } + case 'timeStart': { + newEventData.duration = calculateDuration(value as number, timeEnd); + newEventData.timeStart = value as number; + break; + } + case 'timeEnd': { + newEventData.duration = calculateDuration(timeStart, value as number); + newEventData.timeEnd = value as number; + break; + } + case 'isPublic': { + updateEvent({ id: eventId, isPublic: !(value as boolean) }); + break; + } + default: { + if (field === 'timerType' || field === 'endAction') { + // @ts-expect-error -- not sure how to typecheck here + newEventData[field as keyof OntimeEvent] = value as string; + } else { + return; + } + } + } + updateEvent(newEventData); + }; + + const delayed = delay !== 0; + const addedTime = delayed ? `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))} minutes` : null; + const newStart = delayed ? `New start ${millisToString(timeStart + delay)}` : null; + const newEnd = delayed ? `New end ${millisToString(timeEnd + delay)}` : null; + + return ( +
+
+ + + + + + +
+
+ + + + + + +
+
+ ); +}; + +export default memo(EventEditorTimes); diff --git a/apps/client/src/features/event-editor/composite/EventEditorTitles.tsx b/apps/client/src/features/event-editor/composite/EventEditorTitles.tsx new file mode 100644 index 000000000..49a2ace3f --- /dev/null +++ b/apps/client/src/features/event-editor/composite/EventEditorTitles.tsx @@ -0,0 +1,50 @@ +import { memo } from 'react'; + +import SwatchSelect from '../../../common/components/input/colour-input/SwatchSelect'; +import { useEventAction } from '../../../common/hooks/useEventAction'; + +import CountedTextArea from './CountedTextArea'; +import CountedTextInput from './CountedTextInput'; + +import style from '../EventEditor.module.scss'; + +interface EventEditorTitlesProps { + eventId: string; + title: string; + presenter: string; + subtitle: string; + note: string; + colour: string; +} + +export type TitleActions = 'title' | 'presenter' | 'subtitle' | 'note' | 'colour'; + +const EventEditorTitles = (props: EventEditorTitlesProps) => { + const { eventId, title, presenter, subtitle, note, colour } = props; + const { updateEvent } = useEventAction(); + + const handleSubmit = (field: TitleActions, value: string) => { + updateEvent({ id: eventId, [field]: value }); + }; + + return ( +
+
+ + + +
+
+
+ +
+ +
+
+ +
+
+ ); +}; + +export default memo(EventEditorTitles);