侧边栏

一个可组合、支持主题定制和自定义的侧边栏组件。

一个可折叠为图标的侧边栏。

侧边栏是构建起来最复杂的组件之一。它们是任何应用程序的核心,通常包含许多活动部件。

我不喜欢构建侧边栏。所以我构建了 30 多个。各种配置都有。然后我将核心组件提取到 Sidebar*.vue 中。

我们现在有了一个坚实的基础来构建。可组合。支持主题。可定制。

浏览区块库

安装

安装此组件

pnpm dlx shadcn-vue@latest add sidebar

将以下颜色添加到您的 CSS 文件中

上述命令应为您安装颜色。如果没有,请在您的 CSS 文件中复制并粘贴以下内容。

css
@layer base {
  :root {
    --sidebar-background: 0 0% 98%;
    --sidebar-foreground: 240 5.3% 26.1%;
    --sidebar-primary: 240 5.9% 10%;
    --sidebar-primary-foreground: 0 0% 98%;
    --sidebar-accent: 240 4.8% 95.9%;
    --sidebar-accent-foreground: 240 5.9% 10%;
    --sidebar-border: 220 13% 91%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }

  .dark {
    --sidebar-background: 240 5.9% 10%;
    --sidebar-foreground: 240 4.8% 95.9%;
    --sidebar-primary: 224.3 76.3% 48%;
    --sidebar-primary-foreground: 0 0% 100%;
    --sidebar-accent: 240 3.7% 15.9%;
    --sidebar-accent-foreground: 240 4.8% 95.9%;
    --sidebar-border: 240 3.7% 15.9%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
}

结构

一个 Sidebar 组件由以下部分组成:

  • SidebarProvider - 处理可折叠状态。
  • Sidebar - 侧边栏容器。
  • SidebarHeader 和 SidebarFooter - 固定在侧边栏的顶部和底部
  • SidebarContent - 可滚动内容。
  • SidebarGroup - SidebarContent 内的部分。
  • SidebarTrigger - 侧边栏的触发器

侧边栏结构

用法

App.vue
vue
<script setup lang="ts">
import AppSidebar from '@/components/AppSidebar.vue'
import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
</script>

<template>
  <SidebarProvider>
    <AppSidebar />
    <main>
      <SidebarTrigger />
      <RouterView />
    </main>
  </SidebarProvider>
</template>
@/components/AppSidebar.vue
vue
<script setup lang="ts">
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarHeader,
} from '@/components/ui/sidebar'
</script>

<template>
  <Sidebar>
    <SidebarHeader />
    <SidebarContent>
      <SidebarGroup />
      <SidebarGroup />
    </SidebarContent>
    <SidebarFooter />
  </Sidebar>
</template>

您的第一个侧边栏

让我们从最基本的侧边栏开始:一个带有菜单的可折叠侧边栏。

在应用程序的根目录添加 SidebarProviderSidebarTrigger

src/pages/index.vue
vue
<script setup lang="ts">
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import AppSidebar from "@/components/AppSidebar.vue";
</script>

<template>
  <SidebarProvider>
    <AppSidebar />
    <main>
      <SidebarTrigger />
      <slot />
    </main>
  </SidebarProvider>
</template>

@/components/AppSidebar.vue 创建一个新的侧边栏组件。

@/components/AppSidebar.vue
vue
<script setup lang="ts">
import { Sidebar, SidebarContent } from "@/components/ui/sidebar";
</script>

<template>
  <Sidebar>
    <SidebarContent />
  </Sidebar>
</template>

现在,让我们向侧边栏添加一个 SidebarMenu

我们将在 SidebarGroup 中使用 SidebarMenu 组件。

@/components/AppSidebar.vue
vue
<script setup lang="ts">
import { Calendar, Home, Inbox, Search, Settings } from "lucide-vue-next"
import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/components/ui/sidebar"

