diff --git a/apps/client/src/common/hooks/useFitText.ts b/apps/client/src/common/hooks/useFitText.ts deleted file mode 100644 index ff1470e6a..000000000 --- a/apps/client/src/common/hooks/useFitText.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; - -export type TLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none'; - -export type TOptions = { - logLevel?: TLogLevel; - maxFontSize?: number; - minFontSize?: number; - onFinish?: (fontSize: number) => void; - onStart?: () => void; - resolution?: number; -}; - -const LOG_LEVEL: Record = { - debug: 10, - info: 20, - warn: 30, - error: 40, - none: 100, -}; - -const useFitText = ({ - logLevel: logLevelOption = 'info', - maxFontSize = 100, - minFontSize = 20, - onFinish, - onStart, - resolution = 5, -}: TOptions = {}) => { - const logLevel = LOG_LEVEL[logLevelOption]; - - const initState = useCallback(() => { - return { - calcKey: 0, - fontSize: maxFontSize, - fontSizePrev: minFontSize, - fontSizeMax: maxFontSize, - fontSizeMin: minFontSize, - }; - }, [maxFontSize, minFontSize]); - - const ref = useRef(null); - const innerHtmlPrevRef = useRef(); - const isCalculatingRef = useRef(false); - const [state, setState] = useState(initState); - const { calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state; - - // Monitor div size changes and recalculate on resize - let animationFrameId: number | null = null; - const [ro] = useState( - () => - new ResizeObserver(() => { - animationFrameId = window.requestAnimationFrame(() => { - if (isCalculatingRef.current) { - return; - } - onStart && onStart(); - isCalculatingRef.current = true; - // `calcKey` is used in the dependencies array of - // `useIsoLayoutEffect` below. It is incremented so that the font size - // will be recalculated even if the previous state didn't change (e.g. - // when the text fit initially). - setState({ - ...initState(), - calcKey: calcKey + 1, - }); - }); - }), - ); - - useEffect(() => { - if (ref.current) { - ro.observe(ref.current); - } - return () => { - animationFrameId && window.cancelAnimationFrame(animationFrameId); - ro.disconnect(); - }; - }, [animationFrameId, ro]); - - // Recalculate when the div contents change - const innerHtml = ref.current && ref.current.innerHTML; - useEffect(() => { - if (calcKey === 0 || isCalculatingRef.current) { - return; - } - if (innerHtml !== innerHtmlPrevRef.current) { - onStart && onStart(); - setState({ - ...initState(), - calcKey: calcKey + 1, - }); - } - innerHtmlPrevRef.current = innerHtml; - }, [calcKey, initState, innerHtml, onStart]); - - // Check overflow and resize font - useLayoutEffect(() => { - // Don't start calculating font size until the `resizeKey` is incremented - // above in the `ResizeObserver` callback. This avoids an extra resize - // on initialization. - if (calcKey === 0) { - return; - } - - const isWithinResolution = Math.abs(fontSize - fontSizePrev) <= resolution; - const isOverflow = - !!ref.current && - (ref.current.scrollHeight > ref.current.offsetHeight || ref.current.scrollWidth > ref.current.offsetWidth); - const isFailed = isOverflow && fontSize === fontSizePrev; - const isAsc = fontSize > fontSizePrev; - - // Return if the font size has been adjusted "enough" (change within `resolution`) - // reduce font size by one increment if it's overflowing. - if (isWithinResolution) { - if (isFailed) { - isCalculatingRef.current = false; - if (logLevel <= LOG_LEVEL.info) { - console.info(`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`); - } - } else if (isOverflow) { - setState({ - fontSize: isAsc ? fontSizePrev : fontSizeMin, - fontSizeMax, - fontSizeMin, - fontSizePrev, - calcKey, - }); - } else { - isCalculatingRef.current = false; - onFinish && onFinish(fontSize); - } - return; - } - - // Binary search to adjust font size - let delta: number; - let newMax = fontSizeMax; - let newMin = fontSizeMin; - if (isOverflow) { - delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize; - newMax = Math.min(fontSizeMax, fontSize); - } else { - delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize; - newMin = Math.max(fontSizeMin, fontSize); - } - setState({ - calcKey, - fontSize: fontSize + delta / 2, - fontSizeMax: newMax, - fontSizeMin: newMin, - fontSizePrev: fontSize, - }); - }, [calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev, onFinish, ref, resolution]); - - return { fontSize: `${fontSize}%`, ref }; -}; - -export default useFitText; diff --git a/apps/client/src/features/viewers/studio/StudioClock.scss b/apps/client/src/features/viewers/studio/StudioClock.scss index e9862882d..2968e8a5d 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.scss +++ b/apps/client/src/features/viewers/studio/StudioClock.scss @@ -120,6 +120,9 @@ $orange-active: #f60; } .next-title { + height: 12.5vh; + width: 70%; + font-family: monospace; font-weight: 400; color: var(--studio-active-label, $cyan-active); diff --git a/apps/client/src/features/viewers/studio/StudioClock.tsx b/apps/client/src/features/viewers/studio/StudioClock.tsx index 7a435b579..114105b4a 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.tsx +++ b/apps/client/src/features/viewers/studio/StudioClock.tsx @@ -4,8 +4,8 @@ import { Playback } from 'ontime-types'; import { millisToString, removeSeconds, secondsInMillis } from 'ontime-utils'; import { overrideStylesURL } from '../../../common/api/constants'; +import { FitText } from '../../../common/components/fit-text/FitText'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; -import useFitText from '../../../common/hooks/useFitText'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useWindowTitle } from '../../../common/hooks/useWindowTitle'; import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; @@ -32,15 +32,17 @@ interface StudioClockProps { export default function StudioClock(props: StudioClockProps) { const { isMirrored, eventNext, time, backstageEvents, selectedId, nextId, onAir, viewSettings, settings } = props; - // TODO: can we prevent the Flash of Unstyled Content on the 7segment fonts? - // deferring rendering seems to affect styling (font and useFitText) - useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); - const { fontSize: titleFontSize, ref: titleRef } = useFitText({ minFontSize: 150, maxFontSize: 500 }); + const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [searchParams] = useSearchParams(); useWindowTitle('Studio Clock'); + // defer rendering until we load stylesheets + if (!shouldRender) { + return null; + } + const activeIndicators = [...Array(12).keys()]; const secondsIndicators = [...Array(60).keys()]; @@ -77,13 +79,7 @@ export default function StudioClock(props: StudioClockProps) {
{hasAmPm &&
{hasAmPm}
}
{clock}
-
- {eventNext?.title} -
+ {eventNext?.title}