refactor: format times in editor based on project time format

This commit is contained in:
Brian Rodgers
2026-03-29 15:51:14 -05:00
committed by Carlos Valente
parent b19e9d23ba
commit 9e771a2f24
10 changed files with 34 additions and 25 deletions
@@ -1,7 +1,8 @@
import { millisToString, parseUserTime } from 'ontime-utils'; import { millisToString, parseUserTime } from 'ontime-utils';
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react'; import { FocusEvent, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { cx } from '../../../utils/styleUtils'; import { cx } from '../../../utils/styleUtils';
import { formatTime, getFormatFromSettings } from '../../../utils/time';
import Input from '../input/Input'; import Input from '../input/Input';
import style from './TimeInput.module.scss'; import style from './TimeInput.module.scss';
@@ -16,6 +17,7 @@ interface TimeInputProps<T extends string> {
align?: 'left' | 'center'; align?: 'left' | 'center';
delayed?: boolean; delayed?: boolean;
className?: string; className?: string;
shouldFormat?: boolean;
} }
export default function TimeInput<T extends string>({ export default function TimeInput<T extends string>({
@@ -28,10 +30,12 @@ export default function TimeInput<T extends string>({
align = 'center', align = 'center',
delayed, delayed,
className, className,
shouldFormat,
}: TimeInputProps<T>) { }: TimeInputProps<T>) {
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
const [value, setValue] = useState<string>(''); const [value, setValue] = useState<string>('');
const ignoreChange = useRef(false); const ignoreChange = useRef(false);
const formatAs12Hr = useMemo(() => shouldFormat && getFormatFromSettings() === '12', [shouldFormat]);
/** /**
* @description Resets input value to given * @description Resets input value to given
@@ -39,10 +43,12 @@ export default function TimeInput<T extends string>({
const resetValue = useCallback(() => { const resetValue = useCallback(() => {
if (typeof time !== 'number' || isNaN(time)) { if (typeof time !== 'number' || isNaN(time)) {
setValue('00:00:00'); setValue('00:00:00');
} else if (shouldFormat) {
setValue(formatTime(time));
} else { } else {
setValue(millisToString(time)); setValue(millisToString(time));
} }
}, [time]); }, [time, shouldFormat]);
/** /**
* @description Selects input text on focus * @description Selects input text on focus
@@ -139,9 +145,10 @@ export default function TimeInput<T extends string>({
onBlur={onBlurHandler} onBlur={onBlurHandler}
onKeyDown={onKeyDownHandler} onKeyDown={onKeyDownHandler}
value={value} value={value}
maxLength={8} maxLength={formatAs12Hr ? 11 : 8}
style={{ style={{
textAlign: align, textAlign: align,
width: formatAs12Hr ? '7.5em' : '6.5em',
}} }}
/> />
); );
+1 -1
View File
@@ -42,7 +42,7 @@ function getFormatFromParams() {
* Gets the format options from the applicaton settings * Gets the format options from the applicaton settings
* @returns a string equivalent to the format, ie: hh:mm:ss a or HH:mm:ss * @returns a string equivalent to the format, ie: hh:mm:ss a or HH:mm:ss
*/ */
function getFormatFromSettings(): TimeFormat { export function getFormatFromSettings(): TimeFormat {
const settings: Settings | undefined = ontimeQueryClient.getQueryData(APP_SETTINGS); const settings: Settings | undefined = ontimeQueryClient.getQueryData(APP_SETTINGS);
return settings?.timeFormat ?? '24'; return settings?.timeFormat ?? '24';
} }
@@ -94,7 +94,7 @@ export default function GeneralSettings() {
{submitError && <Panel.Error>{submitError}</Panel.Error>} {submitError && <Panel.Error>{submitError}</Panel.Error>}
<Panel.Divider /> <Panel.Divider />
<Panel.Section> <Panel.Section>
<Info>Changes to the time format and views language do not affect the editor view</Info> <Info>Changes to the views language does not affect the editor view</Info>
<Panel.Loader isLoading={isLoading} /> <Panel.Loader isLoading={isLoading} />
<Panel.ListGroup> <Panel.ListGroup>
<Panel.ListItem> <Panel.ListItem>
@@ -69,7 +69,7 @@ export default function QuickStart({ isOpen, onClose }: QuickStartProps) {
<Panel.ListItem> <Panel.ListItem>
<Panel.Field <Panel.Field
title='Time format' title='Time format'
description='Default time format to show in views 12 / 24 hours (does not affect editor)' description='Default time format to show in views 12 / 24 hours'
error={errors.settings?.timeFormat?.message} error={errors.settings?.timeFormat?.message}
/> />
<Select <Select
@@ -34,10 +34,10 @@ function OverviewPlanning() {
<> <>
<div className={style.inline}> <div className={style.inline}>
<TitleOverview /> <TitleOverview />
<StartTimesPlanning /> <StartTimesPlanning shouldFormat />
<PlanningStats /> <PlanningStats />
</div> </div>
<ClockOverview /> <ClockOverview shouldFormat />
</> </>
); );
} }
@@ -46,12 +46,12 @@ function OverviewTracking() {
return ( return (
<> <>
<div className={style.inline}> <div className={style.inline}>
<StartTimesRuntime /> <StartTimesRuntime shouldFormat />
<ProgressOverview /> <ProgressOverview />
<OffsetOverview /> <OffsetOverview />
</div> </div>
<MetadataTimes /> <MetadataTimes />
<ClockOverview /> <ClockOverview shouldFormat />
</> </>
); );
} }
@@ -61,12 +61,12 @@ function OverviewControl() {
<> <>
<TitleOverview /> <TitleOverview />
<div className={style.inline}> <div className={style.inline}>
<StartTimesRuntime /> <StartTimesRuntime shouldFormat />
<ProgressOverview /> <ProgressOverview />
<OffsetOverview /> <OffsetOverview />
</div> </div>
<MetadataTimes /> <MetadataTimes />
<ClockOverview /> <ClockOverview shouldFormat />
</> </>
); );
} }
@@ -8,7 +8,8 @@ import AppLink from '../../../common/components/link/app-link/AppLink';
import { useEntryActionsContext } from '../../../common/context/EntryActionsContext'; import { useEntryActionsContext } from '../../../common/context/EntryActionsContext';
import useCustomFields from '../../../common/hooks-query/useCustomFields'; import useCustomFields from '../../../common/hooks-query/useCustomFields';
import { getOffsetState } from '../../../common/utils/offset'; import { getOffsetState } from '../../../common/utils/offset';
import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils'; import { cx, enDash } from '../../../common/utils/styleUtils';
import { formatTime } from '../../../common/utils/time';
import TextLikeInput from '../../../views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput'; import TextLikeInput from '../../../views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput';
import EntryEditorCustomFields from './composite/EventEditorCustomFields'; import EntryEditorCustomFields from './composite/EventEditorCustomFields';
import EventTextArea from './composite/EventTextArea'; import EventTextArea from './composite/EventTextArea';
@@ -60,13 +61,13 @@ export default function GroupEditor({ group }: GroupEditorProps) {
<div> <div>
<Editor.Label>First event start</Editor.Label> <Editor.Label>First event start</Editor.Label>
<TextLikeInput className={style.textLikeInput} disabled> <TextLikeInput className={style.textLikeInput} disabled>
{millisToString(group.timeStart, { fallback: timerPlaceholder })} {formatTime(group.timeStart)}
</TextLikeInput> </TextLikeInput>
</div> </div>
<div> <div>
<Editor.Label>Last event end</Editor.Label> <Editor.Label>Last event end</Editor.Label>
<TextLikeInput className={style.textLikeInput} disabled> <TextLikeInput className={style.textLikeInput} disabled>
{millisToString(group.timeEnd, { fallback: timerPlaceholder })} {formatTime(group.timeEnd)}
</TextLikeInput> </TextLikeInput>
</div> </div>
<div> <div>
@@ -1,5 +1,5 @@
import { EndAction, TimeStrategy, TimerType } from 'ontime-types'; import { EndAction, TimeStrategy, TimerType } from 'ontime-types';
import { millisToString, parseUserTime } from 'ontime-utils'; import { parseUserTime } from 'ontime-utils';
import { memo } from 'react'; import { memo } from 'react';
import { IoInformationCircle } from 'react-icons/io5'; import { IoInformationCircle } from 'react-icons/io5';
@@ -10,6 +10,7 @@ import Switch from '../../../../common/components/switch/Switch';
import Tooltip from '../../../../common/components/tooltip/Tooltip'; import Tooltip from '../../../../common/components/tooltip/Tooltip';
import { useEntryActionsContext } from '../../../../common/context/EntryActionsContext'; import { useEntryActionsContext } from '../../../../common/context/EntryActionsContext';
import { millisToDelayString } from '../../../../common/utils/dateConfig'; import { millisToDelayString } from '../../../../common/utils/dateConfig';
import { formatTime } from '../../../../common/utils/time';
import TimeInputFlow from '../../time-input-flow/TimeInputFlow'; import TimeInputFlow from '../../time-input-flow/TimeInputFlow';
import style from '../EntryEditor.module.scss'; import style from '../EntryEditor.module.scss';
@@ -68,9 +69,7 @@ function EventEditorTimes({
const hasDelay = delay !== 0; const hasDelay = delay !== 0;
const delayLabel = hasDelay const delayLabel = hasDelay
? `Event is ${millisToDelayString(delay, 'expanded')}. New schedule ${millisToString( ? `Event is ${millisToDelayString(delay, 'expanded')}. New schedule ${formatTime(timeStart + delay)}${formatTime(timeEnd + delay)}`
timeStart + delay,
)}${millisToString(timeEnd + delay)}`
: ''; : '';
return ( return (
@@ -1,7 +1,7 @@
import { useSortable } from '@dnd-kit/sortable'; import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities'; import { CSS } from '@dnd-kit/utilities';
import { EntryId, OntimeGroup } from 'ontime-types'; import { EntryId, OntimeGroup } from 'ontime-types';
import { MILLIS_PER_MINUTE, millisToString } from 'ontime-utils'; import { MILLIS_PER_MINUTE } from 'ontime-utils';
import { MouseEvent, useRef } from 'react'; import { MouseEvent, useRef } from 'react';
import { import {
IoChevronDown, IoChevronDown,
@@ -19,8 +19,8 @@ import { useContextMenu } from '../../../common/hooks/useContextMenu';
import { useEntryCopy } from '../../../common/stores/entryCopyStore'; import { useEntryCopy } from '../../../common/stores/entryCopyStore';
import { deviceMod } from '../../../common/utils/deviceUtils'; import { deviceMod } from '../../../common/utils/deviceUtils';
import { getOffsetState } from '../../../common/utils/offset'; import { getOffsetState } from '../../../common/utils/offset';
import { cx, getAccessibleColour, timerPlaceholder } from '../../../common/utils/styleUtils'; import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
import { formatDuration } from '../../../common/utils/time'; import { formatDuration, formatTime } from '../../../common/utils/time';
import TitleEditor from '../common/TitleEditor'; import TitleEditor from '../common/TitleEditor';
import { canDrop } from '../rundown.utils'; import { canDrop } from '../rundown.utils';
import { useEventSelection } from '../useEventSelection'; import { useEventSelection } from '../useEventSelection';
@@ -168,11 +168,11 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }:
</div> </div>
<div className={style.metaEntry}> <div className={style.metaEntry}>
<div className={style.metaLabel}>Start</div> <div className={style.metaLabel}>Start</div>
<div>{millisToString(data.timeStart, { fallback: timerPlaceholder })}</div> <div>{formatTime(data.timeStart)}</div>
</div> </div>
<div className={style.metaEntry}> <div className={style.metaEntry}>
<div className={style.metaLabel}>End</div> <div className={style.metaLabel}>End</div>
<div>{millisToString(data.timeEnd, { fallback: timerPlaceholder })}</div> <div>{formatTime(data.timeEnd)}</div>
</div> </div>
<div className={style.metaEntry}> <div className={style.metaEntry}>
<div className={style.metaLabel}>Duration</div> <div className={style.metaLabel}>Duration</div>
@@ -77,6 +77,7 @@ function TimeInputFlow({
placeholder='Start' placeholder='Start'
align='left' align='left'
disabled={linkStart} disabled={linkStart}
shouldFormat
/> />
<Tooltip <Tooltip
text='Link start to previous end' text='Link start to previous end'
@@ -99,6 +100,7 @@ function TimeInputFlow({
placeholder='End' placeholder='End'
align='left' align='left'
disabled={isLockedDuration} disabled={isLockedDuration}
shouldFormat
/> />
<Tooltip <Tooltip
text='Lock end' text='Lock end'
@@ -10,7 +10,7 @@
} }
input { input {
max-width: 6.5em; max-width: fit-content;
border-radius: $component-border-radius-md 0 0 $component-border-radius-md; border-radius: $component-border-radius-md 0 0 $component-border-radius-md;
border: none; border: none;
padding-right: 0; padding-right: 0;