first commit
This commit is contained in:
200
src/layout/MainLayout/Sidebar.jsx
Normal file
200
src/layout/MainLayout/Sidebar.jsx
Normal file
@@ -0,0 +1,200 @@
|
||||
import { useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Drawer,
|
||||
Box,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Typography,
|
||||
Collapse,
|
||||
Tooltip,
|
||||
Toolbar
|
||||
} from '@mui/material';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
|
||||
|
||||
import navItems from '@/menu/navItems';
|
||||
import Logo from '@/components/Logo';
|
||||
|
||||
export const DRAWER_WIDTH = 264;
|
||||
export const MINI_WIDTH = 78;
|
||||
|
||||
const RED = '#C01227';
|
||||
|
||||
function NavLeaf({ item, open, active, depth = 0, onClick }) {
|
||||
const Icon = item.icon;
|
||||
const button = (
|
||||
<ListItemButton
|
||||
selected={active}
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
minHeight: 44,
|
||||
my: 0.25,
|
||||
mx: open ? 1 : 0.75,
|
||||
px: open ? 1.5 : 0,
|
||||
justifyContent: open ? 'flex-start' : 'center',
|
||||
borderRadius: 2,
|
||||
color: 'rgba(255,255,255,0.78)',
|
||||
'& .MuiListItemIcon-root': { color: 'inherit' },
|
||||
'&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' },
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'rgba(255,255,255,0.18)',
|
||||
color: '#fff',
|
||||
'&:hover': { bgcolor: 'rgba(255,255,255,0.22)' }
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: open ? 34 : 'auto', justifyContent: 'center' }}>
|
||||
{depth > 0 && !Icon ? <FiberManualRecordIcon sx={{ fontSize: 8 }} /> : Icon ? <Icon fontSize="small" /> : null}
|
||||
</ListItemIcon>
|
||||
{open && (
|
||||
<ListItemText
|
||||
primary={item.title}
|
||||
primaryTypographyProps={{ fontSize: '0.875rem', fontWeight: active ? 700 : 500 }}
|
||||
/>
|
||||
)}
|
||||
</ListItemButton>
|
||||
);
|
||||
|
||||
return open ? button : <Tooltip title={item.title} placement="right">{button}</Tooltip>;
|
||||
}
|
||||
|
||||
export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const isActive = (url) => url && location.pathname.startsWith(url);
|
||||
const expanded = open || isMobile;
|
||||
|
||||
const initialOpen = navItems
|
||||
.flatMap((g) => g.items)
|
||||
.filter((i) => i.children && i.children.some((c) => isActive(c.url)))
|
||||
.map((i) => i.id);
|
||||
const [collapse, setCollapse] = useState(initialOpen);
|
||||
|
||||
const go = (url) => {
|
||||
navigate(url);
|
||||
if (isMobile) onMobileClose();
|
||||
};
|
||||
|
||||
const content = (
|
||||
<Box sx={{ bgcolor: RED, height: '100%', color: '#fff', display: 'flex', flexDirection: 'column' }}>
|
||||
<Toolbar sx={{ px: expanded ? 2.5 : 0, justifyContent: expanded ? 'flex-start' : 'center', minHeight: 64 }}>
|
||||
<Logo onDark compact={!expanded} />
|
||||
</Toolbar>
|
||||
<Box sx={{ overflowY: 'auto', overflowX: 'hidden', flexGrow: 1, pb: 2 }}>
|
||||
{navItems.map((grp) => (
|
||||
<Box key={grp.group} sx={{ mt: 1 }}>
|
||||
{expanded && (
|
||||
<Typography
|
||||
variant="overline"
|
||||
sx={{ px: 2.5, color: 'rgba(255,255,255,0.55)', fontSize: '0.6875rem', letterSpacing: '0.08em' }}
|
||||
>
|
||||
{grp.group}
|
||||
</Typography>
|
||||
)}
|
||||
<List disablePadding sx={{ mt: 0.5 }}>
|
||||
{grp.items.map((item) => {
|
||||
if (item.children) {
|
||||
const opened = collapse.includes(item.id);
|
||||
const childActive = item.children.some((c) => isActive(c.url));
|
||||
const Icon = item.icon;
|
||||
const head = (
|
||||
<ListItemButton
|
||||
onClick={() =>
|
||||
expanded
|
||||
? setCollapse((p) => (p.includes(item.id) ? p.filter((x) => x !== item.id) : [...p, item.id]))
|
||||
: go(item.children[0].url)
|
||||
}
|
||||
sx={{
|
||||
minHeight: 44,
|
||||
my: 0.25,
|
||||
mx: expanded ? 1 : 0.75,
|
||||
px: expanded ? 1.5 : 0,
|
||||
justifyContent: expanded ? 'flex-start' : 'center',
|
||||
borderRadius: 2,
|
||||
color: childActive ? '#fff' : 'rgba(255,255,255,0.78)',
|
||||
bgcolor: childActive && !opened ? 'rgba(255,255,255,0.12)' : 'transparent',
|
||||
'&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' }
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: expanded ? 34 : 'auto', justifyContent: 'center', color: 'inherit' }}>
|
||||
<Icon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
{expanded && (
|
||||
<>
|
||||
<ListItemText primary={item.title} primaryTypographyProps={{ fontSize: '0.875rem', fontWeight: 500 }} />
|
||||
{opened ? <ExpandLess fontSize="small" /> : <ExpandMore fontSize="small" />}
|
||||
</>
|
||||
)}
|
||||
</ListItemButton>
|
||||
);
|
||||
return (
|
||||
<Box key={item.id}>
|
||||
{expanded ? head : <Tooltip title={item.title} placement="right">{head}</Tooltip>}
|
||||
{expanded && (
|
||||
<Collapse in={opened} timeout="auto" unmountOnExit>
|
||||
<Box sx={{ pl: 1.5 }}>
|
||||
{item.children.map((c) => (
|
||||
<NavLeaf key={c.id} item={c} open depth={1} active={isActive(c.url)} onClick={() => go(c.url)} />
|
||||
))}
|
||||
</Box>
|
||||
</Collapse>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<NavLeaf key={item.id} item={item} open={expanded} active={isActive(item.url)} onClick={() => go(item.url)} />
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
{expanded && (
|
||||
<Box sx={{ p: 2, borderTop: '1px solid rgba(255,255,255,0.12)' }}>
|
||||
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.55)' }}>
|
||||
Doormile Console v1.0
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onClose={onMobileClose}
|
||||
ModalProps={{ keepMounted: true }}
|
||||
sx={{ '& .MuiDrawer-paper': { width: DRAWER_WIDTH, border: 'none' } }}
|
||||
>
|
||||
{content}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
sx={{
|
||||
width: open ? DRAWER_WIDTH : MINI_WIDTH,
|
||||
flexShrink: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
'& .MuiDrawer-paper': {
|
||||
width: open ? DRAWER_WIDTH : MINI_WIDTH,
|
||||
border: 'none',
|
||||
overflowX: 'hidden',
|
||||
transition: (t) => t.transitions.create('width', { duration: t.transitions.duration.standard })
|
||||
}
|
||||
}}
|
||||
open={open}
|
||||
>
|
||||
{content}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user