跳转到内容

ToggleGroup 切换按钮组 ​

将多个切换按钮组合为单选或多选控件。

用法 ​

同时切换多个文本格式

组件结构 ​

加粗的是必需部件。

data-scope="toggle-group":root · item · hidden-input

示例 ​

受控状态 ​

由外部状态控制选中值

多选 ​

同时选择多个格式

禁用 ​

禁用单个选项

方向 ​

水平或垂直排列

宽度充满 ​

选项等分可用宽度

尺寸 ​

提供三种尺寸

变体 ​

设置整组外观

无分隔线 ​

省略分隔线部件

设计指引 ​

何时使用 ​

  • 在少量选项之间切换视图或显示方式。
  • 同时启用多个格式或工具状态。

何时不用 ​

  • 选项较多或需要搜索时,使用选择器。
  • 选项需要完整表单标签时,使用单选组。
  • 各项只执行操作时,使用按钮组。

特性 ​

  • 支持单选和多选模式。
  • 支持受控和非受控状态。
  • 支持水平、垂直、全宽和三种尺寸。
  • 默认在相邻条目之间显示分隔线,可通过 separators=false 关闭。
  • 使用 roving tabindex 管理组内键盘导航。
  • disallowEmpty 可阻止清空最后一个选中项。
  • collection 可统一提供标签和禁用状态。

组合 ​

最佳实践 ​

  • 每组使用二到五个简短选项。
  • 同组条目应保持相近宽度。
  • 必须保留一个选中项时启用 disallowEmpty。

反模式 ​

  • 不要用切换按钮组代替带面板关联的标签页。
  • 关闭 roving focus 时,应提供其他组内导航方式。

API 参考 ​

产物 ​

层值
自定义元素<xh-toggle-group>
Vue 组件XhToggleGroupHiddenInput XhToggleGroupItem XhToggleGroupRoot
组合式函数useToggleGroup
状态机toggleGroupMachine
皮肤@xihan-ui/styles/toggle-group.css

Props ​

属性类型必填说明
collectionToggleGroupNode[]条目数据,显示文本与禁用的事实源。提供后条目部件只需声明 value。 未提供时回到文本与禁用都写在条目部件上的方式。
valueToggleGroupValue选中值。提供即受控:内部不再自行修改,只发 onValueChange。
defaultValueToggleGroupValue
multipleboolean允许多项同时选中;false 时选中一项即替换其余。
disabledboolean整组禁用:条目全部 aria-disabled,点击与方向键都不生效。
disallowEmptyboolean不允许清空值:单选模式下点击当前选中项不再取消它,多选模式下不可移除最后一个。 默认 false(可以点击为无选中)。
variantActionVariant变体:solid / subtle / outline / ghost,决定段的底色与描边使用方式。
toneTone颜色:brand / neutral / success / warning / danger / info。
sizeSize尺寸:sm / md / lg。
fullWidthboolean撑满行宽:整组占满可用宽度,每段等分剩余空间。
separatorsboolean是否自动在相邻条目之间插入分隔线,默认 true。
namestring表单字段名。提供后隐藏输入才带 name 并参与提交。
orientationOrientation视觉排布,默认 horizontal。方向键接受的轴与它无关(四个方向键恒响应)。
dirDirection文字方向,默认 ltr;只改写左右方向键的语义,上下键与之无关。
loopboolean方向键到达末尾是否回绕,默认 true。
rovingFocusbooleanroving tabindex,默认开启:整组只占一个 Tab 位,组内依靠方向键移动。 关闭后每个条目自成一个 Tab 停靠点,方向键不再接管。
onValueChange(details: ToggleGroupValueChangeDetails) => voidvalue 变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。

ToggleGroupNode ​

collection 的元素。

字段类型必填说明
valuestring是
labelstring展示文本;默认回退为 value。
disabledboolean条目禁用:方向键跳过它,但它仍可聚焦、仍是导航起点。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changeToggleGroupValueChangeDetails选中值变化;detail 为 { value: string | string[] | null }(形态随 multiple 决定)

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhToggleGroupItemvaluestring是
XhToggleGroupItemdisabledboolean默认交给 connect 查询 collection,写死 false 会覆盖数据中的禁用。
XhToggleGroupRootrenderItem(node: ToggleGroupNodeMeta) => ReactNode每个条目的自定义内容;未提供时使用 collection 中的 label。
XhToggleGroupRootchildrenReactNode

状态 ​

公开状态写入 data-state。

