refactor: review timer styles

This commit is contained in:
Carlos Valente
2025-07-12 22:06:47 +02:00
committed by Carlos Valente
parent a69c5f354c
commit 7305edc398
13 changed files with 86 additions and 39 deletions
@@ -13,8 +13,13 @@ interface FitTextProps extends HTMLAttributes<HTMLDivElement> {
max?: number; // inclusive max?: number; // inclusive
} }
export function FitText(props: PropsWithChildren<FitTextProps>) { export function FitText({
const { children, mode = 'multi', min = 16, max = 256, ...elementProps } = props; children,
mode = 'multi',
min = 16,
max = 256,
...elementProps
}: PropsWithChildren<FitTextProps>) {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const isOverflown = useCallback(() => { const isOverflown = useCallback(() => {
@@ -25,9 +25,8 @@
} }
&:focus { &:focus {
outline: none; outline: 2px solid $blue-500;
background-color: $gray-1300; background-color: $gray-1350;
border: 2px solid $ui-white;
} }
&.current { &.current {
@@ -18,7 +18,9 @@ export default function PopoverContents({
<Popover.Positioner sideOffset={8} {...popoverProps}> <Popover.Positioner sideOffset={8} {...popoverProps}>
<Popover.Popup className={style.popup}> <Popover.Popup className={style.popup}>
{title && <Popover.Title className={style.title}>{title}</Popover.Title>} {title && <Popover.Title className={style.title}>{title}</Popover.Title>}
<Popover.Description className={className}>{children}</Popover.Description> <Popover.Description className={className} render={<div />}>
{children}
</Popover.Description>
</Popover.Popup> </Popover.Popup>
</Popover.Positioner> </Popover.Positioner>
</Popover.Portal> </Popover.Portal>
@@ -1,7 +1,7 @@
.inline { .inline {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1rem; gap: 0.5rem;
} }
// attempt to match with ontimeTextInputs // attempt to match with ontimeTextInputs
@@ -43,8 +43,7 @@ export default function ParamInput({ paramField }: ParamInputProps) {
} }
if (type === 'boolean') { if (type === 'boolean') {
const defaultCheckedValue = isStringBoolean(searchParams.get(id)) || defaultValue; return <ControlledSwitch id={id} initialValue={isStringBoolean(searchParams.get(id)) || defaultValue} />;
return <Switch size='large' name={id} defaultChecked={defaultCheckedValue} />;
} }
if (type === 'number') { if (type === 'number') {
@@ -123,3 +122,12 @@ function MultiOption({ paramField }: EditFormMultiOptionProps) {
</> </>
); );
} }
interface ControlledSwitchProps {
id: string;
initialValue: boolean;
}
function ControlledSwitch({ id, initialValue }: ControlledSwitchProps) {
const [checked, setChecked] = useState(initialValue);
return <Switch size='large' name={id} checked={checked} onCheckedChange={setChecked} />;
}
@@ -115,7 +115,7 @@ export default function Operator() {
const { process } = makeOperatorMetadata(selectedEventId); const { process } = makeOperatorMetadata(selectedEventId);
return ( return (
<div className={style.operatorContainer}> <div className={style.operatorContainer} data-testid='operator-view'>
<ViewParamsEditor viewOptions={operatorOptions} /> <ViewParamsEditor viewOptions={operatorOptions} />
{editEvent && <EditModal event={editEvent} onClose={() => setEditEvent(null)} />} {editEvent && <EditModal event={editEvent} onClose={() => setEditEvent(null)} />}
+12 -3
View File
@@ -35,9 +35,18 @@ interface BackstageProps {
settings: Settings | undefined; settings: Settings | undefined;
} }
export default function Backstage(props: BackstageProps) { export default function Backstage({
const { events, customFields, eventNext, eventNow, general, time, isMirrored, runtime, selectedId, settings } = props; events,
customFields,
eventNext,
eventNow,
general,
time,
isMirrored,
runtime,
selectedId,
settings,
}: BackstageProps) {
const { getLocalizedString } = useTranslation(); const { getLocalizedString } = useTranslation();
const { secondarySource } = useBackstageOptions(); const { secondarySource } = useBackstageOptions();
const [blinkClass, setBlinkClass] = useState(false); const [blinkClass, setBlinkClass] = useState(false);
@@ -38,7 +38,12 @@ export default function StudioTimers({
const formattedSecondaryMessage = secondaryMessage || '-'; const formattedSecondaryMessage = secondaryMessage || '-';
// gather presentation styles // gather presentation styles
const timerColour = getTimerColour(viewSettings, time.phase === TimerPhase.Warning, time.phase === TimerPhase.Danger); const timerColour = getTimerColour(
viewSettings,
undefined,
time.phase === TimerPhase.Warning,
time.phase === TimerPhase.Danger,
);
return ( return (
<div className='studio__timers'> <div className='studio__timers'>
+4 -4
View File
@@ -101,7 +101,7 @@
.timer { .timer {
opacity: 1; opacity: 1;
font-family: var(--timer-font, var(--font-family-override, $viewer-font-family)); font-family: var(--timer-font, var(--font-family-override, $viewer-font-family));
color: var(--timer-colour, var(--timer-color-override, var(--phase-color))); color: var(--timer-colour, var(--timer-color-override, $ui-white));
line-height: 0.9em; line-height: 0.9em;
text-align: center; text-align: center;
letter-spacing: 0.05em; letter-spacing: 0.05em;
@@ -117,14 +117,14 @@
// use a class instead of a phase, to allow suppressing overtime style // use a class instead of a phase, to allow suppressing overtime style
&--finished { &--finished {
color: var(--timer-overtime-color-override, $timer-finished-color); color: var(--timer-colour, var(--timer-overtime-color-override, $timer-finished-color));
} }
&[data-phase="warning"] { &[data-phase="warning"] {
color: var(--timer-warning-color-override, var(--phase-color)); color: var(--timer-colour, var(--timer-warning-color-override));
} }
&[data-phase="danger"] { &[data-phase="danger"] {
color: var(--timer-danger-color-override, var(--phase-color)); color: var(--timer-colour, var(--timer-danger-color-override));
} }
} }
} }
+5 -8
View File
@@ -68,7 +68,7 @@ export default function Timer({
timerType, timerType,
freezeOvertime, freezeOvertime,
freezeMessage, freezeMessage,
hideOvertime, hidePhase,
font, font,
keyColour, keyColour,
textColour, textColour,
@@ -88,7 +88,7 @@ export default function Timer({
time.phase, time.phase,
freezeOvertime, freezeOvertime,
freezeMessage, freezeMessage,
hideOvertime, hidePhase,
); );
const isPlaying = getIsPlaying(time.playback); const isPlaying = getIsPlaying(time.playback);
const showClock = !hideClock && getShowClock(viewTimerType); const showClock = !hideClock && getShowClock(viewTimerType);
@@ -136,11 +136,11 @@ export default function Timer({
); );
// gather presentation styles // gather presentation styles
const timerColour = getTimerColour(viewSettings, showWarning, showDanger); const timerColour = getTimerColour(viewSettings, textColour, showWarning, showDanger);
const { timerFontSize, externalFontSize } = getEstimatedFontSize(display, secondaryContent); const { timerFontSize, externalFontSize } = getEstimatedFontSize(display, secondaryContent);
const userStyles = { const userStyles = {
...(keyColour && { '--timer-bg': keyColour }), ...(keyColour && { '--timer-bg': keyColour }),
...(textColour && { '--timer-colour': textColour }), ...(textColour && { '--timer-colour': timerColour }),
...(font && { '--timer-font': font }), ...(font && { '--timer-font': font }),
}; };
@@ -183,10 +183,7 @@ export default function Timer({
) : ( ) : (
<div <div
className={cx(['timer', !isPlaying && 'timer--paused', showFinished && 'timer--finished'])} className={cx(['timer', !isPlaying && 'timer--paused', showFinished && 'timer--finished'])}
style={{ style={{ fontSize: `${timerFontSize}vw` }}
fontSize: `${timerFontSize}vw`,
'--phase-color': timerColour,
}}
data-phase={time.phase} data-phase={time.phase}
> >
{display} {display}
+5 -5
View File
@@ -65,9 +65,9 @@ export const getTimerOptions = (timeFormat: string, customFields: CustomFields):
placeholder: 'e.g. Time is up!', placeholder: 'e.g. Time is up!',
}, },
{ {
id: 'hideOvertime', id: 'hidePhase',
title: 'Hide Overtime', title: 'Hide progress styles',
description: 'Whether to suppress overtime styles (red borders and red text)', description: 'Whether to suppress the progress styles (warning, danger and overtime)',
type: 'boolean', type: 'boolean',
defaultValue: false, defaultValue: false,
}, },
@@ -187,7 +187,7 @@ type TimerOptions = {
timerType?: TimerType; timerType?: TimerType;
freezeOvertime: boolean; freezeOvertime: boolean;
freezeMessage: string; freezeMessage: string;
hideOvertime: boolean; hidePhase: boolean;
font?: string; font?: string;
keyColour?: string; keyColour?: string;
textColour?: string; textColour?: string;
@@ -217,7 +217,7 @@ function getOptionsFromParams(searchParams: URLSearchParams): TimerOptions {
timerType: timerType === TimerType.None ? undefined : timerType, timerType: timerType === TimerType.None ? undefined : timerType,
freezeOvertime: isStringBoolean(searchParams.get('freezeOvertime')), freezeOvertime: isStringBoolean(searchParams.get('freezeOvertime')),
freezeMessage: searchParams.get('freezeMessage') ?? '', freezeMessage: searchParams.get('freezeMessage') ?? '',
hideOvertime: isStringBoolean(searchParams.get('hideOvertime')), hidePhase: isStringBoolean(searchParams.get('hidePhase')),
font: searchParams.get('font') ?? undefined, font: searchParams.get('font') ?? undefined,
keyColour: makeColourString(searchParams.get('keyColour')), keyColour: makeColourString(searchParams.get('keyColour')),
+23 -6
View File
@@ -83,15 +83,32 @@ export function getShowModifiers(
phase: TimerPhase, phase: TimerPhase,
freezeOvertime: boolean, freezeOvertime: boolean,
freezeMessage: string, freezeMessage: string,
hideOvertime: boolean, hidePhase: boolean,
) { ) {
if (hidePhase) {
return {
showEndMessage: false,
showFinished: false,
showWarning: false,
showDanger: false,
};
}
const showModifiers = timerType === TimerType.CountDown || countToEnd; const showModifiers = timerType === TimerType.CountDown || countToEnd;
const finished = phase === TimerPhase.Overtime; if (!showModifiers) {
return {
showEndMessage: false,
showFinished: false,
showWarning: false,
showDanger: false,
};
}
return { return {
showEndMessage: showModifiers && finished && freezeOvertime && freezeMessage !== '', showEndMessage: freezeOvertime && freezeMessage !== '',
showFinished: showModifiers && !hideOvertime && finished, showFinished: phase === TimerPhase.Overtime,
showWarning: showModifiers && phase === TimerPhase.Warning, showWarning: phase === TimerPhase.Warning,
showDanger: showModifiers && phase === TimerPhase.Danger, showDanger: phase === TimerPhase.Danger,
}; };
} }
@@ -3,8 +3,13 @@ import { ViewSettings } from 'ontime-types';
/** /**
* Which colour should the timer have at a given moment * Which colour should the timer have at a given moment
*/ */
export function getTimerColour(viewSettings: ViewSettings, showWarning: boolean, showDanger: boolean) { export function getTimerColour(
viewSettings: ViewSettings,
timerColour: string | undefined,
showWarning: boolean,
showDanger: boolean,
) {
if (showWarning) return viewSettings.warningColor; if (showWarning) return viewSettings.warningColor;
if (showDanger) return viewSettings.dangerColor; if (showDanger) return viewSettings.dangerColor;
return viewSettings.normalColor; return timerColour || viewSettings.normalColor;
} }