Sidebar 侧边栏
@lark-ui/core
主要内容区域
<Sidebar.Provider defaultOpen>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={GlobeIcon}>Domains</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

安装

统一导出

import { Sidebar } from "@lark-ui/core";

按需导出

import { Sidebar } from "@lark-ui/core/components/sidebar";

用法

最少需要 Provider、Sidebar、Content(可滚动区域)、Menu 和MenuButton。 添加 Header /Footer 可将内容固定在滚动区域的上方或下方。 使用 Group +GroupLabel 组织各个分区。

import { Sidebar } from "@lark-ui/core";
import { HouseIcon, CodeIcon, GearIcon } from "@phosphor-icons/react";

function AppLayout({ children }) {
  return (
    <Sidebar.Provider defaultOpen>
      <Sidebar>
        <Sidebar.Content>
          <Sidebar.Group>
            <Sidebar.GroupLabel>Navigation</Sidebar.GroupLabel>
            <Sidebar.Menu>
              <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
              {/* MenuItem only needed to wrap Collapsible */}
              <Sidebar.MenuItem>
                <Sidebar.Collapsible>
                  <Sidebar.CollapsibleTrigger
                    render={
                      <Sidebar.MenuButton icon={CodeIcon}>
                        Compute <Sidebar.MenuChevron />
                      </Sidebar.MenuButton>
                    }
                  />
                  <Sidebar.CollapsibleContent>
                    <Sidebar.MenuSub>
                      <Sidebar.MenuSubButton>Workers</Sidebar.MenuSubButton>
                    </Sidebar.MenuSub>
                  </Sidebar.CollapsibleContent>
                </Sidebar.Collapsible>
              </Sidebar.MenuItem>
            </Sidebar.Menu>
          </Sidebar.Group>
        </Sidebar.Content>
        <Sidebar.Footer>
          <Sidebar.Trigger />
        </Sidebar.Footer>
      </Sidebar>
      <div className="flex-1">{children}</div>
    </Sidebar.Provider>
  );
}

示例

基础

最小可用的侧边栏:只有分组、菜单按钮和可折叠子菜单,没有页眉和页脚。MenuButton 和 MenuSubButton 会自动包裹在 <li> 中—无需 MenuItem / MenuSubItem。

主要内容区域
<Sidebar.Provider defaultOpen>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={ChartBarIcon}>Analytics</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

切换与折叠状态

在页脚中使用 Sidebar.Trigger,或以编程方式调用 useSidebar().toggleSidebar。传入 tooltip 可在折叠状态下悬停时显示标签。

点击按钮或侧边栏触发器可切换

<Sidebar.MenuButton icon={HouseIcon} tooltip="Home" active>
  Home
</Sidebar.MenuButton>

<Sidebar.Footer>
  <Sidebar.Trigger />
</Sidebar.Footer>

// Or programmatically:
const { toggleSidebar } = useSidebar();

加载中

Sidebar.Loading 会在路由和权限加载完成之前,以导航项形状的骨架行代替导航内容。折叠状态下只保留图标方块。它会遵循减少动态效果的偏好设置。

切换以对比加载状态与已加载的导航

<Sidebar>
  <Sidebar.Header>…</Sidebar.Header>
  {isLoading ? (
    <Sidebar.Loading />
  ) : (
    <Sidebar.Content>…</Sidebar.Content>
  )}
  <Sidebar.Footer>…</Sidebar.Footer>
</Sidebar>

可调整大小

拖动边缘即可调整大小。拖动到 minWidth 以下会折叠;从折叠状态向外拖动则展开。 调整手柄支持键盘操作:可使用方向键、Home 和 End。

拖动侧边栏边缘可调整大小

<Sidebar.Provider defaultOpen resizable defaultWidth={240} minWidth={180} maxWidth={400}>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
    <Sidebar.ResizeHandle />
  </Sidebar>
</Sidebar.Provider>

右侧

使用 side="right" 可将侧边栏置于右边缘。在 DOM 中需将 <main> 放在 <Sidebar> 之前。

主要内容区域
<Sidebar.Provider defaultOpen side="right">
  <main className="flex-1">...</main>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Details</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={GearIcon} active>Properties</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={ChartBarIcon}>Metrics</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

