Collapsible 折叠面板
点一下触发元素,在原地展开 / 收起一块内容。适合「高级设置」「显示更多」这类默认不必看、 需要时就近展开的区域。
触发元素放进 trigger 插槽,点击切换展开态;内容放默认插槽。受控值走 open + ptChange,ptChange 只在用户点击切换时发。
<pt-collapsible>
<pt-button slot="trigger" variant="secondary" size="sm">显示全部 4 个包</pt-button>
<ul>
<li>@ptengine/ui</li>
<li>@ptengine/ui-react</li>
<li>@ptengine/ui-vue</li>
<li>@ptengine/ui-angular</li>
</ul>
</pt-collapsible>open 让它一开始就展开;disabled 只拦住点击切换,外部改 open 照样生效。触发元素的外观由使用方自己禁用。
<pt-collapsible open>
<pt-button slot="trigger" variant="ghost" size="sm">收起</pt-button>
<p>一开始就是展开的内容。</p>
</pt-collapsible>
<pt-collapsible disabled>
<pt-button slot="trigger" variant="secondary" size="sm" disabled>不可展开</pt-button>
<p>禁用时点不开。</p>
</pt-collapsible>受控值
受控属性是 open,事件是 ptChange(detail 是切换后的 open):
- Vue:
<PtCollapsible v-model="expanded"> - Angular:
<pt-collapsible [(ngModel)]="expanded">,standalone 组件要在imports里加上OpenValueAccessor,否则ngModel找不到 accessor - React:
<PtCollapsible open={expanded} onPtChange={e => setExpanded(e.detail)}>
ptChange 只在用户点击触发元素切换时发;外部直接改 open 只改状态,不发事件, 不会和双向绑定来回打转。
触发元素
触发元素放进 trigger 插槽,通常是一个 <button> 或 pt-button。键盘由它自己负责: 按钮上的 Enter / Space 本来就会派发 click,组件只听 click,不另拦按键。
组件会在触发元素上维护两个属性(WAI-ARIA Disclosure 模式):
aria-expanded:跟着open走;aria-controls:指向默认插槽里的内容元素,它们没有id时自动补一个。默认插槽里只有文本时不写。
触发元素被换掉时,新元素补上这两个属性,旧元素上的清掉。
读屏与 pt-button
属性写在触发元素的宿主上。原生 <button> 直接生效;pt-button 的真实按钮在它自己的影子树里, 宿主上的 aria-expanded 目前读屏读不到。对读屏体验要求高的地方优先用原生按钮。
禁用
disabled 只拦住点击切换,外部改 open 照样生效。它不改触发元素的外观 —— 触发元素是你的,需要一起变灰就在触发元素上写 disabled。
展开与收起
内容区用 grid-template-rows 在 0fr 与 1fr 之间过渡,不需要量高度,内容变高变矮也跟着走。 系统开了「减少动态效果」时直接切换。
收起时内容区 inert 且不可见:Tab 不会走进看不见的内容,读屏也不会读到它。
API
属性
| 属性 | Attribute | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
disabled | disabled | boolean | false | 禁用:点击触发元素不再切换。不影响通过 `open` 属性控制 |
open | open | boolean | false | 是否展开。用户点击触发元素时组件自己改写它并发 `ptChange`;外部直接赋值不发事件 |
事件
| 事件 | detail 类型 | 说明 |
|---|---|---|
ptChange | boolean | 用户点击触发元素切换展开态后触发,detail 是切换后的 `open`。 **不冒泡**(与其它组件的默认相反):折叠面板天然会嵌套,而三个框架的双向绑定都挂在宿主上 —— Vue 的 v-model 只按 tagName 过滤冒泡事件,内外层同是 PT-COLLAPSIBLE 拦不住;React 的 onPtChange 也会收到内层的事件。冒泡的话收起内层会连带改写外层绑定、把外层也收起。 要在外层容器上统一监听,用捕获阶段(`addEventListener('ptChange', fn, true)`)。 |
插槽
| 名称 | 说明 |
|---|---|
(默认) | 折叠的内容 |
trigger | 触发元素,点击切换展开态。通常是一个 button / pt-button |
可定制的内部元素(::part())
| part | 说明 |
|---|---|
content | 内容区(做高度过渡的那一层) |