121
common/resources/client/ui/layout/dashboard-layout.tsx
Executable file
121
common/resources/client/ui/layout/dashboard-layout.tsx
Executable file
@@ -0,0 +1,121 @@
|
||||
import {ComponentPropsWithoutRef, useCallback, useMemo} from 'react';
|
||||
import {
|
||||
DashboardLayoutContext,
|
||||
DashboardSidenavStatus,
|
||||
} from './dashboard-layout-context';
|
||||
import {Underlay} from '../overlays/underlay';
|
||||
import {AnimatePresence} from 'framer-motion';
|
||||
import {useControlledState} from '@react-stately/utils';
|
||||
import {useMediaQuery} from '../../utils/hooks/use-media-query';
|
||||
import {
|
||||
getFromLocalStorage,
|
||||
setInLocalStorage,
|
||||
} from '../../utils/hooks/local-storage';
|
||||
import {useBlockBodyOverflow} from '../../utils/hooks/use-block-body-overflow';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface DashboardLayoutProps extends ComponentPropsWithoutRef<'div'> {
|
||||
name: string;
|
||||
leftSidenavCanBeCompact?: boolean;
|
||||
leftSidenavStatus?: DashboardSidenavStatus;
|
||||
onLeftSidenavChange?: (status: DashboardSidenavStatus) => void;
|
||||
rightSidenavStatus?: DashboardSidenavStatus;
|
||||
initialRightSidenavStatus?: DashboardSidenavStatus;
|
||||
onRightSidenavChange?: (status: DashboardSidenavStatus) => void;
|
||||
height?: string;
|
||||
gridClassName?: string;
|
||||
blockBodyOverflow?: boolean;
|
||||
}
|
||||
export function DashboardLayout({
|
||||
children,
|
||||
leftSidenavStatus: leftSidenav,
|
||||
onLeftSidenavChange,
|
||||
rightSidenavStatus: rightSidenav,
|
||||
initialRightSidenavStatus,
|
||||
onRightSidenavChange,
|
||||
name,
|
||||
leftSidenavCanBeCompact,
|
||||
height = 'h-screen',
|
||||
className,
|
||||
gridClassName = 'dashboard-grid',
|
||||
blockBodyOverflow = true,
|
||||
...domProps
|
||||
}: DashboardLayoutProps) {
|
||||
useBlockBodyOverflow(!blockBodyOverflow);
|
||||
const isMobile = useMediaQuery('(max-width: 1024px)');
|
||||
|
||||
const isCompactModeInitially = useMemo(() => {
|
||||
return !name ? false : getFromLocalStorage(`${name}.sidenav.compact`);
|
||||
}, [name]);
|
||||
const defaultLeftSidenavStatus = isCompactModeInitially ? 'compact' : 'open';
|
||||
const [leftSidenavStatus, setLeftSidenavStatus] = useControlledState(
|
||||
leftSidenav,
|
||||
isMobile ? 'closed' : defaultLeftSidenavStatus,
|
||||
onLeftSidenavChange,
|
||||
);
|
||||
|
||||
const rightSidenavStatusDefault = useMemo(() => {
|
||||
if (isMobile) {
|
||||
return 'closed';
|
||||
}
|
||||
if (initialRightSidenavStatus != null) {
|
||||
return initialRightSidenavStatus;
|
||||
}
|
||||
const userSelected = getFromLocalStorage(
|
||||
`${name}.sidenav.right.position`,
|
||||
'open',
|
||||
);
|
||||
if (userSelected != null) {
|
||||
return userSelected;
|
||||
}
|
||||
return initialRightSidenavStatus || 'closed';
|
||||
}, [isMobile, name, initialRightSidenavStatus]);
|
||||
const [rightSidenavStatus, _setRightSidenavStatus] = useControlledState(
|
||||
rightSidenav,
|
||||
rightSidenavStatusDefault,
|
||||
onRightSidenavChange,
|
||||
);
|
||||
const setRightSidenavStatus = useCallback(
|
||||
(status: DashboardSidenavStatus) => {
|
||||
_setRightSidenavStatus(status);
|
||||
setInLocalStorage(`${name}.sidenav.right.position`, status);
|
||||
},
|
||||
[_setRightSidenavStatus, name],
|
||||
);
|
||||
|
||||
const shouldShowUnderlay =
|
||||
isMobile && (leftSidenavStatus === 'open' || rightSidenavStatus === 'open');
|
||||
|
||||
return (
|
||||
<DashboardLayoutContext.Provider
|
||||
value={{
|
||||
leftSidenavStatus,
|
||||
setLeftSidenavStatus,
|
||||
rightSidenavStatus,
|
||||
setRightSidenavStatus,
|
||||
leftSidenavCanBeCompact,
|
||||
name,
|
||||
isMobileMode: isMobile,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
{...domProps}
|
||||
className={clsx('relative isolate', gridClassName, className, height)}
|
||||
>
|
||||
{children}
|
||||
<AnimatePresence>
|
||||
{shouldShowUnderlay && (
|
||||
<Underlay
|
||||
position="fixed"
|
||||
key="dashboard-underlay"
|
||||
onClick={() => {
|
||||
setLeftSidenavStatus('closed');
|
||||
setRightSidenavStatus('closed');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</DashboardLayoutContext.Provider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user