91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { HomeOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
|
|
import { Divider, Layout, Tooltip } from 'antd';
|
|
import { type ReactNode, memo, useState } from 'react';
|
|
import { NavLink } from 'react-router-dom';
|
|
import styles from './styles.module.css';
|
|
|
|
type NavItem = {
|
|
link: string;
|
|
label: string;
|
|
icon: ReactNode;
|
|
};
|
|
|
|
type Group = {
|
|
title: string;
|
|
key: string;
|
|
children: NavItem[];
|
|
};
|
|
|
|
// Каркас: разделы меню — по карте экранов docs/context/10-user-journey.md, добавляются срезами.
|
|
const config: Group[] = [
|
|
{
|
|
title: 'Главная',
|
|
key: 'home',
|
|
children: [{ link: '/', icon: <HomeOutlined />, label: 'Главная' }],
|
|
},
|
|
];
|
|
|
|
function CollapsedIcon({ icon, title, to }: { icon: ReactNode; title: string; to: string }) {
|
|
return (
|
|
<Tooltip placement='right' title={title}>
|
|
<NavLink to={to} className={({ isActive }) => `${styles.iconBtn} ${isActive ? styles.iconBtnActive : styles.iconBtnNav}`}>
|
|
{icon}
|
|
</NavLink>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
|
|
function NavLinkStyled({ to, children }: { to: string; children: ReactNode }) {
|
|
return (
|
|
<NavLink className={({ isActive }) => `${styles.linkBase} ${isActive ? styles.linkActive : ''}`} to={to}>
|
|
{children}
|
|
</NavLink>
|
|
);
|
|
}
|
|
|
|
const Section = memo(function Section({ isCollapsed, group }: { isCollapsed: boolean; group: Group }) {
|
|
return !isCollapsed ? (
|
|
<div className={styles.section}>
|
|
<div className={styles.sectionTitle}>{group.title}</div>
|
|
{group.children.map((i) => (
|
|
<NavLinkStyled key={i.link} to={i.link}>
|
|
{i.icon}
|
|
{i.label}
|
|
</NavLinkStyled>
|
|
))}
|
|
<Divider size='small' />
|
|
</div>
|
|
) : (
|
|
<>
|
|
{group.children.map((i) => (
|
|
<CollapsedIcon key={i.link} icon={i.icon} to={i.link} title={i.label} />
|
|
))}
|
|
</>
|
|
);
|
|
});
|
|
|
|
export function Sidebar() {
|
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
|
|
return (
|
|
<Layout.Sider
|
|
collapsed={isCollapsed}
|
|
style={{ height: 'auto', margin: '10px', borderRadius: 10 }}
|
|
width={280}
|
|
collapsedWidth={64}
|
|
collapsible
|
|
trigger={null}>
|
|
<div className={`${styles.wrapper} ${isCollapsed ? styles.wrapperCollapsed : ''}`}>
|
|
<div className={styles.topbar}>
|
|
<button type='button' className={styles.collapseBtn} onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
{isCollapsed ? <RightOutlined /> : <LeftOutlined />}
|
|
</button>
|
|
</div>
|
|
{config.map((g) => (
|
|
<Section key={g.key} group={g} isCollapsed={isCollapsed} />
|
|
))}
|
|
</div>
|
|
</Layout.Sider>
|
|
);
|
|
}
|