From b991627cb79ff53a1c01142d365d29e6bf45561c Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Sun, 22 Jun 2025 12:43:11 +0200 Subject: [PATCH] refactor: editor responsive navigation --- .../navigation-menu/ViewNavigationMenu.tsx | 10 ++- .../editor-navigation/EditorNavigation.tsx | 6 +- .../FloatingNavigation.tsx | 22 ++++--- apps/client/src/common/hooks/useIsMobile.ts | 9 +++ .../src/common/hooks/useIsSmallDevice.ts | 9 +++ .../src/common/hooks/useIsSmallScreen.ts | 8 +++ .../control/message/MessageControlExport.jsx | 29 +++++---- .../control/playback/TimerControlExport.tsx | 34 +++++----- .../src/features/rundown/RundownExport.tsx | 63 +++++++++++++------ apps/client/src/views/studio/StudioClock.tsx | 7 +-- 10 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 apps/client/src/common/hooks/useIsMobile.ts create mode 100644 apps/client/src/common/hooks/useIsSmallDevice.ts create mode 100644 apps/client/src/common/hooks/useIsSmallScreen.ts diff --git a/apps/client/src/common/components/navigation-menu/ViewNavigationMenu.tsx b/apps/client/src/common/components/navigation-menu/ViewNavigationMenu.tsx index 652fe03d2..f1efead2a 100644 --- a/apps/client/src/common/components/navigation-menu/ViewNavigationMenu.tsx +++ b/apps/client/src/common/components/navigation-menu/ViewNavigationMenu.tsx @@ -9,10 +9,11 @@ import useViewEditor from './useViewEditor'; interface ViewNavigationMenuProps { isLockable?: boolean; + supressSettings?: boolean; } export default memo(ViewNavigationMenu); -function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) { +function ViewNavigationMenu({ isLockable, supressSettings }: ViewNavigationMenuProps) { const { isOpen: isMenuOpen, onOpen: onMenuOpen, onClose: onMenuClose } = useDisclosure(); const { showEditFormDrawer, isViewLocked } = useViewEditor({ isLockable }); @@ -30,7 +31,7 @@ function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) { [ 'mod + ,', () => { - if (isViewLocked) return; + if (isViewLocked || supressSettings) return; showEditFormDrawer(); }, { preventDefault: true }, @@ -43,7 +44,10 @@ function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) { return ( <> - + showEditFormDrawer()} + /> ); diff --git a/apps/client/src/common/components/navigation-menu/editor-navigation/EditorNavigation.tsx b/apps/client/src/common/components/navigation-menu/editor-navigation/EditorNavigation.tsx index 0d94221c5..7839393c9 100644 --- a/apps/client/src/common/components/navigation-menu/editor-navigation/EditorNavigation.tsx +++ b/apps/client/src/common/components/navigation-menu/editor-navigation/EditorNavigation.tsx @@ -1,14 +1,14 @@ import { IoLockClosedOutline } from 'react-icons/io5'; import { useNavigate } from 'react-router-dom'; -import { useViewportSize } from '@mantine/hooks'; +import { useIsSmallDevice } from '../../../hooks/useIsSmallDevice'; import NavigationMenuItem from '../navigation-menu-item/NavigationMenuItem'; export default function EditorNavigation() { - const { width } = useViewportSize(); const navigate = useNavigate(); + const isSmallDevide = useIsSmallDevice(); - if (width > 1440) { + if (!isSmallDevide) { return ( navigate('/editor')}> diff --git a/apps/client/src/common/components/navigation-menu/floating-navigation/FloatingNavigation.tsx b/apps/client/src/common/components/navigation-menu/floating-navigation/FloatingNavigation.tsx index 0301bdd16..f9f6643ae 100644 --- a/apps/client/src/common/components/navigation-menu/floating-navigation/FloatingNavigation.tsx +++ b/apps/client/src/common/components/navigation-menu/floating-navigation/FloatingNavigation.tsx @@ -9,7 +9,7 @@ import style from './FloatingNavigation.module.scss'; interface FloatingNavigationProps { toggleMenu: () => void; - toggleSettings: () => void; + toggleSettings?: () => void; } export default function FloatingNavigation({ toggleMenu, toggleSettings }: FloatingNavigationProps) { @@ -29,15 +29,17 @@ export default function FloatingNavigation({ toggleMenu, toggleSettings }: Float > - - - + {toggleSettings && ( + + + + )} ); } diff --git a/apps/client/src/common/hooks/useIsMobile.ts b/apps/client/src/common/hooks/useIsMobile.ts new file mode 100644 index 000000000..191bb3556 --- /dev/null +++ b/apps/client/src/common/hooks/useIsMobile.ts @@ -0,0 +1,9 @@ +import { useMemo } from 'react'; +import { useOs, useViewportSize } from '@mantine/hooks'; + +export function useIsMobile(): boolean { + const { width } = useViewportSize(); + const os = useOs(); + + return useMemo(() => (os === 'ios' || os === 'android') && width < 800, [width, os]); +} diff --git a/apps/client/src/common/hooks/useIsSmallDevice.ts b/apps/client/src/common/hooks/useIsSmallDevice.ts new file mode 100644 index 000000000..4d0b4e7e6 --- /dev/null +++ b/apps/client/src/common/hooks/useIsSmallDevice.ts @@ -0,0 +1,9 @@ +import { useMemo } from 'react'; +import { useOs, useViewportSize } from '@mantine/hooks'; + +export function useIsSmallDevice(): boolean { + const { width } = useViewportSize(); + const os = useOs(); + + return useMemo(() => (os === 'ios' || os === 'android') && width < 1300, [width, os]); +} diff --git a/apps/client/src/common/hooks/useIsSmallScreen.ts b/apps/client/src/common/hooks/useIsSmallScreen.ts new file mode 100644 index 000000000..08eb7f27c --- /dev/null +++ b/apps/client/src/common/hooks/useIsSmallScreen.ts @@ -0,0 +1,8 @@ +import { useMemo } from 'react'; +import { useViewportSize } from '@mantine/hooks'; + +export function useIsSmallScreen(): boolean { + const { width } = useViewportSize(); + + return useMemo(() => width < 1300, [width]); +} diff --git a/apps/client/src/features/control/message/MessageControlExport.jsx b/apps/client/src/features/control/message/MessageControlExport.jsx index 9f5fbf117..6769cfa80 100644 --- a/apps/client/src/features/control/message/MessageControlExport.jsx +++ b/apps/client/src/features/control/message/MessageControlExport.jsx @@ -1,6 +1,8 @@ import { memo } from 'react'; 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'; @@ -9,20 +11,23 @@ import MessageControl from './MessageControl'; import style from '../../editors/Editor.module.scss'; -const MessageControlExport = () => { +export default memo(MessageControlExport); +function MessageControlExport() { const isExtracted = window.location.pathname.includes('/messagecontrol'); const classes = cx([style.content, style.contentColumnLayout]); return ( -
- {!isExtracted && handleLinks('messagecontrol', event)} />} -
- - - -
-
- ); -}; + +
+ {!isExtracted && handleLinks('messagecontrol', event)} />} + {isExtracted && } -export default memo(MessageControlExport); +
+ + + +
+
+
+ ); +} diff --git a/apps/client/src/features/control/playback/TimerControlExport.tsx b/apps/client/src/features/control/playback/TimerControlExport.tsx index ac94b8f45..6b6f5861c 100644 --- a/apps/client/src/features/control/playback/TimerControlExport.tsx +++ b/apps/client/src/features/control/playback/TimerControlExport.tsx @@ -1,6 +1,8 @@ import { memo } from 'react'; 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'; @@ -8,18 +10,22 @@ import PlaybackControl from './PlaybackControl'; import style from '../../editors/Editor.module.scss'; -const TimerControlExport = () => { - const isExtracted = window.location.pathname.includes('/timercontrol'); - return ( -
- {!isExtracted && handleLinks('timercontrol', event)} />} -
- - - -
-
- ); -}; - export default memo(TimerControlExport); +function TimerControlExport() { + const isExtracted = window.location.pathname.includes('/timercontrol'); + + return ( + +
+ {!isExtracted && handleLinks('timercontrol', event)} />} + {isExtracted && } + +
+ + + +
+
+
+ ); +} diff --git a/apps/client/src/features/rundown/RundownExport.tsx b/apps/client/src/features/rundown/RundownExport.tsx index 145f0ca43..3d39610f4 100644 --- a/apps/client/src/features/rundown/RundownExport.tsx +++ b/apps/client/src/features/rundown/RundownExport.tsx @@ -2,6 +2,9 @@ import { memo } from 'react'; import { ContextMenu } from '../../common/components/context-menu/ContextMenu'; 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 { useIsSmallDevice } from '../../common/hooks/useIsSmallDevice'; import { useAppMode } from '../../common/stores/appModeStore'; import { handleLinks } from '../../common/utils/linkUtils'; import { cx } from '../../common/utils/styleUtils'; @@ -18,30 +21,50 @@ export default memo(RundownExport); function RundownExport() { const isExtracted = window.location.pathname.includes('/rundown'); const appMode = useAppMode((state) => state.mode); - const hideSideBar = isExtracted && appMode === 'run'; + const isSmallDevice = useIsSmallDevice(); - const classes = cx([style.rundownExport, isExtracted && style.extracted]); - - return ( -
- -
-
- - {!isExtracted && handleLinks('rundown', event)} />} - - - - -
- {!hideSideBar && ( -
+ if (isSmallDevice && isExtracted) { + return ( + +
+ + +
- + + +
- )} +
+
+ ); + } + + const hideSideBar = isExtracted && appMode === 'run'; + + return ( + +
+ +
+
+ + handleLinks('rundown', event)} /> + + + + +
+ {!hideSideBar && ( +
+ + + +
+ )} +
-
+ ); } diff --git a/apps/client/src/views/studio/StudioClock.tsx b/apps/client/src/views/studio/StudioClock.tsx index 3504e1557..053a1fe80 100644 --- a/apps/client/src/views/studio/StudioClock.tsx +++ b/apps/client/src/views/studio/StudioClock.tsx @@ -1,5 +1,4 @@ -import { useViewportSize } from '@mantine/hooks'; - +import { useIsMobile } from '../../common/hooks/useIsMobile'; import { cx } from '../../common/utils/styleUtils'; import { formatTime } from '../../common/utils/time'; import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime'; @@ -18,10 +17,10 @@ interface StudioClockProps { } export default function StudioClock({ onAir, clock, hideCards }: StudioClockProps) { - const { width } = useViewportSize(); + const isMobile = useIsMobile(); // if we are on mobile and have to show the cards - if (width < 800 && !hideCards) { + if (isMobile && !hideCards) { return ; }