临时展开

在 Provider 上设置 peekable。侧边栏折叠时,悬停或聚焦会临时将其展开;移开后会重新折叠。 临时展开期间,data-state 属性为 "peeking"。

状态:展开

先折叠,然后悬停侧边栏即可预览

<Sidebar.Provider defaultOpen peekable>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
    <Sidebar.Footer>
      <Sidebar.Trigger />
    </Sidebar.Footer>
  </Sidebar>
</Sidebar.Provider>

// Read peeking state:
const { state, isPeeking } = useSidebar();
// state: "expanded" | "collapsed" | "peeking"

自动滚动

在较长的可折叠分区上使用 autoScrollOnOpen,可让新展开的内容保持在可视区域内。 当可滚动侧边栏靠下的分组展开到可视区域之外时,这一点尤为有用。

打开列表底部附近的 Workers

<Sidebar.Collapsible autoScrollOnOpen>
  <Sidebar.CollapsibleTrigger
    render={
      <Sidebar.MenuButton icon={CodeIcon}>
        Workers <Sidebar.MenuChevron />
      </Sidebar.MenuButton>
    }
  />
  <Sidebar.CollapsibleContent>
    <Sidebar.MenuSub>...</Sidebar.MenuSub>
  </Sidebar.CollapsibleContent>
</Sidebar.Collapsible>

过渡完成

当某些工作必须等待侧边栏或折叠面板的过渡结束后再执行时(例如测量布局或滚动到新展开的内容),请使用 onOpenChangeComplete。该回调会收到过渡完成后的展开状态;在减少动态效果导致过渡被禁用时同样会触发。当侧边栏或移动端抽屉显示/隐藏已展开的分区时,Sidebar.Collapsible 也会完成过渡,因此回调始终与用户看到的状态一致。

尚未完成任何过渡。

切换侧边栏或打开「计算」以查看完成回调。

const [completion, setCompletion] = useState("");

<Sidebar.Provider
  defaultOpen
  onOpenChangeComplete={(open) => {
    setCompletion("Sidebar " + (open ? "opened." : "collapsed."));
  }}
>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Menu>
        <Sidebar.MenuItem>
          <Sidebar.Collapsible
            onOpenChangeComplete={(open) => {
              setCompletion(
                "Compute section " + (open ? "opened." : "collapsed."),
              );
            }}
          >
            <Sidebar.CollapsibleTrigger render={<Sidebar.MenuButton>Compute</Sidebar.MenuButton>} />
            <Sidebar.CollapsibleContent>...</Sidebar.CollapsibleContent>
          </Sidebar.Collapsible>
        </Sidebar.MenuItem>
      </Sidebar.Menu>
    </Sidebar.Content>
    <Sidebar.Footer><Sidebar.Trigger /></Sidebar.Footer>
  </Sidebar>
</Sidebar.Provider>

滚动到条目

通过 itemId 标记导航项,然后调用 useSidebar().scrollToItem(id, options) 将其滚动到可视区域。 当目标条目已完全可见时,使用 scrollItemIntoView(id, options) 可保持当前位置不变。align 可取 "start"、"center"、"end" 或 "auto"(默认——可见时不做任何处理)。behavior 默认为 "auto"(瞬时),这样从其他应用跳转过来时不会带动画;应用内“跳转到某个分区”的场景请传入 "smooth"。它遵循 prefers-reduced-motion。与 Sidebar.SlidingViews 配合使用时也能正常工作—条目是从每个 Provider 各自的注册表中解析的,而不是通过 DOM 查询。

itemId 在两种用法下都有效:直接加在 Sidebar.MenuButton 上(常见用法,会自动包裹在 <li> 中),或者在需要包裹 Collapsible 时加在 Sidebar.MenuItem 上。如果把 itemId 放在嵌套于 MenuItem 内部的 MenuButton 上,同样可以工作—此时按钮本身会成为滚动目标。

跳转到任意带标记的条目。

<Sidebar.MenuButton itemId="zero-trust" href="/zt">
  Zero Trust
</Sidebar.MenuButton>

// elsewhere:
const { scrollToItem } = useSidebar();
scrollToItem("zero-trust", { align: "center", behavior: "smooth" });