部件取值
item'on' | 'off'

以下名称仅用于内部状态机。

状态:idle

事件:VALUE.SET · ITEM.TOGGLE · ITEM.FOCUS · GROUP.BLUR · FORM.RESET · PRESS.START · PRESS.END

判据:canPress

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
valuestring[]当前选中集合,恒为数组(单选时长度 ≤ 1)。
collectionreadonly ToggleGroupNodeMeta[]由 collection 推导的条目元信息,按数据顺序排列;未提供 collection 时为空数组。
focusedValuestring | null焦点在组外时为 null。
multipleboolean
disabledboolean
orientationOrientation
separatorsboolean
isSelected(value: string) => boolean
setValue(next: ToggleGroupValue) => void传单值 / 数组 / null 均可,内部按 multiple 归一。
getRootProps() => T['element']
getItemProps(props: ToggleGroupItemProps) => T['button']
getHiddenInputProps() => T['input']表单出口:整组只有一份,提交的即当前选中值。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Tab / Shift+TabrovingFocus 开启(默认)整组只占一个 Tab 位:焦点落到锚点条目,无锚点时先落容器再由它转投
ArrowRight / ArrowDownfocus in group, 组未禁用且 rovingFocus 开启焦点移到下一个可停留条目(禁用项跳过、尽头按 loop 回绕),不改选中;dir=rtl 时改由 ArrowLeft 承担
ArrowLeft / ArrowUpfocus in group, 组未禁用且 rovingFocus 开启焦点移到上一个可停留条目,不改选中;dir=rtl 时改由 ArrowRight 承担
Homefocus in group, 组未禁用且 rovingFocus 开启焦点移到首个可停留条目
Endfocus in group, 组未禁用且 rovingFocus 开启焦点移到末个可停留条目
Enter / Spacefocus on item, 条目未禁用切换该条目;条目是原生 button,这两个键由平台翻成 click
Enter / Spaceheld on item, 条目未禁用且组未禁用按住期间该条目投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,按住途中整组转入禁用也撤下。开关态与按压互相独立,切换照旧由平台把这一次按键翻成 click

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-orientationundefined | props.orientation
rootrole'group' | 'radiogroup'
itemaria-checkedundefined | 'true' | 'false'
itemaria-disabled'true' | 'false'
itemaria-pressed'true' | 'false' | undefined
itemroleundefined | 'radio'

样式参考 ​

皮肤 ​

