diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index ea78c44b3..d64f8bd55 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -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')); diff --git a/apps/client/src/common/components/buttons/Button.module.scss b/apps/client/src/common/components/buttons/Button.module.scss index 06e956120..7e7e2f127 100644 --- a/apps/client/src/common/components/buttons/Button.module.scss +++ b/apps/client/src/common/components/buttons/Button.module.scss @@ -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; } diff --git a/apps/client/src/common/components/buttons/Button.tsx b/apps/client/src/common/components/buttons/Button.tsx index 5858ea9a2..b33c9fa9c 100644 --- a/apps/client/src/common/components/buttons/Button.tsx +++ b/apps/client/src/common/components/buttons/Button.tsx @@ -5,7 +5,7 @@ import { cx } from '../../utils/styleUtils'; import style from './Button.module.scss'; interface ButtonProps extends ButtonHTMLAttributes { - variant?: 'subtle' | 'primary'; + variant?: 'subtle' | 'primary' | 'destructive' | 'subtle-destructive'; size?: 'medium' | 'large' | 'xlarge'; } diff --git a/apps/client/src/common/components/dialog/Dialog.module.scss b/apps/client/src/common/components/dialog/Dialog.module.scss index c5599bb93..0957539aa 100644 --- a/apps/client/src/common/components/dialog/Dialog.module.scss +++ b/apps/client/src/common/components/dialog/Dialog.module.scss @@ -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; diff --git a/apps/client/src/common/components/dialog/Dialog.tsx b/apps/client/src/common/components/dialog/Dialog.tsx index ed630372d..de0c8743a 100644 --- a/apps/client/src/common/components/dialog/Dialog.tsx +++ b/apps/client/src/common/components/dialog/Dialog.tsx @@ -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 ( + {showBackdrop && }
{title} diff --git a/apps/client/src/features/editors/editor-utils/EditorUtils.module.scss b/apps/client/src/common/components/editor-utils/EditorUtils.module.scss similarity index 90% rename from apps/client/src/features/editors/editor-utils/EditorUtils.module.scss rename to apps/client/src/common/components/editor-utils/EditorUtils.module.scss index cf4c73a00..8438b2065 100644 --- a/apps/client/src/features/editors/editor-utils/EditorUtils.module.scss +++ b/apps/client/src/common/components/editor-utils/EditorUtils.module.scss @@ -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; +} diff --git a/apps/client/src/features/editors/editor-utils/EditorUtils.tsx b/apps/client/src/common/components/editor-utils/EditorUtils.tsx similarity index 79% rename from apps/client/src/features/editors/editor-utils/EditorUtils.tsx rename to apps/client/src/common/components/editor-utils/EditorUtils.tsx index dd7e59533..6370ac32a 100644 --- a/apps/client/src/features/editors/editor-utils/EditorUtils.tsx +++ b/apps/client/src/common/components/editor-utils/EditorUtils.tsx @@ -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 ); } + +export function Separator({ className, ...elementProps }: HTMLAttributes) { + return
; +} diff --git a/apps/client/src/common/stores/appModeStore.ts b/apps/client/src/common/stores/appModeStore.ts index bb893f1d6..b40d18c78 100644 --- a/apps/client/src/common/stores/appModeStore.ts +++ b/apps/client/src/common/stores/appModeStore.ts @@ -3,7 +3,6 @@ import { create } from 'zustand'; export enum AppMode { Run = 'run', Edit = 'edit', - Freeze = 'freeze', } const appModeKey = 'ontime-app-mode'; diff --git a/apps/client/src/features/control/message/MessageControl.tsx b/apps/client/src/features/control/message/MessageControl.tsx index ebaef268e..af57f05c2 100644 --- a/apps/client/src/features/control/message/MessageControl.tsx +++ b/apps/client/src/features/control/message/MessageControl.tsx @@ -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'; diff --git a/apps/client/src/features/control/message/MessageControlExport.jsx b/apps/client/src/features/control/message/MessageControlExport.tsx similarity index 89% rename from apps/client/src/features/control/message/MessageControlExport.jsx rename to apps/client/src/features/control/message/MessageControlExport.tsx index 6769cfa80..d33956843 100644 --- a/apps/client/src/features/control/message/MessageControlExport.jsx +++ b/apps/client/src/features/control/message/MessageControlExport.tsx @@ -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() { diff --git a/apps/client/src/features/control/message/TimerPreview.tsx b/apps/client/src/features/control/message/TimerPreview.tsx index 1ba9636b3..27e6a5cb1 100644 --- a/apps/client/src/features/control/message/TimerPreview.tsx +++ b/apps/client/src/features/control/message/TimerPreview.tsx @@ -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'; diff --git a/apps/client/src/features/control/playback/TimerControlExport.tsx b/apps/client/src/features/control/playback/TimerControlExport.tsx index 6b6f5861c..126608391 100644 --- a/apps/client/src/features/control/playback/TimerControlExport.tsx +++ b/apps/client/src/features/control/playback/TimerControlExport.tsx @@ -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() { diff --git a/apps/client/src/features/rundown/Empty.module.scss b/apps/client/src/features/rundown/Empty.module.scss index 50b47082b..57f6da842 100644 --- a/apps/client/src/features/rundown/Empty.module.scss +++ b/apps/client/src/features/rundown/Empty.module.scss @@ -7,5 +7,5 @@ display: flex; align-items: center; justify-content: center; - gap: 2rem; + gap: 1rem; } diff --git a/apps/client/src/features/rundown/Rundown.module.scss b/apps/client/src/features/rundown/Rundown.module.scss index 97da8a487..056fd7ddd 100644 --- a/apps/client/src/features/rundown/Rundown.module.scss +++ b/apps/client/src/features/rundown/Rundown.module.scss @@ -16,6 +16,11 @@ display: flex; flex-direction: column; padding-inline: 1rem; + + :is([data-target='small-device']) & { + padding-inline: 0; + overflow-x: auto; + } } .empty { diff --git a/apps/client/src/features/rundown/RundownEmpty.tsx b/apps/client/src/features/rundown/RundownEmpty.tsx index 8f5d14fe0..1a16ed180 100644 --- a/apps/client/src/features/rundown/RundownEmpty.tsx +++ b/apps/client/src/features/rundown/RundownEmpty.tsx @@ -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) {
- -
diff --git a/apps/client/src/features/rundown/RundownExport.module.scss b/apps/client/src/features/rundown/RundownExport.module.scss index 5a2f8b4dd..d22039d14 100644 --- a/apps/client/src/features/rundown/RundownExport.module.scss +++ b/apps/client/src/features/rundown/RundownExport.module.scss @@ -1,4 +1,4 @@ -@use '../editors/EditorMixin' as editor; +@use '../../views/editor/EditorMixin' as editor; .rundownExport { height: 100%; diff --git a/apps/client/src/features/rundown/RundownExport.tsx b/apps/client/src/features/rundown/RundownExport.tsx index 3d39610f4..aa50a229e 100644 --- a/apps/client/src/features/rundown/RundownExport.tsx +++ b/apps/client/src/features/rundown/RundownExport.tsx @@ -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 ( -
+
- +
diff --git a/apps/client/src/features/rundown/RundownWrapper.tsx b/apps/client/src/features/rundown/RundownWrapper.tsx index d79ffd77c..90c28157f 100644 --- a/apps/client/src/features/rundown/RundownWrapper.tsx +++ b/apps/client/src/features/rundown/RundownWrapper.tsx @@ -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 (
- + {isSmallDevice ? : } {status === 'success' && data ? : }
); diff --git a/apps/client/src/features/rundown/event-editor/EventEditor.tsx b/apps/client/src/features/rundown/event-editor/EventEditor.tsx index eb3b6a519..3b60de05c 100644 --- a/apps/client/src/features/rundown/event-editor/EventEditor.tsx +++ b/apps/client/src/features/rundown/event-editor/EventEditor.tsx @@ -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'; diff --git a/apps/client/src/features/rundown/event-editor/EventEditorEmpty.tsx b/apps/client/src/features/rundown/event-editor/EventEditorEmpty.tsx index 01c2c005f..7a90337d7 100644 --- a/apps/client/src/features/rundown/event-editor/EventEditorEmpty.tsx +++ b/apps/client/src/features/rundown/event-editor/EventEditorEmpty.tsx @@ -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'; diff --git a/apps/client/src/features/rundown/event-editor/composite/EventEditorTimes.tsx b/apps/client/src/features/rundown/event-editor/composite/EventEditorTimes.tsx index 182ef166a..42f7e8723 100644 --- a/apps/client/src/features/rundown/event-editor/composite/EventEditorTimes.tsx +++ b/apps/client/src/features/rundown/event-editor/composite/EventEditorTimes.tsx @@ -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'; diff --git a/apps/client/src/features/rundown/event-editor/composite/EventEditorTitles.tsx b/apps/client/src/features/rundown/event-editor/composite/EventEditorTitles.tsx index 2755244cc..cfeb48834 100644 --- a/apps/client/src/features/rundown/event-editor/composite/EventEditorTitles.tsx +++ b/apps/client/src/features/rundown/event-editor/composite/EventEditorTitles.tsx @@ -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'; diff --git a/apps/client/src/features/rundown/event-editor/composite/EventTextArea.tsx b/apps/client/src/features/rundown/event-editor/composite/EventTextArea.tsx index 7f566e148..a01d9a9a5 100644 --- a/apps/client/src/features/rundown/event-editor/composite/EventTextArea.tsx +++ b/apps/client/src/features/rundown/event-editor/composite/EventTextArea.tsx @@ -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 { diff --git a/apps/client/src/features/rundown/event-editor/composite/EventTextInput.tsx b/apps/client/src/features/rundown/event-editor/composite/EventTextInput.tsx index 82bd0db30..e8b5ef1d2 100644 --- a/apps/client/src/features/rundown/event-editor/composite/EventTextInput.tsx +++ b/apps/client/src/features/rundown/event-editor/composite/EventTextInput.tsx @@ -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 { diff --git a/apps/client/src/features/rundown/placements/FinderPlacement.tsx b/apps/client/src/features/rundown/placements/FinderPlacement.tsx index 7fd4da09a..22e397275 100644 --- a/apps/client/src/features/rundown/placements/FinderPlacement.tsx +++ b/apps/client/src/features/rundown/placements/FinderPlacement.tsx @@ -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); diff --git a/apps/client/src/features/rundown/rundown-header/RundownHeader.module.scss b/apps/client/src/features/rundown/rundown-header/RundownHeader.module.scss index a46635d28..16ef27194 100644 --- a/apps/client/src/features/rundown/rundown-header/RundownHeader.module.scss +++ b/apps/client/src/features/rundown/rundown-header/RundownHeader.module.scss @@ -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; } diff --git a/apps/client/src/features/rundown/rundown-header/RundownHeader.tsx b/apps/client/src/features/rundown/rundown-header/RundownHeader.tsx index deb5fdaaa..d7152fd5b 100644 --- a/apps/client/src/features/rundown/rundown-header/RundownHeader.tsx +++ b/apps/client/src/features/rundown/rundown-header/RundownHeader.tsx @@ -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 ( -
- - - - - - - - + + + -
+ ); } diff --git a/apps/client/src/features/rundown/rundown-header/RundownHeaderMobile.tsx b/apps/client/src/features/rundown/rundown-header/RundownHeaderMobile.tsx new file mode 100644 index 000000000..f3f1aec1d --- /dev/null +++ b/apps/client/src/features/rundown/rundown-header/RundownHeaderMobile.tsx @@ -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 ( + + + } value={AppMode.Run} className={style.button}> + Run + + } value={AppMode.Edit} className={style.button}> + Edit + + + + + + + } value={OffsetMode.Absolute} className={style.button}> + Absolute + + } value={OffsetMode.Relative} className={style.button}> + Relative + + + + ); +} diff --git a/apps/client/src/features/rundown/rundown-header/RundownMenu.tsx b/apps/client/src/features/rundown/rundown-header/RundownMenu.tsx index 2e8cdf094..152c8e495 100644 --- a/apps/client/src/features/rundown/rundown-header/RundownMenu.tsx +++ b/apps/client/src/features/rundown/rundown-header/RundownMenu.tsx @@ -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(null); - const deleteAll = useCallback(() => { deleteAllEntries(); clearSelectedEvents(); - onClose(); - }, [clearSelectedEvents, deleteAllEntries, onClose]); + handlers.close(); + }, [clearSelectedEvents, deleteAllEntries, handlers]); return ( <> - - - - - Clear rundown - - - You will lose all data in your rundown.
Are you sure? -
- - - - -
-
-
+ + You will lose all data in your rundown.
Are you sure? + + } + footerElements={ + <> + + + + } + /> ); } diff --git a/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx b/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx index 993af7741..c1fcf0606 100644 --- a/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx +++ b/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx @@ -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'; diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-settings/CuesheetTableSettings.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-settings/CuesheetTableSettings.tsx index 63d4a2d96..943a5b51c 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-settings/CuesheetTableSettings.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-settings/CuesheetTableSettings.tsx @@ -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'; diff --git a/apps/client/src/features/editors/Editor.module.scss b/apps/client/src/views/editor/Editor.module.scss similarity index 100% rename from apps/client/src/features/editors/Editor.module.scss rename to apps/client/src/views/editor/Editor.module.scss diff --git a/apps/client/src/features/editors/Editor.tsx b/apps/client/src/views/editor/Editor.tsx similarity index 82% rename from apps/client/src/features/editors/Editor.tsx rename to apps/client/src/views/editor/Editor.tsx index d0f828b28..b6cb48b3c 100644 --- a/apps/client/src/features/editors/Editor.tsx +++ b/apps/client/src/views/editor/Editor.tsx @@ -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(); diff --git a/apps/client/src/features/editors/EditorMixin.scss b/apps/client/src/views/editor/EditorMixin.scss similarity index 100% rename from apps/client/src/features/editors/EditorMixin.scss rename to apps/client/src/views/editor/EditorMixin.scss diff --git a/apps/client/src/features/editors/ProtectedEditor.tsx b/apps/client/src/views/editor/ProtectedEditor.tsx similarity index 100% rename from apps/client/src/features/editors/ProtectedEditor.tsx rename to apps/client/src/views/editor/ProtectedEditor.tsx diff --git a/apps/client/src/features/editors/finder/Finder.module.scss b/apps/client/src/views/editor/finder/Finder.module.scss similarity index 100% rename from apps/client/src/features/editors/finder/Finder.module.scss rename to apps/client/src/views/editor/finder/Finder.module.scss diff --git a/apps/client/src/features/editors/finder/Finder.tsx b/apps/client/src/views/editor/finder/Finder.tsx similarity index 97% rename from apps/client/src/features/editors/finder/Finder.tsx rename to apps/client/src/views/editor/finder/Finder.tsx index b24a412ed..a6f30f8d2 100644 --- a/apps/client/src/features/editors/finder/Finder.tsx +++ b/apps/client/src/views/editor/finder/Finder.tsx @@ -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'; diff --git a/apps/client/src/features/editors/finder/useFinder.tsx b/apps/client/src/views/editor/finder/useFinder.tsx similarity index 100% rename from apps/client/src/features/editors/finder/useFinder.tsx rename to apps/client/src/views/editor/finder/useFinder.tsx diff --git a/apps/client/src/features/editors/welcome/Welcome.module.scss b/apps/client/src/views/editor/welcome/Welcome.module.scss similarity index 100% rename from apps/client/src/features/editors/welcome/Welcome.module.scss rename to apps/client/src/views/editor/welcome/Welcome.module.scss diff --git a/apps/client/src/features/editors/welcome/Welcome.tsx b/apps/client/src/views/editor/welcome/Welcome.tsx similarity index 97% rename from apps/client/src/features/editors/welcome/Welcome.tsx rename to apps/client/src/views/editor/welcome/Welcome.tsx index 8692c72cb..5d588da04 100644 --- a/apps/client/src/features/editors/welcome/Welcome.tsx +++ b/apps/client/src/views/editor/welcome/Welcome.tsx @@ -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'; diff --git a/apps/client/src/features/editors/welcome/WelcomePlacement.tsx b/apps/client/src/views/editor/welcome/WelcomePlacement.tsx similarity index 100% rename from apps/client/src/features/editors/welcome/WelcomePlacement.tsx rename to apps/client/src/views/editor/welcome/WelcomePlacement.tsx diff --git a/apps/client/src/features/editors/welcome/composite/ImportProjectButton.tsx b/apps/client/src/views/editor/welcome/composite/ImportProjectButton.tsx similarity index 100% rename from apps/client/src/features/editors/welcome/composite/ImportProjectButton.tsx rename to apps/client/src/views/editor/welcome/composite/ImportProjectButton.tsx diff --git a/apps/client/src/features/editors/welcome/composite/WelcomeProjectList.tsx b/apps/client/src/views/editor/welcome/composite/WelcomeProjectList.tsx similarity index 100% rename from apps/client/src/features/editors/welcome/composite/WelcomeProjectList.tsx rename to apps/client/src/views/editor/welcome/composite/WelcomeProjectList.tsx diff --git a/e2e/tests/000-upload-showfile.spec.ts b/e2e/tests/000-upload-showfile.spec.ts index 78b1fc250..d4cc6fa14 100644 --- a/e2e/tests/000-upload-showfile.spec.ts +++ b/e2e/tests/000-upload-showfile.spec.ts @@ -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(); diff --git a/e2e/tests/features/203-delay-block.spec.ts b/e2e/tests/features/203-delay-block.spec.ts index 1a30f7fd7..75f476afe 100644 --- a/e2e/tests/features/203-delay-block.spec.ts +++ b/e2e/tests/features/203-delay-block.spec.ts @@ -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(); diff --git a/e2e/tests/features/204-editor-crud.spec.ts b/e2e/tests/features/204-editor-crud.spec.ts index 819fb1797..6f0b14a6a 100644 --- a/e2e/tests/features/204-editor-crud.spec.ts +++ b/e2e/tests/features/204-editor-crud.spec.ts @@ -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 diff --git a/e2e/tests/features/205-operator.spec.ts b/e2e/tests/features/205-operator.spec.ts index 50faee184..7da096a5e 100644 --- a/e2e/tests/features/205-operator.spec.ts +++ b/e2e/tests/features/205-operator.spec.ts @@ -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(); diff --git a/e2e/tests/features/209-rundown-shortcuts.spec.ts b/e2e/tests/features/209-rundown-shortcuts.spec.ts index f76ac25d6..ca94f8727 100644 --- a/e2e/tests/features/209-rundown-shortcuts.spec.ts +++ b/e2e/tests/features/209-rundown-shortcuts.spec.ts @@ -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 diff --git a/e2e/tests/features/211-report-data.spec.ts b/e2e/tests/features/211-report-data.spec.ts index d128e855a..b46180748 100644 --- a/e2e/tests/features/211-report-data.spec.ts +++ b/e2e/tests/features/211-report-data.spec.ts @@ -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(); diff --git a/e2e/tests/features/212-time-until.spec.ts b/e2e/tests/features/212-time-until.spec.ts index 146be2f16..1bd8c9065 100644 --- a/e2e/tests/features/212-time-until.spec.ts +++ b/e2e/tests/features/212-time-until.spec.ts @@ -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(); diff --git a/e2e/tests/features/213-arrange-while-playing.spec.ts b/e2e/tests/features/213-arrange-while-playing.spec.ts index 1c85fa791..44b449a5b 100644 --- a/e2e/tests/features/213-arrange-while-playing.spec.ts +++ b/e2e/tests/features/213-arrange-while-playing.spec.ts @@ -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 diff --git a/e2e/tests/features/301-spreadsheet-import.spec.ts b/e2e/tests/features/301-spreadsheet-import.spec.ts index 38e014c05..cc0749dab 100644 --- a/e2e/tests/features/301-spreadsheet-import.spec.ts +++ b/e2e/tests/features/301-spreadsheet-import.spec.ts @@ -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();