Preview Updated 2026-05-10

Sidebar

Vertical primary navigation — supports groups, nested submenus, badges, and collapsing into icon mode.

Basic usage

items accepts two kinds of entries: a regular SidebarItem (leaf entry) or a SidebarGroup (group container, type: 'group' + items[]). v-model binds the currently selected key; defaultOpenKeys controls which entries with children are initially expanded.

背景
当前选中:analytics
<script setup lang="ts">
import { ref } from 'vue';
import { CfSidebar, type SidebarEntry } from '@chufix-design/vue';

const active = ref('analytics');
const items: SidebarEntry[] = [
  {
    type: 'group',
    label: '工作台',
    items: [
      { key: 'overview', label: '概览' },
      { key: 'analytics', label: '分析', badge: '12' },
      { key: 'reports', label: '报表' },
    ],
  },
  {
    type: 'group',
    label: '资源',
    items: [
      {
        key: 'team',
        label: '团队',
        children: [
          { key: 'members', label: '成员' },
          { key: 'roles', label: '角色' },
          { key: 'invitations', label: '邀请', badge: 3 },
        ],
      },
      { key: 'settings', label: '设置' },
      { key: 'billing', label: '账单', disabled: true },
    ],
  },
];
</script>

<template>
  <div style="height: 360px; display:flex; gap: 12px;">
    <div style="border: 1px solid var(--line-1); border-radius: 8px; overflow: hidden;">
      <CfSidebar
        v-model="active"
        :items="items"
        :default-open-keys="['team']"
      />
    </div>
    <div style="flex:1; padding: 12px; color: var(--fg-2); font-size: 12px;">
      当前选中:<code>{{ active }}</code>
    </div>
  </div>
</template>
import { useState } from 'react';
import { CfSidebar, type SidebarEntry } from '@chufix-design/react';

const items: SidebarEntry[] = [/* ... */];

export default function Demo() {
const [active, setActive] = useState('analytics');
return (
  <CfSidebar
    value={active}
    items={items}
    defaultOpenKeys={['team']}
    onChange={setActive}
  />
);
}

Collapsed icon mode

collapsed shrinks the sidebar to 56px, showing icons only with a tooltip on hover (title). Submenus no longer expand in this mode — handle that interaction at the application layer.

<script setup lang="ts">
import { ref } from 'vue';
import { CfSidebar, CfButton, type SidebarEntry } from '@chufix-design/vue';

const active = ref('overview');
const collapsed = ref(true);
const homeIcon = '<svg viewBox="0 0 16 16" fill="none"><path d="M3 7l5-4 5 4v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7z" stroke="currentColor" stroke-width="1.4"/></svg>';
const chartIcon = '<svg viewBox="0 0 16 16" fill="none"><path d="M3 13V8m4 5V5m4 8V9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>';
const usersIcon = '<svg viewBox="0 0 16 16" fill="none"><circle cx="6" cy="6" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M2 13a4 4 0 0 1 8 0M11 8.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM10.5 13a3 3 0 0 1 4-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>';
const cogIcon = '<svg viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="2.2" stroke="currentColor" stroke-width="1.4"/><path d="M8 1.5v2M8 12.5v2M14.5 8h-2M3.5 8h-2M12.6 3.4l-1.4 1.4M4.8 11.2l-1.4 1.4M12.6 12.6l-1.4-1.4M4.8 4.8 3.4 3.4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>';

const items: SidebarEntry[] = [
  { key: 'overview', label: '概览', icon: homeIcon },
  { key: 'analytics', label: '分析', icon: chartIcon, badge: '12' },
  { key: 'team', label: '团队', icon: usersIcon },
  { key: 'settings', label: '设置', icon: cogIcon },
];
</script>

<template>
  <div style="display:flex; gap: 12px; align-items: flex-start;">
    <CfButton size="sm" variant="tertiary" @click="collapsed = !collapsed">
      {{ collapsed ? '展开' : '收起' }}
    </CfButton>
    <div style="border: 1px solid var(--line-1); border-radius: 8px; overflow: hidden;">
      <CfSidebar v-model="active" :items="items" :collapsed="collapsed" />
    </div>
  </div>
</template>
<CfSidebar value={active} items={items} collapsed onChange={setActive} />

Badges + disabled

item.badge accepts a number or string and renders to the right of the label. item.disabled greys out and ignores clicks.

<script setup lang="ts">
import { ref } from 'vue';
import { CfSidebar, type SidebarEntry } from '@chufix-design/vue';

const active = ref('inbox');
const items: SidebarEntry[] = [
  { key: 'inbox', label: '收件箱', badge: 12 },
  { key: 'starred', label: '加星' },
  { key: 'sent', label: '已发送' },
  { key: 'drafts', label: '草稿', badge: 'NEW' },
  { key: 'trash', label: '已删除', disabled: true },
];
</script>

<template>
  <CfSidebar v-model="active" :items="items" />
</template>
<CfSidebar value={active} items={items} onChange={setActive} />

Three sizes

sizesm (compact, suits deep nesting or many entries) / md / lg (touch-friendly).

<script setup lang="ts">
import { ref } from 'vue';
import { CfSidebar, type SidebarEntry } from '@chufix-design/vue';

const a = ref('overview');
const b = ref('overview');
const c = ref('overview');

const items: SidebarEntry[] = [
  { key: 'overview', label: '概览' },
  { key: 'analytics', label: '分析' },
  { key: 'reports', label: '报表' },
  { key: 'settings', label: '设置' },
];
</script>

<template>
  <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;">
    <CfSidebar v-model="a" :items="items" size="sm" />
    <CfSidebar v-model="b" :items="items" size="md" />
    <CfSidebar v-model="c" :items="items" size="lg" />
  </div>
</template>
<CfSidebar value={a} onChange={setA} items={items} size="sm" />
<CfSidebar value={b} onChange={setB} items={items} size="md" />
<CfSidebar value={c} onChange={setC} items={items} size="lg" />

API

PropTypeDefaultDescription
itemsSidebarEntry[][]Menu entries; mix leaves and groups
modelValue (Vue) / value (React)stringCurrently selected item.key
openKeys / defaultOpenKeysstring[]Controlled / uncontrolled list of expanded keys
collapsedbooleanfalseCollapse to 56px icon mode
size'sm' | 'md' | 'lg''md'Font size + padding

SidebarItem: { key, label, icon?, href?, badge?, disabled?, children? }. SidebarGroup: { type: 'group', key?, label?, items[] }.

Events: update:modelValue / update:openKeys / select (React: onChange / onOpenKeysChange / onSelect).

When pairing with AppShell, drop <CfSidebar> into the #sidebar slot to get a complete admin shell.

反馈与讨论

Sidebar · Discussion

0
0 / 600
一键发送
正在加载评论...