Collapsible 折叠区域
一块可以展开与收起的单块内容。
用法
不传 open 即为非受控,defaultOpen 只提供初始值,之后由组件自行维护开合
组件结构
加粗的是必需部件。
data-scope="collapsible":root · header · trigger · content · indicator
示例
受控
传入 open 后由宿主决定,组件自身不再修改状态,只发 open-change 报告意图
禁用
disabled 把触发器整个关停,点击与键盘都不再改变开合,已展开的内容维持原样
尺寸
size 改变触发按钮的高度、内边距与字号,三档并排对照
自定义展开标记
在指示符部件中放置自己的图形,转向仍由皮肤按 open 接管
展开动画
收起时节点不卸载,作者接管内容区的 display,用一条行高过渡即可平滑展开
颜色
tone 落在触发按钮的展开态上,六种颜色各展开一份做对照
设计指引
何时使用
- 高级选项、补充说明等默认不需要显示的单块内容。
何时不用
特性
- 触发器与内容通过
aria-controls与aria-expanded关联。 - 展开动画由皮肤提供,内容高度由组件测量。
- 指示符部件留空时由皮肤绘制箭头,放入图形时以作者提供的为准,两种情形的转向都由皮肤处理。
组合
最佳实践
- 触发器文字说明内容是什么,不只写“展开”。
- 收起时内容退出 Tab 序列,焦点不落到不可见的位置。
反模式
- 把必填字段放进折叠区,用户提交失败时无法定位错误。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-collapsible> |
| Vue 组件 | XhCollapsibleContent XhCollapsibleHeader XhCollapsibleIndicator XhCollapsibleRoot XhCollapsibleTrigger |
| 组合式函数 | useCollapsible |
| 状态机 | collapsibleMachine |
| 皮肤 | @xihan-ui/styles/collapsible.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
open | boolean | ||
defaultOpen | boolean | ||
disabled | boolean | ||
tone | Tone | 颜色:brand / neutral / success / warning / danger / info,决定使用哪组状态色。 | |
size | Size | 尺寸:sm / md / lg。 | |
dir | Direction | 文字方向,只作用于排版;作者未提供时不写入。 | |
onOpenChange | (details: CollapsibleOpenChangeDetails) => void | open 变化意图回调;受控时是唯一出口,非受控时随内部转移一并通知。 |
事件
自定义元素将载荷放在 detail;Vue 使用同名 emit。
| 事件 | 载荷 | 说明 |
|---|---|---|
open-change | CollapsibleOpenChangeDetails | open 状态变化;detail 为 { open: boolean } |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhCollapsibleRoot | children | ReactNode |
状态
公开状态写入 data-state。
| 部件 | 取值 |
|---|---|
root | 'open' | 'closed' |
header | 'open' | 'closed' |
trigger | 'open' | 'closed' |
content | 'open' | 'closed' |
indicator | 'open' | 'closed' |
以下名称仅用于内部状态机。
状态:open · closed
事件:OPEN · CLOSE · TOGGLE · CONTROLLED.OPEN · CONTROLLED.CLOSE · PRESS.START · PRESS.END
判据:isOpenControlled · canPress
connect API
getXxxProps() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
open | boolean | |
setOpen | (next: boolean) => void | |
getRootProps | () => T['element'] | |
getHeaderProps | () => T['element'] | |
getTriggerProps | () => T['button'] | |
getContentProps | () => T['element'] | |
getIndicatorProps | () => T['element'] |
无障碍
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Space / Enter | focus in trigger, not disabled | 展开/收起 content |
Enter / Space | held in trigger, not disabled | 按住期间 trigger 投影 data-pressed,与指针 :active 同一副按压面(disclosure trigger 只换面不缩放);抬起、失焦或转禁用撤下 |
ARIA
以下属性由 connect 生成。
| 部件 | 属性 | 值 |
|---|---|---|
trigger | aria-controls | content 部件的 id |
trigger | aria-expanded | 'true' | 'false' |
indicator | aria-hidden | 'true' |
样式参考
皮肤
@xihan-ui/styles/collapsible.css 使用 [data-scope="collapsible"][data-part="root"] 部件选择器,位于 xihan.components 与 xihan.motion 层。覆盖样式使用 xihan.overrides。
数据属性
由 connect 生成;条件不成立时不输出无值属性。
| 部件 | 属性 | 值 |
|---|---|---|
root | data-disabled | ''(条件成立时才出现) |
root | data-size | props.size |
root | data-state | 'open' | 'closed' |
root | data-tone | props.tone |
header | data-disabled | ''(条件成立时才出现) |
header | data-state | 'open' | 'closed' |
trigger | data-disabled | ''(条件成立时才出现) |
trigger | data-pressed | ''(条件成立时才出现) |
trigger | data-state | 'open' | 'closed' |
trigger | data-xh-action-control | '' |
trigger | data-xh-action-display | 'always' |
trigger | data-xh-action-profile | 'disclosure-trigger' |
trigger | data-xh-action-size | props.size |
trigger | data-xh-action-variant | 'ghost' |
content | data-instant | ''(条件成立时才出现) |
content | data-state | 'open' | 'closed' |
indicator | data-disabled | ''(条件成立时才出现) |
indicator | data-instant | ''(条件成立时才出现) |
indicator | data-state | 'open' | 'closed' |
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-collapsible-content-fg | content | color | default | --xh-fg-muted | collapsible 的 content 部件 color 覆盖槽。 |
--xh-collapsible-content-font-size | content | font-size | default | --xh-text-secondary-size | collapsible 的 content 部件 font-size 覆盖槽。 |
--xh-collapsible-content-pb | content | padding-block-end | @keyframes xh-disclosure-collapse@keyframes xh-disclosure-expanddefault | --xh-_collapsible-content-pb | collapsible 的 content 部件 padding-block-end 覆盖槽。 |
--xh-collapsible-content-px | content | padding-inline | default | --xh-_collapsible-content-px | collapsible 的 content 部件 padding-inline 覆盖槽。 |
--xh-collapsible-header-gap | header | gap | default | --xh-_collapsible-trigger-gap | collapsible 的 header 部件 gap 覆盖槽。 |
--xh-collapsible-icon-size | roottrigger | --xh-icon-size | default | --xh-_action-profile-glyph-size--xh-glyph-size-md | collapsible 的 root、trigger 部件 --xh-icon-size 覆盖槽。 |
--xh-collapsible-indicator-fg | indicator | color | default | --xh-fg-muted | collapsible 的 indicator 部件 color 覆盖槽。 |
--xh-collapsible-trigger-bg | trigger | --xh-ink-surfacebackground-color | defaultxh-ink-surface | --xh-_action-variant-bg-rest | collapsible 的 trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-collapsible-trigger-bg-hover | trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | collapsible 的 trigger 部件 background-color 覆盖槽。 |
--xh-collapsible-trigger-fg | trigger | color | defaultdisabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-fg-hover--xh-_action-variant-fg-pressed--xh-_action-variant-fg-rest | collapsible 的 trigger 部件 color 覆盖槽。 |
--xh-collapsible-trigger-fg-disabled | trigger | color | disabled | --xh-_action-variant-fg-disabled | collapsible 的 trigger 部件 color 覆盖槽。 |
--xh-collapsible-trigger-fg-open | trigger | color | disabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressedstate=open | --xh-_collapsible-open-fg | collapsible 的 trigger 部件 color 覆盖槽。 |
--xh-collapsible-trigger-font-size | trigger | font-size | default | --xh-_action-profile-font-size | collapsible 的 trigger 部件 font-size 覆盖槽。 |
--xh-collapsible-trigger-font-weight | trigger | font-weight | default | --xh-text-label-weight | collapsible 的 trigger 部件 font-weight 覆盖槽。 |
--xh-collapsible-trigger-gap | trigger | gap | default | --xh-_action-profile-gap | collapsible 的 trigger 部件 gap 覆盖槽。 |
--xh-collapsible-trigger-h | trigger | block-sizemin-block-size | defaultxh-action-profile=disclosure-trigger | --xh-_action-profile-visual-size | collapsible 的 trigger 部件 block-size、min-block-size 覆盖槽。 |
--xh-collapsible-trigger-px | trigger | padding-inline | default | --xh-_action-profile-padding-inline | collapsible 的 trigger 部件 padding-inline 覆盖槽。 |
--xh-collapsible-trigger-py | trigger | padding-block | xh-action-profile=disclosure-trigger | --xh-_action-profile-padding-block | collapsible 的 trigger 部件 padding-block 覆盖槽。 |
--xh-collapsible-trigger-radius | trigger | border-radius | default | --xh-_action-profile-radius | collapsible 的 trigger 部件 border-radius 覆盖槽。 |
动效
动效角色:按压 · 状态 · 披露(见动效规范)。
共享关键帧 xh-disclosure-collapse · xh-disclosure-expand 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;rotate 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。
皮肤之外还有一段:退场由适配器的退场闸门把关,动画播完才真收起。
系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。
RTL
皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。
