切换按钮组 toggle-group
通用组件。三层同源:无头内核给出解剖与状态机,Vue 组件与自定义元素只是它的两层外壳,行为完全一致。
示例
基础用法
单选分段控件:条目由 collection 铺开,root 是 radiogroup、条目是 radio;整组只占一个 Tab 位,进组后四个方向键都能走
受控与不可清空
传了 value 就由宿主说了算;单选组再点一次当前项会清空成 null,disallow-empty 把这一手关掉
当前:leftdisallow-empty:comfortable,点不成空
多选
multiple 换的是整套 ARIA:root 退回 group、条目退回原生按钮 + aria-pressed,值也从字符串变成数组
当前:bold
禁用
条目一律 aria-disabled 而非原生 disabled:点不动,但焦点落得上去,仍能当方向键的起点
条目来自数据
条目由一份数组渲染,值、文案与禁用都写在数据里;运行期增删条目也照常,删掉的正好是选中项时由宿主把值收拾干净
当前:list
拦下一次切换
受控时 value-change 是唯一出口:宿主不写回,值就原样不动,条件不满足的那一段永远切不过去
当前:draft
整组换一档尺寸
高度、内边距与字号各是一个组件令牌,写在 root 上由整组条目继承,不必逐个条目改
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-toggle-group> |
| Vue 组件 | XhToggleGroupItem XhToggleGroupRoot |
| 组合式函数 | useToggleGroup |
| 状态机 | toggleGroupMachine |
| 皮肤 | @xihan-ui/styles/toggle-group.css |
解剖
部件名即 data-part 属性值,也是皮肤的选择器。加粗的是必备部件,不渲染它组件不工作(Web Components 适配器会在诊断通道上报 wc.missing-part)。
data-scope="toggle-group":root · item
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
collection | ToggleGroupNode[] | 条目数据,显示文本与禁用的事实源。给了它,条目部件只需报 value。 缺省即回到「文本与禁用都写在条目部件上」的老路。 | |
value | ToggleGroupValue | 选中值。给定即受控:内部不再自改,只发 onValueChange。 | |
defaultValue | ToggleGroupValue | ||
multiple | boolean | 允许多项同时选中;false 时选中一项即挤掉其余。 | |
disabled | boolean | 整组禁用:条目全部 aria-disabled,点击与方向键都不生效。 | |
disallowEmpty | boolean | 不许把值清空:单选模式下点当前选中项不再取消它,多选模式下摘不掉最后一个。 默认 false(可以点成无选中)。 | |
orientation | Orientation | 视觉排布,默认 horizontal。方向键接受的轴与它无关(四个方向键恒响应)。 | |
dir | Direction | 文字方向,默认 ltr;只改写左右方向键的语义,上下键与之无关。 | |
loop | boolean | 方向键走到尽头是否回绕,默认 true。 | |
rovingFocus | boolean | roving tabindex,默认开启:整组只占一个 Tab 位,组内靠方向键走。 关掉后每个条目自成一个 Tab 停靠点,方向键不再接管。 | |
onValueChange | (details: ToggleGroupValueChangeDetails) => void | value 变化意图回调;受控时是唯一出口,非受控随内部写入一并通知。 |
状态机
状态:idle
事件:VALUE.SET · ITEM.TOGGLE · ITEM.FOCUS · GROUP.BLUR
connect API
useToggleGroup 产出的对象。getXxxProps() 铺到对应部件的宿主元素上,其余是可读状态与操作入口。
| 成员 | 类型 | 说明 |
|---|---|---|
value | string[] | 当前选中集合,恒为数组(单选时长度 ≤ 1)。 |
collection | readonly ToggleGroupNodeMeta[] | collection 推出的条目元信息,按数据顺序排列;没给 collection 即空数组。 |
focusedValue | string | null | 焦点在组外时为 null。 |
multiple | boolean | |
disabled | boolean | |
isSelected | (value: string) => boolean | |
setValue | (next: ToggleGroupValue) => void | 传单值 / 数组 / null 皆可,内部按 multiple 归一。 |
getRootProps | () => T['element'] | |
getItemProps | (props: ToggleGroupItemProps) => T['button'] |
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Tab / Shift+Tab | rovingFocus 开启(默认) | 整组只占一个 Tab 位:焦点落到锚点条目,无锚点时先落容器再由它转投 |
ArrowRight / ArrowDown | focus in group, 组未禁用且 rovingFocus 开启 | 焦点移到下一个可停留条目(禁用项跳过、尽头按 loop 回绕),不改选中;dir=rtl 时改由 ArrowLeft 承担 |
ArrowLeft / ArrowUp | focus in group, 组未禁用且 rovingFocus 开启 | 焦点移到上一个可停留条目,不改选中;dir=rtl 时改由 ArrowRight 承担 |
Home | focus in group, 组未禁用且 rovingFocus 开启 | 焦点移到首个可停留条目 |
End | focus in group, 组未禁用且 rovingFocus 开启 | 焦点移到末个可停留条目 |
Enter / Space | focus on item, 条目未禁用 | 切换该条目;条目是原生 button,这两个键由平台翻成 click |
