Sidebar
基于 UI SDK core behavior 组合非模态导航面板。
Composition
import { Sidebar, createSidebarController } from "@lenso/primitives/sidebar";
import { useState } from "react";
export function Navigation() {
const [controller] = useState(createSidebarController);
return (
<>
<button type="button" onClick={() => controller.setOpen(true)}>Open navigation</button>
<Sidebar.Root aria-label="Workspace navigation" controller={controller} defaultOpen>
<Sidebar.Trigger>Toggle navigation</Sidebar.Trigger>
<Sidebar.Panel aria-label="Workspace destinations">
<Sidebar.Header>Workspace</Sidebar.Header>
<Sidebar.Content>
<Sidebar.Menu><Sidebar.MenuItem>
<Sidebar.Item href="/" selected>Home</Sidebar.Item>
</Sidebar.MenuItem></Sidebar.Menu>
</Sidebar.Content>
<Sidebar.Footer>Account settings</Sidebar.Footer>
</Sidebar.Panel>
</Sidebar.Root>
</>
);
}框架无关的定义从 @lenso/primitives/sidebar/core 导出,使用 definePrimitive、
createChange、bindPanel 和 createFocusRecovery。React 入口仅通过
createParts 将 slot 映射成原生元素。
open、defaultOpen(默认 true)和 onOpenChange(open, details) 遵循 SDK
受控状态与取消契约。details 提供 reason、event、source 和 cancel()。
原因包括 trigger-press、escape-key 和 controller。受控组件由调用方提交状态。
每个 Root 独立。外部操作使用 createSidebarController() 的 isOpen()、
setOpen(open) 和 toggle()。多个 Root 通过原生 HTML 排布;移除旧 Group 注册表、
Provider、targetId 和单独的方向模型。dir 从 DOM 继承。
Root、Trigger、Rail 和 Panel 暴露 data-open、data-closed、data-side 以及
{ open, side } 渲染状态。Root 的 slot 为 sidebar,其他 slot 以 sidebar- 开头。
Item 默认是链接,接收 selected、nested,暴露 aria-current、data-selected
和 data-nested。Rail 需要通过内容或 aria-label 命名,不再内置英文标签。
切换按钮使用原生 Enter/Space 激活,打开面板保留焦点,Tab 遵循浏览器顺序。 面板内 Escape 请求关闭;后代已阻止的事件不重复处理。SDK panel binding 在实际关闭 后恢复焦点,focus recovery 处理聚焦面板被移除的情况。取消或未提交的受控关闭不会 转移焦点。此组件没有模态焦点限制、外部点击关闭或移动端抽屉;模态面板应组合 SDK Drawer。
@lenso/ui/sidebar 是可放在面板内的导航视觉组件,拥有自己的组合方式,并非此面板
primitive 的视觉别名。