Toggle 切换按钮

可按下与松开的双态按钮。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Bold, Italic, Underline } from '@lucide/vue'
  import { Inline, Toggle } from '@hina-ui/vue'

  const bold = ref(true)
  const italic = ref(false)
  const underline = ref(false)
</script>

<template>
  <Inline gap="xs">
    <Toggle v-model="bold" label="加粗">
      <template #icon><Bold /></template>
    </Toggle>
    <Toggle v-model="italic" label="斜体">
      <template #icon><Italic /></template>
    </Toggle>
    <Toggle v-model="underline" label="下划线">
      <template #icon><Underline /></template>
    </Toggle>
  </Inline>
</template>
vue

用法

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

切换按钮是一个可以保持按下状态的按钮,用于加粗、收藏、筛选这类随时可以打开或者关闭的操作。v-model 绑定是否按下,默认插槽是文字,#icon 放前置图标。外观取自 Button 的 ghost 与 outline 两种形态,按下后字色与墨转为品牌色。未声明的属性都会传给按钮元素。

显示全部作品

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

  const onlyDone = ref(false)
</script>

<template>
  <Stack gap="sm" align="start">
    <Toggle v-model="onlyDone">
      <template #icon><ListFilter /></template>
      仅看已完结
    </Toggle>
    <Text tone="muted" size="sm">{{ onlyDone ? '只显示已完结的作品' : '显示全部作品' }}</Text>
  </Stack>
</template>
vue

它与 Switch 的区别在于场合:开关表示一项设置,总是带文字说明,独立成行;切换按钮是一个动作,常常只有图标,成排出现在工具栏里。与 Chip 的可选中形态相比,Chip 是行内的条目,胶囊形且尺寸更小。

示例

图标型

label 提供无障碍名称。没有默认文字插槽时,按钮使用正方形的图标型尺寸;同时提供文字时,保留常规按钮内边距。在 TooltipProvider 内,label 还会作为 Tooltip 的文字提示。#pressed-icon 给出按下时的图标,两个图标之间交叉淡变。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Bookmark, Star } from '@lucide/vue'
  import { Inline, Toggle } from '@hina-ui/vue'

  const starred = ref(true)
  const saved = ref(false)
</script>

<template>
  <Inline gap="xs">
    <Toggle v-model="starred" label="收藏" pill>
      <template #icon><Star /></template>
      <template #pressed-icon><Star fill="currentColor" /></template>
    </Toggle>
    <Toggle v-model="saved" label="稍后再看" pill>
      <template #icon><Bookmark /></template>
      <template #pressed-icon><Bookmark fill="currentColor" /></template>
    </Toggle>
  </Inline>
</template>
vue

工具栏

成排的图标型切换按钮各自持有一个布尔值,互不影响。需要恰好一项被选中时应使用 SegmentedControl。

<script setup lang="ts">
  import { reactive } from 'vue'
  import { Bold, Italic, Strikethrough, Underline } from '@lucide/vue'
  import { Inline, Toggle } from '@hina-ui/vue'

  const marks = reactive({ bold: false, italic: false, underline: false, strike: false })
</script>

<template>
  <Inline gap="xs">
    <Toggle v-model="marks.bold" label="加粗" size="sm">
      <template #icon><Bold /></template>
    </Toggle>
    <Toggle v-model="marks.italic" label="斜体" size="sm">
      <template #icon><Italic /></template>
    </Toggle>
    <Toggle v-model="marks.underline" label="下划线" size="sm">
      <template #icon><Underline /></template>
    </Toggle>
    <Toggle v-model="marks.strike" label="删除线" size="sm">
      <template #icon><Strikethrough /></template>
    </Toggle>
  </Inline>
</template>
vue

形态

ghost 为默认,outline 带发丝线边框,适合单独放在内容里。

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

  const ghost = ref(true)
  const outline = ref(true)
</script>

<template>
  <Inline gap="sm">
    <Toggle v-model="ghost" variant="ghost">
      <template #icon><Pin /></template>
      置顶
    </Toggle>
    <Toggle v-model="outline" variant="outline">
      <template #icon><Pin /></template>
      置顶
    </Toggle>
  </Inline>
</template>
vue

尺寸

size 有 sm、md、lg 三档,与 Button 逐档相同。

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

<template>
  <Inline gap="sm" align="center">
    <Toggle size="sm" variant="outline" model-value>
      <template #icon><Heart /></template>
      小号
    </Toggle>
    <Toggle size="md" variant="outline" model-value>
      <template #icon><Heart /></template>
      中号
    </Toggle>
    <Toggle size="lg" variant="outline" model-value>
      <template #icon><Heart /></template>
      大号
    </Toggle>
  </Inline>
</template>
vue

状态

disabled 禁用按钮,禁用时保留按下状态。

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

<template>
  <Inline gap="sm">
    <Toggle variant="outline" disabled>
      <template #icon><Bold /></template>
      已禁用
    </Toggle>
    <Toggle variant="outline" disabled model-value>
      <template #icon><Bold /></template>
      禁用且按下
    </Toggle>
  </Inline>
</template>
vue

在表单中

放进 FormField 后,错误信息由字段渲染并关联到按钮;校验规则与提交交给 Form。切换按钮自带文字,字段不必再写标签;它的值决定其他字段是否必填时,规则写在对象层,再指定错误落在哪个字段。

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

  const schema = v.pipe(
    v.object({
      pinned: v.boolean(),
      title: v.string(),
    }),
    v.forward(
      v.check(input => !input.pinned || input.title.trim().length > 0, '置顶的公告需要标题'),
      ['title'],
    ),
  )

  const values = reactive({ pinned: true, title: '' })
  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="title" label="标题">
      <Input v-model="values.title" />
    </FormField>
    <FormField name="pinned">
      <Toggle v-model="values.pinned" variant="outline">置顶</Toggle>
    </FormField>
    <Button type="submit" :loading="submitting" class="self-start">发布</Button>
    <Text v-if="saved" tone="muted" size="sm">已发布:{{ saved }}</Text>
  </Form>
</template>
vue

行为

  • 点击、空格或者 Enter 在按下与松开之间切换。
  • 按下态由品牌色的墨与字色表达,悬停与按压的墨叠加在其上。
  • 有 #pressed-icon 时两个图标交叉淡变,不硬切。

无障碍

  • 根元素是带 aria-pressed 的按钮,读屏软件读作「切换按钮,已按下」。
  • 图标型必须提供 label;文字型默认以文字命名,也可通过 label 补充完整名称。

API

Props

属性
类型
默认值
说明
modelValue
boolean
false
是否按下
label
string
—
无障碍名称;无文字插槽时使用图标型尺寸
tooltip
boolean
true
是否显示 label 对应的文字提示
side
'top' | 'right' | 'bottom' | 'left'
'top'
文字提示的位置
variant
'ghost' | 'outline'
'ghost'
形态
size
'sm' | 'md' | 'lg'
'md'
尺寸
pill
boolean
false
是否为胶囊形
disabled
boolean
false
是否禁用
ripple
boolean
true
是否显示按压波纹
class
string
—
追加至按钮元素的类名

插槽

插槽
参数
说明
default
—
文字
icon
{ pressed: boolean }
前置图标
pressed-icon
—
按下时的前置图标

事件

事件
参数
说明
update:modelValue
value: boolean
按下状态变化