'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 | 值变化时 |