Toc
Article side TOC, indented by depth. Supports manual selection or automatic scroll-spy via IntersectionObserver.
Basic usage
items is an array; each entry needs at least id and label. activeId controls the currently highlighted entry; clicking smoothly scrolls and updates the URL hash.
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';
const items: TocItem[] = [
{ id: 'intro', label: '介绍', depth: 1 },
{ id: 'install', label: '安装', depth: 1 },
{ id: 'usage', label: '基本用法', depth: 1 },
{ id: 'theme', label: '主题', depth: 1 },
];
</script>
<template>
<div style="max-width: 220px;">
<CfToc :items="items" :active-id="'install'" title="本页目录" />
</div>
</template> <script setup>
import { CfToc } from '@chufix-design/vue';
const items= [
{ id: 'intro', label: '介绍', depth: 1 },
{ id: 'install', label: '安装', depth: 1 },
{ id: 'usage', label: '基本用法', depth: 1 },
{ id: 'theme', label: '主题', depth: 1 },
];
</script>
<template>
<div style="max-width: 220px;">
<CfToc :items="items" :active-id="'install'" title="本页目录" />
</div>
</template> <CfToc items={items} activeId="install" title="On this page" /> <CfToc items={items} activeId="install" title="On this page" /> Nested indentation
depth: 1..6 controls the indent level. Given correct depth values, the same items array forms nested indentation naturally.
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';
const items: TocItem[] = [
{ id: 'intro', label: '介绍', depth: 1 },
{ id: 'install', label: '安装', depth: 1 },
{ id: 'install-vue', label: 'Vue 3', depth: 2 },
{ id: 'install-react', label: 'React 18', depth: 2 },
{ id: 'install-vanilla', label: '纯 HTML', depth: 2 },
{ id: 'usage', label: '基本用法', depth: 1 },
{ id: 'theme', label: '主题与 Tokens', depth: 1 },
{ id: 'theme-dark', label: 'dark-cool', depth: 2 },
{ id: 'theme-warm', label: 'dark-warm', depth: 2 },
{ id: 'theme-detail', label: '颜色细节', depth: 3 },
];
</script>
<template>
<div style="max-width: 220px;">
<CfToc :items="items" :active-id="'install-vue'" title="本页目录" />
</div>
</template> <script setup>
import { CfToc } from '@chufix-design/vue';
const items= [
{ id: 'intro', label: '介绍', depth: 1 },
{ id: 'install', label: '安装', depth: 1 },
{ id: 'install-vue', label: 'Vue 3', depth: 2 },
{ id: 'install-react', label: 'React 18', depth: 2 },
{ id: 'install-vanilla', label: '纯 HTML', depth: 2 },
{ id: 'usage', label: '基本用法', depth: 1 },
{ id: 'theme', label: '主题与 Tokens', depth: 1 },
{ id: 'theme-dark', label: 'dark-cool', depth: 2 },
{ id: 'theme-warm', label: 'dark-warm', depth: 2 },
{ id: 'theme-detail', label: '颜色细节', depth: 3 },
];
</script>
<template>
<div style="max-width: 220px;">
<CfToc :items="items" :active-id="'install-vue'" title="本页目录" />
</div>
</template> const items: TocItem[] = [/* ... */];
<CfToc items={items} /> const items= [/* ... */];
<CfToc items={items} /> maxDepth limit
maxDepth caps the deepest level rendered — for a blog sidebar that only wants H1 / H2, pass maxDepth={2} and deeper headings are skipped.
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';
const items: TocItem[] = [
{ id: 'a', label: '一级标题', depth: 1 },
{ id: 'a1', label: '二级标题', depth: 2 },
{ id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
{ id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
];
</script>
<template>
<div style="display: flex; gap: 16px;">
<div style="max-width: 200px;">
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">maxDepth = 2</div>
<CfToc :items="items" :max-depth="2" />
</div>
<div style="max-width: 240px;">
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">maxDepth = 4(默认 6)</div>
<CfToc :items="items" :max-depth="4" />
</div>
</div>
</template> <script setup>
import { CfToc } from '@chufix-design/vue';
const items= [
{ id: 'a', label: '一级标题', depth: 1 },
{ id: 'a1', label: '二级标题', depth: 2 },
{ id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
{ id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
];
</script>
<template>
<div style="display: flex; gap: 16px;">
<div style="max-width: 200px;">
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">maxDepth = 2</div>
<CfToc :items="items" :max-depth="2" />
</div>
<div style="max-width: 240px;">
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">maxDepth = 4(默认 6)</div>
<CfToc :items="items" :max-depth="4" />
</div>
</div>
</template> <CfToc items={items} maxDepth={2} />
<CfToc items={items} maxDepth={4} /> <CfToc items={items} maxDepth={2} />
<CfToc items={items} maxDepth={4} /> Automatic scroll-spy
autoSpy: true makes the component observe headings whose id matches items and switch active based on viewport visibility. scrollRoot specifies the observed container; omit to observe the viewport.
<!-- Vue -->
<CfToc :items="items" auto-spy />
<CfToc :items="items" auto-spy scroll-root=".article-body" />
{/* React */}
<CfToc items={items} autoSpy />
<CfToc items={items} autoSpy scrollRoot=".article-body" />
API
| Prop | Type | Default | Description |
|---|---|---|---|
items | TocItem[] | [] | TOC entries |
autoSpy | boolean | false | Enable IntersectionObserver-based scroll-spy |
scrollRoot | string | — | Selector for the observed container; falls back to viewport |
activeId | string | null | null | Controlled current id |
defaultActiveId (React) | string | null | null | Uncontrolled initial value |
title | string | ReactNode | — | Top heading |
maxDepth | number | 6 | Maximum depth to render |
TocItem: { id, label, depth? }. Events: update:activeId (React: onActiveIdChange).
反馈与讨论
Toc · Discussion