mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-10 09:53:48 +00:00
refactor: consistent time formatting for 12h mode
This commit is contained in:
committed by
Carlos Valente
parent
50c91f976b
commit
0e3b3bcf9a
@@ -202,6 +202,9 @@ export const useStartTimesOverview = createSelector((state: RuntimeStore) => ({
|
||||
plannedStart: state.rundown.plannedStart,
|
||||
actualStart: state.rundown.actualStart,
|
||||
plannedEnd: state.rundown.plannedEnd,
|
||||
}));
|
||||
|
||||
export const useRundownExpectedEnd = createSelector((state: RuntimeStore) => ({
|
||||
expectedEnd: state.offset.expectedRundownEnd,
|
||||
}));
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ describe('formatTime()', () => {
|
||||
|
||||
describe('formatDuration()', () => {
|
||||
it('formats durations correctly', () => {
|
||||
expect(formatDuration(0)).toBe('0h 0m');
|
||||
expect(formatDuration(-5000)).toBe('0h 0m');
|
||||
expect(formatDuration(0)).toBe('0m');
|
||||
expect(formatDuration(-5000)).toBe('0m');
|
||||
expect(formatDuration(MILLIS_PER_MINUTE)).toBe('1m');
|
||||
expect(formatDuration(6 * MILLIS_PER_MINUTE + 11 * MILLIS_PER_SECOND)).toBe('6m');
|
||||
expect(formatDuration(MILLIS_PER_MINUTE * 10)).toBe('10m');
|
||||
|
||||
@@ -112,7 +112,7 @@ export const formatTime = (
|
||||
export function formatDuration(duration: number, hideSeconds = true): string {
|
||||
// durations should never be negative, we handle it here to flag if there is an issue in future
|
||||
if (duration <= 0) {
|
||||
return '0h 0m';
|
||||
return '0m';
|
||||
}
|
||||
|
||||
const hours = Math.floor(duration / MILLIS_PER_HOUR);
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
height: 1.5rem;
|
||||
display: flex;
|
||||
gap: $section-spacing;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
@@ -70,6 +69,7 @@
|
||||
.time {
|
||||
color: $section-white;
|
||||
font-size: $text-body-size;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.rolltag {
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
.plannedStart {
|
||||
font-size: 1.5rem;
|
||||
margin-right: 0.5rem;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.timeUntil {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { memo, RefObject, SyntheticEvent } from 'react';
|
||||
import { useLongPress } from '@mantine/hooks';
|
||||
import { MILLIS_PER_MINUTE, MILLIS_PER_SECOND, millisToString } from 'ontime-utils';
|
||||
import { MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
||||
|
||||
import DelayIndicator from '../../../common/components/delay-indicator/DelayIndicator';
|
||||
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||
import { formatDuration, useTimeUntilExpectedStart } from '../../../common/utils/time';
|
||||
import { formatDuration, formatTime, useTimeUntilExpectedStart } from '../../../common/utils/time';
|
||||
import RunningTime from '../../viewers/common/running-time/RunningTime';
|
||||
import SuperscriptPeriod from '../../viewers/common/superscript-time/SuperscriptPeriod';
|
||||
import type { EditEvent, Subscribed } from '../operator.types';
|
||||
|
||||
import style from './OperatorEvent.module.scss';
|
||||
@@ -64,22 +65,24 @@ function OperatorEvent({
|
||||
const mouseHandlers = useLongPress(handleLongPress);
|
||||
const cueColours = colour && getAccessibleColour(colour);
|
||||
|
||||
const operatorClasses = cx([
|
||||
style.event,
|
||||
isSelected && style.running,
|
||||
isPast && style.past,
|
||||
]);
|
||||
const operatorClasses = cx([style.event, isSelected && style.running, isPast && style.past]);
|
||||
|
||||
return (
|
||||
<div className={operatorClasses} data-testid={cue} ref={selectedRef} onContextMenu={handleLongPress} {...mouseHandlers}>
|
||||
<div
|
||||
className={operatorClasses}
|
||||
data-testid={cue}
|
||||
ref={selectedRef}
|
||||
onContextMenu={handleLongPress}
|
||||
{...mouseHandlers}
|
||||
>
|
||||
<div className={style.binder} style={{ ...cueColours }}>
|
||||
<span className={style.cue}>{cue}</span>
|
||||
</div>
|
||||
|
||||
<span className={style.mainField}>
|
||||
{showStart && <span className={style.plannedStart}>{millisToString(timeStart)}</span>}
|
||||
{showStart && <SuperscriptPeriod className={style.plannedStart} time={formatTime(timeStart)} />}
|
||||
{main}
|
||||
</span>
|
||||
</span>
|
||||
<span className={style.secondaryField}>{secondary}</span>
|
||||
<OperatorEventSchedule
|
||||
timeStart={timeStart}
|
||||
@@ -167,5 +170,9 @@ function TimeUntil({ timeStart, delay, dayOffset, totalGap, isLinkedToLoaded }:
|
||||
const isDue = timeUntil < MILLIS_PER_SECOND;
|
||||
const timeUntilString = isDue ? 'DUE' : `${formatDuration(Math.abs(timeUntil), timeUntil > 2 * MILLIS_PER_MINUTE)}`;
|
||||
|
||||
return <span className={style.timeUntil} data-testid='time-until'>{timeUntilString}</span>;
|
||||
return (
|
||||
<span className={style.timeUntil} data-testid='time-until'>
|
||||
{timeUntilString}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function StatusBarTimers() {
|
||||
return (
|
||||
<div className={style.timers}>
|
||||
<TimerOverview className={style.runningTimer} />
|
||||
<ClockOverview className={style.timeNow} />
|
||||
<ClockOverview className={style.timeNow} shouldFormat />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ function CuesheetDesktop({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<OverviewWrapper navElements={children}>
|
||||
<TitleOverview />
|
||||
<StartTimes />
|
||||
<StartTimes shouldFormat />
|
||||
<TimerOverview />
|
||||
<OffsetOverview />
|
||||
<MetadataTimes />
|
||||
<ClockOverview />
|
||||
<ClockOverview shouldFormat />
|
||||
</OverviewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,4 +34,5 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
useNextFlag,
|
||||
useOffsetOverview,
|
||||
useProgressOverview,
|
||||
useRundownExpectedEnd,
|
||||
useStartTimesOverview,
|
||||
useTimer,
|
||||
} from '../../../common/hooks/useSocket';
|
||||
@@ -27,69 +28,136 @@ import { useEntry } from '../../../common/hooks-query/useRundown';
|
||||
import { getOffsetState, getOffsetText } from '../../../common/utils/offset';
|
||||
import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
import { calculateEndAndDaySpan, formatDueTime, formattedTime } from '../overview.utils';
|
||||
import SuperscriptPeriod from '../../viewers/common/superscript-time/SuperscriptPeriod';
|
||||
import { calculateEndAndDaySpan, formatDueTime } from '../overview.utils';
|
||||
|
||||
import { OverUnder, TimeColumn } from './TimeLayout';
|
||||
import { OverUnder, TimeColumn, WrappedInTimeColumn } from './TimeLayout';
|
||||
|
||||
import style from './TimeElements.module.scss';
|
||||
|
||||
export function StartTimes() {
|
||||
const { plannedEnd, plannedStart, actualStart, expectedEnd } = useStartTimesOverview();
|
||||
interface OverviewTimeElementsProps {
|
||||
shouldFormat?: boolean;
|
||||
}
|
||||
|
||||
const plannedStartText = plannedStart === null ? timerPlaceholder : formatTime(plannedStart);
|
||||
export function StartTimes({ shouldFormat }: OverviewTimeElementsProps) {
|
||||
const { plannedEnd, plannedStart, actualStart } = useStartTimesOverview();
|
||||
|
||||
const formatOptions = { format12: 'hh:mm:ss a', format24: 'HH:mm:ss' };
|
||||
|
||||
const plannedStartText = (() => {
|
||||
if (plannedStart === null) return timerPlaceholder;
|
||||
if (shouldFormat) return formatTime(plannedStart, formatOptions);
|
||||
return millisToString(plannedStart, { fallback: timerPlaceholder });
|
||||
})();
|
||||
|
||||
const actualStartText = (() => {
|
||||
if (actualStart === null) return timerPlaceholder;
|
||||
if (shouldFormat) return formatTime(actualStart, formatOptions);
|
||||
return millisToString(actualStart, { fallback: timerPlaceholder });
|
||||
})();
|
||||
|
||||
const [maybePlannedEnd, maybePlannedDaySpan] = useMemo(() => calculateEndAndDaySpan(plannedEnd), [plannedEnd]);
|
||||
const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]);
|
||||
const plannedEndText = maybePlannedEnd === null ? timerPlaceholder : formatTime(maybePlannedEnd);
|
||||
|
||||
const plannedEndText = (() => {
|
||||
if (maybePlannedEnd === null) return timerPlaceholder;
|
||||
if (shouldFormat) return formatTime(maybePlannedEnd, formatOptions);
|
||||
return millisToString(maybePlannedEnd, { fallback: timerPlaceholder });
|
||||
})();
|
||||
|
||||
const multipleDays = maybePlannedDaySpan > 0;
|
||||
const plannedEndTooltip = multipleDays
|
||||
? `Planned end time (rundown spans over ${maybePlannedDaySpan + 1} days)`
|
||||
: 'Planned end time';
|
||||
|
||||
return (
|
||||
<div className={style.column}>
|
||||
<div className={style.row}>
|
||||
<span className={style.label}>Start</span>
|
||||
<div className={style.labelledElement}>
|
||||
<Tooltip text='Planned start time' render={<TbCalendarPin className={style.icon} />} />
|
||||
<span className={cx([style.time, plannedStart === null && style.muted])}>{plannedStartText}</span>
|
||||
</div>
|
||||
<div className={style.labelledElement} data-testid='actual-start-time'>
|
||||
<Tooltip text='Actual start time' render={<TbCalendarClock className={style.icon} />} />
|
||||
<span className={cx([style.time, actualStart === null && style.muted])}>{formattedTime(actualStart)}</span>
|
||||
</div>
|
||||
<Tooltip
|
||||
text='Planned start time'
|
||||
render={
|
||||
<div className={style.labelledElement}>
|
||||
<TbCalendarPin className={style.icon} />
|
||||
<SuperscriptPeriod
|
||||
className={cx([style.time, plannedStart === null && style.muted])}
|
||||
time={plannedStartText}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Tooltip
|
||||
text='Actual start time'
|
||||
render={
|
||||
<div className={style.labelledElement} data-testid='actual-start-time'>
|
||||
<TbCalendarClock className={style.icon} />
|
||||
<SuperscriptPeriod
|
||||
className={cx([style.time, actualStart === null && style.muted])}
|
||||
time={actualStartText}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={style.row}>
|
||||
<span className={style.label}>End</span>
|
||||
<div className={style.labelledElement}>
|
||||
<Tooltip text='Planned end time' render={<TbCalendarPin className={style.icon} />} />
|
||||
{maybePlannedDaySpan > 0 ? (
|
||||
<Tooltip
|
||||
text={`Rundown spans over ${maybePlannedDaySpan + 1} days`}
|
||||
render={<span className={cx([style.time, style.daySpan])} data-day-offset={maybePlannedDaySpan} />}
|
||||
>
|
||||
{plannedEndText}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span className={cx([style.time, plannedEnd === null && style.muted])}>{plannedEndText}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={style.labelledElement}>
|
||||
<Tooltip text='Expected end time' render={<TbCalendarStar className={style.icon} />} />
|
||||
{maybeExpectedEnd !== null && maybeExpectedDaySpan > 0 ? (
|
||||
<Tooltip
|
||||
text={`Rundown spans over ${maybeExpectedDaySpan + 1} days`}
|
||||
render={<span className={cx([style.time, style.daySpan])} data-day-offset={maybeExpectedDaySpan} />}
|
||||
>
|
||||
{formattedTime(maybeExpectedEnd)}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span className={cx([style.time, maybeExpectedEnd === null && style.muted])}>
|
||||
{formattedTime(maybeExpectedEnd)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Tooltip
|
||||
text={plannedEndTooltip}
|
||||
render={
|
||||
<div className={style.labelledElement}>
|
||||
<TbCalendarPin className={style.icon} />
|
||||
<SuperscriptPeriod
|
||||
className={cx([style.time, plannedEnd === null && style.muted])}
|
||||
time={plannedEndText}
|
||||
/>
|
||||
{multipleDays && (
|
||||
<span className={cx([style.time, style.daySpan])} data-day-offset={maybePlannedDaySpan} />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<RundownExpectedEnd shouldFormat={shouldFormat} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the expected end for the rundown
|
||||
* Extracted to improve performance as this is a ticking value
|
||||
*/
|
||||
function RundownExpectedEnd({ shouldFormat }: OverviewTimeElementsProps) {
|
||||
const { expectedEnd } = useRundownExpectedEnd();
|
||||
|
||||
const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]);
|
||||
const maybeExpectedEndText = (() => {
|
||||
if (maybeExpectedEnd === null) return timerPlaceholder;
|
||||
if (shouldFormat) return formatTime(maybeExpectedEnd, { format12: 'hh:mm:ss a', format24: 'HH:mm:ss' });
|
||||
return millisToString(maybeExpectedEnd, { fallback: timerPlaceholder });
|
||||
})();
|
||||
|
||||
const multipleDays = maybeExpectedEnd !== null && maybeExpectedDaySpan > 0;
|
||||
const tooltip = multipleDays
|
||||
? `Expected end time (rundown spans over ${maybeExpectedDaySpan + 1} days)`
|
||||
: 'Expected end time';
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
text={tooltip}
|
||||
render={
|
||||
<div className={style.labelledElement}>
|
||||
<TbCalendarStar className={style.icon} />
|
||||
<SuperscriptPeriod
|
||||
className={cx([style.time, maybeExpectedEnd === null && style.muted])}
|
||||
time={maybeExpectedEndText}
|
||||
/>
|
||||
{multipleDays && <span className={cx([style.time, style.daySpan])} data-day-offset={maybeExpectedDaySpan} />}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function MetadataTimes() {
|
||||
return (
|
||||
<div className={style.column}>
|
||||
@@ -229,11 +297,17 @@ export function OffsetOverview() {
|
||||
return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
|
||||
}
|
||||
|
||||
export function ClockOverview({ className }: { className?: string }) {
|
||||
export function ClockOverview({ shouldFormat, className }: OverviewTimeElementsProps & { className?: string }) {
|
||||
const { clock } = useClock();
|
||||
const formattedClock = formatTime(clock);
|
||||
const formattedClock = shouldFormat ? formatTime(clock) : millisToString(clock);
|
||||
|
||||
return <TimeColumn label='Time now' value={formattedClock} className={className} />;
|
||||
return (
|
||||
<WrappedInTimeColumn
|
||||
label='Time now'
|
||||
className={className}
|
||||
render={(clockClasses) => <SuperscriptPeriod className={clockClasses} time={formattedClock} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function TimerOverview({ className }: { className?: string }) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
|
||||
import style from './TimeLayout.module.scss';
|
||||
@@ -22,6 +24,21 @@ export function TimeColumn({ label, value, state = 'active', className, testId }
|
||||
);
|
||||
}
|
||||
|
||||
interface WrappedInTimeColumnProps {
|
||||
label: string;
|
||||
state?: 'muted' | 'waiting' | 'active';
|
||||
className?: string;
|
||||
render: (className: string) => ReactNode;
|
||||
}
|
||||
|
||||
export function WrappedInTimeColumn({ label, state = 'active', className, render }: WrappedInTimeColumnProps) {
|
||||
return (
|
||||
<div className={cx([style.column, className])} data-state={state}>
|
||||
<span className={style.label}>{label}</span>
|
||||
{render(style.clock)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
interface OverUnderProps {
|
||||
state: 'over' | 'under' | 'muted' | null;
|
||||
value: string;
|
||||
|
||||
@@ -59,9 +59,6 @@ export default function GroupEditor({ group }: GroupEditorProps) {
|
||||
<Editor.Title>Group schedule</Editor.Title>
|
||||
<div className={style.inline}>
|
||||
<div>
|
||||
{
|
||||
// TODO: format with user time settings
|
||||
}
|
||||
<Editor.Label>First event start</Editor.Label>
|
||||
<TextLikeInput className={style.textLikeInput} disabled>
|
||||
{millisToString(group.timeStart, { fallback: timerPlaceholder })}
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { EntryId, OntimeGroup } from 'ontime-types';
|
||||
import { MILLIS_PER_MINUTE } from 'ontime-utils';
|
||||
import { MILLIS_PER_MINUTE, millisToString } from 'ontime-utils';
|
||||
|
||||
import IconButton from '../../../common/components/buttons/IconButton';
|
||||
import { useContextMenu } from '../../../common/hooks/useContextMenu';
|
||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||
import { getOffsetState } from '../../../common/utils/offset';
|
||||
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||
import { formatDuration, formatTime } from '../../../common/utils/time';
|
||||
import { cx, getAccessibleColour, timerPlaceholder } from '../../../common/utils/styleUtils';
|
||||
import { formatDuration } from '../../../common/utils/time';
|
||||
import TitleEditor from '../common/TitleEditor';
|
||||
import { canDrop } from '../rundown.utils';
|
||||
import { useEventSelection } from '../useEventSelection';
|
||||
@@ -149,11 +149,11 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }:
|
||||
<div className={style.metaRow}>
|
||||
<div className={style.metaEntry}>
|
||||
<div>Start</div>
|
||||
<div>{formatTime(data.timeStart)}</div>
|
||||
<div>{millisToString(data.timeStart, { fallback: timerPlaceholder })}</div>
|
||||
</div>
|
||||
<div className={style.metaEntry}>
|
||||
<div>End</div>
|
||||
<div>{formatTime(data.timeEnd)}</div>
|
||||
<div>{millisToString(data.timeEnd, { fallback: timerPlaceholder })}</div>
|
||||
</div>
|
||||
<div className={style.metaEntry}>
|
||||
<div>Duration</div>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import './SuperscriptTime.scss';
|
||||
|
||||
interface SuperscriptPeriodProps {
|
||||
time: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a time string and formats periods (am/pm) as superscript
|
||||
* @example 12:00 AM -> AM becomes a superscript
|
||||
* @example 12:00:10 -> no formatting changes applied
|
||||
*/
|
||||
export default function SuperscriptPeriod({ time, className }: SuperscriptPeriodProps) {
|
||||
// we assume anything after space is a period tag
|
||||
const [timeString, period] = time.split(' ');
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{timeString}
|
||||
{period && <sup className='period'>{period}</sup>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -76,13 +76,13 @@ function Backstage({ events, customFields, projectData, isMirrored, settings }:
|
||||
const scheduledStart = (() => {
|
||||
if (showNow) return undefined;
|
||||
if (!hasEvents) return undefined;
|
||||
return formatTime(rundown.plannedStart, { format12: 'hh:mm a', format24: 'HH:mm' });
|
||||
return formatTime(rundown.plannedStart, { format12: 'h:mm a', format24: 'HH:mm' });
|
||||
})();
|
||||
|
||||
const scheduledEnd = (() => {
|
||||
if (showNow) return undefined;
|
||||
if (!hasEvents) return undefined;
|
||||
return formatTime(rundown.plannedEnd, { format12: 'hh:mm a', format24: 'HH:mm' });
|
||||
return formatTime(rundown.plannedEnd, { format12: 'h:mm a', format24: 'HH:mm' });
|
||||
})();
|
||||
|
||||
let displayTimer = millisToString(time.current, { fallback: timerPlaceholderMin });
|
||||
|
||||
@@ -5,14 +5,14 @@ import { getOffsetState } from '../../../common/utils/offset';
|
||||
import { ExtendedEntry } from '../../../common/utils/rundownMetadata';
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import { formatTime, getExpectedTimesFromExtendedEvent } from '../../../common/utils/time';
|
||||
import SuperscriptTime from '../../../features/viewers/common/superscript-time/SuperscriptTime';
|
||||
import SuperscriptPeriod from '../../../features/viewers/common/superscript-time/SuperscriptPeriod';
|
||||
|
||||
import { useScheduleOptions } from './schedule.options';
|
||||
|
||||
import './Schedule.scss';
|
||||
|
||||
const formatOptions = {
|
||||
format12: 'hh:mm a',
|
||||
format12: 'h:mm a',
|
||||
format24: 'HH:mm',
|
||||
};
|
||||
|
||||
@@ -84,9 +84,9 @@ function PlannedScheduleItem({
|
||||
return (
|
||||
<>
|
||||
<span className='entry-colour' style={{ backgroundColor: colour }} />
|
||||
<SuperscriptTime time={start} />
|
||||
<SuperscriptPeriod time={start} />
|
||||
→
|
||||
<SuperscriptTime time={end} />
|
||||
<SuperscriptPeriod time={end} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -106,14 +106,14 @@ function DelayedScheduleItem({
|
||||
<>
|
||||
<span className='entry-times--delayed'>
|
||||
<span className='entry-colour' style={{ backgroundColor: colour }} />
|
||||
<SuperscriptTime time={start} />
|
||||
<SuperscriptPeriod time={start} />
|
||||
→
|
||||
<SuperscriptTime time={end} />
|
||||
<SuperscriptPeriod time={end} />
|
||||
</span>
|
||||
<span className='entry-times--delay'>
|
||||
<SuperscriptTime time={delayedStart} />
|
||||
<SuperscriptPeriod time={delayedStart} />
|
||||
→
|
||||
<SuperscriptTime time={delayedEnd} />
|
||||
<SuperscriptPeriod time={delayedEnd} />
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
@@ -161,5 +161,5 @@ interface ExpectedTimeProps {
|
||||
function ExpectedTime({ expectedTime, plannedTime }: ExpectedTimeProps) {
|
||||
const timeDisplay = formatTime(expectedTime);
|
||||
const expectedState = getOffsetState(expectedTime - plannedTime);
|
||||
return <SuperscriptTime className={`entry-times--${expectedState}`} time={timeDisplay} />;
|
||||
return <SuperscriptPeriod className={`entry-times--${expectedState}`} time={timeDisplay} />;
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ export default function CountdownSelect({ events, subscriptions, disableEdit }:
|
||||
>
|
||||
<div className='sub__binder' style={{ '--user-color': event?.colour ?? '' }} />
|
||||
<div className='sub__schedule'>
|
||||
<ClockTime value={event.timeStart} preferredFormat12='h:mm' preferredFormat24='HH:mm' />
|
||||
<ClockTime value={event.timeStart} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
||||
→
|
||||
<ClockTime value={event.timeEnd} preferredFormat12='h:mm' preferredFormat24='HH:mm' />
|
||||
<ClockTime value={event.timeEnd} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
||||
</div>
|
||||
<div className='sub__label'>{isSelected ? 'Click to remove' : 'Click to add'}</div>
|
||||
<div className='sub__title'>{title}</div>
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ function MakeStart({ getValue, row, table, column }: CellContext<ExtendedEntry,
|
||||
}
|
||||
|
||||
const { showDelayedTimes, hideTableSeconds } = table.options.meta.options;
|
||||
const formatOpts = hideTableSeconds ? { format12: 'hh:mm a', format24: 'HH:mm' } : undefined;
|
||||
const formatOpts = hideTableSeconds ? { format12: 'h:mm a', format24: 'HH:mm' } : undefined;
|
||||
|
||||
const event = row.original;
|
||||
if (!isOntimeEvent(event)) {
|
||||
@@ -62,7 +62,7 @@ function MakeEnd({ getValue, row, table, column }: CellContext<ExtendedEntry, un
|
||||
}
|
||||
|
||||
const { showDelayedTimes, hideTableSeconds } = table.options.meta.options;
|
||||
const formatOpts = hideTableSeconds ? { format12: 'hh:mm a', format24: 'HH:mm' } : undefined;
|
||||
const formatOpts = hideTableSeconds ? { format12: 'h:mm a', format24: 'HH:mm' } : undefined;
|
||||
|
||||
const event = row.original;
|
||||
if (!isOntimeEvent(event)) {
|
||||
|
||||
@@ -49,9 +49,7 @@ export default function StudioTimers({ viewSettings }: StudioTimersProps) {
|
||||
</div>
|
||||
<div>
|
||||
<div className='label center'>Over / under</div>
|
||||
<div className={cx(['runtime-timer', 'center', !eventNow && 'muted', offsetState && offsetState])}>
|
||||
{schedule.offset}
|
||||
</div>
|
||||
<div className={cx(['runtime-timer', 'center', !eventNow && 'muted', offsetState])}>{schedule.offset}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='label right'>{getLocalizedString('common.expected_end')}</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ interface TimelineEntryProps {
|
||||
}
|
||||
|
||||
const formatOptions = {
|
||||
format12: 'hh:mm a',
|
||||
format12: 'h:mm a',
|
||||
format24: 'HH:mm',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user