Files
MatrixGW/matrixgw_frontend/src/widgets/dashboard/DashboardSidebar.tsx

282 lines
8.5 KiB
TypeScript

import * as React from "react";
import { useTheme } from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import List from "@mui/material/List";
import Toolbar from "@mui/material/Toolbar";
import type {} from "@mui/material/themeCssVarsAugmentation";
import PersonIcon from "@mui/icons-material/Person";
import BarChartIcon from "@mui/icons-material/BarChart";
import DescriptionIcon from "@mui/icons-material/Description";
import LayersIcon from "@mui/icons-material/Layers";
import { matchPath, useLocation } from "react-router";
import DashboardSidebarContext from "./DashboardSidebarContext";
import { DRAWER_WIDTH, MINI_DRAWER_WIDTH } from "./constants";
import DashboardSidebarPageItem from "./DashboardSidebarPageItem";
import DashboardSidebarHeaderItem from "./DashboardSidebarHeaderItem";
import DashboardSidebarDividerItem from "./DashboardSidebarDividerItem";
import {
getDrawerSxTransitionMixin,
getDrawerWidthTransitionMixin,
} from "./mixins";
export interface DashboardSidebarProps {
expanded?: boolean;
setExpanded: (expanded: boolean) => void;
disableCollapsibleSidebar?: boolean;
container?: Element;
}
export default function DashboardSidebar({
expanded = true,
setExpanded,
disableCollapsibleSidebar = false,
container,
}: DashboardSidebarProps) {
const theme = useTheme();
const { pathname } = useLocation();
const [expandedItemIds, setExpandedItemIds] = React.useState<string[]>([]);
const isOverSmViewport = useMediaQuery(theme.breakpoints.up("sm"));
const isOverMdViewport = useMediaQuery(theme.breakpoints.up("md"));
const [isFullyExpanded, setIsFullyExpanded] = React.useState(expanded);
const [isFullyCollapsed, setIsFullyCollapsed] = React.useState(!expanded);
React.useEffect(() => {
if (expanded) {
const drawerWidthTransitionTimeout = setTimeout(() => {
setIsFullyExpanded(true);
}, theme.transitions.duration.enteringScreen);
return () => clearTimeout(drawerWidthTransitionTimeout);
}
setIsFullyExpanded(false);
return () => {};
}, [expanded, theme.transitions.duration.enteringScreen]);
React.useEffect(() => {
if (!expanded) {
const drawerWidthTransitionTimeout = setTimeout(() => {
setIsFullyCollapsed(true);
}, theme.transitions.duration.leavingScreen);
return () => clearTimeout(drawerWidthTransitionTimeout);
}
setIsFullyCollapsed(false);
return () => {};
}, [expanded, theme.transitions.duration.leavingScreen]);
const mini = !disableCollapsibleSidebar && !expanded;
const handleSetSidebarExpanded = React.useCallback(
(newExpanded: boolean) => () => {
setExpanded(newExpanded);
},
[setExpanded]
);
const handlePageItemClick = React.useCallback(
(itemId: string, hasNestedNavigation: boolean) => {
if (hasNestedNavigation && !mini) {
setExpandedItemIds((previousValue) =>
previousValue.includes(itemId)
? previousValue.filter(
(previousValueItemId) => previousValueItemId !== itemId
)
: [...previousValue, itemId]
);
} else if (!isOverSmViewport && !hasNestedNavigation) {
setExpanded(false);
}
},
[mini, setExpanded, isOverSmViewport]
);
const hasDrawerTransitions =
isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport);
const getDrawerContent = React.useCallback(
(viewport: "phone" | "tablet" | "desktop") => (
<React.Fragment>
<Toolbar />
<Box
component="nav"
aria-label={`${viewport.charAt(0).toUpperCase()}${viewport.slice(1)}`}
sx={{
height: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
overflow: "auto",
scrollbarGutter: mini ? "stable" : "auto",
overflowX: "hidden",
pt: !mini ? 0 : 2,
...(hasDrawerTransitions
? getDrawerSxTransitionMixin(isFullyExpanded, "padding")
: {}),
}}
>
<List
dense
sx={{
padding: mini ? 0 : 0.5,
mb: 4,
width: mini ? MINI_DRAWER_WIDTH : "auto",
}}
>
<DashboardSidebarHeaderItem>Main items</DashboardSidebarHeaderItem>
<DashboardSidebarPageItem
id="employees"
title="Employees"
icon={<PersonIcon />}
href="/employees"
selected={
!!matchPath("/employees/*", pathname) || pathname === "/"
}
/>
<DashboardSidebarDividerItem />
<DashboardSidebarHeaderItem>
Example items
</DashboardSidebarHeaderItem>
<DashboardSidebarPageItem
id="reports"
title="Reports"
icon={<BarChartIcon />}
href="/reports"
selected={!!matchPath("/reports", pathname)}
defaultExpanded={!!matchPath("/reports", pathname)}
expanded={expandedItemIds.includes("reports")}
nestedNavigation={
<List
dense
sx={{
padding: 0,
my: 1,
pl: mini ? 0 : 1,
minWidth: 240,
}}
>
<DashboardSidebarPageItem
id="sales"
title="Sales"
icon={<DescriptionIcon />}
href="/reports/sales"
selected={!!matchPath("/reports/sales", pathname)}
/>
<DashboardSidebarPageItem
id="traffic"
title="Traffic"
icon={<DescriptionIcon />}
href="/reports/traffic"
selected={!!matchPath("/reports/traffic", pathname)}
/>
</List>
}
/>
<DashboardSidebarPageItem
id="integrations"
title="Integrations"
icon={<LayersIcon />}
href="/integrations"
selected={!!matchPath("/integrations", pathname)}
/>
</List>
</Box>
</React.Fragment>
),
[mini, hasDrawerTransitions, isFullyExpanded, expandedItemIds, pathname]
);
const getDrawerSharedSx = React.useCallback(
(isTemporary: boolean) => {
const drawerWidth = mini ? MINI_DRAWER_WIDTH : DRAWER_WIDTH;
return {
displayPrint: "none",
width: drawerWidth,
flexShrink: 0,
...getDrawerWidthTransitionMixin(expanded),
...(isTemporary ? { position: "absolute" } : {}),
[`& .MuiDrawer-paper`]: {
position: "absolute",
width: drawerWidth,
boxSizing: "border-box",
backgroundImage: "none",
...getDrawerWidthTransitionMixin(expanded),
},
};
},
[expanded, mini]
);
const sidebarContextValue = React.useMemo(() => {
return {
onPageItemClick: handlePageItemClick,
mini,
fullyExpanded: isFullyExpanded,
fullyCollapsed: isFullyCollapsed,
hasDrawerTransitions,
};
}, [
handlePageItemClick,
mini,
isFullyExpanded,
isFullyCollapsed,
hasDrawerTransitions,
]);
return (
<DashboardSidebarContext.Provider value={sidebarContextValue}>
<Drawer
container={container}
variant="temporary"
open={expanded}
onClose={handleSetSidebarExpanded(false)}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: {
xs: "block",
sm: disableCollapsibleSidebar ? "block" : "none",
md: "none",
},
...getDrawerSharedSx(true),
}}
>
{getDrawerContent("phone")}
</Drawer>
<Drawer
variant="permanent"
sx={{
display: {
xs: "none",
sm: disableCollapsibleSidebar ? "none" : "block",
md: "none",
},
...getDrawerSharedSx(false),
}}
>
{getDrawerContent("tablet")}
</Drawer>
<Drawer
variant="permanent"
sx={{
display: { xs: "none", md: "block" },
...getDrawerSharedSx(false),
}}
>
{getDrawerContent("desktop")}
</Drawer>
</DashboardSidebarContext.Provider>
);
}