mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-10 16:49:41 +00:00
UI tweaks (#800)
* style: placeholder for progress value * style: rundown menu * style: extracted rundown takes whole screen * style: tweaks to rundown indicators * feat: allow exporting info * style: indicate action
This commit is contained in:
@@ -2,6 +2,7 @@ import { lazy, Suspense } from 'react';
|
|||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
import withAlias from './features/AliasWrapper';
|
import withAlias from './features/AliasWrapper';
|
||||||
|
import Log from './features/log/Log';
|
||||||
import withData from './features/viewers/ViewWrapper';
|
import withData from './features/viewers/ViewWrapper';
|
||||||
|
|
||||||
const Editor = lazy(() => import('./features/editors/ProtectedEditor'));
|
const Editor = lazy(() => import('./features/editors/ProtectedEditor'));
|
||||||
@@ -84,6 +85,14 @@ export default function AppRouter() {
|
|||||||
</EditorFeatureWrapper>
|
</EditorFeatureWrapper>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path='/log'
|
||||||
|
element={
|
||||||
|
<EditorFeatureWrapper>
|
||||||
|
<Log />
|
||||||
|
</EditorFeatureWrapper>
|
||||||
|
}
|
||||||
|
/>
|
||||||
{/*/!* Send to default if nothing found *!/*/}
|
{/*/!* Send to default if nothing found *!/*/}
|
||||||
<Route path='*' element={<STimer />} />
|
<Route path='*' element={<STimer />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { create } from 'zustand';
|
|||||||
export enum AppMode {
|
export enum AppMode {
|
||||||
Run = 'run',
|
Run = 'run',
|
||||||
Edit = 'edit',
|
Edit = 'edit',
|
||||||
|
Freeze = 'freeze',
|
||||||
}
|
}
|
||||||
|
|
||||||
const appModeKey = 'ontime-app-mode';
|
const appModeKey = 'ontime-app-mode';
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ $inner-padding: 1rem;
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
color: $ui-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paragraph {
|
.paragraph {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.iconRotate {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { MouseEvent } from 'react';
|
||||||
|
import { Button } from '@chakra-ui/react';
|
||||||
|
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
|
||||||
|
|
||||||
|
import { handleLinks } from '../../../../common/utils/linkUtils';
|
||||||
|
import Log from '../../../log/Log';
|
||||||
|
import * as Panel from '../PanelUtils';
|
||||||
|
|
||||||
|
import style from './LogExport.module.scss';
|
||||||
|
|
||||||
|
export default function LogExport() {
|
||||||
|
const extract = (event: MouseEvent) => {
|
||||||
|
handleLinks(event, 'log');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Panel.Section>
|
||||||
|
<Panel.Card>
|
||||||
|
<Panel.SubHeader>
|
||||||
|
Network log
|
||||||
|
<Button
|
||||||
|
variant='ontime-subtle'
|
||||||
|
size='sm'
|
||||||
|
rightIcon={<IoArrowUp className={style.iconRotate} />}
|
||||||
|
onClick={extract}
|
||||||
|
>
|
||||||
|
Extract
|
||||||
|
</Button>
|
||||||
|
</Panel.SubHeader>
|
||||||
|
<Panel.Divider />
|
||||||
|
<Log />
|
||||||
|
</Panel.Card>
|
||||||
|
</Panel.Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as Panel from '../PanelUtils';
|
import * as Panel from '../PanelUtils';
|
||||||
|
|
||||||
import Log from './Log';
|
import LogExport from './LogExport';
|
||||||
import InfoNif from './NetworkInterfaces';
|
import InfoNif from './NetworkInterfaces';
|
||||||
|
|
||||||
export default function LogPanel() {
|
export default function LogPanel() {
|
||||||
@@ -8,19 +8,10 @@ export default function LogPanel() {
|
|||||||
<>
|
<>
|
||||||
<Panel.Header>Application log</Panel.Header>
|
<Panel.Header>Application log</Panel.Header>
|
||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.SubHeader>Available networks</Panel.SubHeader>
|
|
||||||
<Panel.Card>
|
|
||||||
<Panel.Paragraph>Ontime is streaming on the following network interfaces</Panel.Paragraph>
|
<Panel.Paragraph>Ontime is streaming on the following network interfaces</Panel.Paragraph>
|
||||||
|
</Panel.Section>
|
||||||
<InfoNif />
|
<InfoNif />
|
||||||
</Panel.Card>
|
<LogExport />
|
||||||
</Panel.Section>
|
|
||||||
|
|
||||||
<Panel.Section>
|
|
||||||
<Panel.SubHeader>Network log</Panel.SubHeader>
|
|
||||||
<Panel.Card>
|
|
||||||
<Log />
|
|
||||||
</Panel.Card>
|
|
||||||
</Panel.Section>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -5,7 +5,7 @@ $info-hover: $section-white;
|
|||||||
font-size: $inner-section-text-size;
|
font-size: $inner-section-text-size;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
min-height: 10vh;
|
min-height: 20vh;
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ $info-hover: $section-white;
|
|||||||
.buttonBar {
|
.buttonBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5rem;
|
gap: 1em;
|
||||||
justify-content: flex-start;
|
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
+14
-14
@@ -2,7 +2,7 @@ import { useCallback, useState } from 'react';
|
|||||||
import { Button } from '@chakra-ui/react';
|
import { Button } from '@chakra-ui/react';
|
||||||
import { LogOrigin } from 'ontime-types';
|
import { LogOrigin } from 'ontime-types';
|
||||||
|
|
||||||
import { clearLogs, useLogData } from '../../../../common/stores/logger';
|
import { clearLogs, useLogData } from '../../common/stores/logger';
|
||||||
|
|
||||||
import style from './Log.module.scss';
|
import style from './Log.module.scss';
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ export default function Log() {
|
|||||||
<>
|
<>
|
||||||
<div className={style.buttonBar}>
|
<div className={style.buttonBar}>
|
||||||
<Button
|
<Button
|
||||||
variant={showUser ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showUser ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowUser((s) => !s)}
|
onClick={() => setShowUser((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.User)}
|
onAuxClick={() => disableOthers(LogOrigin.User)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
@@ -60,8 +60,8 @@ export default function Log() {
|
|||||||
{LogOrigin.User}
|
{LogOrigin.User}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={showClient ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showClient ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowClient((s) => !s)}
|
onClick={() => setShowClient((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.Client)}
|
onAuxClick={() => disableOthers(LogOrigin.Client)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
@@ -69,8 +69,8 @@ export default function Log() {
|
|||||||
{LogOrigin.Client}
|
{LogOrigin.Client}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={showServer ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showServer ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowServer((s) => !s)}
|
onClick={() => setShowServer((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.Server)}
|
onAuxClick={() => disableOthers(LogOrigin.Server)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
@@ -78,8 +78,8 @@ export default function Log() {
|
|||||||
{LogOrigin.Server}
|
{LogOrigin.Server}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={showPlayback ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showPlayback ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowPlayback((s) => !s)}
|
onClick={() => setShowPlayback((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.Playback)}
|
onAuxClick={() => disableOthers(LogOrigin.Playback)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
@@ -87,8 +87,8 @@ export default function Log() {
|
|||||||
{LogOrigin.Playback}
|
{LogOrigin.Playback}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={showRx ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showRx ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowRx((s) => !s)}
|
onClick={() => setShowRx((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.Rx)}
|
onAuxClick={() => disableOthers(LogOrigin.Rx)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
@@ -96,15 +96,15 @@ export default function Log() {
|
|||||||
{LogOrigin.Rx}
|
{LogOrigin.Rx}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={showTx ? 'ontime-filled' : 'ontime-subtle'}
|
variant={showTx ? 'ontime-filled' : 'ontime-outlined'}
|
||||||
size='sm'
|
size='xs'
|
||||||
onClick={() => setShowTx((s) => !s)}
|
onClick={() => setShowTx((s) => !s)}
|
||||||
onAuxClick={() => disableOthers(LogOrigin.Tx)}
|
onAuxClick={() => disableOthers(LogOrigin.Tx)}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
{LogOrigin.Tx}
|
{LogOrigin.Tx}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant='ontime-outlined' size='sm' onClick={clearLogs}>
|
<Button variant='ontime-subtle' size='xs' onClick={clearLogs}>
|
||||||
Clear
|
Clear
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,19 +1,11 @@
|
|||||||
import { memo, useCallback, useEffect } from 'react';
|
import { memo, useCallback, useEffect } from 'react';
|
||||||
import { IconButton, MenuButton, Tooltip } from '@chakra-ui/react';
|
|
||||||
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
|
||||||
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
|
|
||||||
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
|
||||||
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
||||||
import { IoSnowOutline } from '@react-icons/all-files/io5/IoSnowOutline';
|
|
||||||
|
|
||||||
import QuitIconBtn from '../../common/components/buttons/QuitIconBtn';
|
import QuitIconBtn from '../../common/components/buttons/QuitIconBtn';
|
||||||
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
|
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
|
||||||
import useElectronEvent from '../../common/hooks/useElectronEvent';
|
import useElectronEvent from '../../common/hooks/useElectronEvent';
|
||||||
import { AppMode, useAppMode } from '../../common/stores/appModeStore';
|
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
|
|
||||||
import RundownMenu from './RundownMenu';
|
|
||||||
|
|
||||||
import style from './MenuBar.module.scss';
|
import style from './MenuBar.module.scss';
|
||||||
|
|
||||||
interface MenuBarProps {
|
interface MenuBarProps {
|
||||||
@@ -40,11 +32,6 @@ const MenuBar = (props: MenuBarProps) => {
|
|||||||
const { isOldSettingsOpen, onSettingsOpen, onSettingsClose, openSettings, isSettingsOpen } = props;
|
const { isOldSettingsOpen, onSettingsOpen, onSettingsClose, openSettings, isSettingsOpen } = props;
|
||||||
const { isElectron, sendToElectron } = useElectronEvent();
|
const { isElectron, sendToElectron } = useElectronEvent();
|
||||||
|
|
||||||
const appMode = useAppMode((state) => state.mode);
|
|
||||||
const setAppMode = useAppMode((state) => state.setMode);
|
|
||||||
|
|
||||||
const setRunMode = () => setAppMode(AppMode.Run);
|
|
||||||
const setEditMode = () => setAppMode(AppMode.Edit);
|
|
||||||
const sendShutdown = () => {
|
const sendShutdown = () => {
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
sendToElectron('shutdown', 'now');
|
sendToElectron('shutdown', 'now');
|
||||||
@@ -84,39 +71,10 @@ const MenuBar = (props: MenuBarProps) => {
|
|||||||
<div className={style.menu}>
|
<div className={style.menu}>
|
||||||
<QuitIconBtn disabled={!isElectron} clickHandler={sendShutdown} />
|
<QuitIconBtn disabled={!isElectron} clickHandler={sendShutdown} />
|
||||||
<div className={style.gap} />
|
<div className={style.gap} />
|
||||||
<RundownMenu>
|
|
||||||
<Tooltip label='Rundown...'>
|
|
||||||
<MenuButton as={IconButton} icon={<IoAdd />} {...buttonStyle} aria-label='Rundown menu' />
|
|
||||||
</Tooltip>
|
|
||||||
</RundownMenu>
|
|
||||||
<TooltipActionBtn
|
|
||||||
{...buttonStyle}
|
|
||||||
icon={<IoSnowOutline />}
|
|
||||||
className={appMode === AppMode.Run ? style.open : ''}
|
|
||||||
clickHandler={setRunMode}
|
|
||||||
tooltip='Freeze rundown'
|
|
||||||
aria-label='Freeze rundown'
|
|
||||||
/>
|
|
||||||
<TooltipActionBtn
|
|
||||||
{...buttonStyle}
|
|
||||||
icon={<IoPlay />}
|
|
||||||
className={appMode === AppMode.Run ? style.open : ''}
|
|
||||||
clickHandler={setRunMode}
|
|
||||||
tooltip='Run mode'
|
|
||||||
aria-label='Run mode'
|
|
||||||
/>
|
|
||||||
<TooltipActionBtn
|
|
||||||
{...buttonStyle}
|
|
||||||
icon={<IoOptions />}
|
|
||||||
className={appMode === AppMode.Edit ? style.open : ''}
|
|
||||||
clickHandler={setEditMode}
|
|
||||||
tooltip='Edit mode'
|
|
||||||
aria-label='Edit mode'
|
|
||||||
/>
|
|
||||||
<div className={style.gap} />
|
<div className={style.gap} />
|
||||||
<TooltipActionBtn
|
<TooltipActionBtn
|
||||||
{...buttonStyle}
|
{...buttonStyle}
|
||||||
isDisabled={appMode === AppMode.Run}
|
|
||||||
icon={<IoSettingsOutline />}
|
icon={<IoSettingsOutline />}
|
||||||
className={isOldSettingsOpen ? style.open : ''}
|
className={isOldSettingsOpen ? style.open : ''}
|
||||||
clickHandler={onSettingsOpen}
|
clickHandler={onSettingsOpen}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.headerButtons {
|
|
||||||
text-align: right;
|
|
||||||
padding: 1.5rem 1rem 0 1rem;
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ function RuntimeOverview() {
|
|||||||
|
|
||||||
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
|
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
|
||||||
const ofTotal = numEvents || enDash;
|
const ofTotal = numEvents || enDash;
|
||||||
const progressText = numEvents ? `${current} of ${ofTotal}` : '';
|
const progressText = numEvents ? `${current} of ${ofTotal}` : '-';
|
||||||
|
|
||||||
const isAhead = offset <= 0;
|
const isAhead = offset <= 0;
|
||||||
let offsetText = millisToString(Math.abs(offset), { fallback: enDash });
|
let offsetText = millisToString(Math.abs(offset), { fallback: enDash });
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
.rundownExport {
|
.rundownExport {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
&.extracted {
|
||||||
|
.list {
|
||||||
|
flex: 1;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.side {
|
||||||
|
flex: 1;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rundown {
|
.rundown {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
|
|||||||
|
|
||||||
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
||||||
import { handleLinks } from '../../common/utils/linkUtils';
|
import { handleLinks } from '../../common/utils/linkUtils';
|
||||||
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
|
|
||||||
import EventEditor from './event-editor/EventEditor';
|
import EventEditor from './event-editor/EventEditor';
|
||||||
import RundownWrapper from './RundownWrapper';
|
import RundownWrapper from './RundownWrapper';
|
||||||
@@ -12,8 +13,10 @@ import style from './RundownExport.module.scss';
|
|||||||
const RundownExport = () => {
|
const RundownExport = () => {
|
||||||
const isExtracted = window.location.pathname.includes('/rundown');
|
const isExtracted = window.location.pathname.includes('/rundown');
|
||||||
|
|
||||||
|
const classes = cx([style.rundownExport, isExtracted && style.extracted]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.rundownExport} data-testid='panel-rundown'>
|
<div className={classes} data-testid='panel-rundown'>
|
||||||
<div className={style.rundown}>
|
<div className={style.rundown}>
|
||||||
<div className={style.list}>
|
<div className={style.list}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Empty from '../../common/components/state/Empty';
|
import Empty from '../../common/components/state/Empty';
|
||||||
import useRundown from '../../common/hooks-query/useRundown';
|
import useRundown from '../../common/hooks-query/useRundown';
|
||||||
|
|
||||||
|
import RundownHeader from './rundown-header/RundownHeader';
|
||||||
import Rundown from './Rundown';
|
import Rundown from './Rundown';
|
||||||
|
|
||||||
import styles from './Rundown.module.scss';
|
import styles from './Rundown.module.scss';
|
||||||
@@ -10,6 +11,7 @@ export default function RundownWrapper() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.rundownWrapper}>
|
<div className={styles.rundownWrapper}>
|
||||||
|
<RundownHeader />
|
||||||
{status === 'success' && data ? <Rundown data={data} /> : <Empty text='Connecting to server' />}
|
{status === 'success' && data ? <Rundown data={data} /> : <Empty text='Connecting to server' />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ $skip-opacity: 0.1;
|
|||||||
'binder pb-actions estatus estatus'
|
'binder pb-actions estatus estatus'
|
||||||
'binder ... ... ...';
|
'binder ... ... ...';
|
||||||
|
|
||||||
grid-template-columns: $block-binder-width auto 1fr auto;
|
grid-template-columns: $block-binder-width 3rem 1fr auto;
|
||||||
grid-template-rows: 0.25rem 2.25rem 2.25rem 2.25rem 0.25rem;
|
grid-template-rows: 0.25rem 2.25rem 2.25rem 2.25rem 0.25rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-right: $block-clearance;
|
padding-right: $block-clearance;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
|||||||
import { IoTime } from '@react-icons/all-files/io5/IoTime';
|
import { IoTime } from '@react-icons/all-files/io5/IoTime';
|
||||||
import { EndAction, MaybeString, Playback, TimerType, TimeStrategy } from 'ontime-types';
|
import { EndAction, MaybeString, Playback, TimerType, TimeStrategy } from 'ontime-types';
|
||||||
|
|
||||||
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||||
import EditableBlockTitle from '../common/EditableBlockTitle';
|
import EditableBlockTitle from '../common/EditableBlockTitle';
|
||||||
import { EventItemActions } from '../RundownEntry';
|
import { EventItemActions } from '../RundownEntry';
|
||||||
@@ -147,14 +148,16 @@ export default memo(EventBlockInner);
|
|||||||
|
|
||||||
function EndActionIcon(props: { action: EndAction; className: string }) {
|
function EndActionIcon(props: { action: EndAction; className: string }) {
|
||||||
const { action, className } = props;
|
const { action, className } = props;
|
||||||
|
const maybeActiveClasses = cx([action !== EndAction.None && style.active, className]);
|
||||||
|
|
||||||
if (action === EndAction.LoadNext) {
|
if (action === EndAction.LoadNext) {
|
||||||
return <IoPlaySkipForward className={className} />;
|
return <IoPlaySkipForward className={maybeActiveClasses} />;
|
||||||
}
|
}
|
||||||
if (action === EndAction.PlayNext) {
|
if (action === EndAction.PlayNext) {
|
||||||
return <IoPlayForward className={className} />;
|
return <IoPlayForward className={maybeActiveClasses} />;
|
||||||
}
|
}
|
||||||
if (action === EndAction.Stop) {
|
if (action === EndAction.Stop) {
|
||||||
return <IoStop className={className} />;
|
return <IoStop className={maybeActiveClasses} />;
|
||||||
}
|
}
|
||||||
return <IoPlay className={className} />;
|
return <IoPlay className={className} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
/** binder + gap + pb-actions */
|
||||||
|
$gap-left: calc(2rem + 0.25rem + 3rem);
|
||||||
|
|
||||||
.indicators {
|
.indicators {
|
||||||
font-size: calc(1rem - 5px);
|
font-size: calc(1rem - 5px);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -1em;
|
top: -1em;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
margin: 0 20%;
|
margin-left: $gap-left;
|
||||||
width: 60%;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
@@ -14,6 +16,7 @@
|
|||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: $bg-colour;
|
background-color: $bg-colour;
|
||||||
|
color: $ui-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.delay {
|
.delay {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.header {
|
||||||
|
padding-left: 2rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Button, ButtonGroup, MenuButton } from '@chakra-ui/react';
|
||||||
|
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
||||||
|
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
|
||||||
|
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||||
|
import { IoSnowOutline } from '@react-icons/all-files/io5/IoSnowOutline';
|
||||||
|
|
||||||
|
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
|
||||||
|
import { AppMode, useAppMode } from '../../../common/stores/appModeStore';
|
||||||
|
|
||||||
|
import RundownMenu from './RundownMenu';
|
||||||
|
|
||||||
|
import style from './RundownHeader.module.scss';
|
||||||
|
|
||||||
|
export default function RundownHeader() {
|
||||||
|
const appMode = useAppMode((state) => state.mode);
|
||||||
|
const setAppMode = useAppMode((state) => state.setMode);
|
||||||
|
const setRunMode = () => setAppMode(AppMode.Run);
|
||||||
|
const setEditMode = () => setAppMode(AppMode.Edit);
|
||||||
|
const setFreezeMode = () => setAppMode(AppMode.Freeze);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style.header}>
|
||||||
|
<ButtonGroup isAttached>
|
||||||
|
<TooltipActionBtn
|
||||||
|
variant={appMode === AppMode.Freeze ? 'ontime-filled' : 'ontime-ghosted'}
|
||||||
|
size='sm'
|
||||||
|
icon={<IoSnowOutline />}
|
||||||
|
clickHandler={setFreezeMode}
|
||||||
|
tooltip='Freeze rundown'
|
||||||
|
aria-label='Freeze rundown'
|
||||||
|
/>
|
||||||
|
<TooltipActionBtn
|
||||||
|
variant={appMode === AppMode.Run ? 'ontime-filled' : 'ontime-ghosted'}
|
||||||
|
size='sm'
|
||||||
|
icon={<IoPlay />}
|
||||||
|
clickHandler={setRunMode}
|
||||||
|
tooltip='Run mode'
|
||||||
|
aria-label='Run mode'
|
||||||
|
/>
|
||||||
|
<TooltipActionBtn
|
||||||
|
variant={appMode === AppMode.Edit ? 'ontime-filled' : 'ontime-ghosted'}
|
||||||
|
size='sm'
|
||||||
|
icon={<IoOptions />}
|
||||||
|
clickHandler={setEditMode}
|
||||||
|
tooltip='Edit mode'
|
||||||
|
aria-label='Edit mode'
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
<RundownMenu>
|
||||||
|
<MenuButton size='sm' as={Button} rightIcon={<IoAdd />} aria-label='Rundown menu' variant='ontime-ghosted'>
|
||||||
|
Rundown
|
||||||
|
</MenuButton>
|
||||||
|
</RundownMenu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+2
-2
@@ -6,8 +6,8 @@ import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
|
|||||||
import { IoTrashOutline } from '@react-icons/all-files/io5/IoTrashOutline';
|
import { IoTrashOutline } from '@react-icons/all-files/io5/IoTrashOutline';
|
||||||
import { SupportedEvent } from 'ontime-types';
|
import { SupportedEvent } from 'ontime-types';
|
||||||
|
|
||||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||||
import { useEventSelection } from '../rundown/useEventSelection';
|
import { useEventSelection } from '../useEventSelection';
|
||||||
|
|
||||||
const RundownMenu = ({ children }: { children: ReactNode }) => {
|
const RundownMenu = ({ children }: { children: ReactNode }) => {
|
||||||
const { clearSelectedEvents } = useEventSelection();
|
const { clearSelectedEvents } = useEventSelection();
|
||||||
Reference in New Issue
Block a user