diff --git a/apps/client/src/features/viewers/clock/Clock.tsx b/apps/client/src/features/viewers/clock/Clock.tsx
index 36bb3c7ce..ef40764a2 100644
--- a/apps/client/src/features/viewers/clock/Clock.tsx
+++ b/apps/client/src/features/viewers/clock/Clock.tsx
@@ -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'
>
-
+
{follow === null ? (
) : (
diff --git a/apps/client/src/features/viewers/lower-thirds/LowerClean.jsx b/apps/client/src/features/viewers/lower-thirds/LowerClean.jsx
deleted file mode 100644
index 24961f176..000000000
--- a/apps/client/src/features/viewers/lower-thirds/LowerClean.jsx
+++ /dev/null
@@ -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 (
-
-
-
-
- {showLower && (
-
-
- {title.titleNow}
-
-
- {title.presenterNow}
-
-
- )}
-
-
-
- {showLowerMessage && (
-
- {lower.text}
-
- )}
-
-
- );
-}
diff --git a/apps/client/src/features/viewers/lower-thirds/LowerClean.scss b/apps/client/src/features/viewers/lower-thirds/LowerClean.scss
deleted file mode 100644
index ac46163f7..000000000
--- a/apps/client/src/features/viewers/lower-thirds/LowerClean.scss
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/apps/client/src/features/viewers/lower-thirds/LowerLines.jsx b/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx
similarity index 80%
rename from apps/client/src/features/viewers/lower-thirds/LowerLines.jsx
rename to apps/client/src/features/viewers/lower-thirds/LowerLines.tsx
index 533f361c3..1b0b84f52 100644
--- a/apps/client/src/features/viewers/lower-thirds/LowerLines.jsx
+++ b/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx
@@ -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) {
-
+
{showLower && (
{
+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({
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 ;
- case 1:
- return ;
- default:
- return ;
+ const _fadeOut = searchParams.get('fadeout');
+ if (_fadeOut) {
+ const parsedValue = Number(_transitionIn);
+ if (!Number.isNaN(parsedValue)) {
+ options.fadeOut = parsedValue;
+ }
}
+
+ return ;
};
export default memo(Lower, areEqual);
-
-Lower.propTypes = {
- title: PropTypes.object,
- lower: PropTypes.object,
- viewSettings: PropTypes.object,
-};
diff --git a/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx b/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx
index 02ddd59d1..35c77a4fa 100644
--- a/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx
+++ b/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx
@@ -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'
>
-
+
{!hideMessagesOverlay && (
{pres.text}
diff --git a/apps/client/src/features/viewers/public/Public.scss b/apps/client/src/features/viewers/public/Public.scss
index 1606d7288..fd353bfec 100644
--- a/apps/client/src/features/viewers/public/Public.scss
+++ b/apps/client/src/features/viewers/public/Public.scss
@@ -87,7 +87,7 @@
grid-area: schedule;
overflow: hidden;
height: 100%;
- margin-left: 16px;
+ margin-left: clamp(16px, 5vw, 64px);;
}
.schedule-nav-container {
diff --git a/apps/client/src/features/viewers/public/Public.tsx b/apps/client/src/features/viewers/public/Public.tsx
index 8bb98cc36..8e71697e7 100644
--- a/apps/client/src/features/viewers/public/Public.tsx
+++ b/apps/client/src/features/viewers/public/Public.tsx
@@ -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 (
-
+
{general.title}
diff --git a/apps/client/src/features/viewers/studio/StudioClock.jsx b/apps/client/src/features/viewers/studio/StudioClock.jsx
index 255314f7a..433b21270 100644
--- a/apps/client/src/features/viewers/studio/StudioClock.jsx
+++ b/apps/client/src/features/viewers/studio/StudioClock.jsx
@@ -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 (
-
+
{clock}
-
+
diff --git a/apps/client/src/theme/ontimeButton.ts b/apps/client/src/theme/ontimeButton.ts
index 7d1be9a90..ab50a341b 100644
--- a/apps/client/src/theme/ontimeButton.ts
+++ b/apps/client/src/theme/ontimeButton.ts
@@ -47,6 +47,11 @@ export const ontimeButtonSubtle = {
},
};
+export const ontimeButtonGhosted = {
+ ...ontimeButtonSubtle,
+ backgroundColor: 'transparent',
+};
+
export const ontimeButtonSubtleOnLight = {
backgroundColor: '#ececec', // $gray-100
color: '#595959', // $gray-800
diff --git a/apps/client/src/theme/theme.ts b/apps/client/src/theme/theme.ts
index bb3a4706e..44d2dcfe3 100644
--- a/apps/client/src/theme/theme.ts
+++ b/apps/client/src/theme/theme.ts
@@ -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 },