mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 10:23:54 +00:00
breaking: convert offset > over-under
change offset direction and relabel to over-under
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { ViewSettings } from 'ontime-types';
|
||||
|
||||
export const viewsSettingsPlaceholder: ViewSettings = {
|
||||
dangerColor: '#ED3333',
|
||||
dangerColor: '#ff7300',
|
||||
normalColor: '#ffffffcc',
|
||||
overrideStyles: false,
|
||||
warningColor: '#FFAB33',
|
||||
warningColor: '#ffa528',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MaybeNumber } from 'ontime-types';
|
||||
import { millisToString } from 'ontime-utils';
|
||||
|
||||
import { enDash } from './styleUtils';
|
||||
|
||||
/**
|
||||
* Formats offset text
|
||||
*/
|
||||
export function getOffsetText(offset: MaybeNumber): string {
|
||||
if (offset === null) {
|
||||
return enDash;
|
||||
}
|
||||
|
||||
let offsetText = '';
|
||||
if (offset < 0) offsetText += '-';
|
||||
if (offset > 0) offsetText += '+';
|
||||
offsetText += millisToString(Math.abs(offset));
|
||||
return offsetText;
|
||||
}
|
||||
|
||||
export function getOffsetState(offset: MaybeNumber): 'ahead' | 'behind' | 'muted' | null {
|
||||
if (offset === null) return null;
|
||||
if (offset === 0) return 'muted';
|
||||
return offset < 0 ? 'behind' : 'ahead';
|
||||
}
|
||||
@@ -42,8 +42,8 @@ export default function TimerPreview() {
|
||||
|
||||
const overrideColour = (() => {
|
||||
// override fallback colours from starter project
|
||||
if (phase === TimerPhase.Warning) return data.warningColor ?? '#FFAB33';
|
||||
if (phase === TimerPhase.Danger) return data.dangerColor ?? '#ED3333';
|
||||
if (phase === TimerPhase.Warning) return data.warningColor ?? '#ffa528';
|
||||
if (phase === TimerPhase.Danger) return data.dangerColor ?? '#ff7300';
|
||||
return data.normalColor ?? '#FFFC';
|
||||
})();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { memo, PropsWithChildren } from 'react';
|
||||
|
||||
import { useIsMobileScreen } from '../../common/hooks/useIsMobileScreen';
|
||||
|
||||
import { ClockOverview, MetadataTimes, RuntimeOverview, StartTimes, TimerOverview } from './composite/TimeElements';
|
||||
import { ClockOverview, MetadataTimes, OffsetOverview, StartTimes, TimerOverview } from './composite/TimeElements';
|
||||
import TitleOverview from './composite/TitleOverview';
|
||||
import { OverviewWrapper } from './OverviewWrapper';
|
||||
|
||||
@@ -18,7 +18,7 @@ function CuesheetMobile({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<OverviewWrapper navElements={children}>
|
||||
<TimerOverview />
|
||||
<RuntimeOverview />
|
||||
<OffsetOverview />
|
||||
</OverviewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ function CuesheetDesktop({ children }: PropsWithChildren) {
|
||||
<TitleOverview />
|
||||
<StartTimes />
|
||||
<TimerOverview />
|
||||
<RuntimeOverview />
|
||||
<OffsetOverview />
|
||||
<MetadataTimes />
|
||||
<ClockOverview />
|
||||
</OverviewWrapper>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, PropsWithChildren } from 'react';
|
||||
|
||||
import { ClockOverview, MetadataTimes, ProgressOverview, RuntimeOverview, StartTimes } from './composite/TimeElements';
|
||||
import { ClockOverview, MetadataTimes, OffsetOverview, ProgressOverview, StartTimes } from './composite/TimeElements';
|
||||
import TitleOverview from './composite/TitleOverview';
|
||||
import { OverviewWrapper } from './OverviewWrapper';
|
||||
|
||||
@@ -11,7 +11,7 @@ function EditorOverview({ children }: PropsWithChildren) {
|
||||
<TitleOverview />
|
||||
<StartTimes />
|
||||
<ProgressOverview />
|
||||
<RuntimeOverview />
|
||||
<OffsetOverview />
|
||||
<MetadataTimes />
|
||||
<ClockOverview />
|
||||
</OverviewWrapper>
|
||||
|
||||
@@ -13,9 +13,10 @@ import {
|
||||
useTimer,
|
||||
} from '../../../common/hooks/useSocket';
|
||||
import { useEntry } from '../../../common/hooks-query/useRundown';
|
||||
import { getOffsetState, getOffsetText } from '../../../common/utils/offset';
|
||||
import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils';
|
||||
import { formatTime, useTimeUntilStart } from '../../../common/utils/time';
|
||||
import { calculateEndAndDaySpan, formattedTime, getOffsetText } from '../overview.utils';
|
||||
import { calculateEndAndDaySpan, formattedTime } from '../overview.utils';
|
||||
|
||||
import { TimeColumn } from './TimeLayout';
|
||||
|
||||
@@ -193,14 +194,16 @@ export function ProgressOverview() {
|
||||
return <TimeColumn label='Progress' value={progressText} />;
|
||||
}
|
||||
|
||||
export function RuntimeOverview() {
|
||||
export function OffsetOverview() {
|
||||
const { offset, playback } = useRuntimePlaybackOverview();
|
||||
|
||||
const isPlaying = isPlaybackActive(playback);
|
||||
const offsetText = getOffsetText(isPlaying ? offset : null);
|
||||
const offsetClasses = cx([style.offset, isPlaying && (offset < 0 ? style.behind : style.ahead)]);
|
||||
const correctedOffset = offset * -1;
|
||||
const offsetState = getOffsetState(correctedOffset);
|
||||
const offsetClasses = cx([style.offset, offsetState && style[offsetState]]);
|
||||
const offsetText = getOffsetText(isPlaying ? correctedOffset : null);
|
||||
|
||||
return <TimeColumn label='Offset' value={offsetText} className={offsetClasses} testId='offset' />;
|
||||
return <TimeColumn label='Over / under' value={offsetText} className={offsetClasses} testId='offset' />;
|
||||
}
|
||||
|
||||
export function ClockOverview() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MaybeNumber, TimerType } from 'ontime-types';
|
||||
import { dayInMs, millisToString } from 'ontime-utils';
|
||||
|
||||
import { enDash, timerPlaceholder, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
||||
import { timerPlaceholder, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
||||
|
||||
/**
|
||||
* Encapsulates the logic for formatting time in overview
|
||||
@@ -24,15 +24,3 @@ export function calculateEndAndDaySpan(end: MaybeNumber): [MaybeNumber, number]
|
||||
|
||||
return [end, 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats offset text
|
||||
* @param offset
|
||||
* @returns
|
||||
*/
|
||||
export function getOffsetText(offset: MaybeNumber): string {
|
||||
if (offset === null) {
|
||||
return enDash;
|
||||
}
|
||||
return millisToString(offset, { fallback: enDash });
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ export default function BlockEditor({ block }: BlockEditorProps) {
|
||||
);
|
||||
|
||||
const isEditor = window.location.pathname.includes('editor');
|
||||
const planOffset = typeof block.targetDuration !== 'number' ? null : block.targetDuration - block.duration;
|
||||
const planOffset = typeof block.targetDuration !== 'number' ? null : block.duration - block.targetDuration;
|
||||
const planOffsetLabel = planOffset !== null && planOffset > 0 ? 'behind' : 'ahead';
|
||||
|
||||
return (
|
||||
<div className={style.content}>
|
||||
@@ -94,11 +95,8 @@ export default function BlockEditor({ block }: BlockEditorProps) {
|
||||
</div>
|
||||
<div>
|
||||
<Editor.Label htmlFor='eventId'>Plan offset</Editor.Label>
|
||||
{
|
||||
// TODO: update remote data
|
||||
// TODO: remove tab index
|
||||
}
|
||||
<TextLikeInput delayed={Boolean(planOffset)} className={style.textLikeInput}>
|
||||
<TextLikeInput offset={planOffsetLabel} className={style.textLikeInput} tabIndex={-1}>
|
||||
{planOffset !== null && planOffset > 0 ? '+' : ''}
|
||||
{millisToString(planOffset, { fallback: enDash })}
|
||||
</TextLikeInput>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ function DurationInput({
|
||||
onClick={handleFakeFocus}
|
||||
onFocus={handleFakeFocus}
|
||||
muted={!lockedValue}
|
||||
delayed={delayed}
|
||||
offset={delayed ? 'behind' : undefined}
|
||||
ref={textRef}
|
||||
>
|
||||
{children}
|
||||
|
||||
+5
-1
@@ -14,7 +14,11 @@
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
&.delayed {
|
||||
&.ahead {
|
||||
color: $playback-ahead;
|
||||
}
|
||||
|
||||
&.behind {
|
||||
color: $ontime-delay-text;
|
||||
}
|
||||
|
||||
|
||||
+19
-18
@@ -5,29 +5,30 @@ import { cx } from '../../../../common/utils/styleUtils';
|
||||
import style from './TextLikeInput.module.scss';
|
||||
|
||||
interface TextLikeInputProps extends HTMLAttributes<HTMLSpanElement> {
|
||||
delayed?: boolean;
|
||||
offset?: 'ahead' | 'behind';
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
const TextLikeInput = forwardRef((props: PropsWithChildren<TextLikeInputProps>, textRef) => {
|
||||
const { delayed, muted, children, className, ...elementProps } = props;
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const classes = cx([style.textInput, delayed && style.delayed, muted && style.muted, className]);
|
||||
const TextLikeInput = forwardRef(
|
||||
({ offset, muted, children, className, ...elementProps }: PropsWithChildren<TextLikeInputProps>, textRef) => {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const classes = cx([style.textInput, offset && style[offset], muted && style.muted, className]);
|
||||
|
||||
useImperativeHandle(textRef, () => {
|
||||
return {
|
||||
focusParentElement() {
|
||||
ref.current?.parentElement?.focus();
|
||||
},
|
||||
};
|
||||
});
|
||||
useImperativeHandle(textRef, () => {
|
||||
return {
|
||||
focusParentElement() {
|
||||
ref.current?.parentElement?.focus();
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes} {...elementProps} tabIndex={0} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className={classes} tabIndex={0} {...elementProps} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TextLikeInput.displayName = 'TextLikeInput';
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ function TimeInputDuration({
|
||||
onClick={handleFakeFocus}
|
||||
onFocus={handleFakeFocus}
|
||||
muted={!lockedValue}
|
||||
delayed={delayed}
|
||||
offset={delayed ? 'behind' : undefined}
|
||||
ref={textRef}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { OntimeEvent, Playback, Runtime, TimerPhase, TimerState, ViewSettings }
|
||||
import { millisToString } from 'ontime-utils';
|
||||
|
||||
import { useAuxTimersTime } from '../../common/hooks/useSocket';
|
||||
import { getOffsetState } from '../../common/utils/offset';
|
||||
import { cx } from '../../common/utils/styleUtils';
|
||||
import { useTranslation } from '../../translation/TranslationProvider';
|
||||
import { getTimerColour } from '../utils/presentation.utils';
|
||||
@@ -45,6 +46,8 @@ export default function StudioTimers({
|
||||
time.phase === TimerPhase.Danger,
|
||||
);
|
||||
|
||||
const offsetState = getOffsetState(runtime.offset);
|
||||
|
||||
return (
|
||||
<div className='studio__timers'>
|
||||
<div className='card' id='card-schedule'>
|
||||
@@ -54,15 +57,8 @@ export default function StudioTimers({
|
||||
<div className='runtime-timer'>{schedule.actualStart}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='label center'>Offset</div>
|
||||
<div
|
||||
className={cx([
|
||||
'runtime-timer',
|
||||
'center',
|
||||
!eventNow && 'muted',
|
||||
runtime.offset <= 0 ? 'behind' : 'ahead',
|
||||
])}
|
||||
>
|
||||
<div className='label center'>Over / under</div>
|
||||
<div className={cx(['runtime-timer', 'center', !eventNow && 'muted', offsetState && offsetState])}>
|
||||
{schedule.offset}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { OntimeEvent, Runtime, TimerState } from 'ontime-types';
|
||||
import { millisToString } from 'ontime-utils';
|
||||
|
||||
import { getOffsetText } from '../../common/utils/offset';
|
||||
import { formatTime } from '../../common/utils/time';
|
||||
|
||||
const timeFormat = { format12: 'h:mm a', format24: 'HH:mm' };
|
||||
export function getFormattedScheduleTimes(runtime: Runtime) {
|
||||
const correctedOffset = runtime.offset !== null ? runtime.offset * -1 : null;
|
||||
return {
|
||||
actualStart: formatTime(runtime.actualStart, timeFormat),
|
||||
expectedEnd: formatTime(runtime.expectedEnd, timeFormat),
|
||||
offset: millisToString(runtime.offset),
|
||||
offset: getOffsetText(correctedOffset),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ export const dbModel: DatabaseModel = {
|
||||
viewSettings: {
|
||||
overrideStyles: false,
|
||||
normalColor: '#ffffffcc',
|
||||
warningColor: '#FFAB33',
|
||||
dangerColor: '#ED3333',
|
||||
warningColor: '#ffa528',
|
||||
dangerColor: '#ff7300',
|
||||
},
|
||||
urlPresets: [],
|
||||
customFields: {},
|
||||
|
||||
@@ -532,10 +532,10 @@ export const demoDb: DatabaseModel = {
|
||||
language: 'en',
|
||||
},
|
||||
viewSettings: {
|
||||
dangerColor: '#ED3333',
|
||||
dangerColor: '#ff7300',
|
||||
normalColor: '#ffffffcc',
|
||||
overrideStyles: false,
|
||||
warningColor: '#FFAB33',
|
||||
warningColor: '#ffa528',
|
||||
},
|
||||
customFields: {
|
||||
Song: {
|
||||
|
||||
@@ -467,10 +467,10 @@
|
||||
"language": "en"
|
||||
},
|
||||
"viewSettings": {
|
||||
"dangerColor": "#ED3333",
|
||||
"dangerColor": "#ff7300",
|
||||
"normalColor": "#ffffffcc",
|
||||
"overrideStyles": false,
|
||||
"warningColor": "#FFAB33"
|
||||
"warningColor": "#ffa528"
|
||||
},
|
||||
"customFields": {
|
||||
"song": {
|
||||
|
||||
Vendored
+2
-2
@@ -484,10 +484,10 @@
|
||||
"language": "en"
|
||||
},
|
||||
"viewSettings": {
|
||||
"dangerColor": "#ED3333",
|
||||
"dangerColor": "#ff7300",
|
||||
"normalColor": "#ffffffcc",
|
||||
"overrideStyles": false,
|
||||
"warningColor": "#FFAB33"
|
||||
"warningColor": "#ffa528"
|
||||
},
|
||||
"customFields": {
|
||||
"Song": {
|
||||
|
||||
Reference in New Issue
Block a user