// 菜单项。
const items = [
  {
    title: "首页",
    url: "#",
    icon: Home,
  },
  {
    title: "收件箱",
    url: "#",
    icon: Inbox,
  },
  {
    title: "日历",
    url: "#",
    icon: Calendar,
  },
  {
    title: "搜索",
    url: "#",
    icon: Search,
  },
  {
    title: "设置",
    url: "#",
    icon: Settings,
  },
];
</script>

<template>
  <Sidebar>
    <SidebarContent>
      <SidebarGroup>
        <SidebarGroupLabel>应用程序</SidebarGroupLabel>
        <SidebarGroupContent>
          <SidebarMenu>
              <SidebarMenuItem v-for="item in items" :key="item.title">
                <SidebarMenuButton asChild>
                    <a :href="item.url">
                      <component :is="item.icon" />
                      <span>{{item.title}}</span>
                    </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
          </SidebarMenu>
        </SidebarGroupContent>
      </SidebarGroup>
    </SidebarContent>
  </Sidebar>
</template>

您已创建了第一个侧边栏

您的第一个侧边栏

组件

Sidebar*.vue 文件中的组件被设计为可组合的,即您通过将提供的组件组合在一起来构建侧边栏。它们也能很好地与其他 shadcn-vue 组件(如 DropdownMenuCollapsibleDialog 等)组合使用。

如果您需要更改 Sidebar*.vue 文件中的代码,我们鼓励您这样做。代码是您的。使用提供的组件作为起点来构建您自己的组件。

在接下来的部分中,我们将介绍每个组件及其使用方法。

SidebarProvider

SidebarProvider 组件用于向 Sidebar 组件提供侧边栏上下文。您应始终将您的应用程序包装在 SidebarProvider 组件中。

Props

名称类型描述
defaultOpenboolean侧边栏的默认打开状态。
openboolean侧边栏的打开状态(受控)。
onOpenChange(open: boolean) => void设置侧边栏的打开状态(受控)。

宽度

如果您的应用程序中只有一个侧边栏,您可以使用 @/components/ui/sidebar/utils.ts 中的 SIDEBAR_WIDTHSIDEBAR_WIDTH_MOBILE 常量来设置侧边栏的宽度。

@/components/ui/sidebar/utils.ts
ts
export const SIDEBAR_WIDTH = "16rem";
export const SIDEBAR_WIDTH_MOBILE = "18rem";

对于应用程序中的多个侧边栏,您可以使用 style prop 来设置侧边栏的宽度。

要设置侧边栏的宽度,您可以在 style prop 中使用 --sidebar-width--sidebar-width-mobile CSS 变量。

vue
<template>
  <SidebarProvider
    style="--sidebar-width: 20rem; --sidebar-width-mobile: 20rem;"
  >
    <Sidebar />
  </SidebarProvider>
</template>

这不仅会处理侧边栏的宽度,还会处理布局间距。

键盘快捷键

@/components/ui/sidebar/utils.ts 中的 SIDEBAR_KEYBOARD_SHORTCUT 变量用于设置打开和关闭侧边栏的键盘快捷键。

要触发侧边栏,您在 Mac 上使用 cmd+b 键盘快捷键,在 Windows 上使用 ctrl+b

您可以通过更改 SIDEBAR_KEYBOARD_SHORTCUT 变量的值来更改键盘快捷键。

@/components/ui/sidebar/utils.ts
ts
export const SIDEBAR_KEYBOARD_SHORTCUT = "b";

持久化状态

SidebarProvider 支持在页面重新加载和服务器端渲染之间持久化侧边栏状态。它使用 cookie 来存储侧边栏的当前状态。当侧边栏状态更改时,会设置一个名为 sidebar_state 的默认 cookie,其中包含当前的打开/关闭状态。然后在后续页面加载时读取此 cookie 以恢复侧边栏状态。

要在 SSR 中持久化侧边栏状态,请在 App.vue 中像这样设置您的 SidebarProvider

App.vue
vue
<!-- 使用 Nuxt -->
<script setup lang="ts">
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import AppSidebar from "@/components/AppSidebar.vue"

