mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 06:59:09 +00:00
v2 beta 4 (#361)
* ux: rename end action * chore: update demo db * style: tweaks on extracted rundown * chore: prevent console logs in production code * feat: go mode * style: remove window size limits * style: rename delete action * style: small tweaks on icons * fix: style override on params * chore: update test db * refactor: small code quality improvements * refactor: prevent circular imports
This commit is contained in:
@@ -11,9 +11,16 @@
|
|||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["e2e/**/**.spec.ts", "e2e/**/**.test.ts"],
|
"files": [
|
||||||
"extends": ["plugin:playwright/playwright-test"]
|
"e2e/**/**.spec.ts",
|
||||||
|
"e2e/**/**.test.ts"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:playwright/playwright-test"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rules": {}
|
"rules": {
|
||||||
|
"no-console": "warn"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export default function DelayInput(props: DelayInputProps) {
|
|||||||
let ignoreChange = false;
|
let ignoreChange = false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof duration === undefined) {
|
if (typeof duration === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setValue(millisToString(duration));
|
setValue(millisToString(duration));
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export const setPlayback = {
|
|||||||
start: () => socketSendJson('start'),
|
start: () => socketSendJson('start'),
|
||||||
pause: () => socketSendJson('pause'),
|
pause: () => socketSendJson('pause'),
|
||||||
roll: () => socketSendJson('roll'),
|
roll: () => socketSendJson('roll'),
|
||||||
|
startNext: () => socketSendJson('start-next'),
|
||||||
previous: () => {
|
previous: () => {
|
||||||
socketSendJson('previous');
|
socketSendJson('previous');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -461,10 +461,10 @@ describe('millisToDelayString()', () => {
|
|||||||
expect(millisToDelayString(0)).toBeNull();
|
expect(millisToDelayString(0)).toBeNull();
|
||||||
});
|
});
|
||||||
describe('converts values in seconds', () => {
|
describe('converts values in seconds', () => {
|
||||||
it(`shows a simple string with value in seconds`, () => {
|
it('shows a simple string with value in seconds', () => {
|
||||||
expect(millisToDelayString(10000)).toBe('+10 sec');
|
expect(millisToDelayString(10000)).toBe('+10 sec');
|
||||||
});
|
});
|
||||||
it(`... and its negative counterpart`, () => {
|
it('... and its negative counterpart', () => {
|
||||||
expect(millisToDelayString(-10000)).toBe('-10 sec');
|
expect(millisToDelayString(-10000)).toBe('-10 sec');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -478,16 +478,16 @@ describe('millisToDelayString()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('converts values in minutes', () => {
|
describe('converts values in minutes', () => {
|
||||||
it(`shows a simple string with value in minutes`, () => {
|
it('shows a simple string with value in minutes', () => {
|
||||||
expect(millisToDelayString(720000)).toBe('+12 min');
|
expect(millisToDelayString(720000)).toBe('+12 min');
|
||||||
});
|
});
|
||||||
it(`... and its negative counterpart`, () => {
|
it('... and its negative counterpart', () => {
|
||||||
expect(millisToDelayString(-720000)).toBe('-12 min');
|
expect(millisToDelayString(-720000)).toBe('-12 min');
|
||||||
});
|
});
|
||||||
it(`shows a simple string with value in minutes and seconds`, () => {
|
it('shows a simple string with value in minutes and seconds', () => {
|
||||||
expect(millisToDelayString(630000)).toBe('+00:10:30');
|
expect(millisToDelayString(630000)).toBe('+00:10:30');
|
||||||
});
|
});
|
||||||
it(`... and its negative counterpart`, () => {
|
it('... and its negative counterpart', () => {
|
||||||
expect(millisToDelayString(-630000)).toBe('-00:10:30');
|
expect(millisToDelayString(-630000)).toBe('-00:10:30');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -500,10 +500,10 @@ describe('millisToDelayString()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('converts values with full time string', () => {
|
describe('converts values with full time string', () => {
|
||||||
it(`positive added time`, () => {
|
it('positive added time', () => {
|
||||||
expect(millisToDelayString(45015000)).toBe('+12:30:15');
|
expect(millisToDelayString(45015000)).toBe('+12:30:15');
|
||||||
});
|
});
|
||||||
it(`negative added time`, () => {
|
it('negative added time', () => {
|
||||||
expect(millisToDelayString(-45015000)).toBe('-12:30:15');
|
expect(millisToDelayString(-45015000)).toBe('-12:30:15');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,13 +114,13 @@ function checkAmPm(value: string) {
|
|||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
function checkMatchers(value: string) {
|
function checkMatchers(value: string) {
|
||||||
const hoursMatch = value.match(/(\d+)h/);
|
const hoursMatch = /(\d+)h/.exec(value);
|
||||||
const hoursMatchValue = hoursMatch ? parse(hoursMatch[1]) : 0;
|
const hoursMatchValue = hoursMatch ? parse(hoursMatch[1]) : 0;
|
||||||
|
|
||||||
const minutesMatch = value.match(/(\d+)m/);
|
const minutesMatch = /(\d+)m/.exec(value);
|
||||||
const minutesMatchValue = minutesMatch ? parse(minutesMatch[1]) : 0;
|
const minutesMatchValue = minutesMatch ? parse(minutesMatch[1]) : 0;
|
||||||
|
|
||||||
const secondsMatch = value.match(/(\d+)s/);
|
const secondsMatch = /(\d+)s/.exec(value);
|
||||||
const secondsMatchValue = secondsMatch ? parse(secondsMatch[1]) : 0;
|
const secondsMatchValue = secondsMatch ? parse(secondsMatch[1]) : 0;
|
||||||
|
|
||||||
if (hoursMatchValue > 0 || minutesMatchValue > 0 || secondsMatchValue > 0) {
|
if (hoursMatchValue > 0 || minutesMatchValue > 0 || secondsMatchValue > 0) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export function isStringBoolean(text: string | null) {
|
||||||
|
if (text === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return text?.toLowerCase() === 'true' || text === '1';
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Playback } from 'ontime-types';
|
|
||||||
|
|
||||||
import PlaybackDisplay from './PlaybackDisplay';
|
|
||||||
import Transport from './Transport';
|
|
||||||
|
|
||||||
interface PlaybackButtonsProps {
|
|
||||||
playback: Playback;
|
|
||||||
noEvents: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PlaybackButtons(props: PlaybackButtonsProps) {
|
|
||||||
const { playback, noEvents } = props;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PlaybackDisplay playback={playback} noEvents={noEvents} />
|
|
||||||
<Transport playback={playback} noEvents={noEvents} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,130 +1,4 @@
|
|||||||
@use '../../../theme/v2Styles' as *;
|
|
||||||
@use '../../../theme/ontimeColours' as *;
|
|
||||||
@use '../../../theme/mixins' as *;
|
|
||||||
|
|
||||||
.mainContainer {
|
.mainContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
gap: $element-inner-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeContainer {
|
|
||||||
display: grid;
|
|
||||||
grid-template-areas:
|
|
||||||
'ind clk clk btn'
|
|
||||||
'... sta fin btn';
|
|
||||||
grid-template-rows: 1fr auto;
|
|
||||||
grid-template-columns: 1.5em 1fr 1fr 5em;
|
|
||||||
gap: $element-inner-spacing;
|
|
||||||
justify-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timer {
|
|
||||||
grid-area: clk;
|
|
||||||
white-space: nowrap;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indicators {
|
|
||||||
grid-area: ind;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indRoll,
|
|
||||||
.indDelay,
|
|
||||||
.indNegative {
|
|
||||||
background-color: $gray-1350;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indRoll,
|
|
||||||
.indRollActive,
|
|
||||||
.indDelay,
|
|
||||||
.indDelayActive {
|
|
||||||
margin: 0 auto;
|
|
||||||
border-radius: 6px;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indRollActive {
|
|
||||||
background-color: $ontime-roll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indNegative,
|
|
||||||
.indNegativeActive {
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 90%;
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indNegativeActive {
|
|
||||||
background-color: $playback-negative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indDelayActive {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.minus {
|
|
||||||
grid-area: min;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start,
|
|
||||||
.finish,
|
|
||||||
.roll {
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start {
|
|
||||||
grid-area: sta;
|
|
||||||
}
|
|
||||||
|
|
||||||
.finish {
|
|
||||||
grid-area: fin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.roll {
|
|
||||||
grid-area: 2 / 2 / 2 / 4 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
color: $section-white;
|
|
||||||
font-size: $text-body-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
color: $label-gray;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rolltag {
|
|
||||||
color: $ontime-roll;
|
|
||||||
font-size: $text-body-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playbackContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
padding-top: 0.5em;
|
|
||||||
gap: $element-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invertX {
|
|
||||||
transform: rotateY(180deg);
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@ import { Playback } from 'ontime-types';
|
|||||||
|
|
||||||
import { usePlaybackControl } from '../../../common/hooks/useSocket';
|
import { usePlaybackControl } from '../../../common/hooks/useSocket';
|
||||||
|
|
||||||
import PlaybackButtons from './PlaybackButtons';
|
import PlaybackButtons from './playback-buttons/PlaybackButtons';
|
||||||
import PlaybackTimer from './PlaybackTimer';
|
import PlaybackTimer from './playback-timer/PlaybackTimer';
|
||||||
|
|
||||||
import style from './PlaybackControl.module.scss';
|
import style from './PlaybackControl.module.scss';
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
|
||||||
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
|
||||||
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
|
|
||||||
import { Playback } from 'ontime-types';
|
|
||||||
|
|
||||||
import { setPlayback } from '../../../common/hooks/useSocket';
|
|
||||||
|
|
||||||
import TapButton from './TapButton';
|
|
||||||
|
|
||||||
import style from './PlaybackControl.module.scss';
|
|
||||||
|
|
||||||
interface PlaybackProps {
|
|
||||||
playback: Playback;
|
|
||||||
noEvents: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PlaybackDisplay(props: PlaybackProps) {
|
|
||||||
const { playback, noEvents } = props;
|
|
||||||
const isRolling = playback === Playback.Roll;
|
|
||||||
const isPlaying = playback === Playback.Play;
|
|
||||||
const isPaused = playback === Playback.Pause;
|
|
||||||
const isArmed = playback === Playback.Armed;
|
|
||||||
const isStopped = playback === Playback.Stop;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={style.playbackContainer}>
|
|
||||||
<TapButton
|
|
||||||
onClick={() => setPlayback.start()}
|
|
||||||
disabled={isStopped || isRolling}
|
|
||||||
theme={Playback.Play}
|
|
||||||
active={isPlaying}
|
|
||||||
>
|
|
||||||
<IoPlay />
|
|
||||||
</TapButton>
|
|
||||||
|
|
||||||
<TapButton
|
|
||||||
onClick={() => setPlayback.pause()}
|
|
||||||
disabled={isStopped || isRolling || isArmed}
|
|
||||||
theme={Playback.Pause}
|
|
||||||
active={isPaused}
|
|
||||||
>
|
|
||||||
<IoPause />
|
|
||||||
</TapButton>
|
|
||||||
|
|
||||||
<TapButton
|
|
||||||
onClick={() => setPlayback.roll()}
|
|
||||||
disabled={!isStopped || noEvents}
|
|
||||||
theme={Playback.Roll}
|
|
||||||
active={isRolling}
|
|
||||||
>
|
|
||||||
<IoTimeOutline />
|
|
||||||
</TapButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';
|
|
||||||
import { Playback } from 'ontime-types';
|
|
||||||
|
|
||||||
import style from './TapButton.module.scss';
|
|
||||||
|
|
||||||
interface TapButtonProps {
|
|
||||||
disabled?: boolean;
|
|
||||||
square?: boolean;
|
|
||||||
onClick: () => void;
|
|
||||||
theme?: Playback | 'neutral';
|
|
||||||
active?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TapButton = forwardRef((props: PropsWithChildren<TapButtonProps>, ref: ForwardedRef<HTMLButtonElement> ) => {
|
|
||||||
const { children, disabled, onClick, theme = 'neutral', square, active } = props;
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={`${style.tapButton} ${style[theme]} ${square ? style.square : ''} ${active ? style.active : ''}`}
|
|
||||||
disabled={disabled}
|
|
||||||
type='button'
|
|
||||||
onClick={onClick}
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
TapButton.displayName = "TabButton";
|
|
||||||
export default TapButton;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { Tooltip } from '@chakra-ui/react';
|
|
||||||
import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack';
|
|
||||||
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
|
|
||||||
import { IoReload } from '@react-icons/all-files/io5/IoReload';
|
|
||||||
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
|
||||||
import { Playback } from 'ontime-types';
|
|
||||||
|
|
||||||
import { setPlayback } from '../../../common/hooks/useSocket';
|
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
|
||||||
|
|
||||||
import TapButton from './TapButton';
|
|
||||||
|
|
||||||
import style from './PlaybackControl.module.scss';
|
|
||||||
|
|
||||||
interface TransportProps {
|
|
||||||
playback: Playback;
|
|
||||||
noEvents: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Transport(props: TransportProps) {
|
|
||||||
const { playback, noEvents } = props;
|
|
||||||
const isRolling = playback === Playback.Roll;
|
|
||||||
const isStopped = playback === Playback.Stop;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={style.playbackContainer}>
|
|
||||||
<Tooltip label='Previous event' openDelay={tooltipDelayMid}>
|
|
||||||
<TapButton onClick={() => setPlayback.previous()} disabled={isRolling || noEvents}>
|
|
||||||
<IoPlaySkipBack />
|
|
||||||
</TapButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label='Next event' openDelay={tooltipDelayMid}>
|
|
||||||
<TapButton onClick={() => setPlayback.next()} disabled={isRolling || noEvents}>
|
|
||||||
<IoPlaySkipForward />
|
|
||||||
</TapButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label='Reload event' openDelay={tooltipDelayMid}>
|
|
||||||
<TapButton onClick={() => setPlayback.reload()} disabled={isStopped || isRolling}>
|
|
||||||
<IoReload className={style.invertX} />
|
|
||||||
</TapButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label='Unload Event' openDelay={tooltipDelayMid}>
|
|
||||||
<TapButton onClick={() => setPlayback.stop()} disabled={isStopped && !isRolling} theme={Playback.Stop}>
|
|
||||||
<IoStop />
|
|
||||||
</TapButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
@use '../../../../theme/v2Styles' as *;
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
padding-top: $element-spacing;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"go playback"
|
||||||
|
"go transport"
|
||||||
|
"extra extra";
|
||||||
|
grid-template-rows: repeat(3, 32px);
|
||||||
|
gap: $element-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.go {
|
||||||
|
grid-area: go;
|
||||||
|
font-size: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin spaced-flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
gap: $element-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playbackContainer {
|
||||||
|
grid-area: playback;
|
||||||
|
@include spaced-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transportContainer {
|
||||||
|
grid-area: transport;
|
||||||
|
@include spaced-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra {
|
||||||
|
grid-area: extra;
|
||||||
|
@include spaced-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invertX {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { Tooltip } from '@chakra-ui/react';
|
||||||
|
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
||||||
|
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||||
|
import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack';
|
||||||
|
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
|
||||||
|
import { IoReload } from '@react-icons/all-files/io5/IoReload';
|
||||||
|
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
||||||
|
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
|
||||||
|
import { Playback } from 'ontime-types';
|
||||||
|
|
||||||
|
import { setPlayback } from '../../../../common/hooks/useSocket';
|
||||||
|
import { tooltipDelayMid } from '../../../../ontimeConfig';
|
||||||
|
import TapButton from '../tap-button/TapButton';
|
||||||
|
|
||||||
|
import styles from './PlaybackButtons.module.scss';
|
||||||
|
import style from './PlaybackButtons.module.scss';
|
||||||
|
|
||||||
|
interface PlaybackButtonsProps {
|
||||||
|
playback: Playback;
|
||||||
|
noEvents: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PlaybackButtons(props: PlaybackButtonsProps) {
|
||||||
|
const { playback, noEvents } = props;
|
||||||
|
|
||||||
|
const isRolling = playback === Playback.Roll;
|
||||||
|
const isPlaying = playback === Playback.Play;
|
||||||
|
const isPaused = playback === Playback.Pause;
|
||||||
|
const isArmed = playback === Playback.Armed;
|
||||||
|
const isStopped = playback === Playback.Stop;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.buttonContainer}>
|
||||||
|
<TapButton disabled={isRolling} onClick={() => setPlayback.startNext()} aspect='fill' className={styles.go}>
|
||||||
|
GO
|
||||||
|
</TapButton>
|
||||||
|
<div className={style.playbackContainer}>
|
||||||
|
<TapButton
|
||||||
|
onClick={() => setPlayback.start()}
|
||||||
|
disabled={isStopped || isRolling}
|
||||||
|
theme={Playback.Play}
|
||||||
|
active={isPlaying}
|
||||||
|
>
|
||||||
|
<IoPlay />
|
||||||
|
</TapButton>
|
||||||
|
|
||||||
|
<TapButton
|
||||||
|
onClick={() => setPlayback.pause()}
|
||||||
|
disabled={isStopped || isRolling || isArmed}
|
||||||
|
theme={Playback.Pause}
|
||||||
|
active={isPaused}
|
||||||
|
>
|
||||||
|
<IoPause />
|
||||||
|
</TapButton>
|
||||||
|
</div>
|
||||||
|
<div className={style.transportContainer}>
|
||||||
|
<Tooltip label='Previous event' openDelay={tooltipDelayMid}>
|
||||||
|
<TapButton onClick={() => setPlayback.previous()} disabled={isRolling || noEvents}>
|
||||||
|
<IoPlaySkipBack />
|
||||||
|
</TapButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label='Next event' openDelay={tooltipDelayMid}>
|
||||||
|
<TapButton onClick={() => setPlayback.next()} disabled={isRolling || noEvents}>
|
||||||
|
<IoPlaySkipForward />
|
||||||
|
</TapButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div className={styles.extra}>
|
||||||
|
<TapButton
|
||||||
|
onClick={() => setPlayback.roll()}
|
||||||
|
disabled={!isStopped || noEvents}
|
||||||
|
theme={Playback.Roll}
|
||||||
|
active={isRolling}
|
||||||
|
>
|
||||||
|
<IoTimeOutline />
|
||||||
|
</TapButton>
|
||||||
|
<Tooltip label='Reload event' openDelay={tooltipDelayMid}>
|
||||||
|
<TapButton onClick={() => setPlayback.reload()} disabled={isStopped || isRolling}>
|
||||||
|
<IoReload className={style.invertX} />
|
||||||
|
</TapButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label='Unload Event' openDelay={tooltipDelayMid}>
|
||||||
|
<TapButton onClick={() => setPlayback.stop()} disabled={isStopped && !isRolling} theme={Playback.Stop}>
|
||||||
|
<IoStop />
|
||||||
|
</TapButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
@use '../../../../theme/v2Styles' as *;
|
||||||
|
@use '../../../../theme/ontimeColours' as *;
|
||||||
|
|
||||||
|
.timeContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
'ind clk clk btn'
|
||||||
|
'... sta fin btn';
|
||||||
|
grid-template-rows: 1fr auto;
|
||||||
|
grid-template-columns: 1.5em 1fr 1fr 5em;
|
||||||
|
gap: $element-inner-spacing;
|
||||||
|
justify-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer {
|
||||||
|
grid-area: clk;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicators {
|
||||||
|
grid-area: ind;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indRoll,
|
||||||
|
.indDelay,
|
||||||
|
.indNegative {
|
||||||
|
background-color: $black-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indRoll,
|
||||||
|
.indRollActive,
|
||||||
|
.indDelay,
|
||||||
|
.indDelayActive {
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indRollActive {
|
||||||
|
background-color: $ontime-roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indNegative,
|
||||||
|
.indNegativeActive {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 90%;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indNegativeActive {
|
||||||
|
background-color: $playback-negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indDelayActive {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minus {
|
||||||
|
grid-area: min;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start,
|
||||||
|
.finish,
|
||||||
|
.roll {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start {
|
||||||
|
grid-area: sta;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finish {
|
||||||
|
grid-area: fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll {
|
||||||
|
grid-area: 2 / 2 / 2 / 4 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
color: $section-white;
|
||||||
|
font-size: $text-body-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
color: $label-gray;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolltag {
|
||||||
|
color: $ontime-roll;
|
||||||
|
font-size: $text-body-size;
|
||||||
|
}
|
||||||
+11
-12
@@ -2,14 +2,13 @@ import { Tooltip } from '@chakra-ui/react';
|
|||||||
import { Playback } from 'ontime-types';
|
import { Playback } from 'ontime-types';
|
||||||
import { millisToString } from 'ontime-utils';
|
import { millisToString } from 'ontime-utils';
|
||||||
|
|
||||||
import TimerDisplay from '../../../common/components/timer-display/TimerDisplay';
|
import TimerDisplay from '../../../../common/components/timer-display/TimerDisplay';
|
||||||
import { setPlayback, useTimer } from '../../../common/hooks/useSocket';
|
import { setPlayback, useTimer } from '../../../../common/hooks/useSocket';
|
||||||
import { millisToMinutes, millisToSeconds } from '../../../common/utils/dateConfig';
|
import { millisToMinutes, millisToSeconds } from '../../../../common/utils/dateConfig';
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../../ontimeConfig';
|
||||||
|
import TapButton from '../tap-button/TapButton';
|
||||||
|
|
||||||
import TapButton from './TapButton';
|
import style from './PlaybackTimer.module.scss';
|
||||||
|
|
||||||
import style from './PlaybackControl.module.scss';
|
|
||||||
|
|
||||||
interface PlaybackTimerProps {
|
interface PlaybackTimerProps {
|
||||||
playback: Playback;
|
playback: Playback;
|
||||||
@@ -36,7 +35,7 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
|
|||||||
if (ms < 6000) {
|
if (ms < 6000) {
|
||||||
return `${millisToSeconds(ms)} seconds`;
|
return `${millisToSeconds(ms)} seconds`;
|
||||||
} else if (ms < 12000) {
|
} else if (ms < 12000) {
|
||||||
return `1 minute`;
|
return '1 minute';
|
||||||
} else {
|
} else {
|
||||||
return `${millisToMinutes(ms)} minutes`;
|
return `${millisToMinutes(ms)} minutes`;
|
||||||
}
|
}
|
||||||
@@ -87,22 +86,22 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
|
|||||||
)}
|
)}
|
||||||
<div className={style.btn}>
|
<div className={style.btn}>
|
||||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||||
<TapButton onClick={() => setPlayback.delay(-1)} disabled={disableButtons} square>
|
<TapButton onClick={() => setPlayback.delay(-1)} disabled={disableButtons} aspect='square'>
|
||||||
-1
|
-1
|
||||||
</TapButton>
|
</TapButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip label='Add 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
<Tooltip label='Add 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||||
<TapButton onClick={() => setPlayback.delay(1)} disabled={disableButtons} square>
|
<TapButton onClick={() => setPlayback.delay(1)} disabled={disableButtons} aspect='square'>
|
||||||
+1
|
+1
|
||||||
</TapButton>
|
</TapButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||||
<TapButton onClick={() => setPlayback.delay(-5)} disabled={disableButtons} square>
|
<TapButton onClick={() => setPlayback.delay(-5)} disabled={disableButtons} aspect='square'>
|
||||||
-5
|
-5
|
||||||
</TapButton>
|
</TapButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||||
<TapButton onClick={() => setPlayback.delay(+5)} disabled={disableButtons} square>
|
<TapButton onClick={() => setPlayback.delay(+5)} disabled={disableButtons} aspect='square'>
|
||||||
+5
|
+5
|
||||||
</TapButton>
|
</TapButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
+13
-4
@@ -1,15 +1,14 @@
|
|||||||
@use '../../../theme/v2Styles' as *;
|
@use '../../../../theme/v2Styles' as *;
|
||||||
@use '../../../theme/ontimeColours' as *;
|
@use '../../../../theme/ontimeColours' as *;
|
||||||
|
|
||||||
$button-bg-gray: $gray-1050;
|
$button-bg-gray: $gray-1050;
|
||||||
$button-color-white: $gray-50;
|
$button-color-white: $gray-50;
|
||||||
|
|
||||||
@mixin tap-factory($theme-color) {
|
@mixin tap-factory($theme-color) {
|
||||||
font-family: $ontime-font-family;
|
font-family: $ontime-font-family;
|
||||||
font-size: 22px;
|
font-size: 18px;
|
||||||
border-radius: $component-border-radius-md;
|
border-radius: $component-border-radius-md;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 3/1;
|
|
||||||
transition-property: color, background-color;
|
transition-property: color, background-color;
|
||||||
transition-duration: $transition-time-feedback;
|
transition-duration: $transition-time-feedback;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -78,7 +77,17 @@ $button-color-white: $gray-50;
|
|||||||
@include tap-factory($ontime-stop);
|
@include tap-factory($ontime-stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tapButton.normal {
|
||||||
|
aspect-ratio: 3/1;
|
||||||
|
}
|
||||||
|
|
||||||
.tapButton.square {
|
.tapButton.square {
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tapButton.fill {
|
||||||
|
aspect-ratio: unset;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';
|
||||||
|
import { Playback } from 'ontime-types';
|
||||||
|
|
||||||
|
import { cx } from '../../../../common/utils/styleUtils';
|
||||||
|
|
||||||
|
import style from './TapButton.module.scss';
|
||||||
|
|
||||||
|
interface TapButtonProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
aspect?: 'normal' | 'square' | 'fill';
|
||||||
|
square?: boolean;
|
||||||
|
free?: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
theme?: Playback | 'neutral';
|
||||||
|
active?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TapButton = forwardRef((props: PropsWithChildren<TapButtonProps>, ref: ForwardedRef<HTMLButtonElement>) => {
|
||||||
|
const { children, disabled, onClick, theme = 'neutral', aspect = 'normal', active, className } = props;
|
||||||
|
const classes = cx([style.tapButton, className, style[theme], style[aspect], active ? style.active : null]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={classes} disabled={disabled} type='button' onClick={onClick} ref={ref}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TapButton.displayName = 'TabButton';
|
||||||
|
export default TapButton;
|
||||||
@@ -37,11 +37,11 @@ $playback-width: 450px;
|
|||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
grid-template-columns: $menu-width $rundown-width $playback-width auto;
|
grid-template-columns: $menu-width $rundown-width $playback-width auto;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'sett even play info'
|
'sett rundown play info'
|
||||||
'sett even mess info';
|
'sett rundown mess info';
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.editor,
|
.rundown,
|
||||||
.playback,
|
.playback,
|
||||||
.messages,
|
.messages,
|
||||||
.info,
|
.info,
|
||||||
@@ -98,7 +98,7 @@ $playback-width: 450px;
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor,
|
.rundown,
|
||||||
.info,
|
.info,
|
||||||
.settings {
|
.settings {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@@ -117,7 +117,7 @@ $playback-width: 450px;
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor,
|
.rundown,
|
||||||
.messages,
|
.messages,
|
||||||
.info,
|
.info,
|
||||||
.settings {
|
.settings {
|
||||||
@@ -128,7 +128,7 @@ $playback-width: 450px;
|
|||||||
|
|
||||||
.mainContainer {
|
.mainContainer {
|
||||||
.settings,
|
.settings,
|
||||||
.editor,
|
.rundown,
|
||||||
.messages,
|
.messages,
|
||||||
.playback,
|
.playback,
|
||||||
.info {
|
.info {
|
||||||
@@ -171,8 +171,9 @@ $playback-width: 450px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor {
|
.rundown {
|
||||||
grid-area: even;
|
grid-area: rundown;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
height: calc(100% - 24px);
|
height: calc(100% - 24px);
|
||||||
@@ -183,6 +184,7 @@ $playback-width: 450px;
|
|||||||
.info {
|
.info {
|
||||||
grid-area: info;
|
grid-area: info;
|
||||||
min-width: 17em;
|
min-width: 17em;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
|
|||||||
onChange={(event) => handleSubmit('endAction', event.target.value)}
|
onChange={(event) => handleSubmit('endAction', event.target.value)}
|
||||||
variant='ontime'
|
variant='ontime'
|
||||||
>
|
>
|
||||||
<option value={EndAction.Continue}>Continue</option>
|
<option value={EndAction.None}>None</option>
|
||||||
<option value={EndAction.Stop}>Stop</option>
|
<option value={EndAction.Stop}>Stop</option>
|
||||||
<option value={EndAction.LoadNext}>Load Next</option>
|
<option value={EndAction.LoadNext}>Load Next</option>
|
||||||
<option value={EndAction.PlayNext}>Play Next</option>
|
<option value={EndAction.PlayNext}>Play Next</option>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FiSave } from '@react-icons/all-files/fi/FiSave';
|
|||||||
import { FiUpload } from '@react-icons/all-files/fi/FiUpload';
|
import { FiUpload } from '@react-icons/all-files/fi/FiUpload';
|
||||||
import { IoExtensionPuzzle } from '@react-icons/all-files/io5/IoExtensionPuzzle';
|
import { IoExtensionPuzzle } from '@react-icons/all-files/io5/IoExtensionPuzzle';
|
||||||
import { IoExtensionPuzzleOutline } from '@react-icons/all-files/io5/IoExtensionPuzzleOutline';
|
import { IoExtensionPuzzleOutline } from '@react-icons/all-files/io5/IoExtensionPuzzleOutline';
|
||||||
import { IoHelpCircleOutline } from '@react-icons/all-files/io5/IoHelpCircleOutline';
|
import { IoHelp } from '@react-icons/all-files/io5/IoHelp';
|
||||||
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
|
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
|
||||||
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||||
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
||||||
@@ -30,7 +30,7 @@ interface MenuBarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const buttonStyle = {
|
const buttonStyle = {
|
||||||
fontSize: '1.5em',
|
fontSize: '1.25em',
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
colorScheme: 'white',
|
colorScheme: 'white',
|
||||||
_hover: {
|
_hover: {
|
||||||
@@ -155,7 +155,7 @@ export default function MenuBar(props: MenuBarProps) {
|
|||||||
<TooltipActionBtn
|
<TooltipActionBtn
|
||||||
{...buttonStyle}
|
{...buttonStyle}
|
||||||
className={isAboutOpen ? style.open : ''}
|
className={isAboutOpen ? style.open : ''}
|
||||||
icon={<IoHelpCircleOutline />}
|
icon={<IoHelp />}
|
||||||
clickHandler={onAboutOpen}
|
clickHandler={onAboutOpen}
|
||||||
tooltip='About'
|
tooltip='About'
|
||||||
aria-label='About'
|
aria-label='About'
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ export default function Rundown(props: RundownProps) {
|
|||||||
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
||||||
const showQuickEntry = eventSettings.showQuickEntry;
|
const showQuickEntry = eventSettings.showQuickEntry;
|
||||||
|
|
||||||
|
const isExtracted = window.location.pathname.includes('/rundown');
|
||||||
|
|
||||||
// cursor
|
// cursor
|
||||||
const cursor = useAppMode((state) => state.cursor);
|
const cursor = useAppMode((state) => state.cursor);
|
||||||
const appMode = useAppMode((state) => state.mode);
|
const appMode = useAppMode((state) => state.mode);
|
||||||
@@ -238,6 +240,7 @@ export default function Rundown(props: RundownProps) {
|
|||||||
previousEventId={previousEventId}
|
previousEventId={previousEventId}
|
||||||
playback={isSelected ? featureData.playback : undefined}
|
playback={isSelected ? featureData.playback : undefined}
|
||||||
isRolling={featureData.playback === Playback.Roll}
|
isRolling={featureData.playback === Playback.Roll}
|
||||||
|
disableEdit={isExtracted}
|
||||||
/>
|
/>
|
||||||
{((showQuickEntry && hasCursor) || isLast) && (
|
{((showQuickEntry && hasCursor) || isLast) && (
|
||||||
<QuickAddBlock
|
<QuickAddBlock
|
||||||
|
|||||||
@@ -26,11 +26,23 @@ interface RundownEntryProps {
|
|||||||
previousEventId?: string;
|
previousEventId?: string;
|
||||||
playback?: Playback; // we only care about this if this event is playing
|
playback?: Playback; // we only care about this if this event is playing
|
||||||
isRolling: boolean; // we need to know even if not related to this event
|
isRolling: boolean; // we need to know even if not related to this event
|
||||||
|
disableEdit: boolean; // we disable edit when the window is extracted
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RundownEntry(props: RundownEntryProps) {
|
export default function RundownEntry(props: RundownEntryProps) {
|
||||||
const { eventIndex, data, selected, hasCursor, next, delay, previousEnd, previousEventId, playback, isRolling } =
|
const {
|
||||||
props;
|
eventIndex,
|
||||||
|
data,
|
||||||
|
selected,
|
||||||
|
hasCursor,
|
||||||
|
next,
|
||||||
|
delay,
|
||||||
|
previousEnd,
|
||||||
|
previousEventId,
|
||||||
|
playback,
|
||||||
|
isRolling,
|
||||||
|
disableEdit,
|
||||||
|
} = props;
|
||||||
const { emitError } = useEmitLog();
|
const { emitError } = useEmitLog();
|
||||||
const { addEvent, updateEvent, deleteEvent } = useEventAction();
|
const { addEvent, updateEvent, deleteEvent } = useEventAction();
|
||||||
|
|
||||||
@@ -163,6 +175,7 @@ export default function RundownEntry(props: RundownEntryProps) {
|
|||||||
playback={playback}
|
playback={playback}
|
||||||
isRolling={isRolling}
|
isRolling={isRolling}
|
||||||
actionHandler={actionHandler}
|
actionHandler={actionHandler}
|
||||||
|
disableEdit={disableEdit}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (data.type === SupportedEvent.Block) {
|
} else if (data.type === SupportedEvent.Block) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import style from '../editors/Editor.module.scss';
|
|||||||
|
|
||||||
const RundownExport = () => {
|
const RundownExport = () => {
|
||||||
return (
|
return (
|
||||||
<Box className={style.editor} data-testid='panel-rundown'>
|
<Box className={style.rundown} data-testid='panel-rundown'>
|
||||||
<IoArrowUp className={style.corner} onClick={(event) => handleLinks(event, 'rundown')} />
|
<IoArrowUp className={style.corner} onClick={(event) => handleLinks(event, 'rundown')} />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RundownWrapper />
|
<RundownWrapper />
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ $block-cursor-color: $blue-400;
|
|||||||
border-radius: $block-border-radius;
|
border-radius: $block-border-radius;
|
||||||
margin: 4px 2px;
|
margin: 4px 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: $block-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin block-spacing() {
|
@mixin block-spacing() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { OntimeBlock, OntimeEvent } from 'ontime-types';
|
|||||||
import { cx } from '../../../common/utils/styleUtils';
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
import EditableBlockTitle from '../common/EditableBlockTitle';
|
import EditableBlockTitle from '../common/EditableBlockTitle';
|
||||||
import BlockActionMenu from '../event-block/composite/BlockActionMenu';
|
import BlockActionMenu from '../event-block/composite/BlockActionMenu';
|
||||||
import { EventItemActions } from '../RundownEntry';
|
import type { EventItemActions } from '../RundownEntry';
|
||||||
|
|
||||||
import style from './BlockBlock.module.scss';
|
import style from './BlockBlock.module.scss';
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import DelayInput from '../../../common/components/input/delay-input/DelayInput'
|
|||||||
import { useEventAction } from '../../../common/hooks/useEventAction';
|
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
import BlockActionMenu from '../event-block/composite/BlockActionMenu';
|
import BlockActionMenu from '../event-block/composite/BlockActionMenu';
|
||||||
import { EventItemActions } from '../RundownEntry';
|
import type { EventItemActions } from '../RundownEntry';
|
||||||
|
|
||||||
import style from './DelayBlock.module.scss';
|
import style from './DelayBlock.module.scss';
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ $skip-opacity: 0.1;
|
|||||||
padding-right: $block-clearance;
|
padding-right: $block-clearance;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: $transition-time-feedback;
|
||||||
|
|
||||||
@mixin declare-overrides(){
|
@mixin declare-overrides(){
|
||||||
--status-color-override: #{$gray-200};
|
--status-color-override: #{$gray-200};
|
||||||
--status-color-active-override: #{$green-400};
|
--status-color-active-override: #{$green-400};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { EndAction, OntimeEvent, Playback, TimerType } from 'ontime-types';
|
|||||||
|
|
||||||
import { useAppMode } from '../../../common/stores/appModeStore';
|
import { useAppMode } from '../../../common/stores/appModeStore';
|
||||||
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
|
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||||
import { EventItemActions } from '../RundownEntry';
|
import type { EventItemActions } from '../RundownEntry';
|
||||||
|
|
||||||
import EventBlockInner from './EventBlockInner';
|
import EventBlockInner from './EventBlockInner';
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ interface EventBlockProps {
|
|||||||
value: unknown;
|
value: unknown;
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
|
disableEdit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EventBlock(props: EventBlockProps) {
|
export default function EventBlock(props: EventBlockProps) {
|
||||||
@@ -65,6 +66,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
playback,
|
playback,
|
||||||
isRolling,
|
isRolling,
|
||||||
actionHandler,
|
actionHandler,
|
||||||
|
disableEdit,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const moveCursorTo = useAppMode((state) => state.setCursor);
|
const moveCursorTo = useAppMode((state) => state.setCursor);
|
||||||
@@ -164,6 +166,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
playback={playback}
|
playback={playback}
|
||||||
isRolling={isRolling}
|
isRolling={isRolling}
|
||||||
actionHandler={actionHandler}
|
actionHandler={actionHandler}
|
||||||
|
disableEdit={disableEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ interface EventBlockInnerProps {
|
|||||||
playback?: Playback;
|
playback?: Playback;
|
||||||
isRolling: boolean;
|
isRolling: boolean;
|
||||||
actionHandler: (action: EventItemActions, payload?: any) => void;
|
actionHandler: (action: EventItemActions, payload?: any) => void;
|
||||||
|
disableEdit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventBlockInner = (props: EventBlockInnerProps) => {
|
const EventBlockInner = (props: EventBlockInnerProps) => {
|
||||||
@@ -73,6 +74,7 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
|
|||||||
playback,
|
playback,
|
||||||
isRolling,
|
isRolling,
|
||||||
actionHandler,
|
actionHandler,
|
||||||
|
disableEdit,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [renderInner, setRenderInner] = useState(false);
|
const [renderInner, setRenderInner] = useState(false);
|
||||||
@@ -159,6 +161,7 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
backgroundColor={isOpen ? '#2B5ABC' : undefined}
|
backgroundColor={isOpen ? '#2B5ABC' : undefined}
|
||||||
color={isOpen ? 'white' : '#f6f6f6'}
|
color={isOpen ? 'white' : '#f6f6f6'}
|
||||||
|
isDisabled={disableEdit}
|
||||||
/>
|
/>
|
||||||
<BlockActionMenu showAdd showDelay showBlock showClone enableDelete={!selected} actionHandler={actionHandler} />
|
<BlockActionMenu showAdd showDelay showBlock showClone enableDelete={!selected} actionHandler={actionHandler} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default function BlockActionMenu(props: BlockActionMenuProps) {
|
|||||||
)}
|
)}
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<MenuItem icon={<IoTrashBinSharp />} onClick={handleDelete} isDisabled={!enableDelete} color='#D20300'>
|
<MenuItem icon={<IoTrashBinSharp />} onClick={handleDelete} isDisabled={!enableDelete} color='#D20300'>
|
||||||
Delete event
|
Delete
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ export default function Clock(props: ClockProps) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get config from url: key, text, font, size, hidenav, hideovertime
|
// get config from url: key, text, font, size, hidenav
|
||||||
// eg. http://localhost:3000/minimal?key=f00&text=fff
|
// eg. http://localhost:3000/clock?key=f00&text=fff
|
||||||
// Check for user options
|
// Check for user options
|
||||||
const userOptions: OverridableOptions = {
|
const userOptions: OverridableOptions = {
|
||||||
size: 1,
|
size: 1,
|
||||||
@@ -126,7 +126,6 @@ export default function Clock(props: ClockProps) {
|
|||||||
className={`clock-view ${isMirrored ? 'mirror' : ''}`}
|
className={`clock-view ${isMirrored ? 'mirror' : ''}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: userOptions.keyColour,
|
backgroundColor: userOptions.keyColour,
|
||||||
color: userOptions.textColour,
|
|
||||||
justifyContent: userOptions.justifyContent,
|
justifyContent: userOptions.justifyContent,
|
||||||
alignItems: userOptions.alignItems,
|
alignItems: userOptions.alignItems,
|
||||||
}}
|
}}
|
||||||
@@ -136,6 +135,7 @@ export default function Clock(props: ClockProps) {
|
|||||||
<div
|
<div
|
||||||
className='clock'
|
className='clock'
|
||||||
style={{
|
style={{
|
||||||
|
color: userOptions.textColour,
|
||||||
fontSize: `${(89 / (clean.length - 1)) * (userOptions.size || 1)}vw`,
|
fontSize: `${(89 / (clean.length - 1)) * (userOptions.size || 1)}vw`,
|
||||||
fontFamily: userOptions.font,
|
fontFamily: userOptions.font,
|
||||||
top: userOptions.top,
|
top: userOptions.top,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import NavigationMenu from '../../../common/components/navigation-menu/Navigatio
|
|||||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||||
import { OverridableOptions } from '../../../common/models/View.types';
|
import { OverridableOptions } from '../../../common/models/View.types';
|
||||||
|
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||||
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
||||||
|
|
||||||
import './MinimalTimer.scss';
|
import './MinimalTimer.scss';
|
||||||
@@ -33,7 +34,7 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get config from url: key, text, font, size, hidenav, hideovertime
|
// get config from url: key, text, font, size, hideovertime
|
||||||
// eg. http://localhost:3000/minimal?key=f00&text=fff
|
// eg. http://localhost:3000/minimal?key=f00&text=fff
|
||||||
// Check for user options
|
// Check for user options
|
||||||
const userOptions: OverridableOptions = {
|
const userOptions: OverridableOptions = {
|
||||||
@@ -116,19 +117,19 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hideOvertime = searchParams.get('hideovertime');
|
const hideOvertime = searchParams.get('hideovertime');
|
||||||
userOptions.hideOvertime = Boolean(hideOvertime);
|
userOptions.hideOvertime = isStringBoolean(hideOvertime);
|
||||||
|
|
||||||
const hideMessagesOverlay = searchParams.get('hidemessages');
|
const hideMessagesOverlay = searchParams.get('hidemessages');
|
||||||
userOptions.hideMessagesOverlay = Boolean(hideMessagesOverlay);
|
userOptions.hideMessagesOverlay = isStringBoolean(hideMessagesOverlay);
|
||||||
|
|
||||||
const hideEndMessage = searchParams.get('hideendmessage');
|
const hideEndMessage = searchParams.get('hideendmessage');
|
||||||
userOptions.hideEndMessage = Boolean(hideEndMessage);
|
userOptions.hideEndMessage = isStringBoolean(hideEndMessage);
|
||||||
|
|
||||||
const showOverlay = pres.text !== '' && pres.visible;
|
const showOverlay = pres.text !== '' && pres.visible;
|
||||||
const isPlaying = time.playback !== Playback.Pause;
|
const isPlaying = time.playback !== Playback.Pause;
|
||||||
const isNegative =
|
const isNegative =
|
||||||
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
|
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
|
||||||
const showEndMessage = time.current < 0 && general.endMessage && !hideEndMessage;
|
const showEndMessage = (time.current ?? 0) < 0 && general.endMessage && !hideEndMessage;
|
||||||
const showFinished =
|
const showFinished =
|
||||||
time.finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
|
time.finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
|
||||||
|
|
||||||
|
|||||||
@@ -122,8 +122,6 @@ function createWindow() {
|
|||||||
height: 1000,
|
height: 1000,
|
||||||
minWidth: 525,
|
minWidth: 525,
|
||||||
minHeight: 405,
|
minHeight: 405,
|
||||||
maxWidth: 1920,
|
|
||||||
maxHeight: 1440,
|
|
||||||
backgroundColor: '#101010', // $gray-1350
|
backgroundColor: '#101010', // $gray-1350
|
||||||
icon: appIcon,
|
icon: appIcon,
|
||||||
show: false,
|
show: false,
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ export function dispatchFromAdapter(type: string, payload: unknown, source?: 'os
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'start-next': {
|
||||||
|
PlaybackService.startNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'startindex': {
|
case 'startindex': {
|
||||||
const eventIndex = Number(payload);
|
const eventIndex = Number(payload);
|
||||||
if (isNaN(eventIndex) || eventIndex <= 0) {
|
if (isNaN(eventIndex) || eventIndex <= 0) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const event: Omit<OntimeEvent, 'id'> = {
|
|||||||
subtitle: '',
|
subtitle: '',
|
||||||
presenter: '',
|
presenter: '',
|
||||||
note: '',
|
note: '',
|
||||||
endAction: EndAction.Continue,
|
endAction: EndAction.None,
|
||||||
timerType: TimerType.CountDown,
|
timerType: TimerType.CountDown,
|
||||||
timeStart: 0,
|
timeStart: 0,
|
||||||
timeEnd: 0,
|
timeEnd: 0,
|
||||||
|
|||||||
@@ -116,15 +116,15 @@ export class PlaybackService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (fallbackAction === 'stop') {
|
} else if (fallbackAction === 'stop') {
|
||||||
logger.info('PLAYBACK', `No next event found! Stopping playback`);
|
logger.info('PLAYBACK', 'No next event found! Stopping playback');
|
||||||
PlaybackService.stop();
|
PlaybackService.stop();
|
||||||
return false;
|
return false;
|
||||||
} else if (fallbackAction === 'pause') {
|
} else if (fallbackAction === 'pause') {
|
||||||
logger.info('PLAYBACK', `No next event found! Pausing playback`);
|
logger.info('PLAYBACK', 'No next event found! Pausing playback');
|
||||||
PlaybackService.pause();
|
PlaybackService.pause();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
logger.info('PLAYBACK', `No next event found! Continuing playback`);
|
logger.info('PLAYBACK', 'No next event found! Continuing playback');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,10 +371,11 @@ export const fileHandler = async (file) => {
|
|||||||
res.data.userFields = parseUserFields(dataFromExcel);
|
res.data.userFields = parseUserFields(dataFromExcel);
|
||||||
res.message = 'success';
|
res.message = 'success';
|
||||||
} else {
|
} else {
|
||||||
console.log('Error: No sheets found named ontime or event schedule');
|
const errorMessage = 'No sheet found named ontime or event schedule';
|
||||||
|
console.log(errorMessage);
|
||||||
res = {
|
res = {
|
||||||
error: true,
|
error: true,
|
||||||
message: `No sheets found named ontime or event schedule`,
|
message: errorMessage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const parseEventData = (data, enforce): EventData => {
|
|||||||
};
|
};
|
||||||
} else if (enforce) {
|
} else if (enforce) {
|
||||||
newEventData = { ...dbModel.eventData };
|
newEventData = { ...dbModel.eventData };
|
||||||
console.log(`Created event object in db`);
|
console.log('Created event object in db');
|
||||||
}
|
}
|
||||||
return newEventData as EventData;
|
return newEventData as EventData;
|
||||||
};
|
};
|
||||||
@@ -126,7 +126,7 @@ export const parseSettings = (data, enforce): Settings => {
|
|||||||
}
|
}
|
||||||
} else if (enforce) {
|
} else if (enforce) {
|
||||||
newSettings = dbModel.settings;
|
newSettings = dbModel.settings;
|
||||||
console.log(`Created settings object in db`);
|
console.log('Created settings object in db');
|
||||||
}
|
}
|
||||||
return newSettings as Settings;
|
return newSettings as Settings;
|
||||||
};
|
};
|
||||||
@@ -153,7 +153,7 @@ export const parseViewSettings = (data, enforce): ViewSettings => {
|
|||||||
};
|
};
|
||||||
} else if (enforce) {
|
} else if (enforce) {
|
||||||
newViews = dbModel.viewSettings;
|
newViews = dbModel.viewSettings;
|
||||||
console.log(`Created viewSettings object in db`);
|
console.log('Created viewSettings object in db');
|
||||||
}
|
}
|
||||||
return newViews as ViewSettings;
|
return newViews as ViewSettings;
|
||||||
};
|
};
|
||||||
@@ -201,7 +201,7 @@ export const parseOsc = (data: { osc?: Partial<OSCSettings> }, enforce: boolean)
|
|||||||
subscriptions: validatedSubscriptions,
|
subscriptions: validatedSubscriptions,
|
||||||
};
|
};
|
||||||
} else if (enforce) {
|
} else if (enforce) {
|
||||||
console.log(`Created OSC object in db`);
|
console.log('Created OSC object in db');
|
||||||
return { ...dbModel.osc };
|
return { ...dbModel.osc };
|
||||||
} else return {};
|
} else return {};
|
||||||
};
|
};
|
||||||
@@ -232,7 +232,7 @@ export const parseHttp = (data, enforce) => {
|
|||||||
} else if (enforce) {
|
} else if (enforce) {
|
||||||
// @ts-expect-error -- not yet
|
// @ts-expect-error -- not yet
|
||||||
newHttp.http = { ...dbModel.http };
|
newHttp.http = { ...dbModel.http };
|
||||||
console.log(`Created http object in db`);
|
console.log('Created http object in db');
|
||||||
}
|
}
|
||||||
return newHttp;
|
return newHttp;
|
||||||
};
|
};
|
||||||
|
|||||||
+94
-1
@@ -1,5 +1,98 @@
|
|||||||
{
|
{
|
||||||
"rundown": [],
|
"rundown": [
|
||||||
|
{
|
||||||
|
"title": "First test event",
|
||||||
|
"subtitle": "",
|
||||||
|
"presenter": "",
|
||||||
|
"note": "",
|
||||||
|
"endAction": "continue",
|
||||||
|
"timerType": "count-down",
|
||||||
|
"timeStart": 32400000,
|
||||||
|
"timeEnd": 36000000,
|
||||||
|
"duration": 3600000,
|
||||||
|
"isPublic": true,
|
||||||
|
"skip": false,
|
||||||
|
"colour": "",
|
||||||
|
"user0": "",
|
||||||
|
"user1": "",
|
||||||
|
"user2": "",
|
||||||
|
"user3": "",
|
||||||
|
"user4": "",
|
||||||
|
"user5": "",
|
||||||
|
"user6": "",
|
||||||
|
"user7": "",
|
||||||
|
"user8": "",
|
||||||
|
"user9": "",
|
||||||
|
"type": "event",
|
||||||
|
"revision": 0,
|
||||||
|
"id": "aa42f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"duration": 600000,
|
||||||
|
"type": "delay",
|
||||||
|
"revision": 0,
|
||||||
|
"id": "b1d5a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Second test event",
|
||||||
|
"subtitle": "",
|
||||||
|
"presenter": "",
|
||||||
|
"note": "",
|
||||||
|
"endAction": "continue",
|
||||||
|
"timerType": "count-down",
|
||||||
|
"timeStart": 36000000,
|
||||||
|
"timeEnd": 39600000,
|
||||||
|
"duration": 3600000,
|
||||||
|
"isPublic": true,
|
||||||
|
"skip": false,
|
||||||
|
"colour": "",
|
||||||
|
"user0": "",
|
||||||
|
"user1": "",
|
||||||
|
"user2": "",
|
||||||
|
"user3": "",
|
||||||
|
"user4": "",
|
||||||
|
"user5": "",
|
||||||
|
"user6": "",
|
||||||
|
"user7": "",
|
||||||
|
"user8": "",
|
||||||
|
"user9": "",
|
||||||
|
"type": "event",
|
||||||
|
"revision": 0,
|
||||||
|
"id": "d71bc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lunch",
|
||||||
|
"type": "block",
|
||||||
|
"id": "91682"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Third test event",
|
||||||
|
"subtitle": "",
|
||||||
|
"presenter": "",
|
||||||
|
"note": "",
|
||||||
|
"endAction": "continue",
|
||||||
|
"timerType": "count-down",
|
||||||
|
"timeStart": 39600000,
|
||||||
|
"timeEnd": 720000,
|
||||||
|
"duration": 0,
|
||||||
|
"isPublic": true,
|
||||||
|
"skip": false,
|
||||||
|
"colour": "",
|
||||||
|
"user0": "",
|
||||||
|
"user1": "",
|
||||||
|
"user2": "",
|
||||||
|
"user3": "",
|
||||||
|
"user4": "",
|
||||||
|
"user5": "",
|
||||||
|
"user6": "",
|
||||||
|
"user7": "",
|
||||||
|
"user8": "",
|
||||||
|
"user9": "",
|
||||||
|
"type": "event",
|
||||||
|
"revision": 0,
|
||||||
|
"id": "da5b4"
|
||||||
|
}
|
||||||
|
],
|
||||||
"eventData": {
|
"eventData": {
|
||||||
"title": "All about Carlos demo event",
|
"title": "All about Carlos demo event",
|
||||||
"publicUrl": "www.getontime.no",
|
"publicUrl": "www.getontime.no",
|
||||||
|
|||||||
+47
-222
@@ -2,118 +2,15 @@
|
|||||||
"rundown": [
|
"rundown": [
|
||||||
{
|
{
|
||||||
"title": "Welcome to Ontime",
|
"title": "Welcome to Ontime",
|
||||||
"subtitle": "Subtitles are useful",
|
|
||||||
"presenter": "cpvalente",
|
|
||||||
"note": "Maybe a running note for the operator?",
|
|
||||||
"timeStart": 28800000,
|
|
||||||
"timeEnd": 30600000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 1800000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "5946"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "This is your event list",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "cpvalente",
|
|
||||||
"note": "",
|
|
||||||
"timeStart": 30600000,
|
|
||||||
"timeEnd": 34200000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 3600000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "c2e7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Events run in the list order",
|
|
||||||
"subtitle": "",
|
"subtitle": "",
|
||||||
"presenter": "",
|
"presenter": "",
|
||||||
"note": "",
|
"note": "Ontime is an app for managing event rundowns",
|
||||||
"timeStart": 34200000,
|
"endAction": "none",
|
||||||
"timeEnd": 34800000,
|
"timerType": "count-down",
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 600000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "cc0f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Unless recalled by the OSC address",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "",
|
|
||||||
"note": "In green, below",
|
|
||||||
"timeStart": 34800000,
|
|
||||||
"timeEnd": 35400000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 600000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "8ee5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Use simpler times to create a timer",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "",
|
|
||||||
"note": "",
|
|
||||||
"timeStart": 0,
|
"timeStart": 0,
|
||||||
"timeEnd": 600000,
|
"timeEnd": 600000,
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 600000,
|
"duration": 600000,
|
||||||
"isPublic": false,
|
"isPublic": true,
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"user0": "",
|
"user0": "",
|
||||||
@@ -127,100 +24,12 @@
|
|||||||
"user8": "",
|
"user8": "",
|
||||||
"user9": "",
|
"user9": "",
|
||||||
"type": "event",
|
"type": "event",
|
||||||
"revision": 0,
|
"revision": 1,
|
||||||
"id": "8222"
|
"id": "5a6e1"
|
||||||
},
|
|
||||||
{
|
|
||||||
"duration": 900000,
|
|
||||||
"type": "delay",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "a386"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Add delay blocks to affect all events",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "",
|
|
||||||
"note": "* Until a block is found",
|
|
||||||
"timeStart": 35400000,
|
|
||||||
"timeEnd": 36600000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 1200000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "6dce"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Add and remove events with [+] and [-]",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "",
|
|
||||||
"note": "Orange and red buttons on the right",
|
|
||||||
"timeStart": 36600000,
|
|
||||||
"timeEnd": 43200000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 6600000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "2651"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "block",
|
|
||||||
"id": "e6a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "And control whether they are public",
|
|
||||||
"subtitle": "",
|
|
||||||
"presenter": "",
|
|
||||||
"note": "Blue button on the right",
|
|
||||||
"timeStart": 46800000,
|
|
||||||
"timeEnd": 57600000,
|
|
||||||
"timeType": "start-end",
|
|
||||||
"duration": 10800000,
|
|
||||||
"isPublic": false,
|
|
||||||
"skip": false,
|
|
||||||
"colour": "",
|
|
||||||
"user0": "",
|
|
||||||
"user1": "",
|
|
||||||
"user2": "",
|
|
||||||
"user3": "",
|
|
||||||
"user4": "",
|
|
||||||
"user5": "",
|
|
||||||
"user6": "",
|
|
||||||
"user7": "",
|
|
||||||
"user8": "",
|
|
||||||
"user9": "",
|
|
||||||
"type": "event",
|
|
||||||
"revision": 0,
|
|
||||||
"id": "1358"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"eventData": {
|
"eventData": {
|
||||||
"title": "All about Carlos demo event",
|
"title": "Ontime demo event",
|
||||||
"publicUrl": "www.getontime.no",
|
"publicUrl": "www.getontime.no",
|
||||||
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
||||||
"backstageUrl": "www.getontime.no",
|
"backstageUrl": "www.getontime.no",
|
||||||
@@ -229,19 +38,55 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"app": "ontime",
|
"app": "ontime",
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"serverPort": 4001,
|
"serverPort": 4001,
|
||||||
"lock": null,
|
"lock": null,
|
||||||
"pinCode": "1234"
|
"pinCode": "1234",
|
||||||
|
"timeFormat": "24"
|
||||||
},
|
},
|
||||||
"viewSettings": {
|
"viewSettings": {
|
||||||
"overrideStyles": false
|
"overrideStyles": false
|
||||||
},
|
},
|
||||||
|
"aliases": [
|
||||||
|
{
|
||||||
|
"id": "0b0b3",
|
||||||
|
"enabled": true,
|
||||||
|
"alias": "test",
|
||||||
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"userFields": {
|
||||||
|
"user0": "user0",
|
||||||
|
"user1": "user1",
|
||||||
|
"user2": "user2",
|
||||||
|
"user3": "user3",
|
||||||
|
"user4": "user4",
|
||||||
|
"user5": "user5",
|
||||||
|
"user6": "user6",
|
||||||
|
"user7": "user7",
|
||||||
|
"user8": "user8",
|
||||||
|
"user9": "user9"
|
||||||
|
},
|
||||||
"osc": {
|
"osc": {
|
||||||
"port": 8888,
|
"portIn": 8888,
|
||||||
"portOut": 9999,
|
"portOut": 9999,
|
||||||
"targetIP": "127.0.0.1",
|
"targetIP": "127.0.0.1",
|
||||||
"enabled": true
|
"enabledIn": true,
|
||||||
|
"enabledOut": true,
|
||||||
|
"subscriptions": {
|
||||||
|
"onLoad": [],
|
||||||
|
"onStart": [],
|
||||||
|
"onPause": [],
|
||||||
|
"onStop": [],
|
||||||
|
"onUpdate": [
|
||||||
|
{
|
||||||
|
"id": "10eea",
|
||||||
|
"enabled": true,
|
||||||
|
"message": "/ontime/update/{{timer.current}}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"onFinish": []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"http": {
|
"http": {
|
||||||
@@ -275,25 +120,5 @@
|
|||||||
},
|
},
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"aliases": [
|
|
||||||
{
|
|
||||||
"id": "0b0b3",
|
|
||||||
"enabled": true,
|
|
||||||
"alias": "test",
|
|
||||||
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"userFields": {
|
|
||||||
"user0": "user0",
|
|
||||||
"user1": "user1",
|
|
||||||
"user2": "user2",
|
|
||||||
"user3": "user3",
|
|
||||||
"user4": "user4",
|
|
||||||
"user5": "user5",
|
|
||||||
"user6": "user6",
|
|
||||||
"user7": "user7",
|
|
||||||
"user8": "user8",
|
|
||||||
"user9": "user9"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
test.describe('minimal view behaviour can be changed through params', () => {
|
|
||||||
test.use({ viewport: { width: 1920, height: 1080 } });
|
|
||||||
test('without overloads', async ({ page }) => {
|
|
||||||
await page.goto('http://localhost:4001/minimal');
|
|
||||||
await page.getByTestId('minimal-timer').click();
|
|
||||||
});
|
|
||||||
test('hide nav', async ({ page }) => {
|
|
||||||
await page.goto('http://localhost:4001/minimal?hidenav=true');
|
|
||||||
const navBar = await page.locator('data-test-id=nav-logo');
|
|
||||||
await expect(navBar).toHaveCount(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export enum EndAction {
|
export enum EndAction {
|
||||||
Continue = 'continue',
|
None = 'none',
|
||||||
Stop = 'stop',
|
Stop = 'stop',
|
||||||
LoadNext = 'load-next',
|
LoadNext = 'load-next',
|
||||||
PlayNext = 'play-next',
|
PlayNext = 'play-next',
|
||||||
|
|||||||
Reference in New Issue
Block a user