Switch 开关

在开与关之间切换。

'use client'

import { useState } from 'react'
import { Switch } from '@hina-ui/react'

export default function Demo() {
  const [autoplay, setAutoplay] = useState(true)

  return (
    <Switch checked={autoplay} onCheckedChange={setAutoplay}>
      自动播放下一话
    </Switch>
  )
}
tsx

用法

import { Switch } from '@hina-ui/react'
ts

开关表示一个立即生效的设置,与需要提交的复选框不同。checked / onCheckedChange 绑定布尔值,children 是文字,点文字与点轨道都会切换。未声明的属性都会传给内部的轨道元素。

当前值:false

'use client'

import { useState } from 'react'
import { Stack, Switch, Text } from '@hina-ui/react'

export default function Demo() {
  const [nsfw, setNsfw] = useState(false)

  return (
    <Stack gap="sm">
      <Switch checked={nsfw} onCheckedChange={setNsfw}>
        显示限制级内容
      </Switch>
      <Text size="sm" tone="muted">
        当前值:{String(nsfw)}
      </Text>
    </Stack>
  )
}
tsx

示例

描述

description 在文字下方补一行说明,字号比文字小一档。

'use client'

import { useState } from 'react'
import { Switch } from '@hina-ui/react'

export default function Demo() {
  const [sync, setSync] = useState(true)

  return (
    <Switch
      checked={sync}
      onCheckedChange={setSync}
      description="在其他设备上打开同一部作品时,从上次的位置继续。"
    >
      同步阅读进度
    </Switch>
  )
}
tsx

设置行

使用 controlPlacement="end" 将控件放到文案末端,配合 block 撑满容器宽度。说明始终位于标题下方;start 和 end 会跟随文字方向。

'use client'

import { useState } from 'react'
import { Switch } from '@hina-ui/react'

export default function Demo() {
  const [sync, setSync] = useState(true)

  return (
    <Switch
      checked={sync}
      onCheckedChange={setSync}
      controlPlacement="end"
      block
      description="在其他设备上打开同一部作品时,从上次的位置继续。"
    >
      同步阅读进度
    </Switch>
  )
}
tsx

尺寸

size 有 sm、md、lg 三档,轨道高分别为 20、24、28 像素,文字随档位变化。

import { Stack, Switch } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="sm">
      <Switch size="sm" checked>
        小号
      </Switch>
      <Switch size="md" checked>
        中号
      </Switch>
      <Switch size="lg" checked>
        大号
      </Switch>
    </Stack>
  )
}
tsx

状态

invalid 给轨道加上警示色边框;disabled 禁用整个控件。

import { Stack, Switch } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="sm">
      <Switch invalid>必须开启才能继续</Switch>
      <Switch disabled>已禁用</Switch>
      <Switch disabled checked>
        已禁用且开启
      </Switch>
    </Stack>
  )
}
tsx

仅轨道

没有文字时只渲染轨道,此时必须用 aria-label 命名。

'use client'

import { useState } from 'react'
import { Inline, Switch } from '@hina-ui/react'

export default function Demo() {
  const [dark, setDark] = useState(false)

  return (
    <Inline gap="sm">
      <Switch checked={dark} onCheckedChange={setDark} aria-label="深色模式" />
    </Inline>
  )
}
tsx

在表单中

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

'use client'

import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, Input, Switch, Text } from '@hina-ui/react'

const schema = v.pipe(
  v.object({
    isPublic: v.boolean(),
    summary: v.string(),
  }),
  v.forward(
    v.check(input => !input.isPublic || input.summary.trim().length > 0, '公开的作品需要填写简介'),
    ['summary'],
  ),
)

export default function Demo() {
  const [values, setValues] = useState({ isPublic: true, summary: '' })
  const [saved, setSaved] = useState('')

  async function save(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    setSaved(JSON.stringify(data))
  }

  return (
    <Form values={values} rules={schema} className="w-80" onSubmit={save}>
      {({ submitting }) => (
        <>
          <FormField name="isPublic">
            <Switch
              checked={values.isPublic}
              onCheckedChange={isPublic => setValues({ ...values, isPublic })}
            >
              公开显示
            </Switch>
          </FormField>
          <FormField name="summary" label="简介">
            <Input
              value={values.summary}
              onValueChange={summary => setValues({ ...values, summary })}
            />
          </FormField>
          <Button type="submit" loading={submitting} className="self-start">
            保存
          </Button>
          {saved && (
            <Text tone="muted" size="sm">
              已保存:{saved}
            </Text>
          )}
        </>
      )}
    </Form>
  )
}
tsx

行为

  • 点击文字或者轨道都会切换,拇指滑到另一侧,轨道同时换色。键盘 Tab 落在轨道上,空格切换。
  • 悬停整个控件时轨道落墨,按下时加深。

无障碍

  • 轨道是 role="switch" 的按钮,带 aria-checked。根元素是 label,文字即名称。
  • 没有文字时通过 aria-label 或者 aria-labelledby 命名。
  • invalid 会同时设置 aria-invalid。

API

Props

属性
类型
默认值
说明
checked
boolean
false
是否开启
size
'sm' | 'md' | 'lg'
'md'
尺寸
description
string
—
文字下方的说明
controlPlacement
'start' | 'end'
'start'
控件相对于文案的位置
block
boolean
false
整行撑满容器宽度
disabled
boolean
false
是否禁用
invalid
boolean
false
是否处于校验未通过状态
className
string
—
追加至根元素的类名

内容属性

属性
参数
说明
children
—
文字

回调

回调
参数
说明
onCheckedChange
value: boolean
值变化时