This commit is contained in:
seb
2026-08-06 00:19:21 +02:00
parent 80ca59ab11
commit 855710a198
16 changed files with 270 additions and 185 deletions

View File

@@ -1,21 +1,24 @@
import { PureComponent } from 'react';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import IconButton from '@mui/material/IconButton';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import UserMenu from './UserMenu';
import { NAV_ITEMS, isNavItemActive } from '../nav';
import { NAV_ITEMS, SUB_NAV, resolveActiveNav, resolveActiveSub } from '../nav';
import { artikelQueryPath } from '../pages/artikel/artikelQuery';
import { defaultCategoryPath } from '../pages/artikel/categoryRoute';
const SIDEBAR_WIDTH = 144;
const SUB_NAV_WIDTH = 80;
export default function AppShell() {
const navigate = useNavigate();
const { pathname } = useLocation();
class AppSidebar extends PureComponent {
render() {
const { activeNav, navigate } = this.props;
return (
<Box sx={{ display: 'flex', minHeight: '100vh', bgcolor: 'background.default' }}>
return (
<Box
component="nav"
sx={(theme) => ({
@@ -28,7 +31,7 @@ export default function AppShell() {
>
<List disablePadding sx={{ flexGrow: 1 }}>
{NAV_ITEMS.map(({ path, label, icon: Icon }) => {
const active = isNavItemActive(pathname, path);
const active = path === activeNav;
return (
<ListItemButton
@@ -73,17 +76,113 @@ export default function AppShell() {
<UserMenu />
</Box>
);
}
}
class AppSubNav extends PureComponent {
render() {
const { activeNav, activeSub, navigate } = this.props;
const subNav = activeNav ? SUB_NAV[activeNav] : null;
if (!subNav?.length) {
return (
<Box
sx={{
width: SUB_NAV_WIDTH,
flexShrink: 0,
}}
/>
);
}
return (
<Box
component="nav"
aria-label="Unternavigation"
sx={{
width: SUB_NAV_WIDTH,
flexShrink: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 1,
}}
>
{subNav.map(({ segment, label, icon: Icon }) => {
const active = segment === activeSub;
return (
<IconButton
key={segment || 'index'}
type="button"
aria-label={label}
aria-current={active ? 'page' : undefined}
disableRipple
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
if (!segment && activeNav === '/artikel') {
navigate(artikelQueryPath());
return;
}
if (segment === 'kategorien') {
navigate(defaultCategoryPath());
return;
}
navigate(segment ? `${activeNav}/${segment}` : activeNav);
}}
sx={(theme) => ({
width: 80,
height: 80,
borderRadius: '50%',
color: active
? theme.palette.primary.main
: theme.palette.text.secondary,
bgcolor: 'transparent',
'&:hover': {
bgcolor: theme.palette.action.hover,
color: theme.palette.primary.main,
},
})}
>
<Icon sx={{ fontSize: 40 }} />
</IconButton>
);
})}
</Box>
);
}
}
export default function AppShell() {
const navigate = useNavigate();
const { pathname } = useLocation();
const activeNav = resolveActiveNav(pathname);
const activeSub = activeNav ? resolveActiveSub(pathname, activeNav) : null;
return (
<Box sx={{ display: 'flex', minHeight: '100vh', bgcolor: 'background.default' }}>
<AppSidebar activeNav={activeNav} navigate={navigate} />
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
display: 'flex',
gap: 2,
py: 4,
px: 3,
bgcolor: theme.nav.mainBackground,
minWidth: 0,
})}
>
<Outlet />
<AppSubNav
activeNav={activeNav}
activeSub={activeSub}
navigate={navigate}
/>
<Box sx={{ flexGrow: 1, minWidth: 0 }}>
<Outlet />
</Box>
</Box>
</Box>
);