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
@@ -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 { 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'
@@ -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,
};