滑动视图

使用 Sidebar.SlidingViews 和 Sidebar.SlidingView可在导航界面之间实现带动画的横向切换(例如账户 ↔ 区域)。 未激活的视图会自动标记 aria-hidden 和 inert。 动画会遵循 prefers-reduced-motion。

当前:账号界面

点击顶部按钮可在视图之间切换

const [surface, setSurface] = useState("account");

<Sidebar.SlidingViews activeKey={surface} direction="left">
  <Sidebar.SlidingView value="account">
    <Sidebar.Content>...account nav...</Sidebar.Content>
  </Sidebar.SlidingView>
  <Sidebar.SlidingView value="zone">
    <Sidebar.Content>...zone nav...</Sidebar.Content>
  </Sidebar.SlidingView>
</Sidebar.SlidingViews>

完整示例

一个「大而全」的示例,展示了所有子组件:带账户切换器的页眉、带标签的分组、含嵌套展开项的可折叠分区、徽标、滑动视图,以及页脚触发器。

主要内容区域
<Sidebar>
  <Sidebar.Header>
    <AccountSwitcher />
  </Sidebar.Header>
  <Sidebar.Content>
    <Sidebar.Group>
      <Sidebar.Menu>
        <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
      </Sidebar.Menu>
    </Sidebar.Group>
    <Sidebar.Group>
      <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
      <Sidebar.Menu>
        <Sidebar.MenuItem>
          <Sidebar.Collapsible defaultOpen>
            <Sidebar.CollapsibleTrigger
              render={
                <Sidebar.MenuButton icon={CodeIcon}>
                  Compute <Sidebar.MenuChevron />
                </Sidebar.MenuButton>
              }
            />
            <Sidebar.CollapsibleContent>
              <Sidebar.MenuSub>
                <Sidebar.MenuSubButton>
                  Containers <Sidebar.MenuBadge>Beta</Sidebar.MenuBadge>
                </Sidebar.MenuSubButton>
              </Sidebar.MenuSub>
            </Sidebar.CollapsibleContent>
          </Sidebar.Collapsible>
        </Sidebar.MenuItem>
      </Sidebar.Menu>
    </Sidebar.Group>
  </Sidebar.Content>
  <Sidebar.Footer>
    <Sidebar.Trigger />
  </Sidebar.Footer>
</Sidebar>

移动端

在窄视口中,侧边栏会渲染为导航抽屉。使用 mobileBreakpoint 可控制断点阈值。 抽屉关闭时带有 inert 和 aria-hidden,打开时会将焦点移入其中,并支持按 Esc 关闭。 本示例通过设置较大的断点值强制进入移动端模式。

点击按钮打开移动端侧边栏

按 Esc 键或点击遮罩层关闭

<Sidebar.Provider mobileBreakpoint={9999}>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

全屏移动端

传入 fullScreenOnMobile 可让抽屉覆盖整个视口,而不是只露出一小条页面内容。 导航项会获得更舒适的触控区域,并且由于面板后面没有内容,遮罩层会被抑制。 在页眉内添加 Sidebar.Close,这样就可以在导航内部关闭面板。 将它与页面头部中的面包屑导航搭配使用,这样导航关闭后当前路由依然可见。

账号分析

在导航中逐层进入 —— 面包屑由导航树推导,因此始终与你当前所在的位置一致。

<Sidebar.Provider mobileBreakpoint={9999}>
  <Sidebar fullScreenOnMobile>
    <Sidebar.Header>
      <Sidebar.Close />
    </Sidebar.Header>
    <Sidebar.Content>...</Sidebar.Content>
  </Sidebar>

  <header>
    <Sidebar.Trigger />
    <Breadcrumbs size="sm">
      <Breadcrumbs.Link href="/">Company</Breadcrumbs.Link>
      <Breadcrumbs.Separator />
      <Breadcrumbs.Current>Analytics</Breadcrumbs.Current>
    </Breadcrumbs>
  </header>
</Sidebar.Provider>

API 参考

Sidebar

主侧边栏容器。在桌面端渲染为 <aside>,在移动端渲染为导航抽屉。