const defaultOpen = useCookie<boolean>("sidebar_state");
</script>

<template>
  <SidebarProvider :defaultOpen="defaultOpen">
    <AppSidebar />
    <main>
      <SidebarTrigger />
      <RouterView />  <!-- 或 <slot /> -->
    </main>
  </SidebarProvider>
</template>

您可以通过更新 sidebar/utils.ts 中的 SIDEBAR_COOKIE_NAME 变量来更改 cookie 的名称。

@/components/ui/sidebar/utils.ts
ts
export const SIDEBAR_COOKIE_NAME = "sidebar_state"

主要的 Sidebar 组件,用于渲染可折叠侧边栏。

vue
<script setup lang="ts">
import { Sidebar } from "@/components/ui/sidebar";
</script>

<template>
  <Sidebar />
</template>

Props

属性类型描述
sideleftright侧边栏的位置
variantsidebar, floating, 或 inset侧边栏的变体
collapsibleoffcanvas, icon, 或 none侧边栏的可折叠状态

side

使用 side prop 更改侧边栏的位置。

可用选项为 leftright

vue
<Sidebar side="left | right" />

variant

使用 variant prop 更改侧边栏的变体。

可用选项为 sidebarfloatinginset

vue
<Sidebar variant="sidebar | floating | inset" />
vue
<template>
  <SidebarProvider>
    <Sidebar variant="inset">
      <SidebarInset>
        <main>
          <slot />
        </main>
      </SidebarInset>
    </Sidebar>
  </SidebarProvider>
</template>

collapsible

使用 collapsible prop 使侧边栏可折叠。

可用选项为 offcanvasiconnone

vue
<Sidebar collapsible="offcanvas | icon | none" />
Prop描述
offcanvas一个可折叠的侧边栏,从左侧或右侧滑入。
icon一个可折叠为图标的侧边栏。
none一个不可折叠的侧边栏。

useSidebar

useSidebar 组合式函数用于控制侧边栏。

vue
<script setup lang="ts">
import { useSidebar } from "@/components/ui/sidebar";

const {
  state,
  open,
  setOpen,
  openMobile,
  setOpenMobile,
  isMobile,
  toggleSidebar,
} = useSidebar()
</script>
属性类型描述
stateexpandedcollapsed侧边栏的当前状态。
openboolean侧边栏是否打开。
setOpen(open: boolean) => void设置侧边栏的打开状态。
openMobileboolean侧边栏在移动设备上是否打开。
setOpenMobile(open: boolean) => void设置侧边栏在移动设备上的打开状态。
isMobileboolean侧边栏是否在移动设备上。
toggleSidebar() => void切换侧边栏。桌面和移动设备均适用。

SidebarHeader

使用 SidebarHeader 组件向侧边栏添加粘性标题。

以下示例向 SidebarHeader 添加了一个 <DropdownMenu>

带有下拉菜单的侧边栏标题。
@/components/AppSidebar.vue
vue
<template>
  <Sidebar>
    <SidebarHeader>
      <SidebarMenu>
        <SidebarMenuItem>
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
                <SidebarMenuButton>
                  选择工作区
                  <ChevronDown class="ml-auto" />
                </SidebarMenuButton>
            </DropdownMenuTrigger>
            <DropdownMenuContent class="w-[--bits-dropdown-menu-anchor-width]">
              <DropdownMenuItem>
                <span>Acme Inc</span>
              </DropdownMenuItem>
              <DropdownMenuItem>
                <span>Acme Corp.</span>
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </SidebarMenuItem>
      </SidebarMenu>
    </SidebarHeader>
  </Sidebar>
</template>

SidebarFooter

使用 SidebarFooter 组件向侧边栏添加粘性页脚。

以下示例向 SidebarFooter 添加了一个 <DropdownMenu>

