Checkbox(复选框)表示一个可勾选或取消的选择。多个选择属于同一个值时用 CheckboxGroup 分组。它支持半选(部分选中)状态、全选父项与表单提交。表示单一开关用开关,一组里只选一个用单选组。
组合
Checkbox
└── CheckboxIndicator把复选框和它的文字包进一个 label。CheckboxIndicator 在勾选或半选时显示,用来放对勾等图标。
用法
import { Checkbox, CheckboxIndicator } from "@ui-sdk/react/checkbox";
import { CheckIcon } from "lucide-react";
function Example() {
return (
<label>
<Checkbox>
<CheckboxIndicator>
<CheckIcon aria-hidden="true" />
</CheckboxIndicator>
</Checkbox>
接受条款与条件
</label>
);
}点击复选框或其标签,或聚焦后按 Space,即可勾选。需要更多说明时用字段的 FieldLabel 与 FieldDescription,字段会把两者关联到复选框。
半选状态
半选表示一组里只选中了部分。它带 aria-checked="mixed" 与 data-indeterminate;按一次会变为勾选,只有你的代码能把它重新置回半选。用 defaultChecked="indeterminate" 起始,或把 "indeterminate" 作为受控值。指示器可用渲染函数按状态选图标:
<Checkbox defaultChecked="indeterminate">
<CheckboxIndicator>
{(state) => (state.indeterminate ? <MinusIcon /> : <CheckIcon />)}
</CheckboxIndicator>
</Checkbox>下文的父复选框会替你从分组推导出这个状态。
分组与表单
CheckboxGroup 维护一份已勾选值的列表,每个复选框提供自己的 value,分组按复选框出现顺序返回选中值。用 aria-label、aria-labelledby 或字段为分组命名:
<CheckboxGroup aria-labelledby="toppings" defaultValue={["cheese"]}>
<label>
<Checkbox value="cheese">
<CheckboxIndicator />
</Checkbox>
芝士
</label>
<label>
<Checkbox value="olives">
<CheckboxIndicator />
</Checkbox>
橄榄
</label>
</CheckboxGroup>单独使用时,给复选框设 name 与 value 即随原生表单提交(默认值为 "on"),未勾选与半选不提交;required 会阻止提交直到勾选,重置回到 defaultChecked。分组用 name 让每个成员以同一名字提交,required 要求至少勾选一个。
父复选框
给某个复选框加 parent,它就成为分组的全选项:全选时勾选、全不选时取消、其间为半选。它用 aria-controls 指向子项,自身不提交任何值。给分组传 allValues,父项在首帧(含服务端渲染)就有正确状态,否则要等子复选框挂载后才发现这些值:
<CheckboxGroup
aria-labelledby="toppings"
defaultValue={["cheese", "olives"]}
allValues={["cheese", "mushrooms", "olives", "peppers"]}
>
<label>
<Checkbox parent>
<CheckboxIndicator>
<CheckIcon aria-hidden="true" />
<MinusIcon aria-hidden="true" />
</CheckboxIndicator>
</Checkbox>
全选
</label>
{/* 各子复选框 */}
</CheckboxGroup>父项只改变启用的子项;禁用整个分组会锁定所有复选框,禁用单个子项则只固定那一个值。
受控与控制器
传 checked 与 onCheckedChange 自己持有值。createCheckboxController() 创建的控制器可从任意处改变它,写方法像一次按下那样跑变更回调,被取消时返回 false:
const [updates] = React.useState(() => createCheckboxController());
const [checked, setChecked] = React.useState<CheckedState>(false);
<Checkbox
checked={checked}
onCheckedChange={(next) => setChecked(next)}
controller={updates}
>
<CheckboxIndicator />
</Checkbox>
<button onClick={() => updates.toggle()}>切换</button>;CheckedState 为 true、false 或 "indeterminate"。控制器提供 toggle()、getChecked()、setChecked(checked)、focus();分组用 createCheckboxGroupController(),提供 getValue()、setValue(value)、focus()。参见控制器。
样式与状态
用 data-checked、data-unchecked、data-indeterminate 表达勾选态,用 data-disabled、data-readonly、data-required 表达其余状态。指示器继承复选框的同一组状态属性,并额外带 data-starting-style / data-ending-style 供入场退场动画;keepMounted 让指示器在未勾选时也留在页面里,便于两态都做动画:
[data-slot="checkbox"][data-checked] {
background: var(--color-brand);
border-color: var(--color-brand);
}
[data-slot="checkbox"][data-indeterminate] {
background: var(--color-warning);
}
[data-slot="checkbox-indicator"][data-starting-style] {
opacity: 0;
transform: scale(0.6);
}API
Checkbox:
checked/defaultChecked(CheckedState):受控值 / 初始值,true、false或"indeterminate"。onCheckedChange((checked, details) => void):用户或重置想改值时调用,details.reason说明原因,details.cancel()拦截。name/value(string):提交名与勾选时提交的值。disabled/readOnly/required(boolean):禁用、只读、必填。parent(boolean):作为分组的全选父项。controller(object):用createCheckboxController()创建。
CheckboxIndicator:keepMounted(boolean)未勾选时是否保留在页面中。
CheckboxGroup:value / defaultValue(T[])、onValueChange、allValues(T[],供父项与服务端渲染)、name、required、disabled、readOnly、controller。
键盘与无障碍
| 按键 | 行为 |
|---|---|
Tab |
移到下一个复选框 |
Space |
勾选或取消,半选则变为勾选 |
Enter |
提交所在表单(不会切换勾选) |
Checkbox 渲染一个带 checkbox 角色的按钮,外加一个参与表单的隐藏原生复选框,遵循 WAI-ARIA 复选框模式;半选时 aria-checked="mixed"。分组父项遵循其混合复选框示例,用 aria-controls 关联子项。点击标签、点击包裹它的 label 都只切换一次。