smooth sidebar
This commit is contained in:
@@ -12,15 +12,19 @@ import {
|
|||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
CssBaseline,
|
CssBaseline,
|
||||||
IconButton
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import TableViewIcon from '@mui/icons-material/TableView';
|
import TableViewIcon from '@mui/icons-material/TableView';
|
||||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||||
import LogoutIcon from '@mui/icons-material/Logout';
|
import LogoutIcon from '@mui/icons-material/Logout';
|
||||||
|
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||||
|
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||||
import { ResourceConfig } from '../types/config';
|
import { ResourceConfig } from '../types/config';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
const collapsedWidth = 64;
|
||||||
|
|
||||||
interface AdminLayoutProps {
|
interface AdminLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -39,67 +43,148 @@ export default function AdminLayout({
|
|||||||
}: AdminLayoutProps) {
|
}: AdminLayoutProps) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
||||||
const activeResourceName = location.pathname.split('/')[1] || null;
|
const activeResourceName = location.pathname.split('/')[1] || null;
|
||||||
|
|
||||||
|
// AUTO-TOGGLE LOGIC
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (location.pathname === '/' || location.pathname === '') {
|
||||||
|
setIsCollapsed(false);
|
||||||
|
} else {
|
||||||
|
setIsCollapsed(true);
|
||||||
|
}
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
const currentWidth = isCollapsed ? collapsedWidth : drawerWidth;
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
setIsCollapsed(!isCollapsed);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
sx={{
|
||||||
|
zIndex: (theme) => theme.zIndex.drawer + 1,
|
||||||
|
backdropFilter: 'blur(8px)',
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.8)', // Adjust based on theme in real app
|
||||||
|
color: 'text.primary',
|
||||||
|
boxShadow: 'none',
|
||||||
|
borderBottom: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1, fontWeight: 'bold' }}>
|
||||||
Admin Panel
|
Admin Panel
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" sx={{ mr: 2 }}>
|
<Typography variant="body1" sx={{ mr: 2, fontWeight: 500 }}>
|
||||||
{username}
|
{username}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Tooltip title="Logout">
|
||||||
<IconButton color="inherit" onClick={onLogout}>
|
<IconButton color="inherit" onClick={onLogout}>
|
||||||
<LogoutIcon />
|
<LogoutIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Drawer
|
<Drawer
|
||||||
variant="permanent"
|
variant="permanent"
|
||||||
sx={{
|
sx={{
|
||||||
width: drawerWidth,
|
width: currentWidth,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' },
|
whiteSpace: 'nowrap',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
// TRANSITION
|
||||||
|
transition: (theme) => theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
[`& .MuiDrawer-paper`]: {
|
||||||
|
width: currentWidth,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
transition: (theme) => theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
<Box sx={{ overflow: 'auto' }}>
|
<Box sx={{ overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: isCollapsed ? 'center' : 'flex-end', p: 1 }}>
|
||||||
|
<IconButton onClick={handleToggle}>
|
||||||
|
{isCollapsed ? <ChevronRightIcon /> : <ChevronLeftIcon />}
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
<Divider />
|
||||||
<List>
|
<List>
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
|
<Tooltip title={isCollapsed ? "Dashboard" : ""} placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
selected={location.pathname === '/'}
|
selected={location.pathname === '/'}
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
|
sx={{
|
||||||
|
minHeight: 48,
|
||||||
|
justifyContent: isCollapsed ? 'center' : 'initial',
|
||||||
|
px: 2.5,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon sx={{
|
||||||
<DashboardIcon />
|
minWidth: 0,
|
||||||
|
mr: isCollapsed ? 0 : 3,
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<DashboardIcon color={location.pathname === '/' ? 'primary' : 'inherit'} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Dashboard" />
|
{!isCollapsed && <ListItemText primary="Dashboard" />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<Divider />
|
<Divider />
|
||||||
<List>
|
<List sx={{ flexGrow: 1 }}>
|
||||||
{resources.map((res) => (
|
{resources.map((res) => (
|
||||||
<ListItem key={res.name} disablePadding>
|
<ListItem key={res.name} disablePadding>
|
||||||
|
<Tooltip title={isCollapsed ? res.pluralLabel : ""} placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
selected={activeResourceName === res.name}
|
selected={activeResourceName === res.name}
|
||||||
onClick={() => onSelectResource(res.name)}
|
onClick={() => onSelectResource(res.name)}
|
||||||
|
sx={{
|
||||||
|
minHeight: 48,
|
||||||
|
justifyContent: isCollapsed ? 'center' : 'initial',
|
||||||
|
px: 2.5,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon sx={{
|
||||||
<TableViewIcon />
|
minWidth: 0,
|
||||||
|
mr: isCollapsed ? 0 : 3,
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<TableViewIcon color={activeResourceName === res.name ? 'primary' : 'inherit'} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={res.pluralLabel} />
|
{!isCollapsed && <ListItemText primary={res.pluralLabel} />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
<Divider />
|
|
||||||
</Box>
|
</Box>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
p: 3,
|
||||||
|
transition: (theme) => theme.transitions.create(['margin', 'width'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user