mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-09 09:23:51 +00:00
@@ -27,6 +27,7 @@ $button-size: 48px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
@@ -46,7 +47,7 @@ $button-size: 48px;
|
||||
|
||||
.navButton {
|
||||
@extend .button;
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.menuContainer {
|
||||
|
||||
@@ -18,5 +18,6 @@ $progress-bar-br: 6px;
|
||||
height: $progress-bar-size;
|
||||
background-color: var(--timer-progress-override, $accent-color);
|
||||
transition: 1s linear;
|
||||
border-radius: $progress-bar-br 0 0 $progress-bar-br;
|
||||
transition-property: width;
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ interface EditFormInputProps {
|
||||
paramField: ParamField;
|
||||
}
|
||||
|
||||
export default function EditFormInput({ paramField }: EditFormInputProps) {
|
||||
export default function ParamInput({ paramField }: EditFormInputProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { id, type } = paramField;
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function EditFormInput({ paramField }: EditFormInputProps) {
|
||||
if (type === 'number') {
|
||||
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) ?? '';
|
||||
+6
@@ -13,8 +13,14 @@
|
||||
.drawerFooter {
|
||||
@extend .drawerContent;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
gap: $element-spacing;
|
||||
|
||||
button[type='reset'] {
|
||||
padding: 0 2em;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
button[type='submit'] {
|
||||
padding: 0 2em;
|
||||
}
|
||||
+12
-6
@@ -12,16 +12,16 @@ import {
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import EditFormInput from './EditFormInput';
|
||||
import ParamInput from './ParamInput';
|
||||
import { ParamField } from './types';
|
||||
|
||||
import style from './EditFormDrawer.module.scss';
|
||||
import style from './ViewParamsEditor.module.scss';
|
||||
|
||||
interface EditFormDrawerProps {
|
||||
paramFields: ParamField[];
|
||||
}
|
||||
|
||||
export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
|
||||
export default function ViewParamsEditor({ paramFields }: EditFormDrawerProps) {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { isOpen, onClose, onOpen } = useDisclosure();
|
||||
|
||||
@@ -40,6 +40,11 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
const clearParams = () => {
|
||||
setSearchParams();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
|
||||
formEvent.preventDefault();
|
||||
|
||||
@@ -53,8 +58,6 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
|
||||
|
||||
return newSearchParams;
|
||||
}, new URLSearchParams());
|
||||
|
||||
onEditDrawerClose();
|
||||
setSearchParams(newSearchParams);
|
||||
};
|
||||
|
||||
@@ -74,7 +77,7 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
|
||||
<label className={style.label}>
|
||||
<span className={style.title}>{field.title}</span>
|
||||
<span className={style.description}>{field.description}</span>
|
||||
<EditFormInput key={field.title} paramField={field} />
|
||||
<ParamInput key={field.title} paramField={field} />
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
@@ -82,6 +85,9 @@ export default function EditFormDrawer({ paramFields }: EditFormDrawerProps) {
|
||||
</DrawerBody>
|
||||
|
||||
<DrawerFooter className={style.drawerFooter}>
|
||||
<Button variant='ontime-ghosted' onClick={clearParams} type='reset'>
|
||||
Clear
|
||||
</Button>
|
||||
<Button variant='ontime-subtle' onClick={onEditDrawerClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
+16
-32
@@ -9,9 +9,10 @@ export const TIME_FORMAT_OPTION: ParamField = {
|
||||
};
|
||||
|
||||
export const CLOCK_OPTIONS: ParamField[] = [
|
||||
TIME_FORMAT_OPTION,
|
||||
{
|
||||
id: 'key',
|
||||
title: 'Key',
|
||||
title: 'Key Colour',
|
||||
description: 'Background colour in hexadecimal',
|
||||
type: 'string',
|
||||
},
|
||||
@@ -65,30 +66,14 @@ export const CLOCK_OPTIONS: ParamField[] = [
|
||||
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 TIMER_OPTIONS: ParamField[] = [TIME_FORMAT_OPTION];
|
||||
|
||||
export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
|
||||
{
|
||||
id: 'key',
|
||||
title: 'Key',
|
||||
title: 'Key Colour',
|
||||
description: 'Background colour in hexadecimal',
|
||||
type: 'string',
|
||||
},
|
||||
@@ -142,12 +127,6 @@ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
|
||||
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',
|
||||
@@ -160,13 +139,19 @@ export const MINIMAL_TIMER_OPTIONS: ParamField[] = [
|
||||
description: 'Whether to hide the overlay from showing timer screen messages',
|
||||
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[] = [
|
||||
{
|
||||
id: 'preset',
|
||||
title: 'Preset',
|
||||
description: 'Selects a style preset',
|
||||
description: 'Selects a style preset (0-1)',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
@@ -183,19 +168,19 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Text',
|
||||
title: 'Text Colour',
|
||||
description: 'Text colour in hexadecimal',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
id: 'bg',
|
||||
title: 'BG',
|
||||
title: 'Text Background',
|
||||
description: 'Text background colour in hexadecimal',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
id: 'key',
|
||||
title: 'Key',
|
||||
title: 'Key Colour',
|
||||
description: 'Screen background colour in hexadecimal',
|
||||
type: 'string',
|
||||
},
|
||||
@@ -205,15 +190,14 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
|
||||
description: 'Time (in seconds) the lower third displays before fading out',
|
||||
type: 'number',
|
||||
},
|
||||
TIME_FORMAT_OPTION,
|
||||
];
|
||||
|
||||
export const STUDIO_CLOCK_OPTIONS: ParamField[] = [
|
||||
TIME_FORMAT_OPTION,
|
||||
{
|
||||
id: 'seconds',
|
||||
title: 'Seconds',
|
||||
title: 'Show Seconds',
|
||||
description: 'Shows seconds in clock',
|
||||
type: 'boolean',
|
||||
},
|
||||
TIME_FORMAT_OPTION,
|
||||
];
|
||||
@@ -5,14 +5,14 @@ import { EventData, Message, OntimeEvent, ViewSettings } from 'ontime-types';
|
||||
import { formatDisplay } from 'ontime-utils';
|
||||
|
||||
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 ProgressBar from '../../../common/components/progress-bar/ProgressBar';
|
||||
import Schedule from '../../../common/components/schedule/Schedule';
|
||||
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
|
||||
import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
|
||||
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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { getEventsWithDelay } from '../../../common/utils/eventsManager';
|
||||
@@ -79,7 +79,7 @@ export default function Backstage(props: BackstageProps) {
|
||||
return (
|
||||
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<div className='event-header'>
|
||||
{general.title}
|
||||
<div className='clock-container'>
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { ViewSettings } from 'ontime-types';
|
||||
|
||||
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 { 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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { OverridableOptions } from '../../../common/models/View.types';
|
||||
@@ -129,12 +129,12 @@ export default function Clock(props: ClockProps) {
|
||||
style={{
|
||||
backgroundColor: userOptions.keyColour,
|
||||
justifyContent: userOptions.justifyContent,
|
||||
alignItems: userOptions.alignItems,
|
||||
alignContent: userOptions.alignItems,
|
||||
}}
|
||||
data-testid='clock-view'
|
||||
>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={CLOCK_OPTIONS} />
|
||||
<ViewParamsEditor paramFields={CLOCK_OPTIONS} />
|
||||
<div
|
||||
className='clock'
|
||||
style={{
|
||||
|
||||
@@ -4,9 +4,9 @@ import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent, ViewSettings
|
||||
import { formatDisplay } from 'ontime-utils';
|
||||
|
||||
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 { 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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import getDelayTo from '../../../common/utils/getDelayTo';
|
||||
@@ -112,7 +112,7 @@ export default function Countdown(props: CountdownProps) {
|
||||
return (
|
||||
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
|
||||
{follow === null ? (
|
||||
<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-17
@@ -1,20 +1,24 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
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 { 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';
|
||||
|
||||
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 defaults = {
|
||||
size: 1,
|
||||
transitionIn: 3,
|
||||
textColour: '#fffffa',
|
||||
bgColour: '#00000033',
|
||||
};
|
||||
const [showLower, setShowLower] = useState(true);
|
||||
|
||||
// Unmount if fadeOut
|
||||
@@ -22,15 +26,14 @@ export default function LowerLines(props) {
|
||||
if (!options.fadeOut) return;
|
||||
|
||||
// Calculate time
|
||||
const fadeOutTime = (parseInt(options.fadeOut, 10) + (options.transitionIn || defaults.transitionIn)) * 1000;
|
||||
if (isNaN(fadeOutTime)) return;
|
||||
const fadeOutTime = (options.fadeOut + options.transitionIn) * 1000;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
setShowLower(false);
|
||||
}, fadeOutTime);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [options.fadeOut, options.transitionIn, defaults.transitionIn]);
|
||||
}, [options.fadeOut, options.transitionIn]);
|
||||
|
||||
useEffect(() => {
|
||||
setShowLower(title.showNow);
|
||||
@@ -41,7 +44,7 @@ export default function LowerLines(props) {
|
||||
|
||||
// motion
|
||||
// transition segments
|
||||
const t = options.transitionIn || defaults.transitionIn;
|
||||
const t = options.transitionIn;
|
||||
const eight = t / 8;
|
||||
const quarter = t / 4;
|
||||
const third = t / 3;
|
||||
@@ -123,19 +126,19 @@ export default function LowerLines(props) {
|
||||
<div
|
||||
className='lower-third lines'
|
||||
style={{
|
||||
backgroundColor: options.keyColour || defaults.keyColour,
|
||||
color: options.textColour || defaults.textColour,
|
||||
backgroundColor: options.keyColour,
|
||||
color: options.textColour,
|
||||
fontSize: `${sizeMultiplier}vh`,
|
||||
}}
|
||||
>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={LOWER_THIRDS_OPTIONS} />
|
||||
<ViewParamsEditor paramFields={LOWER_THIRDS_OPTIONS} />
|
||||
|
||||
<AnimatePresence>
|
||||
{showLower && (
|
||||
<motion.div
|
||||
className='lower-container'
|
||||
style={{ backgroundColor: options.bgColour || defaults.bgColour }}
|
||||
style={{ backgroundColor: options.bgColour }}
|
||||
variants={lowerThirdVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
+60
-54
@@ -1,28 +1,46 @@
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Message, ViewSettings } from 'ontime-types';
|
||||
|
||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TitleManager } from '../ViewWrapper';
|
||||
|
||||
import LowerClean from './LowerClean';
|
||||
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);
|
||||
};
|
||||
|
||||
const Lower = (props) => {
|
||||
const Lower = (props: LowerProps) => {
|
||||
const { title, lower, viewSettings } = props;
|
||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||
const [searchParams] = useSearchParams();
|
||||
const [titles, setTitles] = useState({
|
||||
const [titles, setTitles] = useState<TitleManager>({
|
||||
titleNow: '',
|
||||
titleNext: '',
|
||||
subtitleNow: '',
|
||||
subtitleNext: '',
|
||||
presenterNow: '',
|
||||
presenterNext: '',
|
||||
noteNow: '',
|
||||
noteNext: '',
|
||||
showNow: false,
|
||||
showNext: false,
|
||||
});
|
||||
@@ -36,7 +54,7 @@ const Lower = (props) => {
|
||||
useEffect(() => {
|
||||
// clear titles if necessary
|
||||
// will trigger an animation out in the component
|
||||
let timeout = null;
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
if (
|
||||
title?.titleNow !== titles?.titleNow ||
|
||||
title?.subtitleNow !== titles?.subtitleNow ||
|
||||
@@ -56,7 +74,7 @@ const Lower = (props) => {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line -- we do this to keep animations
|
||||
}, [title.titleNow, title.subtitleNow, title.presenterNow]);
|
||||
|
||||
// defer rendering until we load stylesheets
|
||||
@@ -64,72 +82,60 @@ const Lower = (props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: sanitize data
|
||||
// getting config from URL: preset, size, transition, bg, text, key
|
||||
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
|
||||
// Check for user options
|
||||
// create aux
|
||||
const options = {};
|
||||
|
||||
// preset: selector
|
||||
// Should be a number 1-n
|
||||
const p = parseInt(searchParams.get('preset'), 10);
|
||||
const preset = !isNaN(p) ? 1 : p;
|
||||
const options: LowerOptions = {
|
||||
size: 1,
|
||||
transitionIn: 3,
|
||||
textColour: '#fffffa',
|
||||
bgColour: '#00000033',
|
||||
fadeOut: 3,
|
||||
};
|
||||
|
||||
// size: multiplier
|
||||
// Should be a number 0.0-n
|
||||
const s = searchParams.get('size');
|
||||
if (s) options.size = s;
|
||||
const _size = searchParams.get('size');
|
||||
if (_size) {
|
||||
const parsedValue = Number(_size);
|
||||
if (!Number.isNaN(parsedValue)) {
|
||||
options.size = parsedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// transitionIn: seconds
|
||||
// Should be a number 0-n
|
||||
const t = parseInt(searchParams.get('transition'), 10);
|
||||
if (!isNaN(t)) options.transitionIn = t;
|
||||
const _transitionIn = searchParams.get('transition');
|
||||
if (_transitionIn) {
|
||||
const parsedValue = Number(_transitionIn);
|
||||
if (!Number.isNaN(parsedValue)) {
|
||||
options.transitionIn = parsedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// textColour: string
|
||||
// Should be a hex string '#ffffff'
|
||||
const c = searchParams.get('text');
|
||||
if (c) options.textColour = `#${c}`;
|
||||
const _textColour = searchParams.get('text');
|
||||
if (_textColour) options.textColour = `#${_textColour}`;
|
||||
|
||||
// bgColour: string
|
||||
// Should be a hex string '#ffffff'
|
||||
const b = searchParams.get('bg');
|
||||
if (b) options.bgColour = `#${b}`;
|
||||
const _bgColour = searchParams.get('bg');
|
||||
if (_bgColour) options.bgColour = `#${_bgColour}`;
|
||||
|
||||
// key: string
|
||||
// Should be a hex string '#00FF00' with key colour
|
||||
const k = searchParams.get('key');
|
||||
if (k) options.keyColour = `#${k}`;
|
||||
const _key = searchParams.get('key');
|
||||
if (_key) options.keyColour = `#${_key}`;
|
||||
|
||||
// fadeOut: seconds
|
||||
// Should be a number 0-n
|
||||
const f = parseInt(searchParams.get('fadeout'), 10);
|
||||
if (!isNaN(f)) options.fadeOut = f;
|
||||
|
||||
// x: pixels
|
||||
// Should be a number 0-n
|
||||
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} />;
|
||||
const _fadeOut = searchParams.get('fadeout');
|
||||
if (_fadeOut) {
|
||||
const parsedValue = Number(_transitionIn);
|
||||
if (!Number.isNaN(parsedValue)) {
|
||||
options.fadeOut = parsedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return <LowerLines lower={lower} title={titles} options={options} />;
|
||||
};
|
||||
|
||||
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 { 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 { 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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { OverridableOptions } from '../../../common/models/View.types';
|
||||
@@ -169,12 +169,12 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
style={{
|
||||
backgroundColor: userOptions.keyColour,
|
||||
justifyContent: userOptions.justifyContent,
|
||||
alignItems: userOptions.alignItems,
|
||||
alignContent: userOptions.alignItems,
|
||||
}}
|
||||
data-testid='minimal-timer'
|
||||
>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={MINIMAL_TIMER_OPTIONS} />
|
||||
<ViewParamsEditor paramFields={MINIMAL_TIMER_OPTIONS} />
|
||||
{!hideMessagesOverlay && (
|
||||
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
|
||||
<div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
grid-area: schedule;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
margin-left: 16px;
|
||||
margin-left: clamp(16px, 5vw, 64px);;
|
||||
}
|
||||
|
||||
.schedule-nav-container {
|
||||
|
||||
@@ -4,13 +4,13 @@ import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { EventData, Message, OntimeEvent, ViewSettings } from 'ontime-types';
|
||||
|
||||
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 Schedule from '../../../common/components/schedule/Schedule';
|
||||
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
|
||||
import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
|
||||
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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
@@ -57,7 +57,7 @@ export default function Public(props: BackstageProps) {
|
||||
return (
|
||||
<div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<div className='event-header'>
|
||||
{general.title}
|
||||
<div className='clock-container'>
|
||||
|
||||
@@ -4,9 +4,9 @@ import { formatDisplay, millisToString } from 'ontime-utils';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
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 { 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 { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { formatEventList, getEventsWithDelay, trimRundown } from '../../../common/utils/eventsManager';
|
||||
@@ -74,7 +74,7 @@ export default function StudioClock(props) {
|
||||
return (
|
||||
<div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={STUDIO_CLOCK_OPTIONS} />
|
||||
<ViewParamsEditor paramFields={STUDIO_CLOCK_OPTIONS} />
|
||||
<div className='clock-container'>
|
||||
<div className={`studio-timer ${showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
|
||||
<div
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
|
||||
|
||||
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 NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
|
||||
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 { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
@@ -100,7 +100,7 @@ export default function Timer(props: TimerProps) {
|
||||
return (
|
||||
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
|
||||
<NavigationMenu />
|
||||
<EditFormDrawer paramFields={TIMER_OPTIONS} />
|
||||
<ViewParamsEditor paramFields={TIMER_OPTIONS} />
|
||||
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
|
||||
<div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,11 @@ export const ontimeButtonSubtle = {
|
||||
},
|
||||
};
|
||||
|
||||
export const ontimeButtonGhosted = {
|
||||
...ontimeButtonSubtle,
|
||||
backgroundColor: 'transparent',
|
||||
};
|
||||
|
||||
export const ontimeButtonSubtleOnLight = {
|
||||
backgroundColor: '#ececec', // $gray-100
|
||||
color: '#595959', // $gray-800
|
||||
|
||||
@@ -3,6 +3,7 @@ import { extendTheme } from '@chakra-ui/react';
|
||||
import { ontimeAlertOnLight } from './OntimeAlert';
|
||||
import {
|
||||
ontimeButtonFilled,
|
||||
ontimeButtonGhosted,
|
||||
ontimeButtonOutlined,
|
||||
ontimeButtonSubtle,
|
||||
ontimeButtonSubtleOnLight,
|
||||
@@ -42,6 +43,7 @@ const theme = extendTheme({
|
||||
'ontime-filled': { ...ontimeButtonFilled },
|
||||
'ontime-outlined': { ...ontimeButtonOutlined },
|
||||
'ontime-subtle': { ...ontimeButtonSubtle },
|
||||
'ontime-ghosted': { ...ontimeButtonGhosted },
|
||||
'ontime-subtle-white': { ...ontimeButtonSubtleWhite },
|
||||
'ontime-subtle-on-light': { ...ontimeButtonSubtleOnLight },
|
||||
'ontime-ghost-on-light': { ...ontimeGhostOnLight },
|
||||
|
||||
Reference in New Issue
Block a user