Checkbox 复选框

勾选一项,或者从多项中勾选若干。

'use client'

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

export default function Demo() {
  const [notify, setNotify] = useState<boolean | 'indeterminate'>(true)

  return (
    <Checkbox checked={notify} onCheckedChange={setNotify}>
      有新章节时通知我
    </Checkbox>
  )
}
tsx

用法

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

复选框把方框与它的文字合成一个可点击的整体。checked / onCheckedChange 绑定布尔值,值为 'indeterminate' 时显示半选。children 是文字,点文字与点方框都会切换。未声明的属性都会传给内部的方框元素。

当前值:false

'use client'

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

export default function Demo() {
  const [remember, setRemember] = useState<boolean | 'indeterminate'>(false)

  return (
    <Stack gap="sm">
      <Checkbox checked={remember} onCheckedChange={setRemember}>
        记住登录状态
      </Checkbox>
      <Text size="sm" tone="muted">
        当前值:{String(remember)}
      </Text>
    </Stack>
  )
}
tsx

示例

描述

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

'use client'

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

export default function Demo() {
  const [weekly, setWeekly] = useState<boolean | 'indeterminate'>(true)

  return (
    <Checkbox
      checked={weekly}
      onCheckedChange={setWeekly}
      description="每周一发送本周的更新汇总,可以随时退订。"
    >
      订阅周报
    </Checkbox>
  )
}
tsx

设置行

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

'use client'

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

export default function Demo() {
  const [weekly, setWeekly] = useState<boolean | 'indeterminate'>(true)

  return (
    <Checkbox
      checked={weekly}
      onCheckedChange={setWeekly}
      controlPlacement="end"
      block
      description="每周一发送本周的更新汇总,可以随时退订。"
    >
      订阅周报
    </Checkbox>
  )
}
tsx

半选

值为 'indeterminate' 时显示横线。常见的用法是「全选」:子项部分选中时父项半选,点击父项后全部选中。

'use client'

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

const types = ['Galgame', '轻小说', '漫画']

export default function Demo() {
  const [picked, setPicked] = useState(['Galgame'])

  const all: boolean | 'indeterminate' =
    picked.length === 0 ? false : picked.length === types.length ? true : 'indeterminate'

  function setAll(value: boolean | 'indeterminate') {
    setPicked(value === true ? [...types] : [])
  }

  function toggle(type: string, on: boolean | 'indeterminate') {
    setPicked(on === true ? [...picked, type] : picked.filter(t => t !== type))
  }

  return (
    <Stack gap="sm">
      <Checkbox checked={all} onCheckedChange={setAll}>
        全部类型
      </Checkbox>
      <Stack gap="sm" className="ps-6">
        {types.map(type => (
          <Checkbox
            key={type}
            checked={picked.includes(type)}
            onCheckedChange={value => toggle(type, value)}
          >
            {type}
          </Checkbox>
        ))}
      </Stack>
    </Stack>
  )
}
tsx

尺寸

size 有 sm、md、lg 三档,方框分别为 14、16、18 像素,文字随档位变化。

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

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

状态

invalid 把方框的边框改为警示色;disabled 禁用整个控件。

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

export default function Demo() {
  return (
    <Stack gap="sm">
      <Checkbox invalid>我已阅读并同意用户协议</Checkbox>
      <Checkbox disabled>已禁用</Checkbox>
      <Checkbox disabled checked>
        已禁用且选中
      </Checkbox>
    </Stack>
  )
}
tsx

仅方框

没有文字时只渲染方框,此时必须用 aria-label 命名。表格的行选择就是这种情形。

'use client'

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

export default function Demo() {
  const [rows, setRows] = useState([true, false, false])

  return (
    <Inline gap="sm">
      {rows.map((row, index) => (
        <Checkbox
          key={index}
          checked={row}
          onCheckedChange={value =>
            setRows(rows.map((item, at) => (at === index ? value === true : item)))
          }
          aria-label={`选择第 ${index + 1} 行`}
        />
      ))}
    </Inline>
  )
}
tsx

在表单中

放进 FormField 后,错误信息由字段渲染并关联到复选框;校验规则与提交交给 Form。复选框自带文字,字段不必再写标签。

'use client'

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

const schema = v.object({
  agreed: v.literal(true, '请先阅读并同意服务条款'),
})

export default function Demo() {
  const [values, setValues] = useState({ agreed: false as boolean | 'indeterminate' })
  const [saved, setSaved] = useState(false)

  async function save() {
    await new Promise(resolve => setTimeout(resolve, 600))
    setSaved(true)
  }

  return (
    <Form values={values} rules={schema} className="w-80" onSubmit={save}>
      {({ submitting }) => (
        <>
          <FormField name="agreed">
            <Checkbox
              checked={values.agreed}
              onCheckedChange={agreed => setValues({ ...values, agreed })}
            >
              我已阅读并同意服务条款
            </Checkbox>
          </FormField>
          <Button type="submit" loading={submitting} className="self-start">
            注册
          </Button>
          {saved && (
            <Text tone="muted" size="sm">
              已注册。
            </Text>
          )}
        </>
      )}
    </Form>
  )
}
tsx

行为

  • 点击文字或者方框都会切换。键盘 Tab 落在方框上,空格切换,Enter 不切换,与原生复选框一致。
  • 半选状态点击一次变为选中。
  • 悬停整个控件时方框落墨,按下时加深;勾以缩放淡入进场,取消时淡出。

无障碍

  • 方框是 role="checkbox" 的按钮,带 aria-checked,半选为 mixed。根元素是 label,文字即名称。
  • 没有文字时通过 aria-label 或者 aria-labelledby 命名。
  • invalid 会同时设置 aria-invalid。

API

Props

属性
类型
默认值
说明
checked
boolean | 'indeterminate'
false
是否选中,'indeterminate' 为半选
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
值变化时