mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 23:19:09 +00:00
refactor: create separation between editor wrapper and contents
This commit is contained in:
committed by
Carlos Valente
parent
1d7ee163a0
commit
972f9e35fd
@@ -59,3 +59,10 @@
|
|||||||
height: 0.75em;
|
height: 0.75em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
border-radius: $panel-border-radius;
|
||||||
|
background-color: $bg-container-l2;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,3 +55,7 @@ interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
export function Separator({ className, orientation = 'vertical', ...elementProps }: SeparatorProps) {
|
export function Separator({ className, orientation = 'vertical', ...elementProps }: SeparatorProps) {
|
||||||
return <div className={cx([style.separator, style[orientation], className])} role='separator' {...elementProps} />;
|
return <div className={cx([style.separator, style[orientation], className])} role='separator' {...elementProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Panel({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return <div className={cx([style.panel, className])} {...props} />;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.flexPanel {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentLayout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $section-spacing;
|
||||||
|
color: $ui-white;
|
||||||
|
padding-top: $panel-top-padding;
|
||||||
|
}
|
||||||
@@ -1,34 +1,32 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { CornerExtract } from '../../../common/components/editor-utils/EditorUtils';
|
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||||
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
|
||||||
import { getIsNavigationLocked } from '../../../externals';
|
import { getIsNavigationLocked } from '../../../externals';
|
||||||
|
|
||||||
import MessageControl from './MessageControl';
|
import MessageControl from './MessageControl';
|
||||||
|
|
||||||
import style from '../../../views/editor/Editor.module.scss';
|
import style from './MessageControlExport.module.scss';
|
||||||
|
|
||||||
export default memo(MessageControlExport);
|
export default memo(MessageControlExport);
|
||||||
function MessageControlExport() {
|
function MessageControlExport() {
|
||||||
const isExtracted = window.location.pathname.includes('/messagecontrol');
|
const isExtracted = window.location.pathname.includes('/messagecontrol');
|
||||||
const classes = cx([style.content, style.contentColumnLayout]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectRoute permission='editor'>
|
<ProtectRoute permission='editor'>
|
||||||
<div className={style.messages} data-testid='panel-messages-control'>
|
<Editor.Panel className={style.flexPanel} data-testid='panel-messages-control'>
|
||||||
{!isExtracted && <CornerExtract onClick={(event) => handleLinks('messagecontrol', event)} />}
|
{!isExtracted && <Editor.CornerExtract onClick={(event) => handleLinks('messagecontrol', event)} />}
|
||||||
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
||||||
|
|
||||||
<div className={classes}>
|
<div className={style.contentLayout}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<MessageControl />
|
<MessageControl />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Editor.Panel>
|
||||||
</ProtectRoute>
|
</ProtectRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.content {
|
||||||
|
padding-top: $panel-top-padding;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { CornerExtract } from '../../../common/components/editor-utils/EditorUtils';
|
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||||
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||||
@@ -9,7 +9,7 @@ import { getIsNavigationLocked } from '../../../externals';
|
|||||||
|
|
||||||
import PlaybackControl from './PlaybackControl';
|
import PlaybackControl from './PlaybackControl';
|
||||||
|
|
||||||
import style from '../../../views/editor/Editor.module.scss';
|
import style from './TimerControlExport.module.scss';
|
||||||
|
|
||||||
export default memo(TimerControlExport);
|
export default memo(TimerControlExport);
|
||||||
function TimerControlExport() {
|
function TimerControlExport() {
|
||||||
@@ -17,8 +17,8 @@ function TimerControlExport() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectRoute permission='editor'>
|
<ProtectRoute permission='editor'>
|
||||||
<div className={style.playback} data-testid='panel-timer-control'>
|
<Editor.Panel data-testid='panel-timer-control'>
|
||||||
{!isExtracted && <CornerExtract onClick={(event) => handleLinks('timercontrol', event)} />}
|
{!isExtracted && <Editor.CornerExtract onClick={(event) => handleLinks('timercontrol', event)} />}
|
||||||
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
||||||
|
|
||||||
<div className={style.content}>
|
<div className={style.content}>
|
||||||
@@ -26,7 +26,7 @@ function TimerControlExport() {
|
|||||||
<PlaybackControl />
|
<PlaybackControl />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Editor.Panel>
|
||||||
</ProtectRoute>
|
</ProtectRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@use '../../views/editor/EditorMixin' as editor;
|
|
||||||
|
|
||||||
.rundownExport {
|
.rundownExport {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1 1 auto; /* flex-grow: 1, flex-shrink: 1, flex-basis: auto */
|
flex: 1 1 auto; /* flex-grow: 1, flex-shrink: 1, flex-basis: auto */
|
||||||
@@ -23,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
@include editor.panel;
|
display: flex;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
padding-inline: 0;
|
padding-inline: 0;
|
||||||
box-shadow: $box-shadow-right;
|
box-shadow: $box-shadow-right;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useSessionStorage } from '@mantine/hooks';
|
import { useSessionStorage } from '@mantine/hooks';
|
||||||
|
|
||||||
import { CornerExtract } from '../../common/components/editor-utils/EditorUtils';
|
import * as Editor from '../../common/components/editor-utils/EditorUtils';
|
||||||
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
||||||
import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu';
|
import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
||||||
@@ -58,14 +58,14 @@ function RundownExport() {
|
|||||||
<FinderPlacement />
|
<FinderPlacement />
|
||||||
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
{isExtracted && <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />}
|
||||||
<div className={style.rundown}>
|
<div className={style.rundown}>
|
||||||
<div className={style.list}>
|
<Editor.Panel className={style.list}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
{!isExtracted && <CornerExtract onClick={(event) => handleLinks('rundown', event)} />}
|
{!isExtracted && <Editor.CornerExtract onClick={(event) => handleLinks('rundown', event)} />}
|
||||||
<RundownContextMenu>
|
<RundownContextMenu>
|
||||||
<RundownWrapper />
|
<RundownWrapper />
|
||||||
</RundownContextMenu>
|
</RundownContextMenu>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</Editor.Panel>
|
||||||
{!hideSideBar && (
|
{!hideSideBar && (
|
||||||
<div className={style.side}>
|
<div className={style.side}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ $component-border-radius-md: 3px;
|
|||||||
$component-border-radius-sm: 2px;
|
$component-border-radius-sm: 2px;
|
||||||
$component-border-radius-full: 99px;
|
$component-border-radius-full: 99px;
|
||||||
|
|
||||||
|
$panel-border-radius: 8px;
|
||||||
|
$panel-gap: 0.5rem;
|
||||||
|
$panel-top-padding: 1.5rem;
|
||||||
|
|
||||||
// indexes
|
// indexes
|
||||||
$zindex-floating: 100;
|
$zindex-floating: 100;
|
||||||
$zindex-nav: 200;
|
$zindex-nav: 200;
|
||||||
|
|||||||
@@ -1,24 +1,5 @@
|
|||||||
@use './EditorMixin' as editor;
|
|
||||||
|
|
||||||
$min-playback-width: 27rem;
|
$min-playback-width: 27rem;
|
||||||
$max-playback-width: 30rem;
|
$max-playback-width: 30rem;
|
||||||
$panel-gap: 0.5rem;
|
|
||||||
|
|
||||||
.mainContainer {
|
|
||||||
background-color: $ui-black;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: $ui-white;
|
|
||||||
padding: 0.5rem;
|
|
||||||
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto;
|
|
||||||
grid-template-rows: 3.5rem 1fr;
|
|
||||||
grid-template-areas:
|
|
||||||
'overview'
|
|
||||||
'main';
|
|
||||||
gap: $panel-gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panelContainer {
|
.panelContainer {
|
||||||
grid-area: main;
|
grid-area: main;
|
||||||
@@ -26,15 +7,6 @@ $panel-gap: 0.5rem;
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: $panel-gap;
|
gap: $panel-gap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.rundown,
|
|
||||||
.playback,
|
|
||||||
.messages {
|
|
||||||
position: relative;
|
|
||||||
border-radius: var(--editor--panel__br);
|
|
||||||
background-color: $bg-container-l2;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
@@ -45,18 +17,3 @@ $panel-gap: 0.5rem;
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $panel-gap;
|
gap: $panel-gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentColumnLayout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: $section-spacing;
|
|
||||||
color: $ui-white;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,4 @@
|
|||||||
import { lazy, useCallback, useEffect } from 'react';
|
import { lazy } from 'react';
|
||||||
import { IoApps, IoClose, IoSettingsOutline } from 'react-icons/io5';
|
|
||||||
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
|
||||||
|
|
||||||
import IconButton from '../../common/components/buttons/IconButton';
|
|
||||||
import NavigationMenu from '../../common/components/navigation-menu/NavigationMenu';
|
|
||||||
import { useElectronListener } from '../../common/hooks/useElectronEvent';
|
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
|
||||||
import AppSettings from '../../features/app-settings/AppSettings';
|
|
||||||
import useAppSettingsNavigation from '../../features/app-settings/useAppSettingsNavigation';
|
|
||||||
import EditorOverview from '../../features/overview/EditorOverview';
|
|
||||||
|
|
||||||
import WelcomePlacement from './welcome/WelcomePlacement';
|
|
||||||
|
|
||||||
import styles from './Editor.module.scss';
|
import styles from './Editor.module.scss';
|
||||||
|
|
||||||
@@ -19,56 +7,13 @@ const TimerControl = lazy(() => import('../../features/control/playback/TimerCon
|
|||||||
const MessageControl = lazy(() => import('../../features/control/message/MessageControlExport'));
|
const MessageControl = lazy(() => import('../../features/control/message/MessageControlExport'));
|
||||||
|
|
||||||
export default function Editor() {
|
export default function Editor() {
|
||||||
const { isOpen: isSettingsOpen, setLocation, close } = useAppSettingsNavigation();
|
|
||||||
const [isOpen, handler] = useDisclosure();
|
|
||||||
|
|
||||||
useWindowTitle('Editor');
|
|
||||||
|
|
||||||
// we need to register the listener to change the editor location
|
|
||||||
useElectronListener();
|
|
||||||
|
|
||||||
// listen to shutdown request from electron process
|
|
||||||
useEffect(() => {
|
|
||||||
if (window.process?.type === 'renderer') {
|
|
||||||
window.ipcRenderer.on('user-request-shutdown', () => {
|
|
||||||
setLocation('shutdown');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [setLocation]);
|
|
||||||
|
|
||||||
const toggleSettings = useCallback(() => {
|
|
||||||
if (isSettingsOpen) {
|
|
||||||
close();
|
|
||||||
} else {
|
|
||||||
setLocation('settings');
|
|
||||||
}
|
|
||||||
}, [close, isSettingsOpen, setLocation]);
|
|
||||||
|
|
||||||
useHotkeys([['mod + ,', toggleSettings]]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.mainContainer} data-testid='event-editor'>
|
<div id='panels' className={styles.panelContainer}>
|
||||||
<WelcomePlacement />
|
<div className={styles.left}>
|
||||||
<NavigationMenu isOpen={isOpen} onClose={handler.close} />
|
<TimerControl />
|
||||||
<EditorOverview>
|
<MessageControl />
|
||||||
<IconButton aria-label='Toggle navigation' variant='subtle-white' size='xlarge' onClick={handler.open}>
|
</div>
|
||||||
<IoApps />
|
<Rundown />
|
||||||
</IconButton>
|
|
||||||
<IconButton aria-label='Toggle settings' variant='subtle-white' size='xlarge' onClick={toggleSettings}>
|
|
||||||
{isSettingsOpen ? <IoClose /> : <IoSettingsOutline />}
|
|
||||||
</IconButton>
|
|
||||||
</EditorOverview>
|
|
||||||
{isSettingsOpen ? (
|
|
||||||
<AppSettings />
|
|
||||||
) : (
|
|
||||||
<div id='panels' className={styles.panelContainer}>
|
|
||||||
<div className={styles.left}>
|
|
||||||
<TimerControl />
|
|
||||||
<MessageControl />
|
|
||||||
</div>
|
|
||||||
<Rundown />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
@use '../../theme/ontimeColours' as *;
|
|
||||||
@use '../../theme/ontimeStyles' as *;
|
|
||||||
|
|
||||||
// declare editor specific styling constants
|
|
||||||
:root {
|
|
||||||
--editor--panel__br: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin panel() {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
border-radius: var(--editor--panel__br);
|
|
||||||
height: 100%;
|
|
||||||
background-color: $bg-container-l2;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
.mainContainer {
|
||||||
|
background-color: $ui-black;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: $ui-white;
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
grid-template-rows: 3.5rem 1fr;
|
||||||
|
grid-template-areas:
|
||||||
|
'overview'
|
||||||
|
'main';
|
||||||
|
gap: $panel-gap;
|
||||||
|
}
|
||||||
@@ -1,11 +1,64 @@
|
|||||||
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { IoApps, IoClose, IoSettingsOutline } from 'react-icons/io5';
|
||||||
|
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
||||||
|
|
||||||
|
import IconButton from '../../common/components/buttons/IconButton';
|
||||||
|
import NavigationMenu from '../../common/components/navigation-menu/NavigationMenu';
|
||||||
|
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
||||||
|
import { useElectronListener } from '../../common/hooks/useElectronEvent';
|
||||||
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
|
import AppSettings from '../../features/app-settings/AppSettings';
|
||||||
|
import useAppSettingsNavigation from '../../features/app-settings/useAppSettingsNavigation';
|
||||||
|
import EditorOverview from '../../features/overview/EditorOverview';
|
||||||
|
|
||||||
|
import WelcomePlacement from './welcome/WelcomePlacement';
|
||||||
import Editor from './Editor';
|
import Editor from './Editor';
|
||||||
|
|
||||||
|
import styles from './ProtectedEditor.module.scss';
|
||||||
|
|
||||||
export default function ProtectedEditor() {
|
export default function ProtectedEditor() {
|
||||||
|
const { isOpen: isSettingsOpen, setLocation, close } = useAppSettingsNavigation();
|
||||||
|
const [isOpen, handler] = useDisclosure();
|
||||||
|
|
||||||
|
useWindowTitle('Editor');
|
||||||
|
|
||||||
|
// we need to register the listener to change the editor location
|
||||||
|
useElectronListener();
|
||||||
|
|
||||||
|
// listen to shutdown request from electron process
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.process?.type === 'renderer') {
|
||||||
|
window.ipcRenderer.on('user-request-shutdown', () => {
|
||||||
|
setLocation('shutdown');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [setLocation]);
|
||||||
|
|
||||||
|
const toggleSettings = useCallback(() => {
|
||||||
|
if (isSettingsOpen) {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
setLocation('settings');
|
||||||
|
}
|
||||||
|
}, [close, isSettingsOpen, setLocation]);
|
||||||
|
|
||||||
|
useHotkeys([['mod + ,', toggleSettings]]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectRoute permission='editor'>
|
<ProtectRoute permission='editor'>
|
||||||
<Editor />
|
<div className={styles.mainContainer} data-testid='event-editor'>
|
||||||
|
<WelcomePlacement />
|
||||||
|
<NavigationMenu isOpen={isOpen} onClose={handler.close} />
|
||||||
|
<EditorOverview>
|
||||||
|
<IconButton aria-label='Toggle navigation' variant='subtle-white' size='xlarge' onClick={handler.open}>
|
||||||
|
<IoApps />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton aria-label='Toggle settings' variant='subtle-white' size='xlarge' onClick={toggleSettings}>
|
||||||
|
{isSettingsOpen ? <IoClose /> : <IoSettingsOutline />}
|
||||||
|
</IconButton>
|
||||||
|
</EditorOverview>
|
||||||
|
{isSettingsOpen ? <AppSettings /> : <Editor />}
|
||||||
|
</div>
|
||||||
</ProtectRoute>
|
</ProtectRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user