mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-24 16:39:13 +00:00
refactor: editor responsive navigation
This commit is contained in:
committed by
Carlos Valente
parent
cff73a65a5
commit
b991627cb7
@@ -9,10 +9,11 @@ import useViewEditor from './useViewEditor';
|
|||||||
|
|
||||||
interface ViewNavigationMenuProps {
|
interface ViewNavigationMenuProps {
|
||||||
isLockable?: boolean;
|
isLockable?: boolean;
|
||||||
|
supressSettings?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(ViewNavigationMenu);
|
export default memo(ViewNavigationMenu);
|
||||||
function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) {
|
function ViewNavigationMenu({ isLockable, supressSettings }: ViewNavigationMenuProps) {
|
||||||
const { isOpen: isMenuOpen, onOpen: onMenuOpen, onClose: onMenuClose } = useDisclosure();
|
const { isOpen: isMenuOpen, onOpen: onMenuOpen, onClose: onMenuClose } = useDisclosure();
|
||||||
const { showEditFormDrawer, isViewLocked } = useViewEditor({ isLockable });
|
const { showEditFormDrawer, isViewLocked } = useViewEditor({ isLockable });
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) {
|
|||||||
[
|
[
|
||||||
'mod + ,',
|
'mod + ,',
|
||||||
() => {
|
() => {
|
||||||
if (isViewLocked) return;
|
if (isViewLocked || supressSettings) return;
|
||||||
showEditFormDrawer();
|
showEditFormDrawer();
|
||||||
},
|
},
|
||||||
{ preventDefault: true },
|
{ preventDefault: true },
|
||||||
@@ -43,7 +44,10 @@ function ViewNavigationMenu({ isLockable }: ViewNavigationMenuProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FloatingNavigation toggleMenu={toggleMenu} toggleSettings={showEditFormDrawer} />
|
<FloatingNavigation
|
||||||
|
toggleMenu={toggleMenu}
|
||||||
|
toggleSettings={supressSettings ? undefined : () => showEditFormDrawer()}
|
||||||
|
/>
|
||||||
<NavigationMenu isOpen={isMenuOpen} onClose={onMenuClose} />
|
<NavigationMenu isOpen={isMenuOpen} onClose={onMenuClose} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-3
@@ -1,14 +1,14 @@
|
|||||||
import { IoLockClosedOutline } from 'react-icons/io5';
|
import { IoLockClosedOutline } from 'react-icons/io5';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useViewportSize } from '@mantine/hooks';
|
|
||||||
|
|
||||||
|
import { useIsSmallDevice } from '../../../hooks/useIsSmallDevice';
|
||||||
import NavigationMenuItem from '../navigation-menu-item/NavigationMenuItem';
|
import NavigationMenuItem from '../navigation-menu-item/NavigationMenuItem';
|
||||||
|
|
||||||
export default function EditorNavigation() {
|
export default function EditorNavigation() {
|
||||||
const { width } = useViewportSize();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const isSmallDevide = useIsSmallDevice();
|
||||||
|
|
||||||
if (width > 1440) {
|
if (!isSmallDevide) {
|
||||||
return (
|
return (
|
||||||
<NavigationMenuItem active={location.pathname === '/editor'} onClick={() => navigate('/editor')}>
|
<NavigationMenuItem active={location.pathname === '/editor'} onClick={() => navigate('/editor')}>
|
||||||
<IoLockClosedOutline />
|
<IoLockClosedOutline />
|
||||||
|
|||||||
+12
-10
@@ -9,7 +9,7 @@ import style from './FloatingNavigation.module.scss';
|
|||||||
|
|
||||||
interface FloatingNavigationProps {
|
interface FloatingNavigationProps {
|
||||||
toggleMenu: () => void;
|
toggleMenu: () => void;
|
||||||
toggleSettings: () => void;
|
toggleSettings?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FloatingNavigation({ toggleMenu, toggleSettings }: FloatingNavigationProps) {
|
export default function FloatingNavigation({ toggleMenu, toggleSettings }: FloatingNavigationProps) {
|
||||||
@@ -29,15 +29,17 @@ export default function FloatingNavigation({ toggleMenu, toggleSettings }: Float
|
|||||||
>
|
>
|
||||||
<IoApps />
|
<IoApps />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
{toggleSettings && (
|
||||||
variant='subtle-white'
|
<IconButton
|
||||||
size='xlarge'
|
variant='subtle-white'
|
||||||
onClick={toggleSettings}
|
size='xlarge'
|
||||||
aria-label='toggle settings'
|
onClick={toggleSettings}
|
||||||
data-testid='navigation__toggle-settings'
|
aria-label='toggle settings'
|
||||||
>
|
data-testid='navigation__toggle-settings'
|
||||||
<IoSettingsOutline />
|
>
|
||||||
</IconButton>
|
<IoSettingsOutline />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||||
|
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
|
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
||||||
@@ -9,20 +11,23 @@ import MessageControl from './MessageControl';
|
|||||||
|
|
||||||
import style from '../../editors/Editor.module.scss';
|
import style from '../../editors/Editor.module.scss';
|
||||||
|
|
||||||
const MessageControlExport = () => {
|
export default memo(MessageControlExport);
|
||||||
|
function MessageControlExport() {
|
||||||
const isExtracted = window.location.pathname.includes('/messagecontrol');
|
const isExtracted = window.location.pathname.includes('/messagecontrol');
|
||||||
const classes = cx([style.content, style.contentColumnLayout]);
|
const classes = cx([style.content, style.contentColumnLayout]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.messages} data-testid='panel-messages-control'>
|
<ProtectRoute permission='editor'>
|
||||||
{!isExtracted && <Corner onClick={(event) => handleLinks('messagecontrol', event)} />}
|
<div className={style.messages} data-testid='panel-messages-control'>
|
||||||
<div className={classes}>
|
{!isExtracted && <Corner onClick={(event) => handleLinks('messagecontrol', event)} />}
|
||||||
<ErrorBoundary>
|
{isExtracted && <ViewNavigationMenu supressSettings />}
|
||||||
<MessageControl />
|
|
||||||
</ErrorBoundary>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(MessageControlExport);
|
<div className={classes}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<MessageControl />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ProtectRoute>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../../common/components/error-boundary/ErrorBoundary';
|
||||||
|
import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
|
import ProtectRoute from '../../../common/components/protect-route/ProtectRoute';
|
||||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
import { handleLinks } from '../../../common/utils/linkUtils';
|
||||||
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
import { Corner } from '../../editors/editor-utils/EditorUtils';
|
||||||
|
|
||||||
@@ -8,18 +10,22 @@ import PlaybackControl from './PlaybackControl';
|
|||||||
|
|
||||||
import style from '../../editors/Editor.module.scss';
|
import style from '../../editors/Editor.module.scss';
|
||||||
|
|
||||||
const TimerControlExport = () => {
|
|
||||||
const isExtracted = window.location.pathname.includes('/timercontrol');
|
|
||||||
return (
|
|
||||||
<div className={style.playback} data-testid='panel-timer-control'>
|
|
||||||
{!isExtracted && <Corner onClick={(event) => handleLinks('timercontrol', event)} />}
|
|
||||||
<div className={style.content}>
|
|
||||||
<ErrorBoundary>
|
|
||||||
<PlaybackControl />
|
|
||||||
</ErrorBoundary>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(TimerControlExport);
|
export default memo(TimerControlExport);
|
||||||
|
function TimerControlExport() {
|
||||||
|
const isExtracted = window.location.pathname.includes('/timercontrol');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProtectRoute permission='editor'>
|
||||||
|
<div className={style.playback} data-testid='panel-timer-control'>
|
||||||
|
{!isExtracted && <Corner onClick={(event) => handleLinks('timercontrol', event)} />}
|
||||||
|
{isExtracted && <ViewNavigationMenu supressSettings />}
|
||||||
|
|
||||||
|
<div className={style.content}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<PlaybackControl />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ProtectRoute>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import { ContextMenu } from '../../common/components/context-menu/ContextMenu';
|
import { ContextMenu } from '../../common/components/context-menu/ContextMenu';
|
||||||
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
||||||
|
import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu';
|
||||||
|
import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
|
||||||
|
import { useIsSmallDevice } from '../../common/hooks/useIsSmallDevice';
|
||||||
import { useAppMode } from '../../common/stores/appModeStore';
|
import { useAppMode } from '../../common/stores/appModeStore';
|
||||||
import { handleLinks } from '../../common/utils/linkUtils';
|
import { handleLinks } from '../../common/utils/linkUtils';
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
@@ -18,30 +21,50 @@ export default memo(RundownExport);
|
|||||||
function RundownExport() {
|
function RundownExport() {
|
||||||
const isExtracted = window.location.pathname.includes('/rundown');
|
const isExtracted = window.location.pathname.includes('/rundown');
|
||||||
const appMode = useAppMode((state) => state.mode);
|
const appMode = useAppMode((state) => state.mode);
|
||||||
const hideSideBar = isExtracted && appMode === 'run';
|
const isSmallDevice = useIsSmallDevice();
|
||||||
|
|
||||||
const classes = cx([style.rundownExport, isExtracted && style.extracted]);
|
if (isSmallDevice && isExtracted) {
|
||||||
|
return (
|
||||||
return (
|
<ProtectRoute permission='editor'>
|
||||||
<div className={classes} data-testid='panel-rundown'>
|
<div className={cx([style.rundownExport, style.extracted])} data-testid='panel-rundown'>
|
||||||
<FinderPlacement />
|
<FinderPlacement />
|
||||||
<div className={style.rundown}>
|
<ViewNavigationMenu supressSettings />
|
||||||
<div className={style.list}>
|
<div className={style.content}>
|
||||||
<ErrorBoundary>
|
|
||||||
{!isExtracted && <Corner onClick={(event) => handleLinks('rundown', event)} />}
|
|
||||||
<ContextMenu>
|
|
||||||
<RundownWrapper />
|
|
||||||
</ContextMenu>
|
|
||||||
</ErrorBoundary>
|
|
||||||
</div>
|
|
||||||
{!hideSideBar && (
|
|
||||||
<div className={style.side}>
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RundownEventEditor />
|
<ContextMenu>
|
||||||
|
<RundownWrapper />
|
||||||
|
</ContextMenu>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
</ProtectRoute>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideSideBar = isExtracted && appMode === 'run';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProtectRoute permission='editor'>
|
||||||
|
<div className={cx([style.rundownExport, isExtracted && style.extracted])} data-testid='panel-rundown'>
|
||||||
|
<FinderPlacement />
|
||||||
|
<div className={style.rundown}>
|
||||||
|
<div className={style.list}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Corner onClick={(event) => handleLinks('rundown', event)} />
|
||||||
|
<ContextMenu>
|
||||||
|
<RundownWrapper />
|
||||||
|
</ContextMenu>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
{!hideSideBar && (
|
||||||
|
<div className={style.side}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<RundownEventEditor />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ProtectRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useViewportSize } from '@mantine/hooks';
|
import { useIsMobile } from '../../common/hooks/useIsMobile';
|
||||||
|
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
import { formatTime } from '../../common/utils/time';
|
import { formatTime } from '../../common/utils/time';
|
||||||
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
||||||
@@ -18,10 +17,10 @@ interface StudioClockProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function StudioClock({ onAir, clock, hideCards }: 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 we are on mobile and have to show the cards
|
||||||
if (width < 800 && !hideCards) {
|
if (isMobile && !hideCards) {
|
||||||
return <StudioClockMobile clock={clock} onAir={onAir} />;
|
return <StudioClockMobile clock={clock} onAir={onAir} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user