属性类型默认值说明
defaultOpenboolean-非受控模式下的初始展开状态。
openboolean-受控的展开状态。
variant"sidebar" | "floating" | "inset""sidebar"侧边栏的布局变体。
side"left" | "right""left"侧边栏位于哪一侧。
collapsible"icon" | "offcanvas" | "none""icon"-
resizableboolean-启用侧边栏边缘的拖拽调整宽度。
defaultWidthnumber-可调整宽度时的初始宽度,单位为像素。
minWidthnumber-调整宽度时的最小宽度,单位为像素。
maxWidthnumber-调整宽度时的最大宽度,单位为像素。
containedboolean-为 true 时,收起的侧边栏使用绝对定位而不是固定定位,作用范围限制在有边界的父容器内。适合用于演示和嵌入式侧边栏。
peekableboolean-为 true 时,鼠标悬停或键盘聚焦收起的侧边栏会临时展开它。展开预览期间,`state` 为 `"peeking"`。移开之后会再次收起。
animationDurationnumber-侧边栏展开/收起动画的时长,单位为毫秒。
mobileBreakpointnumber-视口宽度(单位为 px)低于该值时,侧边栏会渲染为移动端对话框面板,而不是桌面端的侧边栏轨道。
childrenReactNode-内容——通常是 `<Sidebar>` 加上主体内容。
classNamestring-外层 div 的额外 CSS 类名。

Sidebar.Provider

Context Provider,用于管理展开/折叠状态和移动端检测。

属性类型默认值说明
defaultOpenboolean-非受控模式下的初始展开状态。
openboolean-受控的展开状态。
variantSidebarVariant-侧边栏的布局变体。
sideSidebarSide-侧边栏位于哪一侧。
collapsible"icon" | "offcanvas" | "none"--
resizableboolean-启用侧边栏边缘的拖拽调整宽度。
defaultWidthnumber-可调整宽度时的初始宽度,单位为像素。
minWidthnumber-调整宽度时的最小宽度,单位为像素。
maxWidthnumber-调整宽度时的最大宽度,单位为像素。
containedboolean-为 true 时,收起的侧边栏使用绝对定位而不是固定定位,作用范围限制在有边界的父容器内。适合用于演示和嵌入式侧边栏。
peekableboolean-为 true 时,鼠标悬停或键盘聚焦收起的侧边栏会临时展开它。展开预览期间,`state` 为 `"peeking"`。移开之后会再次收起。
animationDurationnumber-侧边栏展开/收起动画的时长,单位为毫秒。
mobileBreakpointnumber-视口宽度(单位为 px)低于该值时,侧边栏会渲染为移动端对话框面板,而不是桌面端的侧边栏轨道。
children*ReactNode-内容——通常是 `<Sidebar>` 加上主体内容。
classNamestring-外层 div 的额外 CSS 类名。

Sidebar.Content

中间的可滚动区域(flex-1 overflow-y-auto)。使用 Header / Footer 可将内容固定在该滚动区域的上方或下方。

Sidebar.MenuButton

主要的交互元素。支持图标、激活状态、链接,以及折叠状态下的自动工具提示。 会自动包裹在 <li> 中—除非需要包裹 Collapsible,否则无需 MenuItem 包装层。

属性类型默认值说明
iconReact.ComponentType<{ className?: string }> | React.ReactNode--
activeboolean--
sizeSidebarMenuButtonSize-按钮尺寸。 - `"base"` — 标准导航项 - `"sm"` — 紧凑导航项
hrefstring--
targetReact.HTMLAttributeAnchorTarget-链接的 target——仅在提供了 `href` 时才有意义。
tooltipstring--
itemIdstring-`useSidebar().scrollToItem(id)` 使用的锚点 id。
classNamestring--
childrenReactNode--

Sidebar.MenuSubButton

子菜单内部的按钮,用于嵌套导航。 会自动包裹在 <li> 中—无需 MenuSubItem 包装层。

属性类型默认值说明
activeboolean-将该子项标记为当前激活/选中。
hrefstring-跳转地址。设置后通过 LinkProvider 渲染为链接。
targetReact.HTMLAttributeAnchorTarget-链接的 target——仅在提供了 `href` 时才有意义。