RadioGroup 单选框组

从多项中选择一项的单选框组。

<script setup lang="ts">
  import { ref } from 'vue'
  import { RadioGroup } from '@hina-ui/vue'

  const status = ref('doing')
  const options = [
    { value: 'wish', label: '想看' },
    { value: 'doing', label: '在看' },
    { value: 'done', label: '看过' },
  ]
</script>

<template>
  <RadioGroup v-model="status" :options="options" aria-label="收藏状态" />
</template>
vue

用法

import { RadioGroup } from '@hina-ui/vue'
ts

单选框组按 options 渲染一列单选框,v-model 绑定选中的值,选项的类型见 Select。未声明的属性都会传给根元素,应当用 aria-label 或者 aria-labelledby 给整组命名。单选框只以组的形式提供,没有单独的 Radio。

当前值:system

<script setup lang="ts">
  import { ref } from 'vue'
  import { RadioGroup, Stack, Text } from '@hina-ui/vue'

  const theme = ref('system')
  const options = [
    { value: 'light', label: '浅色' },
    { value: 'dark', label: '深色' },
    { value: 'system', label: '跟随系统' },
  ]
</script>

<template>
  <Stack gap="sm">
    <RadioGroup v-model="theme" :options="options" aria-label="主题" />
    <Text size="sm" tone="muted">当前值:{{ theme }}</Text>
  </Stack>
</template>
vue

示例

横排

orientation 为 horizontal 时选项横向排列,放不下时折行。

<script setup lang="ts">
  import { ref } from 'vue'
  import { RadioGroup } from '@hina-ui/vue'

  const sort = ref('updated')
  const options = [
    { value: 'updated', label: '最近更新' },
    { value: 'rating', label: '评分' },
    { value: 'title', label: '标题' },
  ]
</script>

<template>
  <RadioGroup v-model="sort" :options="options" orientation="horizontal" aria-label="排序方式" />
</template>
vue

描述

选项的 description 显示在文字下方。

<script setup lang="ts">
  import { ref } from 'vue'
  import { RadioGroup } from '@hina-ui/vue'

  const visibility = ref('friends')
  const options = [
    { value: 'public', label: '公开', description: '所有人都能看到这条动态' },
    { value: 'friends', label: '仅好友', description: '只有互相关注的用户能看到' },
    { value: 'private', label: '仅自己', description: '只有你自己能看到' },
  ]
</script>

<template>
  <RadioGroup v-model="visibility" :options="options" aria-label="可见范围" />
</template>
vue

设置行

使用 control-placement="end" 将控件放到文案末端,配合 block 撑满容器宽度。说明始终位于标题下方;start 和 end 会跟随文字方向。启用 block 后,纵向选项各自撑满一行,横向选项等分行宽。orientation 仍表示多个选项的排列方向。

RTL 布局请向组传入 dir="rtl",或通过 Reka 的方向提供器配置。

<script setup lang="ts">
  import { ref } from 'vue'
  import { RadioGroup } from '@hina-ui/vue'

  const visibility = ref('friends')
  const options = [
    { value: 'public', label: '公开', description: '所有人都能看到这条动态' },
    { value: 'friends', label: '仅好友', description: '只有互相关注的用户能看到' },
    { value: 'private', label: '仅自己', description: '只有你自己能看到' },
  ]
</script>

<template>
  <RadioGroup
    v-model="visibility"
    control-placement="end"
    block
    :options="options"
    aria-label="可见范围"
  />
</template>
vue

尺寸

size 下发到每个单选框,圆与文字随档位变化。

<script setup lang="ts">
  import { RadioGroup, Inline } from '@hina-ui/vue'

  const options = [
    { value: 'wish', label: '想看' },
    { value: 'doing', label: '在看' },
  ]
</script>

<template>
  <Inline gap="lg" align="start">
    <RadioGroup size="sm" :options="options" model-value="wish" aria-label="小号" />
    <RadioGroup size="md" :options="options" model-value="wish" aria-label="中号" />
    <RadioGroup size="lg" :options="options" model-value="wish" aria-label="大号" />
  </Inline>
</template>
vue

状态

