组件
Sidebar 应用侧边栏
融合内嵌工作台布局、多级树目录、悬停槽位切换与账户状态栏的统一侧边栏组件族。
用于桌面与 Web 工作区的主导航壳层。把画布侧栏、圆角内嵌舞台、可拖拽宽度分隔条、移动端模态抽屉、多级目录树、行尾同槽位菜单切换以及底部状态与账户入口统一为一套可组合的原语。
安装
pnpm dlx shadcn@4.22.1 add http://127.0.0.1:3040/r/v0.3.0/sidebar.json首次使用请先完成 Registry 与主题接入。
组件结构与职责
| 组件 | 职责 |
|---|---|
SidebarLayout | 组织画布侧栏、圆角内嵌舞台(Inset Stage)、桌面折叠 inert 保护、窄窗浮动遮罩(overlay)与移动抽屉(SidebarDrawer) |
SidebarResizer | 垂直可拖拽分隔条(默认 220px,范围 190px–300px),支持指针捕获、方向键微调与双击复位 |
SidebarDrawer | 基于 Base UI Dialog 的移动端模态导航抽屉,支持焦点恢复与 onOpenChangeComplete 关闭后延迟动作交接 |
SidebarRoot / SidebarHeader / SidebarBody / SidebarFooter | 语义化侧栏地标与四段式纵向结构,支持桌面端 titlebarInset 原生窗口拖拽留白 |
SidebarSearchTrigger / SidebarFilterInput | 全宽命令面板触发按钮(带 ⌘K 键帽)与栏内实时筛选框(带 IME 组合输入保护与 Escape 清空) |
SidebarGroup | 统一支持可折叠分组(传入 collapsed + onToggle)与静态分组标题(省略折叠参数),右侧可挂载快捷动作 |
SidebarItem | 统一导航行:支持扁平链接、多级树状缩进(depth)与独立展开箭头(expanded)、双色徽标(neutral / brand)、桌面端悬停徽标与更多按钮同槽位无抖动切换、触屏端并排常驻以及右键上下文菜单(contextMenuContent) |
SidebarSkeleton / SidebarInlineAction | 异步目录加载占位骨架与分组内空态/错误重试内联按钮 |
SidebarStatusItem / SidebarAccountTrigger / SidebarAccountSummary | 底部同步/离线/更新状态栏、兼容单行紧凑与双行副标题的账户菜单触发器及弹出菜单顶部身份摘要卡 |
核心 Props
| 组件与属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
SidebarLayout.collapsed | boolean | false | 折叠桌面侧栏并自动标记 hidden 与 inert,防止焦点泄露到不可见导航项 |
SidebarLayout.resizable | boolean | false | 启用侧栏与舞台之间的拖拽分隔条 |
SidebarLayout.sidebarWidth / defaultSidebarWidth | number | 220 | 受控或非受控侧栏宽度,自动钳制在 minSidebarWidth(190)与 maxSidebarWidth(300)之间 |
SidebarItem.badgeTone | "neutral" | "brand" | "neutral" | neutral 用于常规条目/目录计数(静默等宽数字);brand 用于未读回执或高优待办(品牌色胶囊) |
SidebarItem.depth | number | 0 | 多级目录树缩进层级(每级递增 12px) |
SidebarItem.expanded / onToggleExpand | boolean / () => void | — | 传入后在行首渲染独立的子目录展开/折叠按钮,与主导航链接作为同级节点互不嵌套 |
SidebarItem.actions / menuOpen | ReactNode / boolean | — | 行尾更多操作区;桌面端默认预留徽标槽位,悬停/聚焦或菜单打开时平滑淡入操作按钮,文字宽度零跳动 |
SidebarItem.contextMenuContent | ReactNode | — | 为该行启用右键菜单及键盘 ContextMenu / Shift+F10 快捷唤出 |
键盘、触控与无障碍
- 禁止交互控件嵌套:
SidebarGroup的折叠触发器与右侧附加动作、SidebarItem的树展开箭头与主导航链接及行尾菜单触发器、SidebarAccountTrigger与右侧附属按钮均为同级兄弟节点,严格避免<button>或<a>互相嵌套。 - 桌面紧凑与触屏 44px 自适应:桌面精细指针(
pointer: fine)下导航行高为28px,并在右侧共享24px槽位完成徽标与操作按钮切换;在comfortable密度、窄容器(<= 480px)、移动端或粗指针(pointer: coarse)下,所有交互热区自动扩展至>= 44px,且徽标与更多按钮并排常驻显示。 - 折叠与抽屉焦点管理:侧栏折叠时设置
inert阻断 Tab 聚焦;移动抽屉关闭后若需打开二级弹窗(如命令面板或账户设置),通过onDrawerCloseComplete在抽屉退场完成后接力唤起,避免焦点抢占。
来源与边界
0.3.0 本地候选,综合提炼自 Subtic、Kiipu 与 Dooset 三个 Web 项目的八处侧边栏实现。路由跳转、文件夹增删改 API、宽度持久化存储、Electron IPC 窗口桥接及业务权限判断保留在各宿主应用中。
查看由当前源码生成的类型参考
只展示 Kitseed 自身定义的属性,原生 HTML 与 Base UI 透传属性请参阅对应上游说明。
SidebarResizer
| 属性 | 类型 | 默认值 |
|---|---|---|
value | number | — |
min? | number | 190 |
max? | number | 300 |
defaultValue? | number | 220 |
step? | number | 12 |
label? | string | "调整侧栏宽度" |
controls? | string | — |
onChange | (width: number) => void | — |
className? | string | — |
unstyled? | boolean | false |
SidebarDrawer
| 属性 | 类型 | 默认值 |
|---|---|---|
open | boolean | — |
onOpenChange | (open: boolean) => void | — |
onOpenChangeComplete? | ((open: boolean) => void) | undefined | — |
title? | string | undefined | "导航菜单" |
description? | string | undefined | — |
closeLabel? | string | undefined | "关闭导航" |
showCloseButton? | boolean | undefined | false |
finalFocus? | ComponentProps<typeof Dialog.Popup>["finalFocus"] | undefined | — |
stage? | ReactNode | undefined | — |
children | ReactNode | — |
className? | string | undefined | — |
unstyled? | boolean | undefined | false |
slotProps? | | {
backdrop?: ComponentProps<typeof Dialog.Backdrop>;
popup?: ComponentProps<typeof Dialog.Popup>;
close?: ComponentProps<typeof Dialog.Close>;
}
| undefined | {} |
SidebarLayout
| 属性 | 类型 | 默认值 |
|---|---|---|
sidebar | ReactNode | — |
children | ReactNode | — |
collapsed? | boolean | false |
overlay? | boolean | false |
onCloseOverlay? | () => void | — |
overlayCloseLabel? | string | "关闭浮动侧栏" |
inset? | boolean | true |
resizable? | boolean | false |
sidebarWidth? | number | — |
defaultSidebarWidth? | number | 220 |
minSidebarWidth? | number | 190 |
maxSidebarWidth? | number | 300 |
resizerLabel? | string | "调整侧栏宽度" |
onSidebarWidthChange? | (width: number) => void | — |
drawerOpen? | boolean | — |
onDrawerOpenChange? | (open: boolean) => void | — |
onDrawerCloseComplete? | (open: boolean) => void | — |
drawerSidebar? | ReactNode | — |
drawerTitle? | string | "导航菜单" |
className? | string | — |
sidebarClassName? | string | — |
stageClassName? | string | — |
unstyled? | boolean | false |
slotProps? | {
sidebarColumn?: DivSlot;
scrim?: ButtonSlot;
stage?: DivSlot;
} | {} |
SidebarRoot
| 属性 | 类型 | 默认值 |
|---|---|---|
label? | string | "主要导航" |
titlebarInset? | boolean | number | false |
unstyled? | boolean | false |
slotProps? | {
titlebar?: DivSlot;
} | {} |
SidebarHeader
| 属性 | 类型 | 默认值 |
|---|---|---|
brand? | ReactNode | — |
actions? | ReactNode | — |
children? | ReactNode | — |
className? | string | — |
unstyled? | boolean | false |
slotProps? | {
topRow?: DivSlot;
brandSlot?: DivSlot;
actionsSlot?: DivSlot;
} | {} |
SidebarSearchTrigger
| 属性 | 类型 | 默认值 |
|---|---|---|
label? | ReactNode | "搜索…" |
shortcut? | ReactNode | null | "⌘K" |
icon? | ReactNode | — |
active? | boolean | false |
unstyled? | boolean | false |
slotProps? | {
icon?: SpanSlot;
label?: SpanSlot;
shortcut?: SpanSlot;
} | {} |
SidebarFilterInput
| 属性 | 类型 | 默认值 |
|---|---|---|
value | string | — |
onValueChange | (value: string) => void | — |
label? | string | "筛选导航" |
clearLabel? | string | "清除筛选" |
inputRef? | Ref<HTMLInputElement> | — |
unstyled? | boolean | false |
slotProps? | {
root?: DivSlot;
icon?: SpanSlot;
input?: ComponentProps<"input"> & DataAttributes;
clearButton?: ButtonSlot;
} | {} |
SidebarBody
| 属性 | 类型 | 默认值 |
|---|---|---|
label? | string | undefined | "主要导航" |
busy? | boolean | undefined | false |
unstyled? | boolean | undefined | false |
SidebarGroup
| 属性 | 类型 | 默认值 |
|---|---|---|
label? | ReactNode | undefined | — |
collapsed? | boolean | undefined | — |
onToggle? | (() => void) | undefined | — |
action? | ReactNode | undefined | — |
as? | "div" | "section" | undefined | — |
headingAs? | "span" | "h2" | "p" | undefined | — |
headingId? | string | undefined | — |
children | ReactNode | — |
className? | string | undefined | — |
unstyled? | boolean | undefined | false |
slotProps? | | {
header?: DivSlot | undefined;
trigger?: ButtonSlot | undefined;
heading?: (SpanSlot & ComponentProps<"h2"> & ComponentProps<"p">) | undefined;
indicator?: ComponentProps<typeof CaretRight> | undefined;
actionSlot?: DivSlot | undefined;
content?: DivSlot | undefined;
}
| undefined | {} |
SidebarItemContent
| 属性 | 类型 | 默认值 |
|---|---|---|
icon? | ReactNode | undefined | — |
showIcon? | boolean | undefined | false |
label | ReactNode | — |
badge? | ReactNode | undefined | — |
badgeTone? | SidebarBadgeTone | undefined | "neutral" |
badgeLabel? | string | undefined | — |
reserveBadgeSpace? | boolean | undefined | false |
active? | boolean | undefined | false |
unstyled? | boolean | undefined | false |
slotProps? | | {
icon?: SpanSlot | undefined;
label?: SpanSlot | undefined;
badgeSlot?: SpanSlot | undefined;
badge?: SpanSlot | undefined;
announcement?: SpanSlot | undefined;
}
| undefined | {} |
SidebarItem
| 属性 | 类型 | 默认值 |
|---|---|---|
icon? | ReactNode | undefined | — |
activeIcon? | ReactNode | undefined | — |
label | ReactNode | — |
active? | boolean | undefined | false |
disabled? | boolean | undefined | false |
badge? | ReactNode | undefined | — |
badgeTone? | SidebarBadgeTone | undefined | "neutral" |
badgeLabel? | string | undefined | — |
reserveBadgeSpace? | boolean | undefined | — |
depth? | number | undefined | 0 |
expanded? | boolean | undefined | — |
expandLabel? | string | undefined | — |
onToggleExpand? | (() => void) | undefined | — |
actions? | ReactNode | undefined | — |
menuOpen? | boolean | undefined | false |
contextMenuContent? | ReactNode | undefined | — |
contextMenuPopup? | ReactNode | undefined | — |
href? | string | undefined | — |
onClick? | (ComponentProps<"button">["onClick"] & ComponentProps<"a">["onClick"]) | undefined | — |
render? | useRender.RenderProp<{ active: boolean; disabled: boolean }> | undefined | — |
className? | string | undefined | — |
unstyled? | boolean | undefined | false |
slotProps? | | {
row?: DivSlot | undefined;
disclosure?: ButtonSlot | undefined;
primary?: (ComponentProps<"button"> & ComponentProps<"a"> & DataAttributes) | undefined;
content?: SpanSlot | undefined;
icon?: SpanSlot | undefined;
label?: SpanSlot | undefined;
badgeSlot?: SpanSlot | undefined;
badge?: SpanSlot | undefined;
announcement?: SpanSlot | undefined;
actionsSlot?: DivSlot | undefined;
}
| undefined | {} |
SidebarSkeleton
| 属性 | 类型 | 默认值 |
|---|---|---|
rows? | number | 3 |
label? | string | "正在加载导航…" |
className? | string | — |
unstyled? | boolean | false |
SidebarInlineAction
| 属性 | 类型 | 默认值 |
|---|---|---|
icon? | ReactNode | — |
tone? | "neutral" | "danger" | "neutral" |
unstyled? | boolean | false |
SidebarFooter
| 属性 | 类型 | 默认值 |
|---|---|---|
unstyled? | boolean | false |
SidebarStatusItem
| 属性 | 类型 | 默认值 |
|---|---|---|
tone? | SidebarStatusTone | undefined | "neutral" |
pulse? | boolean | undefined | false |
icon? | ReactNode | undefined | — |
label | ReactNode | — |
meta? | ReactNode | undefined | — |
busy? | boolean | undefined | false |
role? | ComponentProps<"div">["role"] | undefined | — |
"aria-label"? | string | undefined | — |
title? | string | undefined | — |
onClick? | ComponentProps<"button">["onClick"] | undefined | — |
className? | string | undefined | — |
unstyled? | boolean | undefined | false |
slotProps? | | {
indicator?: SpanSlot | undefined;
label?: SpanSlot | undefined;
meta?: SpanSlot | undefined;
}
| undefined | {} |
SidebarAccountTrigger
| 属性 | 类型 | 默认值 |
|---|---|---|
avatar | ReactNode | — |
name | ReactNode | — |
subtitle? | ReactNode | — |
indicator? | "caret" | "up-down" | "dots" | "none" | "caret" |
trailingAction? | ReactNode | — |
render? | useRender.RenderProp<Record<string, never>> | — |
unstyled? | boolean | false |
slotProps? | {
row?: DivSlot;
avatar?: SpanSlot;
text?: SpanSlot;
name?: SpanSlot;
subtitle?: SpanSlot;
indicator?: SpanSlot & ComponentProps<typeof CaretDown>;
trailing?: DivSlot;
} | {} |
SidebarAccountSummary
| 属性 | 类型 | 默认值 |
|---|---|---|
avatar | ReactNode | — |
name | ReactNode | — |
email? | ReactNode | — |
badge? | ReactNode | — |
className? | string | — |
unstyled? | boolean | — |
slotProps? | {
avatar?: SpanSlot;
body?: DivSlot;
titleRow?: DivSlot;
name?: SpanSlot & ComponentProps<"p">;
email?: SpanSlot & ComponentProps<"p">;
} | {} |