@xihan-ui/styles/toggle-group.css 使用 [data-scope="toggle-group"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-full-width''(条件成立时才出现)
rootdata-orientationprops.orientation
rootdata-sizeprops.size
rootdata-toneprops.tone
rootdata-variantprops.variant
itemdata-disabled''(条件成立时才出现)
itemdata-pressed''(条件成立时才出现)
itemdata-state'on' | 'off'
itemdata-xh-action-control''
itemdata-xh-action-display'always'
itemdata-xh-action-profile'text'
itemdata-xh-action-sizeprops.size
itemdata-xh-action-variantprops.variant

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-toggle-group-item-bgitem--xh-ink-surface
background-color
default
focus-visible
xh-ink-surface
--xh-_toggle-group-item-bgtoggle-group 的 item 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-toggle-group-item-bg-activeitembackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_toggle-group-item-bg-activetoggle-group 的 item 部件 background-color 覆盖槽。
--xh-toggle-group-item-bg-disableditem--xh-ink-surface
background-color
disabled
xh-ink-surface
--xh-bg-mutedtoggle-group 的 item 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-toggle-group-item-bg-hoveritembackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_toggle-group-item-bg-hovertoggle-group 的 item 部件 background-color 覆盖槽。
--xh-toggle-group-item-bg-onitem--xh-ink-surface
background-color
focus-visible
state=on
xh-ink-surface
--xh-_toggle-group-item-bg-ontoggle-group 的 item 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-toggle-group-item-bg-on-activeitembackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
state=on
--xh-_toggle-group-item-bg-on-activetoggle-group 的 item 部件 background-color 覆盖槽。
--xh-toggle-group-item-bg-on-disableditem--xh-ink-surface
background-color
disabled
state=on
xh-ink-surface
--xh-_toggle-group-item-bg-ontoggle-group 的 item 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-toggle-group-item-bg-on-hoveritembackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
state=on
--xh-_toggle-group-item-bg-on-hovertoggle-group 的 item 部件 background-color 覆盖槽。
--xh-toggle-group-item-borderitemborder
border-color
default
disabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_toggle-group-item-bordertoggle-group 的 item 部件 border、border-color 覆盖槽。
--xh-toggle-group-item-border-disableditemborder-colordisabled--xh-border-subtletoggle-group 的 item 部件 border-color 覆盖槽。
--xh-toggle-group-item-border-onitemborder
border-color
disabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
state=on
--xh-_toggle-group-item-border-ontoggle-group 的 item 部件 border、border-color 覆盖槽。
--xh-toggle-group-item-border-on-disableditemborder-colordisabled
state=on
--xh-_toggle-group-item-border-ontoggle-group 的 item 部件 border-color 覆盖槽。
--xh-toggle-group-item-fgitemcolordefault
disabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_toggle-group-item-fgtoggle-group 的 item 部件 color 覆盖槽。
--xh-toggle-group-item-fg-disableditemcolordisabled--xh-fg-disabledtoggle-group 的 item 部件 color 覆盖槽。
--xh-toggle-group-item-fg-onitemcolordisabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
state=on
--xh-_toggle-group-item-fg-ontoggle-group 的 item 部件 color 覆盖槽。
--xh-toggle-group-item-fg-on-disableditemcolordisabled
state=on
--xh-_toggle-group-item-fg-ontoggle-group 的 item 部件 color 覆盖槽。
--xh-toggle-group-item-font-sizeitemfont-sizedefault--xh-_toggle-group-font-sizetoggle-group 的 item 部件 font-size 覆盖槽。
--xh-toggle-group-item-font-weightitemfont-weightdefault--xh-text-label-weighttoggle-group 的 item 部件 font-weight 覆盖槽。
--xh-toggle-group-item-gapitemgapdefault--xh-_toggle-group-gaptoggle-group 的 item 部件 gap 覆盖槽。
--xh-toggle-group-item-hitemblock-sizedefault--xh-_toggle-group-htoggle-group 的 item 部件 block-size 覆盖槽。
--xh-toggle-group-item-pxitempadding-inlinedefault--xh-_toggle-group-pxtoggle-group 的 item 部件 padding-inline 覆盖槽。
--xh-toggle-group-item-radiusitem
root
border-end-end-radius
border-end-start-radius
border-radius
border-start-end-radius
border-start-start-radius
first-child
first-of-type
last-child
last-of-type
orientation=horizontal
orientation=vertical
variant=outline
--xh-shape-controltoggle-group 的 item、root 部件 border-end-end-radius、border-end-start-radius、border-radius、border-start-end-radius、border-start-start-radius 覆盖槽。
--xh-toggle-group-item-shadowitembox-shadowdisabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
state=on
--xh-_toggle-group-item-highlight-ontoggle-group 的 item 部件 box-shadow 覆盖槽。
--xh-toggle-group-outline-colorrootbordervariant=outline--xh-_tone-border-controltoggle-group 的 root 部件 border 覆盖槽。
--xh-toggle-group-separator-colorrootbackgroundxh-toggle-group-separator--xh-fg-defaulttoggle-group 的 root 部件 background 覆盖槽。
--xh-toggle-group-separator-color-disabledrootbackgrounddisabled
xh-toggle-group-separator
--xh-border-subtletoggle-group 的 root 部件 background 覆盖槽。
--xh-toggle-group-separator-opacityrootopacityxh-toggle-group-separator--xh-control-separator-opacitytoggle-group 的 root 部件 opacity 覆盖槽。
--xh-toggle-group-separator-opacity-disabledrootopacitydisabled
xh-toggle-group-separator
--xh-control-separator-disabled-opacitytoggle-group 的 root 部件 opacity 覆盖槽。
--xh-toggle-group-separator-radiusrootborder-radiusxh-toggle-group-separator--xh-shape-pilltoggle-group 的 root 部件 border-radius 覆盖槽。
--xh-toggle-group-separator-sizerootblock-size
inline-size
orientation=horizontal
orientation=vertical
xh-toggle-group-separator
--xh-_group-separator-sizetoggle-group 的 root 部件 block-size、inline-size 覆盖槽。
--xh-toggle-group-separator-thicknessrootblock-size
inline-size
margin-block-start
margin-inline-start
orientation=horizontal
orientation=vertical
xh-toggle-group-separator
--xh-stroke-thintoggle-group 的 root 部件 block-size、inline-size、margin-block-start、margin-inline-start 覆盖槽。

动效 ​

动效角色:按压 · 状态(见动效规范)。

opacity 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License