mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 11:23:50 +00:00
feat: allow editing times in cuesheet
This commit is contained in:
committed by
Carlos Valente
parent
b38a7e5c92
commit
bdd2a62cee
@@ -28,6 +28,10 @@ declare module '@tanstack/react-table' {
|
||||
interface TableMeta<TData extends RowData> {
|
||||
handleUpdate: (rowIndex: number, accessor: string, payload: string, isCustom: boolean) => void;
|
||||
handleUpdateTimer: (eventId: string, field: TimeField, payload: string) => void;
|
||||
options: {
|
||||
showDelayedTimes: boolean;
|
||||
hideTableSeconds: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+5
-2
@@ -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 {
|
||||
|
||||
+6
-9
@@ -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<TimeInputDurationProps>) {
|
||||
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) {
|
||||
/>
|
||||
) : (
|
||||
<TextLikeInput onClick={handleFakeFocus} onFocus={handleFakeFocus} muted={!lockedValue}>
|
||||
{duration}
|
||||
{children}
|
||||
</TextLikeInput>
|
||||
);
|
||||
}
|
||||
+67
-27
@@ -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<OntimeRundownEntry, unknown>) {
|
||||
if (!table.options.meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function MakeTimer({ getValue, row: { original } }: CellContext<OntimeRundownEntry, unknown>) {
|
||||
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 (
|
||||
<span className={style.time}>
|
||||
<DelayIndicator delayValue={delayValue} />
|
||||
<RunningTime value={cellValue} hideSeconds={hideTableSeconds} />
|
||||
{delayValue !== 0 && showDelayedTimes && (
|
||||
<RunningTime className={style.delayedTime} value={cellValue + delayValue} hideSeconds={hideTableSeconds} />
|
||||
)}
|
||||
</span>
|
||||
<TimeInput initialValue={startTime} onSubmit={update} lockedValue={isStartLocked}>
|
||||
{formattedTime}
|
||||
{delayValue !== 0 && showDelayedTimes && <DelayIndicator delayValue={delayValue} />}
|
||||
</TimeInput>
|
||||
);
|
||||
}
|
||||
|
||||
function MakeEnd({ getValue, row, table }: CellContext<OntimeRundownEntry, unknown>) {
|
||||
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 (
|
||||
<TimeInput initialValue={endTime} onSubmit={update} lockedValue={isEndLocked}>
|
||||
{formattedTime}
|
||||
</TimeInput>
|
||||
);
|
||||
}
|
||||
|
||||
function MakeDuration({ getValue, row, table }: CellContext<OntimeRundownEntry, unknown>) {
|
||||
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 <TimeInputDuration initialValue={duration} onSubmit={update} lockedValue={isDurationLocked} />;
|
||||
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 (
|
||||
<TimeInput initialValue={duration} onSubmit={update} lockedValue={isDurationLocked}>
|
||||
{formattedDuration}
|
||||
</TimeInput>
|
||||
);
|
||||
}
|
||||
|
||||
function MakeMultiLineField({ row, column, table }: CellContext<OntimeRundownEntry, unknown>) {
|
||||
@@ -123,7 +163,7 @@ export function makeCuesheetColumns(customFields: CustomFields): ColumnDef<Ontim
|
||||
accessorKey: 'timeStart',
|
||||
id: 'timeStart',
|
||||
header: 'Start',
|
||||
cell: MakeTimer,
|
||||
cell: MakeStart,
|
||||
size: 75,
|
||||
minSize: 75,
|
||||
},
|
||||
@@ -131,7 +171,7 @@ export function makeCuesheetColumns(customFields: CustomFields): ColumnDef<Ontim
|
||||
accessorKey: 'timeEnd',
|
||||
id: 'timeEnd',
|
||||
header: 'End',
|
||||
cell: MakeTimer,
|
||||
cell: MakeEnd,
|
||||
size: 75,
|
||||
minSize: 75,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user