# 内容大纲 · Outline

- 页面: https://jiguang132.com/zh/resources/astryx/docs/outline
- 资源: Astryx (https://jiguang132.com/zh/resources/astryx.md)
- 全部指南: https://jiguang132.com/zh/resources/astryx/docs.md

内容大纲用于导航场景。下面的官方示例展示原生用法与状态，属性名保持上游 API。

## 安装与前置条件

```bash
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
```

需要 React 19+，按顺序导入基础 CSS 和主题，并设置 Theme Provider。参见[开始使用](/resources/astryx/docs/getting-started)。

## 示例与使用方式

- **内容大纲 · 综合展示 1**：首先体验这个示例，再查看代码理解组件的组合方式。
- **内容大纲 · 受控交互 2**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **内容大纲 · 交互示例 3**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **内容大纲 · 交互示例 4**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。

## API 与上游详细参考

优先使用 0.7.1 官方中文说明；上游尚未提供中文的条目保留英文。属性名、类型及默认值保持原生 API，不把示例状态名称当成组件属性。

## Outline

A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes.

### 使用建议

- Pass a flat ordered list of headings and let level control indentation.
- Use activeId when custom scroll logic owns the active section.
- Use density="compact" in dense sidebars where vertical space is tight.
- Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.
- 避免：Use Outline for application navigation - use SideNav or TopNav for routes.
- 避免：Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-outline",
      "visualProps": [
        "density"
      ]
    },
    {
      "className": "astryx-outline-indicator"
    },
    {
      "className": "astryx-outline-item",
      "visualProps": [
        "level"
      ],
      "states": [
        "active"
      ]
    }
  ]
}
```

## Outline

文档大纲导航，带有滑动指示器轨道。将扁平标题列表渲染为锚点链接，支持密度变体和非受控模式下的滚动监听。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| items * | OutlineItem[] |  | 有序标题项。每项包含 id、label 和 level (1-6)。id 应匹配目标标题元素的 id。 |
| activeId | string |  | 当前激活的标题 id。提供后组件进入受控模式，并禁用内置滚动监听。 |
| onActiveIdChange | (id: string) => void |  | 当内置滚动监听或点击大纲链接改变激活项时调用。 |
| label | string | 'Table of contents' | nav 地标的无障碍标签。 |
| density | 'default' \| 'compact' | 'default' | 控制项目内边距的密度变体。'compact' 用于紧凑界面，'default' 为标准间距。 |
| onNavigateStart | (id: string) => void |  | 导航开始时（滚动前）调用，传入项目 id。与 onNavigateEnd 搭配可在目标标题上实现到达效果（闪烁、描边、脉冲）。 |
| onNavigateEnd | (id: string) => void |  | 导航结束时调用（平滑滚动停止，或减弱动效将其变为瞬时跳转）。每次 onNavigateStart 必定对应一次调用，包括用户中途手动滚动打断的情况，因此“导航中”状态不会泄漏。 |
| offset | number | 0 | 覆盖滚动根顶部的固定顶栏高度（px）。同时位移激活线与滚动落点，因此标题被激活的位置正是点击后停留的位置——落在顶栏下方，而非被顶栏遮住。它与每个标题自身的 scroll-margin-top 叠加（先顶栏，再留白），而非取代它。若没有元素覆盖内容，保持 0，交给 scroll-margin-top 处理。 |
| scrollContainerRef | React.RefObject<HTMLElement \| null> |  | 要跟踪的滚动容器，替代自动探测最近的可滚动祖先。当内容在分栏、弹窗或面板中滚动而非视口时使用。 |
| hasScrollOnClick | boolean | true | 激活项目时是否平滑滚动。设为 false 可自行接管滚动（虚拟列表、路由）：Outline 仍会更新激活项、hash 和导航回调，但不执行滚动。 |
| xstyle | StyleXStyles |  | 用于布局自定义的 StyleX 样式。必须是 stylex.create() 值。 |

### Basic

```tsx
import {Outline} from '@astryxdesign/core/Outline';

const items = [
  {id: 'overview', label: 'Overview', level: 2},
  {id: 'installation', label: 'Installation', level: 2},
  {id: 'theming', label: 'Theming', level: 2},
  {id: 'tokens', label: 'Tokens', level: 3},
  {id: 'accessibility', label: 'Accessibility', level: 2},
];

// Uncontrolled: built-in scroll-spy tracks the topmost visible heading.
<Outline items={items} />;
```

### Compact (density="compact")

```tsx
import {Outline} from '@astryxdesign/core/Outline';

// Dense sidebars use the compact variant; the sliding indicator
// automatically matches the shorter item height.
<Outline items={items} density="compact" />;
```

### Controlled active section

```tsx
import {useState} from 'react';
import {Outline} from '@astryxdesign/core/Outline';

function ControlledOutline() {
  const [activeId, setActiveId] = useState('overview');

  // Providing activeId disables built-in scroll-spy; you own the active state.
  return (
    <Outline
      items={items}
      activeId={activeId}
      onActiveIdChange={setActiveId}
    />
  );
}
```

### Generate items from markdown

```tsx
import {Markdown} from '@astryxdesign/core/Markdown';
import {createMarkdownHeadingLinks} from '@astryxdesign/core/Markdown/plugins';
import {Outline, useOutlineFromMarkdown} from '@astryxdesign/core/Outline';

const markdownRootId = 'guide';
const plugins = [
  createMarkdownHeadingLinks({headingIdPrefix: markdownRootId}),
];

function MarkdownOutline({markdown, isStreaming}) {
  // Pass the same plugin entry and finality to Markdown and Outline. Nested
  // headings consume the shared depth-first sequence, while Outline continues
  // to return root headings only.
  const items = useOutlineFromMarkdown(markdown, {
    plugins,
    isFinal: !isStreaming,
  });
  return (
    <>
      <Markdown id={markdownRootId} plugins={plugins}>{markdown}</Markdown>
      <Outline items={items} />
    </>
  );
}
```

### Scoped scroll container (split pane, modal, panel)

```tsx
import {useRef} from 'react';
import {Outline} from '@astryxdesign/core/Outline';

function PanelDocs() {
  const scrollContainerRef = useRef(null);

  // Without scrollContainerRef the outline auto-detects the nearest scrollable
  // ancestor, which is the viewport here - so the highlight would never move.
  return (
    <div style={{display: 'flex'}}>
      <div ref={scrollContainerRef} style={{overflowY: 'auto', height: 480}}>
        <h2 id="overview">Overview</h2>
        {/* ...content... */}
      </div>
      <Outline items={items} scrollContainerRef={scrollContainerRef} offset={64} />
    </div>
  );
}
```

### Navigate callbacks (flash the heading on arrival)

```tsx
import {useState} from 'react';
import {Outline} from '@astryxdesign/core/Outline';

function FlashOnArrival() {
  const [flashId, setFlashId] = useState(null);

  // onNavigateEnd fires once the smooth scroll settles - and once per
  // onNavigateStart even if the user scrolls away mid-jump, so this never
  // gets stuck.
  return (
    <Outline
      items={items}
      onNavigateStart={() => setFlashId(null)}
      onNavigateEnd={id => setFlashId(id)}
    />
  );
}
```

### 组件结构

- **Outline**: Navigation container for the same-page heading links and indicator.
- **Heading link**: Anchor for one heading, indented by level and marked when active.
- **Label**: Heading text displayed inside its heading link.
- **Indicator track**: Painted vertical rule behind the active indicator.
- **Active indicator**: Sliding bar positioned beside the currently active heading link.

---

JGuang 全部资源索引: https://jiguang132.com/llms.txt
