Sidebar
侧边栏:可折叠为图标栏的应用导航,支持分组、二级嵌套、激活态与徽标。折叠时标签在视觉上隐藏但仍留在 DOM,读屏器可读;含 WAI-ARIA 的 aria-current / aria-expanded 语义。
安装
添加组件
pnpm dlx @elyri/cli@latest add sidebar示例
基础用法
顶栏 + 分组导航 + 底栏,项带图标、徽标与禁用态;受控 active 同步显示在右侧内容区。
import { useState } from 'react';
import { Sidebar } from './components/elyri/Sidebar';
// 图标组件此处省略,可换成任意图标库
export function Example() {
const [active, setActive] = useState('home');
return (
<Sidebar active={active} onActiveChange={setActive} aria-label="主导航">
<Sidebar.Header>
<span className="brand">Elyri</span>
<Sidebar.Trigger />
</Sidebar.Header>
<Sidebar.Content>
<Sidebar.Group label="工作区">
<Sidebar.Item value="home" icon={<HomeIcon />}>概览</Sidebar.Item>
<Sidebar.Item value="projects" icon={<FolderIcon />} badge={3}>项目</Sidebar.Item>
<Sidebar.Item value="inbox" icon={<InboxIcon />} badge="New">收件箱</Sidebar.Item>
</Sidebar.Group>
<Sidebar.Separator />
<Sidebar.Group label="账户">
<Sidebar.Item value="settings" icon={<CogIcon />}>设置</Sidebar.Item>
<Sidebar.Item value="billing" icon={<CardIcon />} disabled>账单</Sidebar.Item>
</Sidebar.Group>
</Sidebar.Content>
<Sidebar.Footer>
<span className="brand">Ada Lovelace</span>
</Sidebar.Footer>
</Sidebar>
);
}折叠为图标栏
点击标题栏的 Trigger 在完整宽度与图标栏之间切换,带宽度过渡动画。
import { Sidebar } from './components/elyri/Sidebar';
export function Example() {
return (
<Sidebar defaultActive="projects" aria-label="主导航">
<Sidebar.Header>
<span className="brand">Elyri</span>
<Sidebar.Trigger />
</Sidebar.Header>
<Sidebar.Content>
{/* 分组带图标,折叠成图标栏后仍能看到分组语义 */}
<Sidebar.Group label="工作区" icon={<FolderIcon />}>
<Sidebar.Item value="home" icon={<HomeIcon />}>概览</Sidebar.Item>
<Sidebar.Item value="projects" icon={<FolderIcon />} badge={3}>项目</Sidebar.Item>
<Sidebar.Item value="inbox" icon={<InboxIcon />}>收件箱</Sidebar.Item>
</Sidebar.Group>
</Sidebar.Content>
</Sidebar>
);
}分组与嵌套项
Item 内嵌 Sub / SubItem 形成二级导航,子级激活时父级自动展开,可用方向键展开收起。
import { Sidebar } from './components/elyri/Sidebar';
export function Example() {
return (
<Sidebar defaultActive="alpha" aria-label="主导航">
<Sidebar.Content>
<Sidebar.Group label="项目">
<Sidebar.Item value="all" icon={<FolderIcon />}>全部项目</Sidebar.Item>
<Sidebar.Item value="team" icon={<UsersIcon />}>
团队空间
<Sidebar.Sub>
<Sidebar.SubItem value="alpha">Alpha</Sidebar.SubItem>
<Sidebar.SubItem value="beta">Beta</Sidebar.SubItem>
<Sidebar.SubItem value="gamma" badge={2}>Gamma</Sidebar.SubItem>
</Sidebar.Sub>
</Sidebar.Item>
</Sidebar.Group>
</Sidebar.Content>
</Sidebar>
);
}asChild 接入路由
Item 传 asChild 时把行为合并到唯一子元素上,可换成 react-router 的 Link 等。
import { Link } from 'react-router-dom';
import { Sidebar } from './components/elyri/Sidebar';
export function Example() {
return (
<Sidebar defaultActive="docs" aria-label="主导航">
<Sidebar.Content>
<Sidebar.Group label="资源">
<Sidebar.Item value="docs" asChild icon={<BookIcon />} badge="9">
<Link to="/docs">文档</Link>
</Sidebar.Item>
<Sidebar.Item value="api" asChild icon={<CodeIcon />}>
<Link to="/api">API 参考</Link>
</Sidebar.Item>
</Sidebar.Group>
</Sidebar.Content>
</Sidebar>
);
}外部按钮控制
外层包一个 SidebarProvider 把折叠状态提升出来,顶部导航栏里的按钮调用 useSidebar() 即可折叠 / 展开侧边栏。
import { Sidebar, SidebarProvider, useSidebar } from './components/elyri/Sidebar';
// 顶部导航栏里的按钮,位于 Sidebar 外部
function TopbarToggle() {
const { collapsed, toggleCollapsed } = useSidebar();
return (
<button type="button" onClick={toggleCollapsed}>
{collapsed ? '展开菜单' : '收起菜单'}
</button>
);
}
export function Example() {
return (
<SidebarProvider defaultActive="home">
<header className="topbar">
<span className="brand">Elyri</span>
<TopbarToggle />
</header>
<div className="shell">
<Sidebar aria-label="主导航">
<Sidebar.Content>
<Sidebar.Group label="工作区">
<Sidebar.Item value="home" icon={<HomeIcon />}>概览</Sidebar.Item>
<Sidebar.Item value="projects" icon={<FolderIcon />} badge={3}>项目</Sidebar.Item>
<Sidebar.Item value="inbox" icon={<InboxIcon />}>收件箱</Sidebar.Item>
</Sidebar.Group>
</Sidebar.Content>
</Sidebar>
<main className="shell-main">当前页面</main>
</div>
</SidebarProvider>
);
}属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
collapsed | boolean | — | 受控折叠态 |
defaultCollapsed | boolean | false | 非受控初始折叠态 |
onCollapsedChange | (collapsed: boolean) => void | — | 折叠态变化回调 |
collapsible | boolean | true | 是否允许折叠,为 false 时 Trigger 禁用 |
active | string | — | 受控激活项 value |
defaultActive | string | '' | 非受控初始激活项 value |
onActiveChange | (value: string) => void | — | 激活项变化回调,受控与非受控都会触发 |
SidebarProvider | component | — | 把折叠、激活状态提升到 Sidebar 之外,供顶部导航栏等外部按钮共享;状态相关 props 与 Sidebar 一致 |
useSidebar() | hook | — | 读取 collapsed / active 并提供 toggleCollapsed,可在 Sidebar 或 SidebarProvider 内调用 |
Sidebar.Header | HTMLAttributes | — | 顶部区域,折叠时只保留 Trigger |
Sidebar.Content | HTMLAttributes | — | 导航主体,可滚动,默认渲染为 <nav> |
Sidebar.Group | HTMLAttributes | — | 分组容器,可配 label 与 icon |
Group.label | ReactNode | — | 分组标题,折叠时隐藏 |
Group.icon | ReactNode | — | 分组图标,折叠时保留 |
Sidebar.Item | HTMLAttributes | — | 导航项;含 Sub 时变为可展开的父项 |
Item.value | string | — | 唯一值,与根组件的 active 对应 |
Item.icon | ReactNode | — | 前置图标 |
Item.badge | ReactNode | — | 右侧徽标 / 计数 |
Item.active | boolean | — | 显式指定激活态,覆盖 root 的 active 匹配 |
Item.disabled | boolean | — | 禁用后不可点击,也不参与激活 |
Item.href | string | — | 渲染为 <a> 链接 |
Item.asChild | boolean | false | 把行为合并到唯一子元素(如路由 Link)上,仅用于叶子项 |
Sidebar.Sub | HTMLAttributes | — | 子级导航容器,展开态由父级 Item 注入 |
Sidebar.SubItem | HTMLAttributes | — | 二级导航项,属性与 Item 一致 |
Sidebar.Separator | HTMLAttributes | — | 分隔线 |
Sidebar.Footer | HTMLAttributes | — | 底部区域,折叠时只保留 Trigger |
Sidebar.Trigger | HTMLAttributes | — | 折叠开关,切换 collapsed |
Trigger.asChild | boolean | false | 不渲染自带按钮,把行为合并到唯一子元素上 |
Trigger.expandLabel | string | — | 折叠时按钮的无障碍名称 |
Trigger.collapseLabel | string | — | 展开时按钮的无障碍名称 |