Custom add (#889)

This commit is contained in:
Carlos Valente
2024-04-14 21:52:12 +02:00
committed by GitHub
parent 0ef1b44d9e
commit 70c93a69fe
7 changed files with 155 additions and 127 deletions
@@ -2,6 +2,7 @@ import { Playback } from 'ontime-types';
import { usePlaybackControl } from '../../../common/hooks/useSocket';
import AddTime from './add-time/AddTime';
import { ExtraTimer } from './extra-timer/ExtraTimer';
import PlaybackButtons from './playback-buttons/PlaybackButtons';
import PlaybackTimer from './playback-timer/PlaybackTimer';
@@ -13,7 +14,9 @@ export default function PlaybackControl() {
return (
<div className={style.mainContainer}>
<PlaybackTimer playback={data.playback as Playback} />
<PlaybackTimer playback={data.playback as Playback}>
<AddTime playback={data.playback} />
</PlaybackTimer>
<PlaybackButtons
playback={data.playback}
numEvents={data.numEvents}
@@ -0,0 +1,16 @@
.addTime {
grid-area: addtime;
display: flex;
flex-direction: column;
gap: $element-inner-spacing;
}
.addButtons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $element-inner-spacing;
}
.tallButtons {
height: 28px;
}
@@ -0,0 +1,58 @@
import { Tooltip } from '@chakra-ui/react';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
import { Playback } from 'ontime-types';
import { MILLIS_PER_SECOND } from 'ontime-utils';
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
import { useLocalStorage } from '../../../../common/hooks/useLocalStorage';
import { setPlayback } from '../../../../common/hooks/useSocket';
import { forgivingStringToMillis } from '../../../../common/utils/dateConfig';
import { tooltipDelayMid } from '../../../../ontimeConfig';
import TapButton from '../tap-button/TapButton';
import style from './AddTime.module.scss';
interface AddTimeProps {
playback: Playback;
}
export default function AddTime(props: AddTimeProps) {
const { playback } = props;
const [time, setTime] = useLocalStorage('add-time', 300_000); // 5 minutes
const handleTimeChange = (_field: string, value: string) => {
const newTime = forgivingStringToMillis(value);
setTime(newTime);
};
const handleAddTime = (direction: 'add' | 'remove') => {
// API expects input in seconds
if (direction === 'add') {
setPlayback.addTime(time / MILLIS_PER_SECOND);
} else {
setPlayback.addTime((-1 * time) / MILLIS_PER_SECOND);
}
};
const canAddTime = playback === Playback.Play || playback === Playback.Pause;
const doDisableButtons = !canAddTime || time === 0;
return (
<div className={style.addTime}>
<TimeInput name='addtime' submitHandler={handleTimeChange} time={time} placeholder='Add time' />
<div className={style.addButtons}>
<Tooltip label='Remove time' openDelay={tooltipDelayMid} shouldWrapChildren>
<TapButton onClick={() => handleAddTime('remove')} disabled={doDisableButtons} className={style.tallButtons}>
<IoRemove />
</TapButton>
</Tooltip>
<Tooltip label='Add time' openDelay={tooltipDelayMid} shouldWrapChildren>
<TapButton onClick={() => handleAddTime('add')} disabled={doDisableButtons} className={style.tallButtons}>
<IoAdd />
</TapButton>
</Tooltip>
</div>
</div>
);
}
@@ -1,16 +1,17 @@
.timeContainer {
display: grid;
grid-template-areas:
'ind clk clk btn'
'... sta fin btn';
'indicators timer addtime'
'status status addtime';
grid-template-rows: 1fr auto;
grid-template-columns: 1.25rem 1fr 1fr 5rem;
grid-template-columns: 1.25rem 1fr 6.5rem;
gap: $element-inner-spacing;
justify-items: start;
}
// ---------> INDICATORS
.indicators {
grid-area: ind;
grid-area: indicators;
width: 100%;
height: 100%;
display: flex;
@@ -18,72 +19,46 @@
justify-content: space-evenly;
}
.indRoll,
.indDelay,
.indNegative {
.indicatorRoll,
.indicatorDelay,
.indicatorNegative {
background-color: $black-10;
}
.indRoll,
.indRollActive,
.indDelay,
.indDelayActive {
.indicatorRoll,
.indicatorDelay {
margin: 0 auto;
border-radius: 6px;
width: 12px;
height: 12px;
border-radius: 5px;
width: 10px;
height: 10px;
}
.indRollActive {
.indicatorRoll[data-active='true'] {
background-color: $ontime-roll;
}
.indNegative,
.indNegativeActive {
margin: 0 auto;
width: 90%;
height: 0.25rem;
}
.indNegativeActive {
background-color: $playback-negative;
}
.indDelayActive {
.indicatorDelay[data-active='true'] {
background-color: $ontime-delay;
}
.btn {
grid-area: btn;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
width: 100%;
gap: $element-inner-spacing;
.indicatorNegative {
margin: 0 auto;
width: 90%;
height: 0.25rem;
&[data-active='true'] {
background-color: $playback-negative;
}
}
.minus {
grid-area: min;
display: flex;
flex-direction: column;
}
// ---------> LABELS
.start,
.finish,
.roll {
.status {
grid-area: status;
height: 1.5rem;
}
.start {
grid-area: sta;
}
.finish {
grid-area: fin;
}
.roll {
grid-area: 2 / 2 / 2 / 4 ;
display: flex;
gap: $section-spacing;
margin-left: 1.5rem;
}
.tag {
@@ -1,10 +1,9 @@
import { PropsWithChildren } from 'react';
import { Tooltip } from '@chakra-ui/react';
import { Playback } from 'ontime-types';
import { dayInMs, millisToMinutes, millisToSeconds, millisToString } from 'ontime-utils';
import { setPlayback, useTimer } from '../../../../common/hooks/useSocket';
import { tooltipDelayMid } from '../../../../ontimeConfig';
import TapButton from '../tap-button/TapButton';
import { useTimer } from '../../../../common/hooks/useSocket';
import TimerDisplay from '../timer-display/TimerDisplay';
import style from './PlaybackTimer.module.scss';
@@ -13,8 +12,30 @@ interface PlaybackTimerProps {
playback: Playback;
}
export default function PlaybackTimer(props: PlaybackTimerProps) {
const { playback } = props;
function resolveAddedTimeLabel(addedTime: number) {
function resolveClosestUnit(ms: number) {
if (ms < 6000) {
return `${millisToSeconds(ms)} seconds`;
} else if (ms < 12000) {
return '1 minute';
} else {
return `${millisToMinutes(ms)} minutes`;
}
}
if (addedTime > 0) {
return `Added ${resolveClosestUnit(addedTime)}`;
}
if (addedTime < 0) {
return `Removed ${resolveClosestUnit(addedTime)}`;
}
return '';
}
export default function PlaybackTimer(props: PropsWithChildren<PlaybackTimerProps>) {
const { playback, children } = props;
const timer = useTimer();
const started = millisToString(timer.startedAt);
@@ -22,88 +43,43 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
const finish = millisToString(expectedFinish);
const isRolling = playback === Playback.Roll;
const isStopped = playback === Playback.Stop;
const isWaiting = timer.secondaryTimer !== null && timer.secondaryTimer > 0 && timer.current === null;
const disableButtons = isStopped || isRolling;
const isOvertime = timer.current !== null && timer.current < 0;
const hasAddedTime = Boolean(timer.addedTime);
const rollLabel = isRolling ? 'Roll mode active' : '';
const resolveAddedTimeLabel = () => {
function resolveClosestUnit(ms: number) {
if (ms < 6000) {
return `${millisToSeconds(ms)} seconds`;
} else if (ms < 12000) {
return '1 minute';
} else {
return `${millisToMinutes(ms)} minutes`;
}
}
if (timer.addedTime > 0) {
return `Added ${resolveClosestUnit(timer.addedTime)}`;
}
if (timer.addedTime < 0) {
return `Removed ${resolveClosestUnit(timer.addedTime)}`;
}
return '';
};
const addedTimeLabel = resolveAddedTimeLabel();
const addedTimeLabel = resolveAddedTimeLabel(timer.addedTime);
return (
<div className={style.timeContainer}>
<div className={style.indicators}>
<Tooltip label={rollLabel}>
<div className={isRolling ? style.indRollActive : style.indRoll} />
<div className={style.indicatorRoll} data-active={isRolling} />
</Tooltip>
<div className={isOvertime ? style.indNegativeActive : style.indNegative} />
<div className={style.indicatorNegative} data-active={isOvertime} />
<Tooltip label={addedTimeLabel}>
<div className={hasAddedTime ? style.indDelayActive : style.indDelay} />
<div className={style.indicatorDelay} data-active={hasAddedTime} />
</Tooltip>
</div>
<TimerDisplay time={isWaiting ? timer.secondaryTimer : timer.current} />
{isWaiting ? (
<div className={style.roll}>
<div className={style.status}>
{isWaiting ? (
<span className={style.rolltag}>Roll: Countdown to start</span>
</div>
) : (
<>
<div className={style.start}>
<span className={style.tag}>Started at</span>
<span className={style.time}>{started}</span>
</div>
<div className={style.finish}>
<span className={style.tag}>Expect end</span>
<span className={style.time}>{finish}</span>
</div>
</>
)}
<div className={style.btn}>
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
<TapButton onClick={() => setPlayback.addTime(-60)} disabled={disableButtons} aspect='square'>
-1
</TapButton>
</Tooltip>
<Tooltip label='Add 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
<TapButton onClick={() => setPlayback.addTime(60)} disabled={disableButtons} aspect='square'>
+1
</TapButton>
</Tooltip>
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
<TapButton onClick={() => setPlayback.addTime(-5 * 60)} disabled={disableButtons} aspect='square'>
-5
</TapButton>
</Tooltip>
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
<TapButton onClick={() => setPlayback.addTime(+5 * 60)} disabled={disableButtons} aspect='square'>
+5
</TapButton>
</Tooltip>
) : (
<>
<span className={style.start}>
<span className={style.tag}>Started at</span>
<span className={style.time}>{started}</span>
</span>
<span className={style.finish}>
<span className={style.tag}>Expect end</span>
<span className={style.time}>{finish}</span>
</span>
</>
)}
</div>
{children}
</div>
);
}
@@ -1,7 +1,7 @@
@use '../../../../theme/viewerDefs' as *;
.timer {
grid-area: clk;
grid-area: timer;
white-space: nowrap;
max-width: 18.75rem;
min-width: 5em;
@@ -1,7 +1,7 @@
@use './EditorMixin' as editor;
$min-playback-width: 27rem;
$max-playback-width: 35rem;
$max-playback-width: 30rem;
$panel-gap: 0.5rem;
.corner {