Toggle Group 切换按钮组

FluereToggleGroup 把若干 FluereToggleButton 组成一个语义化的组:role="group"、组内单一 Tab 停靠点(roving tabindex)、方向键 / Home / End 移动焦点,取值支持单选(string)与多选(string[])。

WinUI 没有 ToggleGroup 控件:microsoft-ui-xaml tag winui3/release/2.5.1 里只有 ToggleButton(git ls-tree -r winui3/release/2.5.1 | grep -i togglegroup 为空)。所以本组件的分工是——每一项的视觉严格以 WinUI ToggleButton 为准(每项直接复用 FluereToggleButton 的同一份 SFC 样式,不复制状态表),分组语义则以 WAI-ARIA 的 toggle button group 契约 + reka-ui 为准,这一层没有 A 级源码可对。

基础用法(单选)

v-model 绑定当前取值(string),FluereToggleGroupItem 的 value 决定各自的值。单选组只保留一个选中项;再次点击已选中项会取消选中(值为 undefined,ARIA toggle button 允许全部未按下,与 RadioGroup 的「必须选中一项」不同)。

当前对齐:左对齐

单选组只保留一个选中项;再次点击已选中项会取消选中(ARIA toggle button 语义)。Tab 进入组后用方向键或 Home / End 在项间移动焦点。悬停与按下时底色各加深一档(未选中档 NeutralBackground1Hover / Pressed,选中档 BrandBackgroundHover / Pressed)。

单选

若业务要求「必须选中一项」,在 update:modelValue 里把 undefined 回退成旧值即可:

<FluereToggleGroup :model-value="align" @update:model-value="(value) => (align = value ?? align)">
  <!-- … -->
</FluereToggleGroup>

多选

type="multiple" 时 v-model 是 string[],点击即增删数组元素。type 缺省时按 modelValue / defaultValue 是否为数组推断(数组 → 多选)。

已应用:加粗

多选

禁用

组上 disabled 会叠加到每一项(整组不可交互,方向键也不会进入);只禁用个别项时在对应 FluereToggleGroupItem 上传 disabled,方向键会跳过它。

整组禁用

单项禁用

禁用

尺寸、纵向与受控 / 非受控

组上 size 决定组内统一尺寸(FluereToggleButton 的 24 / 32 / 40 三档),项自己的 size 优先。orientation="vertical" 改为竖排,同时把方向键从左右换成上下。不传 modelValue 时是非受控(底座自管状态,default-value 给初值),传了就是受控。

小(24px,非受控 default-value)

中(32px,v-model 受控)

大(40px,纵向组)

尺寸与受控 / 非受控

无障碍

  • 容器是 role="group";组内没有可见标题时用 label 提供 aria-label。
  • 每一项都是原生 <button type="button">,选中态由 aria-pressed 表达(三态项为 "mixed");可访问名来自可见文本,纯图标项用 label。
  • roving tabindex:Tab 先把焦点交给组容器,再落到组内当前项;随后用 ← / →(纵向组为 ↑ / ↓)、Home / End 在项之间移动焦点,跳过禁用项,loop(缺省 true)让首尾循环。焦点环沿用 0.4.3 的双层焦点矩形原语(useFocusVisual)。
  • Space / Enter 触发切换由原生 <button> 保证。

规格来源

项值
控件WinUI 3 没有 ToggleGroup 控件;视觉以 ToggleButton 为准,分组语义以 WAI-ARIA / reka-ui 为准
tag/commitwinui3/release/2.5.1 / ba3a8d5(本地快照 temp/winui-src)
视觉关键文件src/controls/dev/CommonStyles/ToggleButton_themeresources.xaml(完整映射表见 toggle-button.vue 文件头)
行为关键文件src/dxaml/xcp/dxaml/lib/ToggleButton_Partial.cpp(ChangeVisualState / OnToggleImpl)
分组底座reka-ui 2.10.1:ToggleGroupRoot(取值语义 + roving focus)、RovingFocusItem(roving tabindex)

实现要点

  • 视觉只有一处:FluereToggleGroupItem 渲染的就是 FluereToggleButton(toggle-group-item.vue 里没有第二个 <style> 块、也没有任何颜色 / 描边声明),组里组外的按钮共用同一份状态表、几何与焦点原语,不会两套漂移。
  • 底座分工:reka 的 ToggleGroupRoot 产出 role="group"、管 single / multiple 取值、提供 roving focus;项这边用 RovingFocusItem(不用 reka 的 ToggleGroupItem——它渲染的是 reka 自己的中性按钮外观,拿不到本库的 WinUI 外观)+ 注入 reka 的组上下文读写取值。
  • 单选可清空:沿用 reka / ARIA toggle button 语义——再次点击已选中项 → 值变 undefined;要「必须选中一项」用上面「基础用法」里的一行配方。
  • 受控 / 非受控:modelValue === undefined 时原样交给底座自管,并用底座抛回的 update:modelValue 回写本地状态;受控时只认 Prop。
  • 组级尺寸:WinUI 里「一组切换按钮」只是并排的按钮,没有组级尺寸概念;本库用自有 provide/inject 把组上的 size 下发给项(--fui-* 之外的库内扩展,见 toggle-group/context.ts),避免同组 24 / 32 / 40 混排。
  • 已知缺口:① 高对比度未实现(与库内其它组件一致,WinUI HC 下这些画刷走 SystemColor*);② 组容器间距是本库自定(--spacingHorizontalXS 4px),WinUI 无对应控件可对;③ 未暴露 reka 的 name / required,需要随表单提交时请自行在组外补隐藏 input。

API

FluereToggleGroup

属性(Props)类型默认说明
modelValuestring | string[]—当前取值(v-model);不传则非受控
defaultValuestring | string[]—非受控初始值
type'single' | 'multiple'按取值形状推断单选(string)/ 多选(string[]);数组 → 'multiple'
disabledbooleanfalse禁用整组
orientation'horizontal' | 'vertical''horizontal'排布方向;同时决定方向键用左右还是上下
loopbooleantrue方向键在首尾项之间循环
dir'ltr' | 'rtl'—阅读方向(一般无需设置)
size'small' | 'medium' | 'large'—组内统一尺寸档;项自己的 size 优先
labelstring—组的可访问名称(等价 aria-label)
事件(Emits)载荷说明
update:modelValuestring | string[] | undefined取值变化;单选组取消选中时载荷为 undefined
插槽(Slots)说明
default组内的 FluereToggleGroupItem 列表

FluereToggleGroupItem

属性(Props)类型默认说明
valuestring—该按钮在组内的取值(必填)
disabledbooleanfalse是否禁用该项(叠加在组 disabled 之上)
size'small' | 'medium' | 'large'继承组尺寸档;缺省继承组上的 size,再缺省为 'medium'
blockbooleanfalse撑满容器宽度
labelstring—可访问名(等价 aria-label);纯图标项必须提供
iconComponent—图标组件(@fluere-vue/icons),装饰性、对 AT 隐藏
iconOnlybooleanfalse仅显示图标(等比紧凑模式)
插槽(Slots)说明
default按钮文本
icon替换内建图标

FluereToggleGroupItem 必须位于 FluereToggleGroup 内:组上下文由 reka 的 provide/inject 提供,脱离组会直接抛出可读错误。它的外观、禁用配色、焦点环全部来自 FluereToggleButton,因此那三张表同样适用于组内每一项。

v0.5.0-alpha.1