Feat/countdown style (#290)

* feat: Countdown Style

---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>
Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com>
Co-authored-by: Fabian Posenau <19673098+kellhogs@users.noreply.github.com>
This commit is contained in:
Fabian Posenau
2023-02-26 19:53:26 +01:00
committed by GitHub
parent 6d138ce9f1
commit c405698813
16 changed files with 228 additions and 139 deletions
@@ -0,0 +1,44 @@
import { TimerType } from 'ontime-types';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
import { formatTime } from '../../../common/utils/time';
const formatOptions = {
showSeconds: true,
format: 'hh:mm:ss a',
};
type TimerTypeParams = Pick<TimeManagerType, 'timerType' | 'current' | 'elapsed' | 'clock'>;
export function getTimerByType(timerObject?: TimerTypeParams): string | number | null {
let timer = null;
if (!timerObject) {
return timer;
}
if (timerObject.timerType === TimerType.CountDown) {
timer = timerObject.current;
} else if (timerObject.timerType === TimerType.CountUp) {
//todo: fix counting down issue in backend later
timer = (timerObject.elapsed ?? 0) < 0 ? 0 : timerObject.elapsed;
} else if (timerObject.timerType === TimerType.Clock) {
timer = formatTime(timerObject.clock, formatOptions);
}
return timer;
}
export function formatTimerDisplay(timer?: string | number | null): string {
let display = '';
if (typeof timer === 'string') {
display = timer;
} else if (timer === null || typeof timer === 'undefined' || isNaN(timer)) {
display = '-- : -- : --';
} else {
display = formatDisplay(millisToSeconds(timer), true);
}
return display;
}
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useAtom } from 'jotai';
import { EventData, Message, ViewSettings } from 'ontime-types';
import { EventData, Message, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings';
@@ -9,7 +9,7 @@ import NavigationMenu from '../../../common/components/navigation-menu/Navigatio
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { OverridableOptions } from '../../../common/models/View.types';
import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
import './MinimalTimer.scss';
@@ -128,23 +128,24 @@ export default function MinimalTimer(props: MinimalTimerProps) {
const showOverlay = pres.text !== '' && pres.visible;
const isPlaying = time.playback !== 'pause';
const isNegative = (time.current ?? 0) < 0;
const showFinished = isNegative && !userOptions?.hideOvertime;
const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
const showEndMessage = time.current < 0 && general.endMessage && !hideEndMessage;
const showFinished =
time.finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
const stageTimer = getTimerByType(time);
let display = formatTimerDisplay(stageTimer);
if (isNegative) {
display = `-${display}`;
}
const stageTimerCharacters = display.replace('/:/g', '').length;
const timerFontSize = (89 / (stageTimerCharacters - 1)) * (userOptions.size || 1);
const timerClasseNames = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
const baseClasses = `minimal-timer ${isMirrored ? 'mirror' : ''}`;
let stageTimer;
if (time.current === null) {
stageTimer = '- - : - -';
} else {
stageTimer = formatDisplay(Math.abs(millisToSeconds(time.current)), true);
if (time.current < 0) {
stageTimer = `-${stageTimer}`;
}
}
const stageTimerCharacters = stageTimer.replace('/:/g', '').length;
return (
<div
className={showFinished ? `${baseClasses} minimal-timer--finished` : baseClasses}
@@ -165,17 +166,17 @@ export default function MinimalTimer(props: MinimalTimerProps) {
<div className='end-message'>{general.endMessage}</div>
) : (
<div
className={`timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`}
className={timerClasseNames}
style={{
color: userOptions.textColour,
fontSize: `${(89 / (stageTimerCharacters - 1)) * (userOptions.size || 1)}vw`,
fontSize: `${timerFontSize}vw`,
fontFamily: userOptions.font,
top: userOptions.top,
left: userOptions.left,
backgroundColor: userOptions.textBackground,
}}
>
{stageTimer}
{display}
</div>
)}
</div>
@@ -35,6 +35,7 @@
grid-area: clock;
margin-left: auto;
font-weight: 600;
transition: opacity $viewer-transition-time;
.label {
font-size: clamp(16px, 1.5vw, 24px);
@@ -47,6 +48,10 @@
color: var(--secondary-color-override, $viewer-secondary-color);
letter-spacing: 0.05em;
}
&--hidden {
opacity: 0;
}
}
/* =================== TITLES ===================*/
@@ -71,7 +76,6 @@
grid-area: timer;
justify-self: center;
align-self: center;
color: var(--timer-color-override, $timer-color);
.end-message {
text-align: center;
@@ -84,6 +88,22 @@
.timer {
opacity: 1;
font-size: 20vw;
font-family: var(--font-family-override, $viewer-font-family);
color: var(--timer-color-override, $timer-color);
line-height: 0.9em;
text-align: center;
letter-spacing: 0.05em;
font-weight: 600;
&--paused {
opacity: $viewer-opacity-disabled;
transition: $viewer-transition-time;
}
&--finished {
color: $timer-finished-color;
}
}
}
@@ -93,13 +113,12 @@
margin: 0 auto;
opacity: 1;
transition: $viewer-transition-time;
}
.timer--paused,
.progress-container--paused {
&--paused {
opacity: $viewer-opacity-disabled;
transition: $viewer-transition-time;
}
}
/* =================== OVERLAY ===================*/
@@ -1,16 +1,17 @@
import { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { useAtom } from 'jotai';
import { TimerType } from 'ontime-types';
import PropTypes from 'prop-types';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import ProgressBar from '../../../common/components/progress-bar/ProgressBar';
import TimerDisplay from '../../../common/components/timer-display/TimerDisplay';
import TitleCard from '../../../common/components/title-card/TitleCard';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { formatTime } from '../../../common/utils/time';
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
import './Timer.scss';
@@ -61,29 +62,49 @@ export default function Timer(props) {
const clock = formatTime(time.clock, formatOptions);
const showOverlay = pres.text !== '' && pres.visible;
const isPlaying = time.playback !== 'pause';
const normalisedTime = time.current === null ? null : Math.max(time.current, 0);
const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
const showEndMessage = time.current < 0 && general.endMessage;
const showProgress = time.playback !== 'stop';
const showFinished = time.finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showClock = time.timerType !== TimerType.Clock;
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''}`;
const stageTimer = getTimerByType(time);
let display = formatTimerDisplay(stageTimer);
const stageTimerCharacters = display.replace('/:/g', '').length;
if (isNegative) {
display = `-${display}`;
}
const timerFontSize = 100 / stageTimerCharacters;
const timerClasseNames = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
return (
<div className={time.finished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
<NavigationMenu />
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
<div className='message'>{pres.text}</div>
</div>
<div className='clock-container'>
<div className={`clock-container ${showClock ? '' : 'clock-container--hidden'}`}>
<div className='label'>Time Now</div>
<div className='clock'>{clock}</div>
</div>
<div className='timer-container'>
{time.finished ? (
<div className='end-message'>
{!general.endMessage ? <TimerDisplay time={time.current} hideZeroHours /> : general.endMessage}
</div>
{showEndMessage ? (
<div className='end-message'>{general.endMessage}</div>
) : (
<TimerDisplay time={normalisedTime} hideZeroHours className={isPlaying ? 'timer' : 'timer--paused'} />
<div
className={timerClasseNames}
style={{
fontSize: `${timerFontSize}vw`,
}}
>
{display}
</div>
)}
</div>