带有下拉菜单的侧边栏页脚。
@/components/AppSidebar.vue
vue
<template>
  <SidebarProvider>
    <Sidebar>
      <SidebarHeader />
      <SidebarContent />
      <SidebarFooter>
        <SidebarMenu>
          <SidebarMenuItem>
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <SidebarMenuButton>
                  <User2 /> 用户名
                  <ChevronUp class="ml-auto" />
                </SidebarMenuButton>
              </DropdownMenuTrigger>
              <DropdownMenuContent
                side="top"
                class="w-[--reka-popper-anchor-width]"
              >
                <DropdownMenuItem>
                  <span>账户</span>
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <span>账单</span>
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <span>退出登录</span>
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </SidebarMenuItem>
        </SidebarMenu>
      </SidebarFooter>
    </Sidebar>
  </SidebarProvider>
</template>

SidebarContent

SidebarContent 组件用于包装侧边栏的内容。这是您添加 SidebarGroup 组件的地方。它是可滚动的。

vue
<template>
  <Sidebar>
    <SidebarContent>
      <SidebarGroup />
      <SidebarGroup />
    </SidebarContent>
  </Sidebar>
</template>

SidebarGroup

使用 SidebarGroup 组件在侧边栏内创建一个部分。

一个 SidebarGroup 有一个 SidebarGroupLabel、一个 SidebarGroupContent 和一个可选的 SidebarGroupAction

一个侧边栏组。
vue
<template>
  <Sidebar>
    <SidebarContent>
      <SidebarGroup>
        <SidebarGroupLabel>应用程序</SidebarGroupLabel>
        <SidebarGroupAction>
          <Plus /> <span class="sr-only">添加项目</span>
        </SidebarGroupAction>
        <SidebarGroupContent></SidebarGroupContent>
      </SidebarGroup>
    </SidebarContent>
  </Sidebar>
</template>

可折叠的 SidebarGroup

要使 SidebarGroup 可折叠,请将其包装在 Collapsible 中。

一个可折叠的侧边栏组。
vue
<template>
  <Collapsible defaultOpen class="group/collapsible">
    <SidebarGroup>
      <SidebarGroupLabel asChild>
        <CollapsibleTrigger>
          帮助
          <ChevronDown class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
        </CollapsibleTrigger>
      </SidebarGroupLabel>
      <CollapsibleContent>
        <SidebarGroupContent />
      </CollapsibleContent>
    </SidebarGroup>
  </Collapsible>
</template>

SidebarGroupAction

使用 SidebarGroupAction 组件向 SidebarGroup 添加操作。

vue
<template>
  <SidebarGroup>
    <SidebarGroupLabel>项目</SidebarGroupLabel>
    <SidebarGroupAction title="添加项目">
      <Plus /> <span class="sr-only">添加项目</span>
    </SidebarGroupAction>
    <SidebarGroupContent />
  </SidebarGroup>
</template>
一个带有操作按钮的侧边栏组。

SidebarMenu

SidebarMenu 组件用于在 SidebarGroup 内构建菜单。

一个 SidebarMenuSidebarMenuItemSidebarMenuButtonSidebarMenuActionSidebarMenuSub 组件组成。

侧边栏菜单

以下是渲染项目列表的 SidebarMenu 组件示例。

带有项目列表的侧边栏菜单。
vue
<template>
<Sidebar>
  <SidebarContent>
    <SidebarGroup>
      <SidebarGroupLabel>项目</SidebarGroupLabel>
      <SidebarGroupContent>
        <SidebarMenu>
            <SidebarMenuItem v-for="project in projects" :key="project.name">
              <SidebarMenuButton asChild>
                <a :href="project.url">
                  <component :is="project.icon" />
                  <span>{{project.name}}</span>
                </a>
              </SidebarMenuButton>
            </SidebarMenuItem>
        </SidebarMenu>
      </SidebarGroupContent>
    </SidebarGroup>
  </SidebarContent>
</Sidebar>
</template>

SidebarMenuButton

SidebarMenuButton 组件用于在 SidebarMenuItem 内渲染菜单按钮。

链接或锚点

