'use client'; import { FunctionComponent, useMemo, useState } from 'react'; import { Stack, StackProps } from '@mui/material'; import { IS_DEBUG } from '@/config'; import { AppIconButton, ErrorBoundary } from '@/components'; import { useAppStore } from '@/store'; import { LinkToPage } from '@/utils'; import { useEventSwitchDarkMode, useIsMobile } from '@/hooks'; import { TopBar } from './components'; import SideBar, { SideBarProps } from './components/SideBar'; import { SIDE_BAR_DESKTOP_ANCHOR, SIDE_BAR_MOBILE_ANCHOR, SIDE_BAR_WIDTH, TOP_BAR_DESKTOP_HEIGHT, TOP_BAR_MOBILE_HEIGHT, } from './config'; interface Props extends StackProps { sidebarItems: Array; title: string; variant: 'sidebarAlwaysTemporary' | 'sidebarPersistentOnDesktop' | 'sidebarAlwaysPersistent'; } /** * Renders "TopBar and SideBar" composition * @layout TopBarAndSideBarLayout */ const TopBarAndSideBarLayout: FunctionComponent = ({ children, sidebarItems, title, variant }) => { const [state] = useAppStore(); const [sidebarVisible, setSidebarVisible] = useState(false); // TODO: Verify is default value is correct const onMobile = useIsMobile(); const onSwitchDarkMode = useEventSwitchDarkMode(); const sidebarProps = useMemo((): Partial => { const anchor = onMobile ? SIDE_BAR_MOBILE_ANCHOR : SIDE_BAR_DESKTOP_ANCHOR; let open = sidebarVisible; let sidebarVariant: SideBarProps['variant'] = 'temporary'; switch (variant) { case 'sidebarAlwaysTemporary': break; case 'sidebarPersistentOnDesktop': open = onMobile ? sidebarVisible : true; sidebarVariant = onMobile ? 'temporary' : 'persistent'; break; case 'sidebarAlwaysPersistent': open = true; sidebarVariant = 'persistent'; break; } return { anchor, open, variant: sidebarVariant }; }, [onMobile, sidebarVisible, variant]); const stackStyles = useMemo( () => ({ minHeight: '100vh', // Full screen height paddingTop: onMobile ? TOP_BAR_MOBILE_HEIGHT : TOP_BAR_DESKTOP_HEIGHT, paddingLeft: sidebarProps.variant === 'persistent' && sidebarProps.open && sidebarProps?.anchor?.includes('left') ? SIDE_BAR_WIDTH : undefined, paddingRight: sidebarProps.variant === 'persistent' && sidebarProps.open && sidebarProps?.anchor?.includes('right') ? SIDE_BAR_WIDTH : undefined, }), [onMobile, sidebarProps] ); const onSideBarOpen = () => { if (!sidebarVisible) setSidebarVisible(true); // Don't re-render Layout when SideBar is already open }; const onSideBarClose = () => { if (sidebarVisible) setSidebarVisible(false); // Don't re-render Layout when SideBar is already closed }; const LogoButton = ( ); const DarkModeButton = ( ); // Note: useMemo() is not needed for startNode, endNode. We need respect store.darkMode and so on. const { startNode, endNode } = sidebarProps?.anchor?.includes('left') ? { startNode: LogoButton, endNode: DarkModeButton } : { startNode: DarkModeButton, endNode: LogoButton }; IS_DEBUG && console.log('Render ', { onMobile, darkMode: state.darkMode, sidebarProps, }); return ( {children} ); }; export default TopBarAndSideBarLayout;