折叠/步骤
Accordion
手风琴组件,用于折叠/展开内容
单选模式(一次只能展开一个)
多选模式(可以同时展开多个)
受控模式(当前值: 无)
"use client";
import * as React from "react";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
// ==================== 演示组件 ====================
export function AccordionDemo() {
// 状态管理
const [singleValue, setSingleValue] = React.useState<string>("");
return (
<div className="w-full max-w-[650px] mx-auto p-4 space-y-8">
{/* 单选模式(默认) */}
<div className="">
<h3 className="text-xl mb-3">单选模式(一次只能展开一个)</h3>
<Accordion type="single" collapsible gap="gap-4">
<AccordionItem
value="item-1"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
<AccordionItem
value="item-2"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
<AccordionItem
value="item-3"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
</Accordion>
</div>
{/* 多选模式 */}
<div>
<h3 className="text-xl mb-3">多选模式(可以同时展开多个)</h3>
<Accordion type="multiple" gap="gap-3">
<AccordionItem
value="feature-1"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
<AccordionItem
value="feature-2"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
<AccordionItem
value="feature-3"
trigger="工作目标:5"
content={
<div className="text-sm text-[var(--Text-text-secondary)]">
工作内容
</div>
}
/>
</Accordion>
</div>
{/* 受控模式 */}
<div>
<h3 className="text-xl mb-3">
受控模式(当前值: {singleValue || "无"})
</h3>
<Accordion
type="single"
value={singleValue}
onValueChange={(value) => setSingleValue(value as string)}
gap="gap-2"
>
<AccordionItem
value="controlled-1"
trigger="受控项目一"
content={
<div className="p-4">这是受控模式下的第一个项目内容。</div>
}
/>
<AccordionItem
value="controlled-2"
trigger="受控项目二"
content={
<div className="p-4">这是受控模式下的第二个项目内容。</div>
}
/>
<AccordionItem
value="controlled-3"
trigger="受控项目三"
content={
<div className="p-4">这是受控模式下的第三个项目内容。</div>
}
/>
</Accordion>
<button
className="mt-3 px-3 py-1.5 text-sm bg-[var(--Container-bg-brand)] text-white rounded-[var(--radius-md)]"
onClick={() => setSingleValue("")}
>
收起全部
</button>
</div>
</div>
);
}
手风琴组件是一种常用的 UI 模式,可以帮助用户快速导航和浏览长内容。支持单选和多选两种模式。
概述
- 两种模式:single(单选)和 multiple(多选)
- 折叠支持:单选模式下可配置是否允许折叠
- 默认展开:支持默认展开单个或多个项目
- 展开全部:多选模式下支持一键展开所有项
- 受控模式:支持完全由代码控制展开状态
- 间距控制:支持自定义项目间距
- 无障碍:遵循 WAI-ARIA 标准,支持键盘导航
- 动画效果:平滑的展开/收起动画
快速开始
import { Accordion, AccordionItem } from "@/registry/wuhan/composed/block-accordion/block-accordion";
export function Example() {
return (
<Accordion type="single" collapsible>
<AccordionItem
value="item-1"
trigger="第一章:概述"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这里是第一章的内容。
</div>
}
/>
<AccordionItem
value="item-2"
trigger="第二章:详情"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这里是第二章的内容。
</div>
}
/>
</Accordion>
);
}特性
- 单选模式:一次只能展开一个项目,适合互斥展开的场景
- 多选模式:可以同时展开多个项目,适合对比查看的场景
- 默认展开:支持默认展开单个或多个项目
- 展开全部:多选模式下可一键展开所有项目
- 受控模式:支持完全由代码控制展开状态
- 间距控制:可自定义项目间距
- 无障碍:遵循 WAI-ARIA 标准,支持键盘导航
安装
代码演示
单选模式
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionSingleDemo() {
return (
<div className="w-full max-w-[650px]">
<Accordion type="single" collapsible>
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目一的内容
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目二的内容
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目三的内容
</div>
}
/>
</Accordion>
</div>
);
}
单选模式下,一次只能展开一个项目。设置 collapsible 为 true 可以允许收起所有项目。
多选模式
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionMultipleDemo() {
return (
<div className="w-full max-w-[650px]">
<Accordion type="multiple">
<AccordionItem
value="feature-1"
trigger="特性一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
特性一的详细内容说明
</div>
}
/>
<AccordionItem
value="feature-2"
trigger="特性二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
特性二的详细内容说明
</div>
}
/>
<AccordionItem
value="feature-3"
trigger="特性三"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
特性三的详细内容说明
</div>
}
/>
</Accordion>
</div>
);
}
多选模式下,可以同时展开多个项目。
默认展开(单选)
这个项目默认是展开的
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionDefaultSingleDemo() {
return (
<div className="w-full max-w-[650px]">
<Accordion type="single" defaultValue="item-2">
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是收起的
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是展开的
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是收起的
</div>
}
/>
</Accordion>
</div>
);
}
单选模式下,通过 defaultValue 设置默认展开的项目(字符串)。
默认展开(多选)
默认展开多个项(使用数组)
这个项目默认是展开的
这个项目默认是展开的
默认展开单个项(使用字符串)
这个项目默认是展开的
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionDefaultMultipleDemo() {
return (
<div className="w-full max-w-[650px] space-y-8">
{/* 默认展开多个 */}
<div>
<h3 className="text-sm font-medium mb-3 text-[var(--Text-text-primary)]">
默认展开多个项(使用数组)
</h3>
<Accordion type="multiple" defaultValue={["item-1", "item-3"]}>
<AccordionItem
value="item-1"
trigger="项目一(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是展开的
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是收起的
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是展开的
</div>
}
/>
</Accordion>
</div>
{/* 默认展开单个(使用字符串) */}
<div>
<h3 className="text-sm font-medium mb-3 text-[var(--Text-text-primary)]">
默认展开单个项(使用字符串)
</h3>
<Accordion type="multiple" defaultValue="item-2">
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是收起的
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是展开的
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这个项目默认是收起的
</div>
}
/>
</Accordion>
</div>
</div>
);
}
多选模式下,defaultValue 可以是字符串(展开单个)或字符串数组(展开多个)。
展开全部
所有项目都默认展开
所有项目都默认展开
所有项目都默认展开
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionExpandAllDemo() {
return (
<div className="w-full max-w-[650px]">
<Accordion type="multiple" expandAll>
<AccordionItem
value="item-1"
trigger="项目一(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
所有项目都默认展开
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
所有项目都默认展开
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三(默认展开)"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
所有项目都默认展开
</div>
}
/>
</Accordion>
</div>
);
}
多选模式下,设置 expandAll 为 true 可以默认展开所有项目。
受控模式
当前展开: 无
"use client";
import * as React from "react";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionControlledDemo() {
const [value, setValue] = React.useState("");
return (
<div className="w-full max-w-[650px] space-y-4">
<div className="flex items-center gap-4">
<span className="text-sm text-[var(--Text-text-secondary)]">
当前展开: <span className="font-medium">{value || "无"}</span>
</span>
<button
className="px-3 py-1.5 text-sm bg-[var(--Container-bg-brand)] text-white rounded-[var(--radius-md)] hover:opacity-90"
onClick={() => setValue("")}
>
收起全部
</button>
</div>
<Accordion
type="single"
value={value}
onValueChange={(val) => setValue(val)}
>
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目一的内容
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目二的内容
</div>
}
/>
<AccordionItem
value="item-3"
trigger="项目三"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
这是项目三的内容
</div>
}
/>
</Accordion>
</div>
);
}
通过 value 和 onValueChange 属性实现完全受控,可以通过代码控制展开状态。
间距控制
无间距 (gap-0)
中等间距 (gap-4)
"use client";
import {
Accordion,
AccordionItem,
} from "@/components/composed/block-accordion/block-accordion";
export function AccordionGapDemo() {
return (
<div className="w-full max-w-[650px] space-y-8">
{/* gap-0 (无间距) */}
<div>
<h3 className="text-sm font-medium mb-3 text-[var(--Text-text-primary)]">
无间距 (gap-0)
</h3>
<Accordion type="single" collapsible gap="gap-0">
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
内容一
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
内容二
</div>
}
/>
</Accordion>
</div>
{/* gap-4 (中等间距) */}
<div>
<h3 className="text-sm font-medium mb-3 text-[var(--Text-text-primary)]">
中等间距 (gap-4)
</h3>
<Accordion type="single" collapsible gap="gap-4">
<AccordionItem
value="item-1"
trigger="项目一"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
内容一
</div>
}
/>
<AccordionItem
value="item-2"
trigger="项目二"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
内容二
</div>
}
/>
</Accordion>
</div>
</div>
);
}
通过 gap 属性控制 AccordionItem 之间的间距,支持所有 Tailwind gap 类名。
API
Accordion Props
通用属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| type | 手风琴类型 | 'single' | 'multiple' | 'single' |
| gap | AccordionItem 之间的间距(支持 Tailwind gap 类名) | string | - |
| className | 自定义类名 | string | - |
| children | 子元素 | ReactNode | - |
单选模式属性 (type="single")
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| collapsible | 是否允许折叠所有项 | boolean | true |
| defaultValue | 默认展开的值(支持 string 或 string[],数组时取第一项) | string | string[] | - |
| value | 当前展开的值(受控模式) | string | - |
| onValueChange | 值变化回调 | (value: string) => void | - |
多选模式属性 (type="multiple")
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| expandAll | 是否默认展开所有项 | boolean | false |
| defaultValue | 默认展开的值(支持 string 或 string[],字符串时自动转为数组) | string | string[] | - |
| value | 当前展开的值(受控模式) | string[] | - |
| onValueChange | 值变化回调 | (value: string[]) => void | - |
AccordionItem Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| value | 项目的唯一标识 | string | - |
| trigger | 触发器内容(标题) | ReactNode | - |
| content | 展开的内容 | ReactNode | - |
设计规范
尺寸规范
| 尺寸 | 高度 | 内边距 | 字号 |
|---|---|---|---|
| 默认 | auto | 16px | 14px |
交互规范
- 单选模式:用于需要互斥展开的场景,如 FAQ、分类列表
- 多选模式:用于需要对比多个展开项的场景,如特性对比
- 展开全部:用于需要快速查看所有内容的场景
- 内容样式:建议为内容添加内边距(如
p-4)和次要文本颜色 - 键盘支持:使用方向键和 Enter 键可以展开/收起项目
- 标题命名:trigger 应该是简洁的标题,避免过长的文本
最佳实践
内容容器样式:为了更好的视觉效果,建议为 content 添加样式:
<AccordionItem
value="item-1"
trigger="标题"
content={
<div className="p-4 text-sm text-[var(--Text-text-secondary)]">
内容文本
</div>
}
/>