URL Params Form (#420)

* feat create form to edit URL params in views
This commit is contained in:
asharonbaltazar
2023-06-05 13:54:03 -04:00
committed by GitHub
parent 5cb8020ab9
commit bfb483508d
17 changed files with 549 additions and 99 deletions
@@ -0,0 +1,45 @@
@use '../../../theme/v2Styles' as *;
@use '../../../theme/ontimeColours' as *;
.drawerContent {
background-color: $gray-1200;
}
.drawerHeader {
@extend .drawerContent;
color: $section-white;
}
.drawerFooter {
@extend .drawerContent;
display: flex;
gap: $element-spacing;
button[type='submit'] {
padding: 0 2em;
}
}
.label {
font-size: $inner-section-text-size;
color: $label-gray;
}
.columnSection {
display: flex;
padding: $element-spacing;
flex-direction: column;
gap: $element-inner-spacing;
}
.title {
font-size: $inner-section-text-size;
display: block;
width: 100%;
}
.description {
font-size: $inner-section-text-size;
display: block;
color: $modal-note-color;
}
@@ -0,0 +1,95 @@
import { FormEvent, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
useDisclosure,
} from '@chakra-ui/react';
import EditFormInput from './EditFormInput';
import { ParamField } from './types';
import style from './EditFormDrawer.module.scss';
interface EditFormDrawerProps {
paramFields: ParamField[];
}
export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
const [searchParams, setSearchParams] = useSearchParams();
const { isOpen, onClose, onOpen } = useDisclosure();
useEffect(() => {
const isEditing = searchParams.get('edit');
if (isEditing === 'true') {
return onOpen();
}
}, [searchParams, onOpen]);
const onEditDrawerClose = () => {
onClose();
searchParams.delete('edit');
setSearchParams(searchParams);
};
const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
formEvent.preventDefault();
const newParamsObject = Object.fromEntries(new FormData(formEvent.currentTarget));
const newSearchParams = Object.entries(newParamsObject).reduce((newSearchParams, [id, value]) => {
if (typeof value === 'string' && value.length) {
newSearchParams.set(id, value);
return newSearchParams;
}
return newSearchParams;
}, new URLSearchParams());
onEditDrawerClose();
setSearchParams(newSearchParams);
};
return (
<Drawer isOpen={isOpen} placement='right' onClose={onEditDrawerClose} size='lg'>
<DrawerOverlay />
<DrawerContent>
<DrawerHeader className={style.drawerHeader}>
<DrawerCloseButton _hover={{ bg: '#ebedf0', color: '#333' }} size='lg' />
Customise
</DrawerHeader>
<DrawerBody className={style.drawerContent}>
<form id='edit-params-form' onSubmit={onParamsFormSubmit}>
{paramFields.map((field) => (
<div key={field.title} className={style.columnSection}>
<label className={style.label}>
<span className={style.title}>{field.title}</span>
<span className={style.description}>{field.description}</span>
<EditFormInput key={field.title} paramField={field} />
</label>
</div>
))}
</form>
</DrawerBody>
<DrawerFooter className={style.drawerFooter}>
<Button variant='ontime-subtle' onClick={onEditDrawerClose}>
Cancel
</Button>
<Button variant='ontime-filled' form='edit-params-form' type='submit'>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
);
}
@@ -0,0 +1,47 @@
import { useSearchParams } from 'react-router-dom';
import { Input, Select, Switch } from '@chakra-ui/react';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { ParamField } from './types';
interface EditFormInputProps {
paramField: ParamField;
}
export default function EditFormInput({ paramField }: EditFormInputProps) {
const [searchParams] = useSearchParams();
const { id, type } = paramField;
if (type === 'option') {
const optionFromParams = searchParams.get(id);
const defaultOptionValue = paramField.values.find((value) => value === optionFromParams);
return (
<Select placeholder='Select an option' variant='ontime' name={id} defaultValue={defaultOptionValue}>
{paramField.values.map((value) => (
<option key={value} value={value}>
{value}
</option>
))}
</Select>
);
}
if (type === 'boolean') {
const defaultCheckedValue = isStringBoolean(searchParams.get(id)) ?? false;
// checked value should be 'true', so it can be captured by the form event
return <Switch variant='ontime' name={id} defaultChecked={defaultCheckedValue} value='true' />;
}
if (type === 'number') {
const defaultNumberValue = searchParams.get(id) ?? '';
return <Input type='number' variant='ontime-filled' name={id} defaultValue={defaultNumberValue} />;
}
const defaultStringValue = searchParams.get(id) ?? '';
return <Input variant='ontime-filled' name={id} defaultValue={defaultStringValue} />;
}
@@ -0,0 +1,219 @@
import { ParamField } from './types';
export const TIME_FORMAT_OPTION: ParamField = {
id: 'format',
title: '12 / 24 hour timer',
description: 'Whether to show the time in 12 or 24 hour mode. Overrides the global setting from preferences',
type: 'option',
values: ['12', '24'],
};
export const CLOCK_OPTIONS: ParamField[] = [
{
id: 'key',
title: 'Key',
description: 'Background colour in hexadecimal',
type: 'string',
},
{
id: 'text',
title: 'Text Colour',
description: 'Text colour in hexadecimal',
type: 'string',
},
{
id: 'textbg',
title: 'Text Background',
description: 'Colour of text background in hexadecimal',
type: 'string',
},
{
id: 'font',
title: 'Font',
description: 'Font family, will use the fonts available in the system',
type: 'string',
},
{
id: 'size',
title: 'Text Size',
description: 'Scales the current style (0.5 = 50% 1 = 100% 2 = 200%)',
type: 'number',
},
{
id: 'alignx',
title: 'Align Horizontal',
description: 'Moves the horizontally in page to start = left | center | end = right',
type: 'option',
values: ['start', 'center', 'end'],
},
{
id: 'offsetx',
title: 'Offset Horizontal',
description: 'Offsets the timer horizontal position by a given amount in pixels',
type: 'number',
},
{
id: 'aligny',
title: 'Align Vertical',
description: 'Moves the vertically in page to start = left | center | end = right',
type: 'option',
values: ['start', 'center', 'end'],
},
{
id: 'offsety',
title: 'Offset Vertical',
description: 'Offsets the timer vertical position by a given amount in pixels',
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[] = [
{
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[] = [
{
id: 'key',
title: 'Key',
description: 'Background colour in hexadecimal',
type: 'string',
},
{
id: 'text',
title: 'Text Colour',
description: 'Text colour in hexadecimal',
type: 'string',
},
{
id: 'textbg',
title: 'Text Background',
description: 'Colour of text background in hexadecimal',
type: 'string',
},
{
id: 'font',
title: 'Font',
description: 'Font family, will use the fonts available in the system',
type: 'string',
},
{
id: 'size',
title: 'Text Size',
description: 'Scales the current style (0.5 = 50% 1 = 100% 2 = 200%)',
type: 'number',
},
{
id: 'alignx',
title: 'Align Horizontal',
description: 'Moves the horizontally in page to start = left | center | end = right',
type: 'option',
values: ['start', 'center', 'end'],
},
{
id: 'offsetx',
title: 'Offset Horizontal',
description: 'Offsets the timer horizontal position by a given amount in pixels',
type: 'number',
},
{
id: 'aligny',
title: 'Align Vertical',
description: 'Moves the vertically in page to start = left | center | end = right',
type: 'option',
values: ['start', 'center', 'end'],
},
{
id: 'offsety',
title: 'Offset Vertical',
description: 'Offsets the timer vertical position by a given amount in pixels',
type: 'number',
},
{
id: 'hidenav',
title: 'Hide Nav',
description: 'Whether to hide the nav logo in the right corner',
type: 'boolean',
},
{
id: 'hideovertime',
title: 'Hide Overtime',
description: 'Whether to supress overtime styles (red borders and red text)',
type: 'boolean',
},
{
id: 'hidemessages',
title: 'Hide Message Overlay',
description: 'Whether to hide the overlay from showing timer screen messages',
type: 'boolean',
},
];
export const LOWER_THIRDS_OPTIONS: ParamField[] = [
{
id: 'preset',
title: 'Preset',
description: 'Selects a style preset',
type: 'number',
},
{
id: 'size',
title: 'Size',
description: 'Scales the current style (0.5 = 50% 1 = 100% 2 = 200%)',
type: 'number',
},
{
id: 'transition',
title: 'Transition',
description: 'Transition in time in seconds (default 5)',
type: 'number',
},
{
id: 'text',
title: 'Text',
description: 'Text colour in hexadecimal',
type: 'string',
},
{
id: 'bg',
title: 'BG',
description: 'Text background colour in hexadecimal',
type: 'string',
},
{
id: 'key',
title: 'Key',
description: 'Screen background colour in hexadecimal',
type: 'string',
},
{
id: 'fadeout',
title: 'Fadeout',
description: 'Time (in seconds) the lower third displays before fading out',
type: 'number',
},
TIME_FORMAT_OPTION,
];
export const STUDIO_CLOCK_OPTIONS: ParamField[] = [
{
id: 'seconds',
title: 'Seconds',
description: 'Shows seconds in clock',
type: 'boolean',
},
TIME_FORMAT_OPTION,
];
@@ -0,0 +1,12 @@
type BaseField = {
id: string;
title: string;
description: string;
};
type OptionsField = { type: 'option'; values: string[] };
type StringField = { type: 'string' };
type BooleanField = { type: 'boolean' };
type NumberField = { type: 'number' };
export type ParamField = BaseField & (StringField | BooleanField | NumberField | OptionsField);
@@ -1,6 +1,6 @@
@use "../../../theme/v2Styles" as *; @use '../../../theme/v2Styles' as *;
@use "../../../theme/mixins" as *; @use '../../../theme/mixins' as *;
@use "../../../theme/ontimeColours" as *; @use '../../../theme/ontimeColours' as *;
$menu-bg: $gray-1200; $menu-bg: $gray-1200;
$menu-hover-bg: $gray-1350; $menu-hover-bg: $gray-1350;
@@ -14,14 +14,26 @@ $button-size: 48px;
transform: rotate(180deg); transform: rotate(180deg);
} }
.navButton { .buttonContainer {
z-index: 2; display: flex;
position: absolute; flex-direction: column;
left: 0.5em; row-gap: 1rem;
top: 0.5em; padding: 0.5em;
transition-property: opacity; transition-property: opacity;
transition-duration: 0.3s; transition-duration: 0.3s;
opacity: 1; opacity: 1;
position: fixed;
left: 0;
top: 0;
&.hidden {
opacity: 0;
}
}
.button {
font-size: 24px; font-size: 24px;
color: $icon-color; color: $icon-color;
background-color: $button-bg; background-color: $button-bg;
@@ -30,10 +42,11 @@ $button-size: 48px;
display: grid; display: grid;
place-content: center; place-content: center;
border-radius: 3px; border-radius: 3px;
}
&.hidden { .navButton {
opacity: 0; @extend .button;
} z-index: 2;
} }
.menuContainer { .menuContainer {
@@ -1,10 +1,11 @@
import { KeyboardEvent, useEffect, useRef, useState } from 'react'; import { KeyboardEvent, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation, useSearchParams } from 'react-router-dom';
import { IoApps } from '@react-icons/all-files/io5/IoApps'; import { IoApps } from '@react-icons/all-files/io5/IoApps';
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp'; import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
import { IoContract } from '@react-icons/all-files/io5/IoContract'; import { IoContract } from '@react-icons/all-files/io5/IoContract';
import { IoExpand } from '@react-icons/all-files/io5/IoExpand'; import { IoExpand } from '@react-icons/all-files/io5/IoExpand';
import { IoPencilSharp } from '@react-icons/all-files/io5/IoPencilSharp';
import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical'; import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical';
import { navigatorConstants } from '../../../viewerConfig'; import { navigatorConstants } from '../../../viewerConfig';
@@ -21,12 +22,13 @@ export default function NavigationMenu() {
const { isFullScreen, toggleFullScreen } = useFullscreen(); const { isFullScreen, toggleFullScreen } = useFullscreen();
const { mirror, toggleMirror } = useViewOptionsStore(); const { mirror, toggleMirror } = useViewOptionsStore();
const [showButton, setShowButton] = useState(false); const [showButton, setShowButton] = useState(false);
const [searchParams, setSearchParams] = useSearchParams();
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const menuRef = useRef<HTMLDivElement | null>(null); const menuRef = useRef<HTMLDivElement | null>(null);
useClickOutside(menuRef, () => setShowMenu(false)); useClickOutside(menuRef, () => setShowMenu(false));
const toggleMenu = () => setShowMenu((prev) => !prev); const toggleMenu = () => setShowMenu((prev) => !prev);
useKeyDown(toggleMenu, ' '); useKeyDown(toggleMenu, ' ', { isDisabled: searchParams.get('edit') === 'true' });
useEffect(() => { useEffect(() => {
let fadeOut: NodeJS.Timeout | null = null; let fadeOut: NodeJS.Timeout | null = null;
@@ -49,63 +51,68 @@ export default function NavigationMenu() {
const isKeyEnter = (event: KeyboardEvent<HTMLDivElement>) => event.key === 'Enter'; const isKeyEnter = (event: KeyboardEvent<HTMLDivElement>) => event.key === 'Enter';
const handleFullscreen = () => toggleFullScreen(); const handleFullscreen = () => toggleFullScreen();
const handleMirror = () => toggleMirror(); const handleMirror = () => toggleMirror();
const showEditFormDrawer = () => {
searchParams.append('edit', 'true');
setSearchParams(searchParams);
};
return createPortal( return createPortal(
<div id='navigation-menu-portal' ref={menuRef} className={mirror ? style.mirror : ''}> <div id='navigation-menu-portal' ref={menuRef} className={mirror ? style.mirror : ''}>
<button <div className={`${style.buttonContainer} ${!showButton && !showMenu ? style.hidden : ''}`}>
onClick={toggleMenu} <button onClick={toggleMenu} aria-label='toggle menu' className={style.navButton}>
aria-label='toggle menu' <IoApps />
className={`${style.navButton} ${!showButton && !showMenu ? style.hidden : ''}`} </button>
> <button className={style.button} onClick={showEditFormDrawer}>
<IoApps /> <IoPencilSharp />
</button> </button>
{showMenu && (
{showMenu && ( <div className={style.menuContainer} data-testid='navigation-menu'>
<div className={style.menuContainer} data-testid='navigation-menu'> <div className={style.buttonsContainer}>
<div className={style.buttonsContainer}> <div
<div className={style.link}
className={style.link} tabIndex={0}
tabIndex={0} role='button'
role='button' onClick={handleFullscreen}
onClick={handleFullscreen} onKeyDown={(event) => {
onKeyDown={(event) => { isKeyEnter(event) && handleFullscreen();
isKeyEnter(event) && handleFullscreen(); }}
}} >
> Toggle Fullscreen
Toggle Fullscreen {isFullScreen ? <IoContract /> : <IoExpand />}
{isFullScreen ? <IoContract /> : <IoExpand />} </div>
<div
className={style.link}
tabIndex={0}
role='button'
onClick={handleMirror}
onKeyDown={(event) => {
isKeyEnter(event) && handleMirror();
}}
>
Flip Screen
<IoSwapVertical />
</div>
{/*<div className={style.link} tabIndex={0}>*/}
{/* Rename Client*/}
{/*</div>*/}
</div> </div>
<div <hr className={style.separator} />
className={style.link} {navigatorConstants.map((route) => (
tabIndex={0} <Link
role='button' key={route.url}
onClick={handleMirror} to={route.url}
onKeyDown={(event) => { className={`${style.link} ${route.url === location.pathname ? style.current : undefined}`}
isKeyEnter(event) && handleMirror(); tabIndex={0}
}} >
> {route.label}
Flip Screen <IoArrowUp className={style.linkIcon} />
<IoSwapVertical /> </Link>
</div> ))}
{/*<div className={style.link} tabIndex={0}>*/}
{/* Rename Client*/}
{/*</div>*/}
</div> </div>
<hr className={style.separator} /> )}
{navigatorConstants.map((route) => ( </div>
<Link
key={route.url}
to={route.url}
className={`${style.link} ${route.url === location.pathname ? style.current : undefined}`}
tabIndex={0}
>
{route.label}
<IoArrowUp className={style.linkIcon} />
</Link>
))}
</div>
)}
</div>, </div>,
document.body, document.body,
); );
} }
+17 -10
View File
@@ -1,18 +1,25 @@
import { useEffect } from 'react'; import { useCallback, useEffect } from 'react';
export const useKeyDown = (callback: () => void, targetKey: string) => { type UseKeyDown = (callback: () => void, targetKey: string, options?: { isDisabled?: boolean }) => void;
const onKeyDown = (event: KeyboardEvent) => {
const targetKeyPressed = event.key === targetKey && !event.repeat; export const useKeyDown: UseKeyDown = (callback, targetKey, options = {}) => {
if (targetKeyPressed) { const { isDisabled = false } = options;
event.preventDefault();
callback(); const onKeyDown = useCallback(
} (event: KeyboardEvent) => {
}; const targetKeyPressed = event.key === targetKey && !event.repeat;
if (targetKeyPressed && isDisabled === false) {
event.preventDefault();
callback();
}
},
[callback, isDisabled, targetKey],
);
useEffect(() => { useEffect(() => {
document.addEventListener('keydown', onKeyDown); document.addEventListener('keydown', onKeyDown);
return () => { return () => {
document.removeEventListener('keydown', onKeyDown); document.removeEventListener('keydown', onKeyDown);
}; };
}, []); }, [onKeyDown]);
}; };
@@ -4,6 +4,8 @@ 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 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';
@@ -77,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]} />
<div className='event-header'> <div className='event-header'>
{general.title} {general.title}
<div className='clock-container'> <div className='clock-container'>
@@ -3,6 +3,8 @@ 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 { 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';
@@ -132,6 +134,7 @@ export default function Clock(props: ClockProps) {
data-testid='clock-view' data-testid='clock-view'
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={CLOCK_OPTIONS} />
<div <div
className='clock' className='clock'
style={{ style={{
@@ -3,6 +3,8 @@ import { useSearchParams } from 'react-router-dom';
import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent, ViewSettings } from 'ontime-types'; import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent, 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 { 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';
@@ -110,6 +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]} />
{follow === null ? ( {follow === null ? (
<CountdownSelect events={backstageEvents} /> <CountdownSelect events={backstageEvents} />
) : ( ) : (
@@ -21,10 +21,7 @@ export default function LowerClean(props) {
if (!options.fadeOut) return; if (!options.fadeOut) return;
// Calculate time // Calculate time
const fadeOutTime = const fadeOutTime = (parseInt(options.fadeOut, 10) + (options.transitionIn || defaults.transitionIn)) * 1000;
(parseInt(options.fadeOut, 10) +
(options.transitionIn || defaults.transitionIn)) *
1000;
if (isNaN(fadeOutTime)) return; if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
@@ -86,8 +83,7 @@ export default function LowerClean(props) {
fontSize: `${sizeMultiplier}vh`, fontSize: `${sizeMultiplier}vh`,
}} }}
> >
<NavigationMenu isEditBtnHidden />
<NavigationMenu />
<AnimatePresence> <AnimatePresence>
{showLower && ( {showLower && (
@@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
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 './LowerLines.scss'; import './LowerLines.scss';
@@ -20,10 +22,7 @@ export default function LowerLines(props) {
if (!options.fadeOut) return; if (!options.fadeOut) return;
// Calculate time // Calculate time
const fadeOutTime = const fadeOutTime = (parseInt(options.fadeOut, 10) + (options.transitionIn || defaults.transitionIn)) * 1000;
(parseInt(options.fadeOut, 10) +
(options.transitionIn || defaults.transitionIn)) *
1000;
if (isNaN(fadeOutTime)) return; if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
@@ -129,8 +128,8 @@ export default function LowerLines(props) {
fontSize: `${sizeMultiplier}vh`, fontSize: `${sizeMultiplier}vh`,
}} }}
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer paramFields={LOWER_THIRDS_OPTIONS} />
<AnimatePresence> <AnimatePresence>
{showLower && ( {showLower && (
@@ -142,24 +141,15 @@ export default function LowerLines(props) {
animate='visible' animate='visible'
exit='exit' exit='exit'
> >
<motion.div <motion.div className='title-container' variants={titleContainerVariants}>
className='title-container'
variants={titleContainerVariants}
>
<motion.div className='title' variants={titleVariants}> <motion.div className='title' variants={titleVariants}>
{title.titleNow} {title.titleNow}
</motion.div> </motion.div>
<div className='title-decor' /> <div className='title-decor' />
</motion.div> </motion.div>
<motion.div <motion.div className='subtitle-container' variants={subtitleContainerVariants}>
className='subtitle-container'
variants={subtitleContainerVariants}
>
<div className='sub-decor' /> <div className='sub-decor' />
<motion.div <motion.div className='subtitle' variants={subtitleVariants}>
className='subtitle'
variants={subtitleVariants}
>
{title.presenterNow} {title.presenterNow}
</motion.div> </motion.div>
</motion.div> </motion.div>
@@ -3,6 +3,8 @@ 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 { 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';
@@ -172,6 +174,7 @@ export default function MinimalTimer(props: MinimalTimerProps) {
data-testid='minimal-timer' data-testid='minimal-timer'
> >
<NavigationMenu /> <NavigationMenu />
<EditFormDrawer 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>
@@ -4,6 +4,8 @@ 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';
@@ -55,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]} />
<div className='event-header'> <div className='event-header'>
{general.title} {general.title}
<div className='clock-container'> <div className='clock-container'>
@@ -4,6 +4,8 @@ import { 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 useFitText from '../../../common/hooks/useFitText'; import useFitText from '../../../common/hooks/useFitText';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
@@ -73,6 +75,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} />
<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,6 +3,8 @@ 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';
@@ -98,6 +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} />
<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>