diff --git a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts index 224bf33646..8a02c3779e 100644 --- a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts +++ b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts @@ -1,7 +1,7 @@ import { hideTimerSeconds } from '../../../common/components/view-params-editor/constants'; import { ViewOption } from '../../../common/components/view-params-editor/types'; -export const MINIMAL_TIMER_OPTIONS: ViewOption[] = [ +export const POPOUT_TIMER_OPTIONS: ViewOption[] = [ { section: 'Timer Options' }, hideTimerSeconds, { section: 'Element visibility' }, @@ -58,34 +58,4 @@ export const MINIMAL_TIMER_OPTIONS: ViewOption[] = [ type: 'number', placeholder: '1 (default)', }, - { - id: 'alignx', - title: 'Align Horizontal', - description: 'Moves the horizontally in page to start = left | center | end = right', - type: 'option', - values: { start: 'Start', center: 'Center', end: 'End' }, - defaultValue: 'center', - }, - { - id: 'offsetx', - title: 'Offset Horizontal', - description: 'Offsets the timer horizontal position by a given amount in pixels', - type: 'number', - placeholder: '0 (default)', - }, - { - id: 'aligny', - title: 'Align Vertical', - description: 'Moves the vertically in page to start = left | center | end = right', - type: 'option', - values: { start: 'Start', center: 'Center', end: 'End' }, - defaultValue: 'center', - }, - { - id: 'offsety', - title: 'Offset Vertical', - description: 'Offsets the timer vertical position by a given amount in pixels', - type: 'number', - placeholder: '0 (default)', - }, ]; diff --git a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss index 6b99c00ecf..68a8c6fae1 100644 --- a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss +++ b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss @@ -1,6 +1,6 @@ @use '../../../theme/viewerDefs' as *; -.minimal-timer { +.popput-timer { margin: 0; box-sizing: border-box; /* reset */ overflow: hidden; @@ -19,10 +19,14 @@ transition: $viewer-transition-time; } + .mirror { + transform: rotate(180deg); + } + .timer { opacity: 1; font-family: var(--font-family-bold-override, $timer-bold-font-family); - font-size: 15vw; + font-size: 20vw; position: relative; color: var(--timer-color-override, var(--phase-color)); transition: $viewer-transition-time; @@ -50,4 +54,12 @@ color: $timer-finished-color; padding: 0; } + + .logo { + position: absolute; + top: 2vw; + left: 2vw; + max-width: min(200px, 20vw); + max-height: min(100px, 20vh); + } } diff --git a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx index 9cedad98e1..85d5a803ca 100644 --- a/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx +++ b/apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx @@ -1,34 +1,152 @@ import { useCallback, useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { Button } from '@chakra-ui/react'; +import { Playback, ProjectData, TimerPhase, TimerType, ViewSettings } from 'ontime-types'; +import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; +import { useWindowTitle } from '../../../common/hooks/useWindowTitle'; import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; +import { OverridableOptions } from '../../../common/models/View.types'; import { useTranslation } from '../../../translation/TranslationProvider'; -import { getFormattedTimer, getTimerByType } from '../common/viewUtils'; +import { getFormattedTimer, getTimerByType, isStringBoolean } from '../common/viewUtils'; + +import { POPOUT_TIMER_OPTIONS } from './PopOutTimer.options'; import './PopOutTimer.scss'; interface PopTimerProps { + general: ProjectData; + isMirrored: boolean; time: ViewExtendedTimer; + viewSettings: ViewSettings; } export default function PopOutClock(props: PopTimerProps) { - const { time } = props; - const [pipElement, setPipElement] = useState<{ timer: HTMLDivElement; pipWindow: Window } | false>(false); + const { general, isMirrored, time, viewSettings } = props; + const [pipElement, setPipElement] = useState< + { timer: HTMLDivElement; pipWindow: Window; background: HTMLDivElement } | false + >(false); + const [searchParams] = useSearchParams(); const { getLocalizedString } = useTranslation(); + useWindowTitle('Popout Timer'); + const stageTimer = getTimerByType(false, time); const display = getFormattedTimer(stageTimer, time.timerType, getLocalizedString('common.minutes'), { removeSeconds: false, removeLeadingZero: true, }); + // TODO: this should be tied to the params + // USER OPTIONS + const userOptions: OverridableOptions = { + size: 1, + }; + + // key: string + // Should be a hex string '#00FF00' with key colour + const key = searchParams.get('key'); + if (key) { + userOptions.keyColour = `#${key}`; + } + + // textColour: string + // Should be a hex string '#ffffff' + const textColour = searchParams.get('text'); + if (textColour) { + userOptions.textColour = `#${textColour}`; + } + + // textBackground: string + // Should be a hex string '#ffffff' + const textBackground = searchParams.get('textbg'); + if (textBackground) { + userOptions.textBackground = `#${textBackground}`; + } + + // font: string + // Should be a string with a font name 'arial' + const font = searchParams.get('font'); + if (font) { + userOptions.font = font; + } + + // size: multiplier + // Should be a number 0.0-n + const size = searchParams.get('size'); + if (size !== null && typeof size !== 'undefined') { + if (!Number.isNaN(Number(size))) { + userOptions.size = Number(size); + } + } + const stageTimerCharacters = display.replace('/:/g', '').length; + const timerFontSize = (89 / (stageTimerCharacters - 1)) * (userOptions.size || 1); + + const hideOvertime = searchParams.get('hideovertime'); + userOptions.hideOvertime = isStringBoolean(hideOvertime); + + const hideEndMessage = searchParams.get('hideendmessage'); + userOptions.hideEndMessage = isStringBoolean(hideEndMessage); + + const hideTimerSeconds = searchParams.get('hideTimerSeconds'); + userOptions.hideTimerSeconds = isStringBoolean(hideTimerSeconds); + + const showLeadingZeros = searchParams.get('showLeadingZeros'); + userOptions.removeLeadingZeros = !isStringBoolean(showLeadingZeros); + + const timerIsTimeOfDay = time.timerType === TimerType.Clock; + + const isPlaying = time.playback !== Playback.Pause; + + const shouldShowModifiers = time.timerType === TimerType.CountDown || time.countToEnd; + const finished = time.phase === TimerPhase.Overtime; + const showEndMessage = shouldShowModifiers && finished && viewSettings.endMessage && !hideEndMessage; + const showFinished = + shouldShowModifiers && finished && !userOptions?.hideOvertime && (shouldShowModifiers || showEndMessage); + + const showProgress = time.playback !== Playback.Stop; + const showWarning = shouldShowModifiers && time.phase === TimerPhase.Warning; + const showDanger = shouldShowModifiers && time.phase === TimerPhase.Danger; + + let timerColor = viewSettings.normalColor; + if (!timerIsTimeOfDay && showProgress && showWarning) timerColor = viewSettings.warningColor; + if (!timerIsTimeOfDay && showProgress && showDanger) timerColor = viewSettings.dangerColor; + useEffect(() => { if (pipElement) { pipElement.timer.innerText = display; } }, [display, pipElement]); + useEffect(() => { + if (pipElement) { + pipElement.background.classList.toggle('mirror', isMirrored); + if (userOptions.keyColour) pipElement.background.style.setProperty('background-color', userOptions.keyColour); + + pipElement.timer.classList.toggle('timer--paused', !isPlaying); + pipElement.timer.classList.toggle('timer--finished', !showFinished); + if (userOptions.textColour) pipElement.timer.style.setProperty('color', userOptions.textColour); + if (userOptions.textBackground) + pipElement.timer.style.setProperty('background-color', userOptions.textBackground); + if (userOptions.font) pipElement.timer.style.setProperty('font-family', userOptions.font); + pipElement.timer.style.setProperty('font-size', `${timerFontSize}vw`); + pipElement.timer.style.setProperty('--phase-color', timerColor); + } + }, [ + isMirrored, + isPlaying, + pipElement, + showFinished, + timerColor, + timerFontSize, + userOptions.font, + userOptions.justifyContent, + userOptions.keyColour, + userOptions.textBackground, + userOptions.textColour, + ]); + const closePip = useCallback(() => { if (pipElement) { pipElement.pipWindow.close(); @@ -52,7 +170,9 @@ export default function PopOutClock(props: PopTimerProps) { // create the backgoind element const background = document.createElement('div'); - background.classList.add('minimal-timer'); + background.classList.add('popout-timer'); + background.classList.toggle('mirror', isMirrored); + pipWindow.document.body.append(background); // create the timer element @@ -62,7 +182,7 @@ export default function PopOutClock(props: PopTimerProps) { pipWindow.document.title = 'ONTIME'; //TODO: trying to hide or change the title bar - setPipElement({ timer, pipWindow }); + setPipElement({ timer, pipWindow, background }); //clear state when the pip is closed pipWindow.addEventListener( @@ -73,7 +193,7 @@ export default function PopOutClock(props: PopTimerProps) { { once: true }, ); }); - }, []); + }, [isMirrored]); return (