feat: patch params editor (#428)

* feat: configure params in views
This commit is contained in:
Carlos Valente
2023-06-06 21:23:00 +02:00
committed by GitHub
parent a3487e7073
commit cf6ce880f4
21 changed files with 150 additions and 294 deletions
@@ -27,6 +27,7 @@ $button-size: 48px;
position: fixed; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
z-index: 2;
&.hidden { &.hidden {
opacity: 0; opacity: 0;
@@ -46,7 +47,7 @@ $button-size: 48px;
.navButton { .navButton {
@extend .button; @extend .button;
z-index: 2; z-index: 3;
} }
.menuContainer { .menuContainer {
@@ -18,5 +18,6 @@ $progress-bar-br: 6px;
height: $progress-bar-size; height: $progress-bar-size;
background-color: var(--timer-progress-override, $accent-color); background-color: var(--timer-progress-override, $accent-color);
transition: 1s linear; transition: 1s linear;
border-radius: $progress-bar-br 0 0 $progress-bar-br;
transition-property: width; transition-property: width;
} }
@@ -9,7 +9,7 @@ interface EditFormInputProps {
paramField: ParamField; paramField: ParamField;
} }
export default function EditFormInput({ paramField }: EditFormInputProps) { export default function ParamInput({ paramField }: EditFormInputProps) {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { id, type } = paramField; const { id, type } = paramField;
@@ -38,7 +38,7 @@ export default function EditFormInput({ paramField }: EditFormInputProps) {
if (type === 'number') { if (type === 'number') {
const defaultNumberValue = searchParams.get(id) ?? ''; const defaultNumberValue = searchParams.get(id) ?? '';
return <Input type='number' variant='ontime-filled' name={id} defaultValue={defaultNumberValue} />; return <Input type='number' step='any' variant='ontime-filled' name={id} defaultValue={defaultNumberValue} />;
} }
const defaultStringValue = searchParams.get(id) ?? ''; const defaultStringValue = searchParams.get(id) ?? '';
@@ -13,8 +13,14 @@
.drawerFooter { .drawerFooter {
@extend .drawerContent; @extend .drawerContent;
display: flex; display: flex;
justify-content: start;
gap: $element-spacing; gap: $element-spacing;
button[type='reset'] {
padding: 0 2em;
margin-right: auto;
}
button[type='submit'] { button[type='submit'] {
padding: 0 2em; padding: 0 2em;
} }
@@ -12,16 +12,16 @@ import {
useDisclosure, useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import EditFormInput from './EditFormInput'; import ParamInput from './ParamInput';
import { ParamField } from './types'; import { ParamField } from './types';
import style from './EditFormDrawer.module.scss'; import style from './ViewParamsEditor.module.scss';
interface EditFormDrawerProps { interface EditFormDrawerProps {
paramFields: ParamField[]; paramFields: ParamField[];
} }
export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) { export default function ViewParamsEditor({ paramFields }: EditFormDrawerProps) {
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const { isOpen, onClose, onOpen } = useDisclosure(); const { isOpen, onClose, onOpen } = useDisclosure();
@@ -40,6 +40,11 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
setSearchParams(searchParams); setSearchParams(searchParams);
}; };
const clearParams = () => {
setSearchParams();
onClose();
};
const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => { const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
formEvent.preventDefault(); formEvent.preventDefault();
@@ -53,8 +58,6 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
return newSearchParams; return newSearchParams;
}, new URLSearchParams()); }, new URLSearchParams());
onEditDrawerClose();
setSearchParams(newSearchParams); setSearchParams(newSearchParams);
}; };
@@ -74,7 +77,7 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
<label className={style.label}> <label className={style.label}>
<span className={style.title}>{field.title}</span> <span className={style.title}>{field.title}</span>
<span className={style.description}>{field.description}</span> <span className={style.description}>{field.description}</span>
<EditFormInput key={field.title} paramField={field} /> <ParamInput key={field.title} paramField={field} />
</label> </label>
</div> </div>
))} ))}
@@ -82,6 +85,9 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
</DrawerBody> </DrawerBody>
<DrawerFooter className={style.drawerFooter}> <DrawerFooter className={style.drawerFooter}>
<Button variant='ontime-ghosted' onClick={clearParams} type='reset'>
Clear
</Button>
<Button variant='ontime-subtle' onClick={onEditDrawerClose}> <Button variant='ontime-subtle' onClick={onEditDrawerClose}>
Cancel Cancel
</Button> </Button>
@@ -9,9 +9,10 @@ export const TIME_FORMAT_OPTION: ParamField = {
}; };
export const CLOCK_OPTIONS: ParamField[] = [ export const CLOCK_OPTIONS: ParamField[] = [
TIME_FORMAT_OPTION,
{ {
id: 'key', id: 'key',
title: 'Key', title: 'Key Colour',
description: 'Background colour in hexadecimal', description: 'Background colour in hexadecimal',
type: 'string', type: 'string',
}, },
@@ -65,30 +66,14 @@ export const CLOCK_OPTIONS: ParamField[] = [
description: 'Offsets the timer vertical position by a given amount in pixels', description: 'Offsets the timer vertical position by a given amount in pixels',
type: 'number', type: 'number',
}, },
{
id: 'hidenav',
title: 'Hide Nav',
description: 'Whether to hide the nav logo in the right corner',
type: 'boolean',
},
TIME_FORMAT_OPTION,
]; ];
export const TIMER_OPTIONS: ParamField[] = [ export const TIMER_OPTIONS: ParamField[] = [TIME_FORMAT_OPTION];
{
id: 'progress',
title: 'Progress Bar',
description: 'Whether bar counts up or down',
type: 'option',
values: ['up', 'down'],
},
TIME_FORMAT_OPTION,
];
export const MINIMAL_TIMER_OPTIONS: ParamField[] = [ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
{ {
id: 'key', id: 'key',
title: 'Key', title: 'Key Colour',
description: 'Background colour in hexadecimal', description: 'Background colour in hexadecimal',
type: 'string', type: 'string',
}, },
@@ -142,12 +127,6 @@ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
description: 'Offsets the timer vertical position by a given amount in pixels', description: 'Offsets the timer vertical position by a given amount in pixels',
type: 'number', type: 'number',
}, },
{
id: 'hidenav',
title: 'Hide Nav',
description: 'Whether to hide the nav logo in the right corner',
type: 'boolean',
},
{ {
id: 'hideovertime', id: 'hideovertime',
title: 'Hide Overtime', title: 'Hide Overtime',
@@ -160,13 +139,19 @@ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
description: 'Whether to hide the overlay from showing timer screen messages', description: 'Whether to hide the overlay from showing timer screen messages',
type: 'boolean', type: 'boolean',
}, },
{
id: 'hideendmessage',
title: 'Hide End Message',
description: 'Whether to hide end message and continue showing the clock if timer is in overtime',
type: 'boolean',
},
]; ];
export const LOWER_THIRDS_OPTIONS: ParamField[] = [ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
{ {
id: 'preset', id: 'preset',
title: 'Preset', title: 'Preset',
description: 'Selects a style preset', description: 'Selects a style preset (0-1)',
type: 'number', type: 'number',
}, },
{ {
@@ -183,19 +168,19 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
}, },
{ {
id: 'text', id: 'text',
title: 'Text', title: 'Text Colour',
description: 'Text colour in hexadecimal', description: 'Text colour in hexadecimal',
type: 'string', type: 'string',
}, },
{ {
id: 'bg', id: 'bg',
title: 'BG', title: 'Text Background',
description: 'Text background colour in hexadecimal', description: 'Text background colour in hexadecimal',
type: 'string', type: 'string',
}, },
{ {
id: 'key', id: 'key',
title: 'Key', title: 'Key Colour',
description: 'Screen background colour in hexadecimal', description: 'Screen background colour in hexadecimal',
type: 'string', type: 'string',
}, },
@@ -205,15 +190,14 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
description: 'Time (in seconds) the lower third displays before fading out', description: 'Time (in seconds) the lower third displays before fading out',
type: 'number', type: 'number',
}, },
TIME_FORMAT_OPTION,
]; ];
export const STUDIO_CLOCK_OPTIONS: ParamField[] = [ export const STUDIO_CLOCK_OPTIONS: ParamField[] = [
TIME_FORMAT_OPTION,
{ {
id: 'seconds', id: 'seconds',
title: 'Seconds', title: 'Show Seconds',
description: 'Shows seconds in clock', description: 'Shows seconds in clock',
type: 'boolean', type: 'boolean',
}, },
TIME_FORMAT_OPTION,
]; ];
@@ -5,14 +5,14 @@ import { EventData, Message, OntimeEvent, ViewSettings } from 'ontime-types';
import { formatDisplay } from 'ontime-utils'; import { formatDisplay } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { TIME_FORMAT_OPTION } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import ProgressBar from '../../../common/components/progress-bar/ProgressBar'; import ProgressBar from '../../../common/components/progress-bar/ProgressBar';
import Schedule from '../../../common/components/schedule/Schedule'; import Schedule from '../../../common/components/schedule/Schedule';
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext'; import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
import ScheduleNav from '../../../common/components/schedule/ScheduleNav'; import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
import TitleCard from '../../../common/components/title-card/TitleCard'; import TitleCard from '../../../common/components/title-card/TitleCard';
import { TIME_FORMAT_OPTION } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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 { getEventsWithDelay } from '../../../common/utils/eventsManager'; import { getEventsWithDelay } from '../../../common/utils/eventsManager';
@@ -79,7 +79,7 @@ export default function Backstage(props: BackstageProps) {
return ( return (
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'> <div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} /> <ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
<div className='event-header'> <div className='event-header'>
{general.title} {general.title}
<div className='clock-container'> <div className='clock-container'>
@@ -3,9 +3,9 @@ import { useSearchParams } from 'react-router-dom';
import { ViewSettings } from 'ontime-types'; import { ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { CLOCK_OPTIONS } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { CLOCK_OPTIONS } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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';
@@ -129,12 +129,12 @@ export default function Clock(props: ClockProps) {
style={{ style={{
backgroundColor: userOptions.keyColour, backgroundColor: userOptions.keyColour,
justifyContent: userOptions.justifyContent, justifyContent: userOptions.justifyContent,
alignItems: userOptions.alignItems, alignContent: userOptions.alignItems,
}} }}
data-testid='clock-view' data-testid='clock-view'
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={CLOCK_OPTIONS} /> <ViewParamsEditor paramFields={CLOCK_OPTIONS} />
<div <div
className='clock' className='clock'
style={{ style={{
@@ -4,9 +4,9 @@ import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent, ViewSettings
import { formatDisplay } from 'ontime-utils'; import { formatDisplay } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { TIME_FORMAT_OPTION } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { TIME_FORMAT_OPTION } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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 getDelayTo from '../../../common/utils/getDelayTo'; import getDelayTo from '../../../common/utils/getDelayTo';
@@ -112,7 +112,7 @@ export default function Countdown(props: CountdownProps) {
return ( return (
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'> <div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} /> <ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
{follow === null ? ( {follow === null ? (
<CountdownSelect events={backstageEvents} /> <CountdownSelect events={backstageEvents} />
) : ( ) : (
@@ -1,131 +0,0 @@
import { useEffect, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import './LowerClean.scss';
export default function LowerClean(props) {
const { lower, title, options } = props;
const defaults = {
size: 1,
transitionIn: 3,
textColour: '#fffffa',
posX: 50,
posY: 700,
};
const [showLower, setShowLower] = useState(true);
// Unmount if fadeOut
useEffect(() => {
if (!options.fadeOut) return;
// Calculate time
const fadeOutTime = (parseInt(options.fadeOut, 10) + (options.transitionIn || defaults.transitionIn)) * 1000;
if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => {
setShowLower(false);
}, fadeOutTime);
return () => clearTimeout(timeout);
}, [options.fadeOut, options.transitionIn, defaults.transitionIn]);
// Format messages
const showLowerMessage = lower.text !== '' && lower.visible;
// motion
// transition segments
const t = options.transitionIn || defaults.transitionIn;
const quarter = t / 4;
const third = t / 3;
const half = t / 2;
const lowerThirdVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
duration: third,
},
},
exit: {
opacity: 0,
transition: {
duration: third,
},
},
};
const titleVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
delay: quarter,
duration: half,
},
},
};
const sizeMultiplier = (options.size || 1) * 4;
return (
<div
className='lower-third clean'
style={{
backgroundColor: options.keyColour || defaults.keyColour,
color: options.textColour || defaults.textColour,
fontSize: `${sizeMultiplier}vh`,
}}
>
<NavigationMenu isEditBtnHidden />
<AnimatePresence>
{showLower && (
<motion.div
className='lower-container'
style={{
backgroundColor: options.bgColour || defaults.bgColour,
top: options.posY || defaults.posY,
left: options.posX || defaults.posX,
}}
variants={lowerThirdVariants}
initial='hidden'
animate='visible'
exit='exit'
>
<motion.div className='title' variants={titleVariants}>
{title.titleNow}
</motion.div>
<motion.div className='subtitle' variants={titleVariants}>
{title.presenterNow}
</motion.div>
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{showLowerMessage && (
<motion.div
className='message-container'
style={{
backgroundColor: options.bgColour || defaults.bgColour,
}}
key='modal'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ scaleY: 0, opacity: 0 }}
transition={{ duration: 0.5 }}
>
<div className='message'>{lower.text}</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
@@ -1,27 +0,0 @@
.lower-third.clean {
margin: 0;
box-sizing: border-box;
overflow: hidden;
width: 100%;
height: 100%;
color: #fffffa;
font-size: 4vh;
background-color: white;
.lower-container {
position: absolute;
padding: 1vh 2vh;
border-radius: 8px;
}
.message-container {
position: absolute;
bottom: 2vh;
width: 100%;
text-align: center;
.message {
font-size: 3.5vh;
}
}
}
@@ -1,20 +1,24 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { Message } from 'ontime-types';
import { LOWER_THIRDS_OPTIONS } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { LOWER_THIRDS_OPTIONS } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import { TitleManager } from '../ViewWrapper';
import { LowerOptions } from './LowerWrapper';
import './LowerLines.scss'; import './LowerLines.scss';
export default function LowerLines(props) { interface LowerLinesProps {
lower: Message;
title: TitleManager;
options: LowerOptions;
}
export default function LowerLines(props: LowerLinesProps) {
const { lower, title, options } = props; const { lower, title, options } = props;
const defaults = {
size: 1,
transitionIn: 3,
textColour: '#fffffa',
bgColour: '#00000033',
};
const [showLower, setShowLower] = useState(true); const [showLower, setShowLower] = useState(true);
// Unmount if fadeOut // Unmount if fadeOut
@@ -22,15 +26,14 @@ export default function LowerLines(props) {
if (!options.fadeOut) return; if (!options.fadeOut) return;
// Calculate time // Calculate time
const fadeOutTime = (parseInt(options.fadeOut, 10) + (options.transitionIn || defaults.transitionIn)) * 1000; const fadeOutTime = (options.fadeOut + options.transitionIn) * 1000;
if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
setShowLower(false); setShowLower(false);
}, fadeOutTime); }, fadeOutTime);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [options.fadeOut, options.transitionIn, defaults.transitionIn]); }, [options.fadeOut, options.transitionIn]);
useEffect(() => { useEffect(() => {
setShowLower(title.showNow); setShowLower(title.showNow);
@@ -41,7 +44,7 @@ export default function LowerLines(props) {
// motion // motion
// transition segments // transition segments
const t = options.transitionIn || defaults.transitionIn; const t = options.transitionIn;
const eight = t / 8; const eight = t / 8;
const quarter = t / 4; const quarter = t / 4;
const third = t / 3; const third = t / 3;
@@ -123,19 +126,19 @@ export default function LowerLines(props) {
<div <div
className='lower-third lines' className='lower-third lines'
style={{ style={{
backgroundColor: options.keyColour || defaults.keyColour, backgroundColor: options.keyColour,
color: options.textColour || defaults.textColour, color: options.textColour,
fontSize: `${sizeMultiplier}vh`, fontSize: `${sizeMultiplier}vh`,
}} }}
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={LOWER_THIRDS_OPTIONS} /> <ViewParamsEditor paramFields={LOWER_THIRDS_OPTIONS} />
<AnimatePresence> <AnimatePresence>
{showLower && ( {showLower && (
<motion.div <motion.div
className='lower-container' className='lower-container'
style={{ backgroundColor: options.bgColour || defaults.bgColour }} style={{ backgroundColor: options.bgColour }}
variants={lowerThirdVariants} variants={lowerThirdVariants}
initial='hidden' initial='hidden'
animate='visible' animate='visible'
@@ -1,28 +1,46 @@
import { memo, useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { Message, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import PropTypes from 'prop-types'; import { TitleManager } from '../ViewWrapper';
import LowerClean from './LowerClean';
import LowerLines from './LowerLines'; import LowerLines from './LowerLines';
const areEqual = (prevProps, nextProps) => { export type LowerOptions = {
size: number;
transitionIn: number;
textColour: string;
bgColour: string;
keyColour?: string;
fadeOut: number;
};
interface LowerProps {
title: TitleManager;
lower: Message;
viewSettings: ViewSettings;
}
// prevent triggering animation without a content change
const areEqual = (prevProps: LowerProps, nextProps: LowerProps) => {
return isEqual(prevProps.title, nextProps.title) && isEqual(prevProps.lower, nextProps.lower); return isEqual(prevProps.title, nextProps.title) && isEqual(prevProps.lower, nextProps.lower);
}; };
const Lower = (props) => { const Lower = (props: LowerProps) => {
const { title, lower, viewSettings } = props; const { title, lower, viewSettings } = props;
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [titles, setTitles] = useState({ const [titles, setTitles] = useState<TitleManager>({
titleNow: '', titleNow: '',
titleNext: '', titleNext: '',
subtitleNow: '', subtitleNow: '',
subtitleNext: '', subtitleNext: '',
presenterNow: '', presenterNow: '',
presenterNext: '', presenterNext: '',
noteNow: '',
noteNext: '',
showNow: false, showNow: false,
showNext: false, showNext: false,
}); });
@@ -36,7 +54,7 @@ const Lower = (props) => {
useEffect(() => { useEffect(() => {
// clear titles if necessary // clear titles if necessary
// will trigger an animation out in the component // will trigger an animation out in the component
let timeout = null; let timeout: NodeJS.Timeout | null = null;
if ( if (
title?.titleNow !== titles?.titleNow || title?.titleNow !== titles?.titleNow ||
title?.subtitleNow !== titles?.subtitleNow || title?.subtitleNow !== titles?.subtitleNow ||
@@ -56,7 +74,7 @@ const Lower = (props) => {
clearTimeout(timeout); clearTimeout(timeout);
} }
}; };
// eslint-disable-next-line // eslint-disable-next-line -- we do this to keep animations
}, [title.titleNow, title.subtitleNow, title.presenterNow]); }, [title.titleNow, title.subtitleNow, title.presenterNow]);
// defer rendering until we load stylesheets // defer rendering until we load stylesheets
@@ -64,72 +82,60 @@ const Lower = (props) => {
return null; return null;
} }
// TODO: sanitize data const options: LowerOptions = {
// getting config from URL: preset, size, transition, bg, text, key size: 1,
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5 transitionIn: 3,
// Check for user options textColour: '#fffffa',
// create aux bgColour: '#00000033',
const options = {}; fadeOut: 3,
};
// preset: selector
// Should be a number 1-n
const p = parseInt(searchParams.get('preset'), 10);
const preset = !isNaN(p) ? 1 : p;
// size: multiplier // size: multiplier
// Should be a number 0.0-n // Should be a number 0.0-n
const s = searchParams.get('size'); const _size = searchParams.get('size');
if (s) options.size = s; if (_size) {
const parsedValue = Number(_size);
if (!Number.isNaN(parsedValue)) {
options.size = parsedValue;
}
}
// transitionIn: seconds // transitionIn: seconds
// Should be a number 0-n // Should be a number 0-n
const t = parseInt(searchParams.get('transition'), 10); const _transitionIn = searchParams.get('transition');
if (!isNaN(t)) options.transitionIn = t; if (_transitionIn) {
const parsedValue = Number(_transitionIn);
if (!Number.isNaN(parsedValue)) {
options.transitionIn = parsedValue;
}
}
// textColour: string // textColour: string
// Should be a hex string '#ffffff' // Should be a hex string '#ffffff'
const c = searchParams.get('text'); const _textColour = searchParams.get('text');
if (c) options.textColour = `#${c}`; if (_textColour) options.textColour = `#${_textColour}`;
// bgColour: string // bgColour: string
// Should be a hex string '#ffffff' // Should be a hex string '#ffffff'
const b = searchParams.get('bg'); const _bgColour = searchParams.get('bg');
if (b) options.bgColour = `#${b}`; if (_bgColour) options.bgColour = `#${_bgColour}`;
// key: string // key: string
// Should be a hex string '#00FF00' with key colour // Should be a hex string '#00FF00' with key colour
const k = searchParams.get('key'); const _key = searchParams.get('key');
if (k) options.keyColour = `#${k}`; if (_key) options.keyColour = `#${_key}`;
// fadeOut: seconds // fadeOut: seconds
// Should be a number 0-n // Should be a number 0-n
const f = parseInt(searchParams.get('fadeout'), 10); const _fadeOut = searchParams.get('fadeout');
if (!isNaN(f)) options.fadeOut = f; if (_fadeOut) {
const parsedValue = Number(_transitionIn);
// x: pixels if (!Number.isNaN(parsedValue)) {
// Should be a number 0-n options.fadeOut = parsedValue;
const x = parseInt(searchParams.get('x'), 10); }
if (!isNaN(x)) options.posX = x;
// y: pixels
// Should be a number 0-n
const y = parseInt(searchParams.get('y'), 10);
if (!isNaN(y)) options.posY = y;
switch (preset) {
case 0:
return <LowerClean lower={lower} title={titles} options={options} />;
case 1:
return <LowerLines lower={lower} title={titles} options={options} />;
default:
return <LowerLines lower={lower} title={titles} options={options} />;
} }
return <LowerLines lower={lower} title={titles} options={options} />;
}; };
export default memo(Lower, areEqual); export default memo(Lower, areEqual);
Lower.propTypes = {
title: PropTypes.object,
lower: PropTypes.object,
viewSettings: PropTypes.object,
};
@@ -3,9 +3,9 @@ import { useSearchParams } from 'react-router-dom';
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types'; import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { MINIMAL_TIMER_OPTIONS } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { MINIMAL_TIMER_OPTIONS } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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';
@@ -169,12 +169,12 @@ export default function MinimalTimer(props: MinimalTimerProps) {
style={{ style={{
backgroundColor: userOptions.keyColour, backgroundColor: userOptions.keyColour,
justifyContent: userOptions.justifyContent, justifyContent: userOptions.justifyContent,
alignItems: userOptions.alignItems, alignContent: userOptions.alignItems,
}} }}
data-testid='minimal-timer' data-testid='minimal-timer'
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={MINIMAL_TIMER_OPTIONS} /> <ViewParamsEditor paramFields={MINIMAL_TIMER_OPTIONS} />
{!hideMessagesOverlay && ( {!hideMessagesOverlay && (
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}> <div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
<div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div> <div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
@@ -87,7 +87,7 @@
grid-area: schedule; grid-area: schedule;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
margin-left: 16px; margin-left: clamp(16px, 5vw, 64px);;
} }
.schedule-nav-container { .schedule-nav-container {
@@ -4,13 +4,13 @@ import { AnimatePresence, motion } from 'framer-motion';
import { EventData, Message, OntimeEvent, ViewSettings } from 'ontime-types'; import { EventData, Message, OntimeEvent, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { TIME_FORMAT_OPTION } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import Schedule from '../../../common/components/schedule/Schedule'; import Schedule from '../../../common/components/schedule/Schedule';
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext'; import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
import ScheduleNav from '../../../common/components/schedule/ScheduleNav'; import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
import TitleCard from '../../../common/components/title-card/TitleCard'; import TitleCard from '../../../common/components/title-card/TitleCard';
import { TIME_FORMAT_OPTION } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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 { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
@@ -57,7 +57,7 @@ export default function Public(props: BackstageProps) {
return ( return (
<div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'> <div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'>
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} /> <ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
<div className='event-header'> <div className='event-header'>
{general.title} {general.title}
<div className='clock-container'> <div className='clock-container'>
@@ -4,9 +4,9 @@ import { formatDisplay, millisToString } from 'ontime-utils';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { STUDIO_CLOCK_OPTIONS } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { STUDIO_CLOCK_OPTIONS } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import useFitText from '../../../common/hooks/useFitText'; import useFitText from '../../../common/hooks/useFitText';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { formatEventList, getEventsWithDelay, trimRundown } from '../../../common/utils/eventsManager'; import { formatEventList, getEventsWithDelay, trimRundown } from '../../../common/utils/eventsManager';
@@ -74,7 +74,7 @@ export default function StudioClock(props) {
return ( return (
<div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'> <div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'>
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={STUDIO_CLOCK_OPTIONS} /> <ViewParamsEditor paramFields={STUDIO_CLOCK_OPTIONS} />
<div className='clock-container'> <div className='clock-container'>
<div className={`studio-timer ${showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div> <div className={`studio-timer ${showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
<div <div
@@ -3,11 +3,11 @@ import { AnimatePresence, motion } from 'framer-motion';
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types'; import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { TIMER_OPTIONS } from '../../../common/components/edit-form-drawer/constants';
import EditFormDrawer from '../../../common/components/edit-form-drawer/EditFormDrawer';
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar'; import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import TitleCard from '../../../common/components/title-card/TitleCard'; import TitleCard from '../../../common/components/title-card/TitleCard';
import { TIMER_OPTIONS } from '../../../common/components/view-params-editor/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
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 { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
@@ -100,7 +100,7 @@ export default function Timer(props: TimerProps) {
return ( return (
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'> <div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={TIMER_OPTIONS} /> <ViewParamsEditor paramFields={TIMER_OPTIONS} />
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}> <div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
<div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div> <div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
</div> </div>
+5
View File
@@ -47,6 +47,11 @@ export const ontimeButtonSubtle = {
}, },
}; };
export const ontimeButtonGhosted = {
...ontimeButtonSubtle,
backgroundColor: 'transparent',
};
export const ontimeButtonSubtleOnLight = { export const ontimeButtonSubtleOnLight = {
backgroundColor: '#ececec', // $gray-100 backgroundColor: '#ececec', // $gray-100
color: '#595959', // $gray-800 color: '#595959', // $gray-800
+2
View File
@@ -3,6 +3,7 @@ import { extendTheme } from '@chakra-ui/react';
import { ontimeAlertOnLight } from './OntimeAlert'; import { ontimeAlertOnLight } from './OntimeAlert';
import { import {
ontimeButtonFilled, ontimeButtonFilled,
ontimeButtonGhosted,
ontimeButtonOutlined, ontimeButtonOutlined,
ontimeButtonSubtle, ontimeButtonSubtle,
ontimeButtonSubtleOnLight, ontimeButtonSubtleOnLight,
@@ -42,6 +43,7 @@ const theme = extendTheme({
'ontime-filled': { ...ontimeButtonFilled }, 'ontime-filled': { ...ontimeButtonFilled },
'ontime-outlined': { ...ontimeButtonOutlined }, 'ontime-outlined': { ...ontimeButtonOutlined },
'ontime-subtle': { ...ontimeButtonSubtle }, 'ontime-subtle': { ...ontimeButtonSubtle },
'ontime-ghosted': { ...ontimeButtonGhosted },
'ontime-subtle-white': { ...ontimeButtonSubtleWhite }, 'ontime-subtle-white': { ...ontimeButtonSubtleWhite },
'ontime-subtle-on-light': { ...ontimeButtonSubtleOnLight }, 'ontime-subtle-on-light': { ...ontimeButtonSubtleOnLight },
'ontime-ghost-on-light': { ...ontimeGhostOnLight }, 'ontime-ghost-on-light': { ...ontimeGhostOnLight },