Accordion(折叠面板)把内容分成若干分区,每个标题是一个按钮,按下展开对应面板。适合 FAQ、设置页、课程大纲这类逐段浏览的内容。只有一块内容要开合时用可折叠区域;要在同一块空间里切换互斥视图时用标签页。
组合
Accordion
├── AccordionItem
│ ├── AccordionHeader
│ │ └── AccordionTrigger
│ └── AccordionPanel
└── AccordionItem
├── AccordionHeader
│ └── AccordionTrigger
└── AccordionPanel每个 AccordionItem 要有唯一的 value,触发器放进标题里,面板跟在后面。根的值始终是展开项 value 的数组,即使只允许展开一项。AccordionHeader 默认渲染 h3,按周围文档结构选用合适的标题级别。
用法
import {
Accordion,
AccordionHeader,
AccordionItem,
AccordionPanel,
AccordionTrigger,
} from "@ui-sdk/react/accordion";
function Example() {
return (
<Accordion defaultValue={["shipping"]}>
<AccordionItem value="shipping">
<AccordionHeader>
<AccordionTrigger>配送方式</AccordionTrigger>
</AccordionHeader>
<AccordionPanel>标准件 5 到 7 天送达,加急件 2 到 3 天。</AccordionPanel>
</AccordionItem>
<AccordionItem value="returns">
<AccordionHeader>
<AccordionTrigger>退货政策</AccordionTrigger>
</AccordionHeader>
<AccordionPanel>签收后 30 天内可退未使用的商品。</AccordionPanel>
</AccordionItem>
</Accordion>
);
}多开与常驻
multiple 让多个分区同时展开、互不影响。collapsible={false} 则保证总有一项展开:打开的项不能被自己关闭,其触发器报告 aria-disabled="true",常与 defaultValue 搭配使用。
<Accordion multiple defaultValue={["notifications"]}>
<AccordionItem value="notifications">
<AccordionHeader>
<AccordionTrigger>通知设置</AccordionTrigger>
</AccordionHeader>
<AccordionPanel>选择邮件通知的内容与频率。</AccordionPanel>
</AccordionItem>
<AccordionItem value="privacy">
<AccordionHeader>
<AccordionTrigger>隐私与安全</AccordionTrigger>
</AccordionHeader>
<AccordionPanel>开启两步验证,查看已登录设备。</AccordionPanel>
</AccordionItem>
</Accordion>受控与拦截
value 加 onValueChange 接管展开项。项的 onOpenChange(open, details) 先于根的回调运行(包括因别项打开而将要关闭的项),任何一处调用 details.cancel() 都会取消整个变更。details.reason 说明来源:trigger-press、find-in-page 或 imperative。
const [value, setValue] = React.useState(["security"]);
<Accordion value={value} onValueChange={(next) => setValue(next)}>
<AccordionItem
value="security"
onOpenChange={(open, details) => {
if (!open && !window.confirm("确定关闭安全设置?")) {
details.cancel();
}
}}
>
<AccordionHeader>
<AccordionTrigger>安全</AccordionTrigger>
</AccordionHeader>
<AccordionPanel>密码、通行密钥与两步验证。</AccordionPanel>
</AccordionItem>
</Accordion>;用 createAccordionController()(来自 @ui-sdk/core/accordion)创建控制器传给 controller,可在组件树外调用 open / close / toggle / setValue / getValue / isOpen / focus;multiple 下还有 openAll() 与 closeAll()。参见控制器。
保留内容与页内查找
关闭的面板默认卸载。keepMounted 让它隐藏留在页面里,保住输入草稿等局部状态;hiddenUntilFound 进一步以 hidden="until-found" 保留,浏览器页内查找命中时立即展开(不播放动画),reason 为 find-in-page。两者可设在根上,也可设在单个 AccordionPanel 上覆盖根的设置。
样式与状态
各部件带 data-slot:accordion、accordion-item、accordion-header、accordion-trigger、accordion-panel。项、标题、触发器、面板都带 data-open / data-closed / data-disabled;面板另有 data-starting-style(入场首帧)与 data-ending-style(退场期间)。
面板把测量到的高度写进 --accordion-panel-height(横向开合用 --accordion-panel-width),配合状态属性做高度动画:
[data-slot="accordion-panel"] {
height: var(--accordion-panel-height);
overflow: hidden;
transition: height 0.15s;
}
[data-slot="accordion-panel"]:is([data-starting-style], [data-ending-style]) {
height: 0;
}
[data-slot="accordion-trigger"][data-open] > svg {
rotate: 180deg;
}
[data-slot="accordion-trigger"][data-disabled] {
color: gray;
}面板会等退场动画结束再卸载,随后运行项的 onOpenChangeComplete(open);初始就展开的面板直接显示,不播动画。详见样式与动画。
API
根 Accordion 的主要 props:
| Prop | 类型 | 说明 |
|---|---|---|
value |
T[] |
展开项的 value 数组,传入即受控。 |
defaultValue |
T[] |
非受控时初始展开的项。 |
onValueChange |
(value, details) => void |
展开项要变化时运行,details.cancel() 可拦截。 |
multiple |
boolean |
允许多项同时展开,否则开一项关其余。 |
collapsible |
boolean |
为 false 时总保留一项展开。 |
disabled |
boolean |
禁用全部项,触发器仍可聚焦。 |
keepMounted |
boolean |
关闭的面板隐藏保留在页面中。 |
hiddenUntilFound |
boolean |
关闭面板可被页内查找命中并展开。 |
controller |
object |
用 createAccordionController() 创建。 |
AccordionItem 另有 value、disabled、onOpenChange、onOpenChangeComplete;AccordionTrigger 可单独 disabled。
键盘与无障碍
遵循 WAI-ARIA accordion 模式:
Tab依次经过每个触发器,禁用的也在内;展开后Tab进入面板。Enter/Space开合焦点所在的面板。- 方向键、
Home、End在这里不动作:APG 已把它们从该模式移除,屏幕阅读器的标题导航足以到达每个触发器。
每个触发器位于标题元素内,带 aria-expanded 并通过 aria-controls 指向面板;面板是以触发器命名的 region,屏幕阅读器可把展开的面板列为地标。关闭持有焦点的面板时,焦点回到它的触发器。multiple 加「全部展开」时请控制项数,避免地标列表过长。