选项上的 disabled 只禁用该项,组上的 disabled 禁用整组;invalid 落到每个圆。

<script setup lang="ts">
  import { RadioGroup, Inline } from '@hina-ui/vue'

  const options = [
    { value: 'wish', label: '想看' },
    { value: 'doing', label: '在看' },
    { value: 'done', label: '看过', disabled: true },
  ]
</script>

<template>
  <Inline gap="lg" align="start">
    <RadioGroup :options="options" model-value="wish" aria-label="含禁用项" />
    <RadioGroup disabled :options="options" model-value="wish" aria-label="整组禁用" />
    <RadioGroup invalid :options="options" aria-label="校验未通过" />
  </Inline>
</template>
vue

定制内容

option 插槽定制每一项的文字。

组件从 options 推断完整选项类型,插槽中的 option 保留额外字段及其类型;v-model 仍绑定 value。类型定义见 Select。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Monitor, Moon, Sun } from '@lucide/vue'
  import { RadioGroup, Inline } from '@hina-ui/vue'

  const icons = { light: Sun, dark: Moon, system: Monitor }
  const theme = ref('light')
  const options = [
    { value: 'light', label: '浅色' },
    { value: 'dark', label: '深色' },
    { value: 'system', label: '跟随系统' },
  ]
</script>

<template>
  <RadioGroup v-model="theme" :options="options" aria-label="主题">
    <template #option="{ option }">
      <Inline as="span" gap="xs" align="center">
        <component :is="icons[option.value as keyof typeof icons]" class="text-muted size-4" />
        {{ option.label }}
      </Inline>
    </template>
  </RadioGroup>
</template>
vue

在表单中

放进 FormField 后,标签通过 aria-labelledby 关联到整组,错误信息由字段渲染;校验规则与提交交给 Form。

<script setup lang="ts">
  import { reactive, ref } from 'vue'
  import * as v from 'valibot'
  import { Button, Form, FormField, RadioGroup, Text } from '@hina-ui/vue'

  const options = [
    { label: '公开', value: 'public', description: '所有人可见' },
    { label: '仅关注者', value: 'followers', description: '关注你的人可见' },
    { label: '私密', value: 'private', description: '只有自己可见' },
  ]

  const schema = v.object({
    visibility: v.string('请选择可见范围'),
  })

  const values = reactive({ visibility: null as string | number | null })
  const saved = ref('')

  async function save(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    saved.value = JSON.stringify(data)
  }
</script>

<template>
  <Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
    <FormField name="visibility" label="可见范围" required>
      <RadioGroup v-model="values.visibility" :options="options" />
    </FormField>
    <Button type="submit" :loading="submitting" class="self-start">发布</Button>
    <Text v-if="saved" tone="muted" size="sm">已发布:{{ saved }}</Text>
  </Form>
</template>
vue

行为

  • 点击文字或者圆即选中该项,已选中的项不能再点成未选中。
  • 整组只占一个 Tab 停靠点,落在已选项上;方向键在项之间移动焦点并同时选中,禁用项会被跳过,到底后回到另一端。与原生单选框一致。

无障碍

  • 根元素是 role="radiogroup",通过 aria-label 或者 aria-labelledby 命名;每一项是 role="radio" 的按钮,带 aria-checked,外层 label 的文字即名称。
  • invalid 会给每个单选框设置 aria-invalid。

API

Props

T extends SelectOption 从 options 推断,默认是 SelectOption。

属性
类型
默认值
说明
modelValue
string | number | null
—
选中的值
options
T[]
—
选项,类型见 Select
orientation
'vertical' | 'horizontal'
'vertical'
排列方向
size
'sm' | 'md' | 'lg'
'md'
每个单选框的尺寸
controlPlacement
'start' | 'end'
'start'
控件相对于文案的位置
block
boolean
false
整组撑满,横向选项等分宽度
disabled
boolean
false
是否禁用整组
invalid
boolean
false
是否处于校验未通过状态
class
string
—
追加至根元素的类名

插槽

插槽
参数
说明
option
{ option: T }
每一项的文字

事件

事件
参数
说明
update:modelValue
value: string | number
选中值变化