mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 15:09:10 +00:00
feat: timer overview shows waiting timer
This commit is contained in:
committed by
Carlos Valente
parent
dddeefd544
commit
f1a8db9649
@@ -1,5 +1,3 @@
|
|||||||
import { Playback } from 'ontime-types';
|
|
||||||
|
|
||||||
import { usePlaybackControl } from '../../../common/hooks/useSocket';
|
import { usePlaybackControl } from '../../../common/hooks/useSocket';
|
||||||
|
|
||||||
import AddTime from './add-time/AddTime';
|
import AddTime from './add-time/AddTime';
|
||||||
@@ -14,7 +12,7 @@ export default function PlaybackControl() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.mainContainer}>
|
<div className={style.mainContainer}>
|
||||||
<PlaybackTimer playback={data.playback as Playback}>
|
<PlaybackTimer>
|
||||||
<AddTime playback={data.playback} />
|
<AddTime playback={data.playback} />
|
||||||
</PlaybackTimer>
|
</PlaybackTimer>
|
||||||
<PlaybackButtons
|
<PlaybackButtons
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ import TimerDisplay from '../timer-display/TimerDisplay';
|
|||||||
|
|
||||||
import style from './PlaybackTimer.module.scss';
|
import style from './PlaybackTimer.module.scss';
|
||||||
|
|
||||||
interface PlaybackTimerProps {
|
|
||||||
playback: Playback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveAddedTimeLabel(addedTime: number) {
|
function resolveAddedTimeLabel(addedTime: number) {
|
||||||
if (addedTime > 0) {
|
if (addedTime > 0) {
|
||||||
return `Added ${formatDuration(addedTime, false)}`;
|
return `Added ${formatDuration(addedTime, false)}`;
|
||||||
@@ -27,11 +23,10 @@ function resolveAddedTimeLabel(addedTime: number) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PlaybackTimer(props: PropsWithChildren<PlaybackTimerProps>) {
|
export default function PlaybackTimer({ children }: PropsWithChildren) {
|
||||||
const { playback, children } = props;
|
|
||||||
const timer = useTimer();
|
const timer = useTimer();
|
||||||
|
|
||||||
const isRolling = playback === Playback.Roll;
|
const isRolling = timer.playback === Playback.Roll;
|
||||||
const isWaiting = timer.phase === TimerPhase.Pending;
|
const isWaiting = timer.phase === TimerPhase.Pending;
|
||||||
const isOvertime = timer.phase === TimerPhase.Overtime;
|
const isOvertime = timer.phase === TimerPhase.Overtime;
|
||||||
const hasAddedTime = Boolean(timer.addedTime);
|
const hasAddedTime = Boolean(timer.addedTime);
|
||||||
@@ -52,7 +47,7 @@ export default function PlaybackTimer(props: PropsWithChildren<PlaybackTimerProp
|
|||||||
{isWaiting ? (
|
{isWaiting ? (
|
||||||
<span className={style.rolltag}>Roll: Countdown to start</span>
|
<span className={style.rolltag}>Roll: Countdown to start</span>
|
||||||
) : (
|
) : (
|
||||||
<RunningStatus startedAt={timer.startedAt} expectedFinish={timer.expectedFinish} playback={playback} />
|
<RunningStatus startedAt={timer.startedAt} expectedFinish={timer.expectedFinish} playback={timer.playback} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
@@ -65,9 +60,7 @@ interface RunningStatusProps {
|
|||||||
expectedFinish: MaybeNumber;
|
expectedFinish: MaybeNumber;
|
||||||
playback: Playback;
|
playback: Playback;
|
||||||
}
|
}
|
||||||
function RunningStatus(props: RunningStatusProps) {
|
function RunningStatus({ startedAt, expectedFinish, playback }: RunningStatusProps) {
|
||||||
const { startedAt, expectedFinish, playback } = props;
|
|
||||||
|
|
||||||
if (playback === Playback.Stop) {
|
if (playback === Playback.Stop) {
|
||||||
return <StoppedStatus />;
|
return <StoppedStatus />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,12 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: $zindex-floating;
|
z-index: $zindex-floating;
|
||||||
|
|
||||||
background-color: $gray-1350;
|
background-color: $gray-1350;
|
||||||
border-bottom: 1px solid $white-10;
|
|
||||||
box-shadow: $large-top-drawer-shadow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timers {
|
.timers {
|
||||||
padding-block: 0.75rem 0.5rem;
|
padding-block: 0.75rem 0.5rem;
|
||||||
padding-inline: 1rem;
|
padding-inline: 1rem 0;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
@@ -20,35 +17,14 @@
|
|||||||
|
|
||||||
.runningTimer {
|
.runningTimer {
|
||||||
grid-area: timers;
|
grid-area: timers;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
color: $muted-gray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeNow {
|
.timeNow {
|
||||||
grid-area: clock;
|
grid-area: clock;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-self: right;
|
justify-self: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: calc(1rem - 2px);
|
|
||||||
color: $gray-700;
|
|
||||||
line-height: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timer {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
color: $ui-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progressOverride {
|
.progressOverride {
|
||||||
grid-area: bar;
|
grid-area: bar;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
|||||||
@@ -1,33 +1,12 @@
|
|||||||
import { isPlaybackActive } from 'ontime-utils';
|
import { ClockOverview, TimerOverview } from '../../overview/composite/TimeElements';
|
||||||
|
|
||||||
import { useClock, useTimer } from '../../../common/hooks/useSocket';
|
import style from './StatusBar.module.scss';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
|
||||||
import ClockTime from '../../viewers/common/clock-time/ClockTime';
|
|
||||||
import RunningTime from '../../viewers/common/running-time/RunningTime';
|
|
||||||
|
|
||||||
import styles from './StatusBar.module.scss';
|
|
||||||
|
|
||||||
export default function StatusBarTimers() {
|
export default function StatusBarTimers() {
|
||||||
const timer = useTimer();
|
|
||||||
const { clock } = useClock();
|
|
||||||
|
|
||||||
const playbackActive = isPlaybackActive(timer.playback);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.timers}>
|
<div className={style.timers}>
|
||||||
<div className={styles.runningTimer}>
|
<TimerOverview className={style.runningTimer} />
|
||||||
<span className={styles.label}>Running timer</span>
|
<ClockOverview className={style.timeNow} />
|
||||||
<RunningTime
|
|
||||||
className={cx([styles.timer, playbackActive && styles.active])}
|
|
||||||
value={timer.current}
|
|
||||||
hideLeadingZero
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.timeNow}>
|
|
||||||
<span className={styles.label}>Time now</span>
|
|
||||||
<ClockTime className={styles.timer} value={clock} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { TbCalendar, TbCalendarClock, TbCalendarDown, TbCalendarStar, TbFlagDown, TbFlagStar } from 'react-icons/tb';
|
import { TbCalendar, TbCalendarClock, TbCalendarDown, TbCalendarStar, TbFlagDown, TbFlagStar } from 'react-icons/tb';
|
||||||
import { isOntimeBlock, OntimeBlock, OntimeEvent, TimerType } from 'ontime-types';
|
import { isOntimeBlock, OntimeBlock, OntimeEvent, TimerPhase, TimerType } from 'ontime-types';
|
||||||
import { isPlaybackActive, millisToString } from 'ontime-utils';
|
import { isPlaybackActive, millisToString } from 'ontime-utils';
|
||||||
|
|
||||||
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
||||||
@@ -194,7 +194,7 @@ export function ProgressOverview() {
|
|||||||
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
|
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
|
||||||
const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash;
|
const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash;
|
||||||
|
|
||||||
return <TimeColumn label='Progress' value={progressText} muted={selectedEventIndex === null} />;
|
return <TimeColumn label='Progress' value={progressText} state={selectedEventIndex === null ? 'muted' : 'active'} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OffsetOverview() {
|
export function OffsetOverview() {
|
||||||
@@ -208,16 +208,23 @@ export function OffsetOverview() {
|
|||||||
return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
|
return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ClockOverview() {
|
export function ClockOverview({ className }: { className?: string }) {
|
||||||
const { clock } = useClock();
|
const { clock } = useClock();
|
||||||
|
|
||||||
return <TimeColumn label='Time now' value={formattedTime(clock)} />;
|
return <TimeColumn label='Time now' value={formattedTime(clock)} className={className} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TimerOverview() {
|
export function TimerOverview({ className }: { className?: string }) {
|
||||||
const { current } = useTimer();
|
const timer = useTimer();
|
||||||
|
|
||||||
const display = millisToString(current, { fallback: timerPlaceholder });
|
const isWaiting = timer.phase === TimerPhase.Pending;
|
||||||
|
const title = isWaiting ? 'Count to start' : 'Running timer';
|
||||||
|
const display = millisToString(isWaiting ? timer.secondaryTimer : timer.current, { fallback: timerPlaceholder });
|
||||||
|
const timerState = (() => {
|
||||||
|
if (isWaiting) return 'waiting';
|
||||||
|
if (timer.current === null) return 'muted';
|
||||||
|
return 'active';
|
||||||
|
})();
|
||||||
|
|
||||||
return <TimeColumn label='Running timer' value={display} muted={current === null} />;
|
return <TimeColumn label={title} value={display} state={timerState} className={className} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,11 @@
|
|||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-state='waiting'] {
|
||||||
|
.label {
|
||||||
|
color: $ontime-roll;
|
||||||
|
}
|
||||||
|
}
|
||||||
&[data-state='over'] {
|
&[data-state='over'] {
|
||||||
.label .over {
|
.label .over {
|
||||||
color: $playback-over;
|
color: $playback-over;
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ import style from './TimeLayout.module.scss';
|
|||||||
interface TimeLayoutProps {
|
interface TimeLayoutProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
muted?: boolean;
|
state?: 'muted' | 'waiting' | 'active';
|
||||||
daySpan?: number;
|
daySpan?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TimeColumn({ label, value, muted, className, testId }: TimeLayoutProps) {
|
export function TimeColumn({ label, value, state = 'active', className, testId }: TimeLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<div className={style.column} data-state={muted ? 'muted' : 'active'}>
|
<div className={cx([style.column, className])} data-state={state}>
|
||||||
<span className={style.label}>{label}</span>
|
<span className={style.label}>{label}</span>
|
||||||
<span className={cx([style.clock, className])} data-testid={testId}>
|
<span className={style.clock} data-testid={testId}>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ $active-green: $green-700;
|
|||||||
|
|
||||||
// playback colours
|
// playback colours
|
||||||
$playback-start: $green-600;
|
$playback-start: $green-600;
|
||||||
$ontime-roll: #0274B6;
|
$ontime-roll: #22a0e9;
|
||||||
$ontime-delay: #F57C13;
|
$ontime-delay: #F57C13;
|
||||||
$ontime-delay-text: #E69056;
|
$ontime-delay-text: #E69056;
|
||||||
$ontime-paused: #c05621;
|
$ontime-paused: #c05621;
|
||||||
|
|||||||
Reference in New Issue
Block a user