import React, { type FC, memo, useCallback, useEffect, useState, } from 'react'; import dynamic from 'next/dynamic'; import { SidebarMode } from '~/interfaces/ui'; import { useDrawerOpened, useCollapsedContentsOpened, useCurrentProductNavWidth, usePreferCollapsedMode, useSidebarMode, } from '~/stores/ui'; import { DrawerToggler } from '../Common/DrawerToggler'; import { AppTitleOnSidebarHead, AppTitleOnSubnavigation } from './AppTitle/AppTitle'; import { ResizableArea } from './ResizableArea/ResizableArea'; import { SidebarHead } from './SidebarHead'; import { SidebarNav, type SidebarNavProps } from './SidebarNav'; import styles from './Sidebar.module.scss'; const SidebarContents = dynamic(() => import('./SidebarContents').then(mod => mod.SidebarContents), { ssr: false }); const resizableAreaMinWidth = 348; const sidebarNavCollapsedWidth = 48; type ResizableContainerProps = { children?: React.ReactNode, } const ResizableContainer = memo((props: ResizableContainerProps): JSX.Element => { const { children } = props; const { isDrawerMode, isCollapsedMode, isDockMode } = useSidebarMode(); const { mutate: mutateDrawerOpened } = useDrawerOpened(); const { data: currentProductNavWidth, mutateAndSave: mutateProductNavWidth } = useCurrentProductNavWidth(); const { mutateAndSave: mutatePreferCollapsedMode } = usePreferCollapsedMode(); const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened(); const [resizableAreaWidth, setResizableAreaWidth] = useState(undefined); const resizeHandler = useCallback((newWidth: number) => { setResizableAreaWidth(newWidth); }, []); const resizeDoneHandler = useCallback((newWidth: number) => { mutateProductNavWidth(newWidth, false); }, [mutateProductNavWidth]); const collapsedByResizableAreaHandler = useCallback(() => { mutatePreferCollapsedMode(true); mutateCollapsedContentsOpened(false); }, [mutateCollapsedContentsOpened, mutatePreferCollapsedMode]); // open/close resizable container when drawer mode useEffect(() => { if (isDrawerMode()) { setResizableAreaWidth(undefined); } else if (isCollapsedMode()) { setResizableAreaWidth(sidebarNavCollapsedWidth); } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion setResizableAreaWidth(currentProductNavWidth!); } mutateDrawerOpened(false); }, [currentProductNavWidth, isCollapsedMode, isDrawerMode, mutateDrawerOpened]); return ( {children} ); }); type CollapsibleContainerProps = { Nav: FC, className?: string, children?: React.ReactNode, } const CollapsibleContainer = memo((props: CollapsibleContainerProps): JSX.Element => { const { Nav, className, children } = props; const { isCollapsedMode } = useSidebarMode(); const { data: currentProductNavWidth } = useCurrentProductNavWidth(); const { data: isCollapsedContentsOpened, mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened(); // open menu when collapsed mode const primaryItemHoverHandler = useCallback(() => { // reject other than collapsed mode if (!isCollapsedMode()) { return; } mutateCollapsedContentsOpened(true); }, [isCollapsedMode, mutateCollapsedContentsOpened]); // close menu when collapsed mode const mouseLeaveHandler = useCallback(() => { // reject other than collapsed mode if (!isCollapsedMode()) { return; } mutateCollapsedContentsOpened(false); }, [isCollapsedMode, mutateCollapsedContentsOpened]); const openClass = `${isCollapsedContentsOpened ? 'open' : ''}`; const collapsibleContentsWidth = isCollapsedMode() ? currentProductNavWidth : undefined; return (
); }); type DrawableContainerProps = { className?: string, children?: React.ReactNode, } const DrawableContainer = memo((props: DrawableContainerProps): JSX.Element => { const { className, children } = props; const { data: isDrawerOpened, mutate } = useDrawerOpened(); const openClass = `${isDrawerOpened ? 'open' : ''}`; return ( <>
{children}
{ isDrawerOpened && (
mutate(false)} /> ) } ); }); export const Sidebar = (): JSX.Element => { const { data: sidebarMode, isDrawerMode, isCollapsedMode, isDockMode, } = useSidebarMode(); // css styles const grwSidebarClass = styles['grw-sidebar']; // eslint-disable-next-line no-nested-ternary let modeClass; switch (sidebarMode) { case SidebarMode.DRAWER: modeClass = 'grw-sidebar-drawer'; break; case SidebarMode.COLLAPSED: modeClass = 'grw-sidebar-collapsed'; break; case SidebarMode.DOCK: modeClass = 'grw-sidebar-dock'; break; } return ( <> { sidebarMode != null && isDrawerMode() && ( reorder ) } { sidebarMode != null && !isDockMode() && } { sidebarMode != null && !isCollapsedMode() && } ); };