mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-10 01:43:43 +00:00
refactor: expose editor panels in small device
This commit is contained in:
committed by
Carlos Valente
parent
b991627cb7
commit
984dcb0181
@@ -18,7 +18,7 @@ import ViewLoader from './views/ViewLoader';
|
||||
import { ONTIME_VERSION } from './ONTIME_VERSION';
|
||||
import { sentryDsn, sentryRecommendedIgnore } from './sentry.config';
|
||||
|
||||
const Editor = React.lazy(() => import('./features/editors/ProtectedEditor'));
|
||||
const Editor = React.lazy(() => import('./views/editor/ProtectedEditor'));
|
||||
const Cuesheet = React.lazy(() => import('./views/cuesheet/ProtectedCuesheet'));
|
||||
const Operator = React.lazy(() => import('./features/operator/OperatorExport'));
|
||||
|
||||
|
||||
@@ -53,14 +53,46 @@
|
||||
}
|
||||
}
|
||||
|
||||
.destructive {
|
||||
background: $red-700;
|
||||
color: $ui-white;
|
||||
|
||||
&:hover:not(:disabled):not(:active) {
|
||||
background: $red-600;
|
||||
color: $ui-white;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background: $red-800;
|
||||
border-color: $red-900;
|
||||
}
|
||||
}
|
||||
|
||||
.subtle-destructive {
|
||||
background: $gray-1050;
|
||||
color: $red-400;
|
||||
|
||||
&:hover:not(:disabled):not(:active) {
|
||||
background: $gray-1000;
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background: $gray-1100;
|
||||
border-color: $gray-1250;
|
||||
}
|
||||
}
|
||||
|
||||
.medium {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.large {
|
||||
height: 2.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.xlarge {
|
||||
height: 3rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cx } from '../../utils/styleUtils';
|
||||
import style from './Button.module.scss';
|
||||
|
||||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'subtle' | 'primary';
|
||||
variant?: 'subtle' | 'primary' | 'destructive' | 'subtle-destructive';
|
||||
size?: 'medium' | 'large' | 'xlarge';
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,22 @@
|
||||
border: 1px solid $gray-1200;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: $zindex-backdrop;
|
||||
background-color: $backdrop-color;
|
||||
transition: opacity 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
|
||||
|
||||
&[data-starting-style],
|
||||
&[data-ending-style] {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
padding-block: 1rem;
|
||||
|
||||
display: flex;
|
||||
|
||||
@@ -10,12 +10,21 @@ interface DialogProps {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
showCloseButton?: boolean;
|
||||
showBackdrop?: boolean;
|
||||
bodyElements: ReactNode;
|
||||
footerElements: ReactNode;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function Dialog({ isOpen, title, showCloseButton, bodyElements, footerElements, onClose }: DialogProps) {
|
||||
export default function Dialog({
|
||||
isOpen,
|
||||
title,
|
||||
showCloseButton,
|
||||
showBackdrop,
|
||||
bodyElements,
|
||||
footerElements,
|
||||
onClose,
|
||||
}: DialogProps) {
|
||||
return (
|
||||
<BaseDialog.Root
|
||||
open={isOpen}
|
||||
@@ -24,6 +33,7 @@ export default function Dialog({ isOpen, title, showCloseButton, bodyElements, f
|
||||
}}
|
||||
>
|
||||
<BaseDialog.Portal>
|
||||
{showBackdrop && <BaseDialog.Backdrop className={style.backdrop} />}
|
||||
<BaseDialog.Popup className={style.dialog}>
|
||||
<div className={style.title}>
|
||||
{title}
|
||||
|
||||
+6
@@ -40,3 +40,9 @@
|
||||
margin-bottom: $element-inner-spacing;
|
||||
max-width: max-content; // prevent label from taking entire row
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 1px;
|
||||
height: 0.75em;
|
||||
background-color: $border-color-ondark;
|
||||
}
|
||||
+5
-1
@@ -2,7 +2,7 @@ import type { HTMLAttributes, LabelHTMLAttributes } from 'react';
|
||||
import { IconBaseProps } from 'react-icons';
|
||||
import { IoArrowUp } from 'react-icons/io5';
|
||||
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import { cx } from '../../utils/styleUtils';
|
||||
|
||||
import style from './EditorUtils.module.scss';
|
||||
|
||||
@@ -27,3 +27,7 @@ export function Label({ children, className, ...elementProps }: LabelHTMLAttribu
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function Separator({ className, ...elementProps }: HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cx([style.separator, className])} {...elementProps} />;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { create } from 'zustand';
|
||||
export enum AppMode {
|
||||
Run = 'run',
|
||||
Edit = 'edit',
|
||||
Freeze = 'freeze',
|
||||
}
|
||||
|
||||
const appModeKey = 'ontime-app-mode';
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { IoEye, IoEyeOffOutline } from 'react-icons/io5';
|
||||
|
||||
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
|
||||
import { setMessage, useExternalMessageInput as useSecondaryMessageInput, useTimerMessageInput } from '../../../common/hooks/useSocket';
|
||||
import {
|
||||
setMessage,
|
||||
useExternalMessageInput as useSecondaryMessageInput,
|
||||
useTimerMessageInput,
|
||||
} from '../../../common/hooks/useSocket';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import InputRow from './InputRow';
|
||||
|
||||
+2
-2
@@ -1,15 +1,15 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { Corner } from '../../../common/components/editor-utils/EditorUtils';
|
||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import MessageControl from './MessageControl';
|
||||
|
||||
import style from '../../editors/Editor.module.scss';
|
||||
import style from '../../../views/editor/Editor.module.scss';
|
||||
|
||||
export default memo(MessageControlExport);
|
||||
function MessageControlExport() {
|
||||
@@ -2,12 +2,12 @@ import { IoArrowDown, IoArrowUp, IoBan, IoFlag, IoTime } from 'react-icons/io5';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import { TimerPhase, TimerType } from 'ontime-types';
|
||||
|
||||
import { Corner } from '../../../common/components/editor-utils/EditorUtils';
|
||||
import { useMessagePreview } from '../../../common/hooks/useSocket';
|
||||
import useViewSettings from '../../../common/hooks-query/useViewSettings';
|
||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||
import { cx, timerPlaceholder } from '../../../common/utils/styleUtils';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import style from './MessageControl.module.scss';
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { Corner } from '../../../common/components/editor-utils/EditorUtils';
|
||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import PlaybackControl from './PlaybackControl';
|
||||
|
||||
import style from '../../editors/Editor.module.scss';
|
||||
import style from '../../../views/editor/Editor.module.scss';
|
||||
|
||||
export default memo(TimerControlExport);
|
||||
function TimerControlExport() {
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-inline: 1rem;
|
||||
|
||||
:is([data-target='small-device']) & {
|
||||
padding-inline: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IoAdd } from 'react-icons/io5';
|
||||
import { Button } from '@chakra-ui/react';
|
||||
import { SupportedEntry } from 'ontime-types';
|
||||
|
||||
import Button from '../../common/components/buttons/Button';
|
||||
import * as Editor from '../../common/components/editor-utils/EditorUtils';
|
||||
import Empty from '../../common/components/state/Empty';
|
||||
|
||||
import style from './Empty.module.scss';
|
||||
@@ -17,12 +18,15 @@ export default function RundownEmpty(props: RundownEmptyProps) {
|
||||
<div className={style.empty}>
|
||||
<Empty style={{ marginTop: '5vh', marginBottom: '3rem' }} />
|
||||
<div className={style.inline}>
|
||||
<Button onClick={() => handleAddNew(SupportedEntry.Event)} variant='ontime-filled' leftIcon={<IoAdd />}>
|
||||
<Button onClick={() => handleAddNew(SupportedEntry.Event)} variant='primary' size='large'>
|
||||
<IoAdd />
|
||||
Create Event
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => handleAddNew(SupportedEntry.Block)} variant='ontime-filled' leftIcon={<IoAdd />}>
|
||||
Create Block
|
||||
<Editor.Separator />
|
||||
|
||||
<Button onClick={() => handleAddNew(SupportedEntry.Block)} variant='primary' size='large'>
|
||||
<IoAdd /> Create Block
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../editors/EditorMixin' as editor;
|
||||
@use '../../views/editor/EditorMixin' as editor;
|
||||
|
||||
.rundownExport {
|
||||
height: 100%;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { ContextMenu } from '../../common/components/context-menu/ContextMenu';
|
||||
import { Corner } from '../../common/components/editor-utils/EditorUtils';
|
||||
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
||||
import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu';
|
||||
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
||||
@@ -8,7 +9,6 @@ import { useIsSmallDevice } from '../../common/hooks/useIsSmallDevice';
|
||||
import { useAppMode } from '../../common/stores/appModeStore';
|
||||
import { handleLinks } from '../../common/utils/linkUtils';
|
||||
import { cx } from '../../common/utils/styleUtils';
|
||||
import { Corner } from '../editors/editor-utils/EditorUtils';
|
||||
|
||||
import RundownEventEditor from './event-editor/RundownEventEditor';
|
||||
import FinderPlacement from './placements/FinderPlacement';
|
||||
@@ -26,13 +26,17 @@ function RundownExport() {
|
||||
if (isSmallDevice && isExtracted) {
|
||||
return (
|
||||
<ProtectRoute permission='editor'>
|
||||
<div className={cx([style.rundownExport, style.extracted])} data-testid='panel-rundown'>
|
||||
<div
|
||||
className={cx([style.rundownExport, style.extracted])}
|
||||
data-target='small-device'
|
||||
data-testid='panel-rundown'
|
||||
>
|
||||
<FinderPlacement />
|
||||
<ViewNavigationMenu supressSettings />
|
||||
<div className={style.content}>
|
||||
<ErrorBoundary>
|
||||
<ContextMenu>
|
||||
<RundownWrapper />
|
||||
<RundownWrapper isSmallDevice />
|
||||
</ContextMenu>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,21 @@ import Empty from '../../common/components/state/Empty';
|
||||
import useRundown from '../../common/hooks-query/useRundown';
|
||||
|
||||
import RundownHeader from './rundown-header/RundownHeader';
|
||||
import RundownHeaderMobile from './rundown-header/RundownHeaderMobile';
|
||||
import Rundown from './Rundown';
|
||||
|
||||
import styles from './Rundown.module.scss';
|
||||
|
||||
export default function RundownWrapper() {
|
||||
interface RundownWrapperProps {
|
||||
isSmallDevice?: boolean;
|
||||
}
|
||||
|
||||
export default function RundownWrapper({ isSmallDevice }: RundownWrapperProps) {
|
||||
const { data, status } = useRundown();
|
||||
|
||||
return (
|
||||
<div className={styles.rundownWrapper}>
|
||||
<RundownHeader />
|
||||
{isSmallDevice ? <RundownHeaderMobile /> : <RundownHeader />}
|
||||
{status === 'success' && data ? <Rundown data={data} /> : <Empty text='Connecting to server' />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback } from 'react';
|
||||
import { OntimeEvent } from 'ontime-types';
|
||||
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import AppLink from '../../../common/components/link/app-link/AppLink';
|
||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||
import useCustomFields from '../../../common/hooks-query/useCustomFields';
|
||||
import * as Editor from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import EventCustom from './composite/EventEditorCustom';
|
||||
import EventEditorTimes from './composite/EventEditorTimes';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { memo, PropsWithChildren } from 'react';
|
||||
import { Kbd } from '@chakra-ui/react';
|
||||
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import { deviceAlt, deviceMod } from '../../../common/utils/deviceUtils';
|
||||
import * as Editor from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import style from './EventEditorEmpty.module.scss';
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import { Select, Switch, Tooltip } from '@chakra-ui/react';
|
||||
import { EndAction, TimerType, TimeStrategy } from 'ontime-types';
|
||||
import { millisToString, parseUserTime } from 'ontime-utils';
|
||||
|
||||
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
|
||||
import { useEntryActions } from '../../../../common/hooks/useEntryAction';
|
||||
import { millisToDelayString } from '../../../../common/utils/dateConfig';
|
||||
import * as Editor from '../../../editors/editor-utils/EditorUtils';
|
||||
import TimeInputFlow from '../../time-input-flow/TimeInputFlow';
|
||||
|
||||
import style from '../EventEditor.module.scss';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { memo } from 'react';
|
||||
import { Input } from '@chakra-ui/react';
|
||||
import { sanitiseCue } from 'ontime-utils';
|
||||
|
||||
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||
import SwatchSelect from '../../../../common/components/input/colour-input/SwatchSelect';
|
||||
import * as Editor from '../../../editors/editor-utils/EditorUtils';
|
||||
import { type EditorUpdateFields } from '../EventEditor';
|
||||
|
||||
import EventTextArea from './EventTextArea';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type CSSProperties, useCallback, useRef } from 'react';
|
||||
|
||||
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||
import { AutoTextArea } from '../../../../common/components/input/auto-text-area/AutoTextArea';
|
||||
import useReactiveTextInput from '../../../../common/components/input/text-input/useReactiveTextInput';
|
||||
import * as Editor from '../../../editors/editor-utils/EditorUtils';
|
||||
import { EditorUpdateFields } from '../EventEditor';
|
||||
|
||||
interface CountedTextAreaProps {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { Input, InputProps } from '@chakra-ui/react';
|
||||
|
||||
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||
import useReactiveTextInput from '../../../../common/components/input/text-input/useReactiveTextInput';
|
||||
import * as Editor from '../../../editors/editor-utils/EditorUtils';
|
||||
import { EditorUpdateFields } from '../EventEditor';
|
||||
|
||||
interface EventTextInputProps extends InputProps {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { memo } from 'react';
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
|
||||
import Finder from '../../editors/finder/Finder';
|
||||
import Finder from '../../../views/editor/finder/Finder';
|
||||
|
||||
export default memo(FinderPlacement);
|
||||
|
||||
|
||||
@@ -1,7 +1,59 @@
|
||||
.header {
|
||||
padding-inline: 1rem 2rem;
|
||||
|
||||
padding-inline: calc(2.5rem - 6px) calc(1rem + 2px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1px;
|
||||
|
||||
:is([data-target='small-device']) & {
|
||||
padding-inline: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 2rem;
|
||||
padding-inline: 1em;
|
||||
border: 1px solid transparent;
|
||||
border-radius: $component-border-radius-md;
|
||||
color: $gray-400;
|
||||
font-size: calc(1rem - 2px);
|
||||
font-weight: 600;
|
||||
|
||||
&:focus-visible {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not(:active) {
|
||||
background: $gray-1000;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $gray-1100;
|
||||
}
|
||||
|
||||
&[data-pressed] {
|
||||
background: $blue-700;
|
||||
color: $ui-white;
|
||||
|
||||
&:hover:not(:disabled):not(:active) {
|
||||
background: $blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.apart {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin-inline: 1rem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Button, ButtonGroup } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { Toggle } from '@base-ui-components/react/toggle';
|
||||
import { ToggleGroup } from '@base-ui-components/react/toggle-group';
|
||||
import { Toolbar } from '@base-ui-components/react/toolbar';
|
||||
import { OffsetMode } from 'ontime-types';
|
||||
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import { setOffsetMode, useOffsetMode } from '../../../common/hooks/useSocket';
|
||||
import { AppMode, useAppMode } from '../../../common/stores/appModeStore';
|
||||
|
||||
@@ -8,41 +12,50 @@ import RundownMenu from './RundownMenu';
|
||||
|
||||
import style from './RundownHeader.module.scss';
|
||||
|
||||
export default function RundownHeader() {
|
||||
export default memo(RundownHeader);
|
||||
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 { offsetMode } = useOffsetMode();
|
||||
|
||||
const toggleAppMode = (mode: AppMode[]) => {
|
||||
// we need to stop user from deselecting a mode
|
||||
const newValue = mode.at(0);
|
||||
if (!newValue) return;
|
||||
setAppMode(newValue);
|
||||
};
|
||||
|
||||
const toggleOffsetMode = (mode: OffsetMode[]) => {
|
||||
// we need to stop user from deselecting a mode
|
||||
const newValue = mode.at(0);
|
||||
if (!newValue) return;
|
||||
setOffsetMode(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.header}>
|
||||
<ButtonGroup isAttached>
|
||||
<Button size='sm' variant={appMode === AppMode.Run ? 'ontime-filled' : 'ontime-subtle'} onClick={setRunMode}>
|
||||
<Toolbar.Root className={style.header}>
|
||||
<ToggleGroup value={[appMode]} onValueChange={toggleAppMode} className={style.group}>
|
||||
<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.button}>
|
||||
Run
|
||||
</Button>
|
||||
<Button size='sm' variant={appMode === AppMode.Edit ? 'ontime-filled' : 'ontime-subtle'} onClick={setEditMode}>
|
||||
</Toolbar.Button>
|
||||
<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.button}>
|
||||
Edit
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached>
|
||||
<Button
|
||||
size='sm'
|
||||
variant={offsetMode === OffsetMode.Absolute ? 'ontime-filled' : 'ontime-subtle'}
|
||||
onClick={() => setOffsetMode(OffsetMode.Absolute)}
|
||||
>
|
||||
</Toolbar.Button>
|
||||
</ToggleGroup>
|
||||
|
||||
<Editor.Separator className={style.separator} />
|
||||
|
||||
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
|
||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.button}>
|
||||
Absolute
|
||||
</Button>
|
||||
<Button
|
||||
size='sm'
|
||||
variant={offsetMode === OffsetMode.Relative ? 'ontime-filled' : 'ontime-subtle'}
|
||||
onClick={() => setOffsetMode(OffsetMode.Relative)}
|
||||
>
|
||||
</Toolbar.Button>
|
||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.button}>
|
||||
Relative
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Toolbar.Button>
|
||||
</ToggleGroup>
|
||||
|
||||
<RundownMenu />
|
||||
</div>
|
||||
</Toolbar.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { memo } from 'react';
|
||||
import { Toggle } from '@base-ui-components/react/toggle';
|
||||
import { ToggleGroup } from '@base-ui-components/react/toggle-group';
|
||||
import { Toolbar } from '@base-ui-components/react/toolbar';
|
||||
import { OffsetMode } from 'ontime-types';
|
||||
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import { setOffsetMode, useOffsetMode } from '../../../common/hooks/useSocket';
|
||||
import { AppMode, useAppMode } from '../../../common/stores/appModeStore';
|
||||
|
||||
import style from './RundownHeader.module.scss';
|
||||
|
||||
export default memo(RundownHeader);
|
||||
function RundownHeader() {
|
||||
const appMode = useAppMode((state) => state.mode);
|
||||
const setAppMode = useAppMode((state) => state.setMode);
|
||||
|
||||
const { offsetMode } = useOffsetMode();
|
||||
|
||||
const toggleAppMode = (mode: AppMode[]) => {
|
||||
// we need to stop user from deselecting a mode
|
||||
const newValue = mode.at(0);
|
||||
if (!newValue) return;
|
||||
setAppMode(newValue);
|
||||
};
|
||||
|
||||
const toggleOffsetMode = (mode: OffsetMode[]) => {
|
||||
// we need to stop user from deselecting a mode
|
||||
const newValue = mode.at(0);
|
||||
if (!newValue) return;
|
||||
setOffsetMode(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Toolbar.Root className={style.header}>
|
||||
<ToggleGroup value={[appMode]} onValueChange={toggleAppMode} className={style.group}>
|
||||
<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.button}>
|
||||
Run
|
||||
</Toolbar.Button>
|
||||
<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.button}>
|
||||
Edit
|
||||
</Toolbar.Button>
|
||||
</ToggleGroup>
|
||||
|
||||
<Editor.Separator className={style.separator} />
|
||||
|
||||
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
|
||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.button}>
|
||||
Absolute
|
||||
</Toolbar.Button>
|
||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.button}>
|
||||
Relative
|
||||
</Toolbar.Button>
|
||||
</ToggleGroup>
|
||||
</Toolbar.Root>
|
||||
);
|
||||
}
|
||||
@@ -1,66 +1,62 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { IoTrash } from 'react-icons/io5';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
|
||||
import Button from '../../../common/components/buttons/Button';
|
||||
import Dialog from '../../../common/components/dialog/Dialog';
|
||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||
import { useAppMode } from '../../../common/stores/appModeStore';
|
||||
import { AppMode, useAppMode } from '../../../common/stores/appModeStore';
|
||||
import { useEventSelection } from '../useEventSelection';
|
||||
|
||||
export default function RundownMenu() {
|
||||
import style from './RundownHeader.module.scss';
|
||||
|
||||
export default memo(RundownMenu);
|
||||
function RundownMenu() {
|
||||
const [isOpen, handlers] = useDisclosure();
|
||||
|
||||
const clearSelectedEvents = useEventSelection((state) => state.clearSelectedEvents);
|
||||
const appMode = useAppMode((state) => state.mode);
|
||||
const { deleteAllEntries } = useEntryActions();
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const cancelRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
const deleteAll = useCallback(() => {
|
||||
deleteAllEntries();
|
||||
clearSelectedEvents();
|
||||
onClose();
|
||||
}, [clearSelectedEvents, deleteAllEntries, onClose]);
|
||||
handlers.close();
|
||||
}, [clearSelectedEvents, deleteAllEntries, handlers]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size='sm'
|
||||
variant='ontime-outlined'
|
||||
leftIcon={<IoTrash />}
|
||||
onClick={onOpen}
|
||||
color='#FA5656'
|
||||
isDisabled={appMode === 'run'}
|
||||
variant='subtle-destructive'
|
||||
className={style.apart}
|
||||
onClick={() => handlers.open()}
|
||||
disabled={appMode === AppMode.Run}
|
||||
>
|
||||
Clear rundown
|
||||
<IoTrash />
|
||||
Clear All
|
||||
</Button>
|
||||
<AlertDialog variant='ontime' isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize='lg' fontWeight='bold'>
|
||||
Clear rundown
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogBody>
|
||||
You will lose all data in your rundown. <br /> Are you sure?
|
||||
</AlertDialogBody>
|
||||
<AlertDialogFooter>
|
||||
<Button ref={cancelRef} onClick={onClose} variant='ontime-ghosted-white'>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button colorScheme='red' onClick={deleteAll} ml={4}>
|
||||
Delete all
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
<Dialog
|
||||
isOpen={isOpen}
|
||||
onClose={handlers.close}
|
||||
title='Clear Rundown'
|
||||
showBackdrop
|
||||
showCloseButton
|
||||
bodyElements={
|
||||
<>
|
||||
You will lose all data in your rundown. <br /> Are you sure?
|
||||
</>
|
||||
}
|
||||
footerElements={
|
||||
<>
|
||||
<Button size='large' onClick={handlers.close}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='destructive' size='large' onClick={deleteAll}>
|
||||
Delete all
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import { InputRightElement, Tooltip } from '@chakra-ui/react';
|
||||
import { TimeField, TimeStrategy } from 'ontime-types';
|
||||
import { dayInMs } from 'ontime-utils';
|
||||
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import TimeInputWithButton from '../../../common/components/input/time-input/TimeInputWithButton';
|
||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import { tooltipDelayFast, tooltipDelayMid } from '../../../ontimeConfig';
|
||||
import * as Editor from '../../editors/editor-utils/EditorUtils';
|
||||
|
||||
import style from './TimeInputFlow.module.scss';
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { Button, Checkbox } from '@chakra-ui/react';
|
||||
import { Column } from '@tanstack/react-table';
|
||||
import { OntimeEntry } from 'ontime-types';
|
||||
|
||||
import * as Editor from '../../../../features/editors/editor-utils/EditorUtils';
|
||||
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||
|
||||
import style from './CuesheetTableSettings.module.scss';
|
||||
|
||||
|
||||
+6
-6
@@ -6,17 +6,17 @@ import { useHotkeys } from '@mantine/hooks';
|
||||
import NavigationMenu from '../../common/components/navigation-menu/NavigationMenu';
|
||||
import { useElectronListener } from '../../common/hooks/useElectronEvent';
|
||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||
import AppSettings from '../app-settings/AppSettings';
|
||||
import useAppSettingsNavigation from '../app-settings/useAppSettingsNavigation';
|
||||
import { EditorOverview } from '../overview/Overview';
|
||||
import AppSettings from '../../features/app-settings/AppSettings';
|
||||
import useAppSettingsNavigation from '../../features/app-settings/useAppSettingsNavigation';
|
||||
import { EditorOverview } from '../../features/overview/Overview';
|
||||
|
||||
import WelcomePlacement from './welcome/WelcomePlacement';
|
||||
|
||||
import styles from './Editor.module.scss';
|
||||
|
||||
const Rundown = lazy(() => import('../rundown/RundownExport'));
|
||||
const TimerControl = lazy(() => import('../control/playback/TimerControlExport'));
|
||||
const MessageControl = lazy(() => import('../control/message/MessageControlExport'));
|
||||
const Rundown = lazy(() => import('../../features/rundown/RundownExport'));
|
||||
const TimerControl = lazy(() => import('../../features/control/playback/TimerControlExport'));
|
||||
const MessageControl = lazy(() => import('../../features/control/message/MessageControlExport'));
|
||||
|
||||
export default function Editor() {
|
||||
const { isOpen: isSettingsOpen, setLocation, close } = useAppSettingsNavigation();
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { Input, Modal, ModalBody, ModalContent, ModalFooter, ModalOverlay } from
|
||||
import { useDebouncedCallback } from '@mantine/hooks';
|
||||
import { SupportedEntry } from 'ontime-types';
|
||||
|
||||
import { useEventSelection } from '../../rundown/useEventSelection';
|
||||
import { useEventSelection } from '../../../features/rundown/useEventSelection';
|
||||
|
||||
import useFinder from './useFinder';
|
||||
|
||||
+1
-1
@@ -4,9 +4,9 @@ import { Button, Checkbox, Modal, ModalBody, ModalCloseButton, ModalContent, Mod
|
||||
import { loadDemo, loadProject } from '../../../common/api/db';
|
||||
import { postShowWelcomeDialog } from '../../../common/api/settings';
|
||||
import { invalidateAllCaches } from '../../../common/api/utils';
|
||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||
import ExternalLink from '../../../common/components/link/external-link/ExternalLink';
|
||||
import { appVersion, discordUrl, documentationUrl, websiteUrl } from '../../../externals';
|
||||
import * as Editor from '../editor-utils/EditorUtils';
|
||||
|
||||
import ImportProjectButton from './composite/ImportProjectButton';
|
||||
import WelcomeProjectList from './composite/WelcomeProjectList';
|
||||
@@ -14,7 +14,7 @@ test('project file upload', async ({ page }) => {
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'toggle settings' }).click();
|
||||
|
||||
@@ -5,7 +5,7 @@ test('delay blocks add time to events', async ({ page }) => {
|
||||
|
||||
// delete all events and add a new one
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
await page.getByRole('button', { name: 'Create event' }).click();
|
||||
|
||||
@@ -53,7 +53,7 @@ test('delays are show correctly', async ({ page }) => {
|
||||
|
||||
// add a test event
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ test('CRUD operations on the rundown', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
// create event from the rundown empty button
|
||||
|
||||
@@ -4,7 +4,7 @@ test('smoke test operator', async ({ page }) => {
|
||||
// make some boilerplate
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ test('Copy-paste', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
// create event
|
||||
@@ -43,7 +43,7 @@ test('Move', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
// create events
|
||||
@@ -67,7 +67,7 @@ test('Add block', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
// create events
|
||||
@@ -95,7 +95,7 @@ test('Add delay', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
//create events
|
||||
@@ -116,7 +116,7 @@ test('Add event', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
//create events
|
||||
@@ -138,7 +138,7 @@ test('Delete event', async ({ page }) => {
|
||||
|
||||
// clear rundown
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
//create event
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
test('show warning when event crosses midnight', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
@@ -23,7 +23,7 @@ test('show warning when event crosses midnight', async ({ page }) => {
|
||||
test('show warning when event starts next day midnight', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
test('time until absolute', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
@@ -40,7 +40,7 @@ test('time until absolute', async ({ page }) => {
|
||||
test('time until relative', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Create Event' }).click();
|
||||
|
||||
@@ -4,7 +4,7 @@ test('Rearrange while playing', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
|
||||
// clear rundown
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
// create events
|
||||
|
||||
@@ -5,7 +5,7 @@ const fileToUpload = 'e2e/tests/fixtures/test-sheet.xlsx';
|
||||
test('sheet file upload', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/editor');
|
||||
await page.getByRole('button', { name: 'Edit' }).click();
|
||||
await page.getByRole('button', { name: 'Clear rundown' }).click();
|
||||
await page.getByRole('button', { name: 'Clear all' }).click();
|
||||
await page.getByRole('button', { name: 'Delete all' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Toggle settings' }).click();
|
||||
|
||||
Reference in New Issue
Block a user