默认情况下,SidebarMenuButton 渲染一个按钮,但您可以使用 asChild prop 来渲染不同的组件,例如 <a> 标签。

vue
<template>
  <SidebarMenuButton asChild>
    <a href="#">首页</a>
  </SidebarMenuButton>
</template>

图标和标签

您可以在按钮内渲染一个图标和一个被截断的标签。记得将标签包装在 <span> 标签中。

vue
<template>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>首页</span>
    </a>
  </SidebarMenuButton>
</template>

isActive

使用 isActive prop 将菜单项标记为活动状态。

vue
<template>
  <SidebarMenuButton asChild isActive>
    <a href="#">首页</a>
  </SidebarMenuButton>
</template>

SidebarMenuAction

SidebarMenuAction 组件用于在 SidebarMenuItem 内渲染菜单操作。

此按钮独立于 SidebarMenuButton 工作,即您可以将 SidebarMenuButton 作为可点击链接,而将 SidebarMenuAction 作为按钮。

vue
<template>
  <SidebarMenuItem>
    <SidebarMenuButton asChild>
      <a href="#">
        <Home />
        <span>首页</span>
      </a>
    </SidebarMenuButton>
    <SidebarMenuAction>
      <Plus /> <span class="sr-only">添加项目</span>
    </SidebarMenuAction>
  </SidebarMenuItem>
</template>

以下是渲染 DropdownMenuSidebarMenuAction 组件示例。

带有下拉菜单的侧边栏菜单操作。
vue
<template>
<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>首页</span>
    </a>
  </SidebarMenuButton>
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <SidebarMenuAction>
        <MoreHorizontal />
      </SidebarMenuAction>
    </DropdownMenuTrigger>
    <DropdownMenuContent side="right" align="start">
      <DropdownMenuItem>
        <span>编辑项目</span>
      </DropdownMenuItem>
      <DropdownMenuItem>
        <span>删除项目</span>
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</SidebarMenuItem>
</template>

SidebarMenuSub

SidebarMenuSub 组件用于在 SidebarMenu 内渲染子菜单。

使用 SidebarMenuSubItemSidebarMenuSubButton 来渲染子菜单项。

侧边栏子菜单。
vue
<template>
  <SidebarMenuItem>
    <SidebarMenuButton />
    <SidebarMenuSub>
      <SidebarMenuSubItem>
        <SidebarMenuSubButton />
      </SidebarMenuSubItem>
      <SidebarMenuSubItem>
        <SidebarMenuSubButton />
      </SidebarMenuSubItem>
    </SidebarMenuSub>
  </SidebarMenuItem>
</template>

可折叠的 SidebarMenu

要使 SidebarMenu 组件可折叠,请将其和 SidebarMenuSub 组件包装在 Collapsible 中。

可折叠的侧边栏菜单。
vue
<template>
  <SidebarMenu>
    <Collapsible defaultOpen class="group/collapsible">
      <SidebarMenuItem>
        <CollapsibleTrigger asChild>
          <SidebarMenuButton />
        </CollapsibleTrigger>
        <CollapsibleContent>
          <SidebarMenuSub>
            <SidebarMenuSubItem />
          </SidebarMenuSub>
        </CollapsibleContent>
      </SidebarMenuItem>
    </Collapsible>
  </SidebarMenu>
</template>

SidebarMenuBadge

SidebarMenuBadge 组件用于在 SidebarMenuItem 内渲染徽章。

侧边栏菜单徽章。
vue
<template>
  <SidebarMenuItem>
    <SidebarMenuButton />
    <SidebarMenuBadge>24</SidebarMenuBadge>
  </SidebarMenuItem>
</template>

SidebarMenuSkeleton

SidebarMenuSkeleton 组件用于在 SidebarMenu 内渲染骨架屏。您可以使用它来在等待数据加载时显示加载状态。

vue
<template>
  <SidebarMenu>
    <SidebarMenuItem v-for="i in 5" :key="i">
      <SidebarMenuSkeleton />
    </SidebarMenuItem>
  </SidebarMenu>
