From bdd2a62cee6c9a3750d576945d38974d4813210e Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Sat, 21 Dec 2024 12:59:50 +0100 Subject: [PATCH] feat: allow editing times in cuesheet --- apps/client/src/declarations/declaration.d.ts | 4 + .../cuesheet-table/CuesheetTable.module.scss | 10 -- .../cuesheet/cuesheet-table/CuesheetTable.tsx | 6 +- .../TextLikeInput.module.scss | 7 +- .../{TimeInputDuration.tsx => TimeInput.tsx} | 15 ++- .../cuesheet-table-elements/cuesheetCols.tsx | 94 +++++++++++++------ 6 files changed, 87 insertions(+), 49 deletions(-) rename apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/{TimeInputDuration.tsx => TimeInput.tsx} (84%) diff --git a/apps/client/src/declarations/declaration.d.ts b/apps/client/src/declarations/declaration.d.ts index dd5531a05..70cd162d1 100644 --- a/apps/client/src/declarations/declaration.d.ts +++ b/apps/client/src/declarations/declaration.d.ts @@ -28,6 +28,10 @@ declare module '@tanstack/react-table' { interface TableMeta { handleUpdate: (rowIndex: number, accessor: string, payload: string, isCustom: boolean) => void; handleUpdateTimer: (eventId: string, field: TimeField, payload: string) => void; + options: { + showDelayedTimes: boolean; + hideTableSeconds: boolean; + }; } } diff --git a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss index 5ba435717..620bfbb1f 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss +++ b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss @@ -121,16 +121,6 @@ th { margin: 0 auto; } -.time { - display: flex; - gap: 0.5rem; - align-items: center; - - > * { - @include ellipsis-overflow; - } -} - .delayedTime { color: $ontime-delay-text; font-size: calc(1rem - 2px); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx index 482e75b86..d4bca9cfc 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx @@ -40,7 +40,7 @@ export default function CuesheetTable(props: CuesheetTableProps) { const { updateEvent, updateTimer } = useEventAction(); const { selectedEventId } = useSelectedEventId(); - const { followSelected, hideDelays, hidePast } = useCuesheetOptions(); + const { followSelected, hideDelays, hidePast, showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); const { columnVisibility, columnOrder, columnSizing, resetColumnOrder, setColumnVisibility, setColumnSizing } = useColumnManager(columns); @@ -86,6 +86,10 @@ export default function CuesheetTable(props: CuesheetTableProps) { // the timer element already contains logic to avoid submitting a unchanged value updateTimer(eventId, field, payload, true); }, + options: { + showDelayedTimes, + hideTableSeconds, + }, }, }); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss index 80fe80196..f239d38c9 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss @@ -1,12 +1,15 @@ /* element attempts matching input styles */ .textInput { - padding-top: 0.25rem; height: 2rem; background-color: transparent; border-radius: 3px; + display: flex; + align-items: center; + gap: 0.25rem; + &.muted { - color: $label-gray; + color: $muted-gray; } &:hover { diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInputDuration.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx similarity index 84% rename from apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInputDuration.tsx rename to apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx index 3a636cd7a..48fe7151e 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInputDuration.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx @@ -1,8 +1,6 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { memo, PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react'; import { millisToString, parseUserTime } from 'ontime-utils'; -import { formatDuration } from '../../../../common/utils/time'; - import SingleLineCell from './SingleLineCell'; import TextLikeInput from './TextLikeInput'; @@ -12,8 +10,10 @@ interface TimeInputDurationProps { onSubmit: (value: string) => void; } -export default function TimeInputDuration(props: TimeInputDurationProps) { - const { initialValue, lockedValue, onSubmit } = props; +export default memo(TimeInputDuration); + +function TimeInputDuration(props: PropsWithChildren) { + const { initialValue, lockedValue, onSubmit, children } = props; const [isEditing, setIsEditing] = useState(false); const [value, setValue] = useState(initialValue); @@ -47,7 +47,6 @@ export default function TimeInputDuration(props: TimeInputDurationProps) { return; } - // TODO: is this valid in the duration input? // we dont know the values in the rundown, escalate to handler if (newValue.startsWith('p') || newValue.startsWith('+')) { onSubmit(newValue); @@ -71,8 +70,6 @@ export default function TimeInputDuration(props: TimeInputDurationProps) { [initialValue, lockedValue, onSubmit], ); - // duration times have a special format - const duration = formatDuration(value, false); const timeString = millisToString(value); return isEditing ? ( @@ -85,7 +82,7 @@ export default function TimeInputDuration(props: TimeInputDurationProps) { /> ) : ( - {duration} + {children} ); } diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/cuesheetCols.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/cuesheetCols.tsx index ac755d101..128627791 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/cuesheetCols.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/cuesheetCols.tsx @@ -1,45 +1,85 @@ import { useCallback } from 'react'; import { CellContext, ColumnDef } from '@tanstack/react-table'; import { CustomFields, isOntimeEvent, OntimeEvent, OntimeRundownEntry, TimeStrategy } from 'ontime-types'; +import { millisToString, removeSeconds } from 'ontime-utils'; import DelayIndicator from '../../../../common/components/delay-indicator/DelayIndicator'; -import RunningTime from '../../../../features/viewers/common/running-time/RunningTime'; -import { useCuesheetOptions } from '../../cuesheet.options'; +import { formatDuration } from '../../../../common/utils/time'; import MultiLineCell from './MultiLineCell'; import SingleLineCell from './SingleLineCell'; -import TimeInputDuration from './TimeInputDuration'; +import TimeInput from './TimeInput'; -import style from '../CuesheetTable.module.scss'; +function MakeStart({ getValue, row, table }: CellContext) { + if (!table.options.meta) { + return null; + } -function MakeTimer({ getValue, row: { original } }: CellContext) { - const { showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); - const cellValue = (getValue() as number | null) ?? 0; - const delayValue = (original as OntimeEvent)?.delay ?? 0; + const { handleUpdateTimer } = table.options.meta; + const { showDelayedTimes, hideTableSeconds } = table.options.meta.options; + + const update = (newValue: string) => handleUpdateTimer(row.original.id, 'timeStart', newValue); + + const startTime = getValue() as number; + const isStartLocked = (row.original as OntimeEvent).linkStart === null; + const delayValue = (row.original as OntimeEvent)?.delay ?? 0; + + let formattedTime = millisToString(startTime); + if (hideTableSeconds) { + formattedTime = removeSeconds(formattedTime); + } return ( - - - - {delayValue !== 0 && showDelayedTimes && ( - - )} - + + {formattedTime} + {delayValue !== 0 && showDelayedTimes && } + + ); +} + +function MakeEnd({ getValue, row, table }: CellContext) { + if (!table.options.meta) { + return null; + } + + const { handleUpdateTimer } = table.options.meta; + const { hideTableSeconds } = table.options.meta.options; + + const update = (newValue: string) => handleUpdateTimer(row.original.id, 'timeEnd', newValue); + + const endTime = getValue() as number; + const isEndLocked = (row.original as OntimeEvent).timeStrategy === TimeStrategy.LockEnd; + + let formattedTime = millisToString(endTime); + if (hideTableSeconds) { + formattedTime = removeSeconds(formattedTime); + } + + return ( + + {formattedTime} + ); } function MakeDuration({ getValue, row, table }: CellContext) { - const update = useCallback( - (newValue: string) => { - table.options.meta?.handleUpdateTimer(row.original.id, 'duration', newValue); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable - [row.original.id], - ); + if (!table.options.meta) { + return null; + } - const duration = (getValue() as number | null) ?? 0; - const isDurationLocked = (row.original as OntimeEvent)?.timeStrategy === TimeStrategy.LockDuration; - return ; + const { handleUpdateTimer } = table.options.meta; + + const update = (newValue: string) => handleUpdateTimer(row.original.id, 'duration', newValue); + + const duration = getValue() as number; + const isDurationLocked = (row.original as OntimeEvent).timeStrategy === TimeStrategy.LockDuration; + const formattedDuration = formatDuration(duration, false); + + return ( + + {formattedDuration} + + ); } function MakeMultiLineField({ row, column, table }: CellContext) { @@ -123,7 +163,7 @@ export function makeCuesheetColumns(customFields: CustomFields): ColumnDef