</template>

SidebarSeparator

SidebarSeparator 组件用于在 Sidebar 内渲染分隔符。

vue
<template>
  <Sidebar>
    <SidebarHeader />
    <SidebarSeparator />
    <SidebarContent>
      <SidebarGroup />
      <SidebarSeparator />
      <SidebarGroup />
    </SidebarContent>
  </Sidebar>
</template>

SidebarTrigger

使用 SidebarTrigger 组件渲染一个切换侧边栏的按钮。

SidebarTrigger 组件必须在 SidebarProvider 内使用。

vue
<template>
  <SidebarProvider>
    <Sidebar />
    <main>
      <SidebarTrigger />
    </main>
  </SidebarProvider>
</template>

自定义触发器

要创建自定义触发器,您可以使用 useSidebar 组合式函数。

vue
<script setup lang="ts">
import { useSidebar } from "@/components/ui/sidebar";
const { toggleSidebar } = useSidebar();
</script>

<template>
  <button @click="toggleSidebar">切换侧边栏</button>
</template>

SidebarRail

SidebarRail 组件用于在 Sidebar 内渲染轨道。此轨道可用于切换侧边栏。

vue
<template>
  <Sidebar>
    <SidebarHeader />
    <SidebarContent>
      <SidebarGroup />
    </SidebarContent>
    <SidebarFooter />
    <SidebarRail />
  </Sidebar>
</template>

受控侧边栏

使用 open prop 和 @update:open 事件(或 v-model:open)来控制侧边栏状态。

受控侧边栏。
vue
<script setup lang="ts">
import { SidebarProvider, Sidebar } from "@/components/ui/sidebar";
import { ref } from "vue"

const open = ref(false)
</script>

<template>
  <SidebarProvider v-model:open="open">
    <Sidebar />
  </SidebarProvider>
</template>

主题定制

我们使用以下 CSS 变量来为主题化侧边栏。

css
@layer base {
  :root {
    --sidebar-background: 0 0% 98%;
    --sidebar-foreground: 240 5.3% 26.1%;
    --sidebar-primary: 240 5.9% 10%;
    --sidebar-primary-foreground: 0 0% 98%;
    --sidebar-accent: 240 4.8% 95.9%;
    --sidebar-accent-foreground: 240 5.9% 10%;
    --sidebar-border: 220 13% 91%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }

  .dark {
    --sidebar-background: 240 5.9% 10%;
    --sidebar-foreground: 240 4.8% 95.9%;
    --sidebar-primary: 0 0% 98%;
    --sidebar-primary-foreground: 240 5.9% 10%;
    --sidebar-accent: 240 3.7% 15.9%;
    --sidebar-accent-foreground: 240 4.8% 95.9%;
    --sidebar-border: 240 3.7% 15.9%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
}

我们特意为侧边栏和应用程序的其余部分使用不同的变量,以便轻松地使侧边栏的样式与应用程序的其余部分不同。想象一个侧边栏比主应用程序颜色更深的场景。

样式

以下是一些根据不同状态为侧边栏设置样式的技巧。

  • 根据侧边栏可折叠状态设置元素样式。 以下将在侧边栏处于 icon 模式时隐藏 SidebarGroup
vue
<template>
  <Sidebar collapsible="icon">
    <SidebarContent>
      <SidebarGroup class="group-data-[collapsible=icon]:hidden" />
    </SidebarContent>
  </Sidebar>
</template>
  • 根据菜单按钮活动状态设置菜单操作样式。 以下将在菜单按钮处于活动状态时强制显示菜单操作。
vue
<template>
  <SidebarMenuItem>
    <SidebarMenuButton />
    <SidebarMenuAction
      class="peer-data-[active=true]/menu-button:opacity-100"
    />
  </SidebarMenuItem>
</template>

您可以在这篇 Twitter 帖子中找到更多关于使用状态进行样式设置的技巧。

Edit this page on GitHub