组件
多选框 Checkbox
支持选中、未选中与不确定状态的多选控件。
基础示例
Loading…
pnpm dlx wui@latest add @wui/checkbox"use client"
import * as React from "react"
import { Checkbox as CheckboxPrimitive } from "radix-ui"
import { AnimatePresence, motion, useReducedMotion } from "motion/react"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const checkboxVariants = cva(
"peer inline-flex shrink-0 cursor-pointer items-center justify-center rounded-[5px] border border-input bg-background text-primary-foreground shadow-xs outline-none transition-[background-color,border-color,box-shadow,transform] duration-200 ease-out hover:border-ring/70 active:scale-[0.92] focus-visible:ring-[3px] focus-visible:ring-ring/35 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary",
{
variants: {
size: {
sm: "size-4 rounded-[4px]",
default: "size-5",
lg: "size-6 rounded-md",
},
},
defaultVariants: { size: "default" },
}
)
export interface CheckboxProps
extends React.ComponentProps<typeof CheckboxPrimitive.Root> {
/** Physical size of the checkbox. @default "default" */
size?: "sm" | "default" | "lg"
}
/** An accessible checkbox with a smoothly drawn checkmark. */
function Checkbox({
className,
size = "default",
checked,
defaultChecked = false,
onCheckedChange,
...props
}: CheckboxProps) {
const reduceMotion = useReducedMotion()
const [internalChecked, setInternalChecked] = React.useState(defaultChecked)
const currentChecked = checked ?? internalChecked
function handleCheckedChange(next: boolean | "indeterminate") {
if (checked === undefined) setInternalChecked(next)
onCheckedChange?.(next)
}
return (
<CheckboxPrimitive.Root
data-slot="checkbox"
data-size={size}
className={cn(checkboxVariants({ size }), className)}
checked={checked}
defaultChecked={defaultChecked}
onCheckedChange={handleCheckedChange}
{...props}
>
<CheckboxPrimitive.Indicator
forceMount
data-slot="checkbox-indicator"
className="flex size-full items-center justify-center"
>
<AnimatePresence initial={false} mode="wait">
{currentChecked ? (
<motion.svg
key={String(currentChecked)}
className="size-[78%]"
viewBox="0 0 16 16"
fill="none"
initial={reduceMotion ? false : { opacity: 0, scale: 0.55 }}
animate={{ opacity: 1, scale: 1 }}
exit={reduceMotion ? undefined : { opacity: 0, scale: 0.7 }}
transition={reduceMotion ? { duration: 0 } : { type: "spring", stiffness: 560, damping: 30, mass: 0.55 }}
aria-hidden="true"
>
<motion.path
d={currentChecked === "indeterminate" ? "M3.5 8h9" : "M3.25 8.25 6.5 11.25 12.75 4.75"}
stroke="currentColor"
strokeWidth="2.25"
strokeLinecap="round"
strokeLinejoin="round"
initial={reduceMotion ? false : { pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={reduceMotion ? { duration: 0 } : { duration: 0.24, ease: [0.22, 1, 0.36, 1] }}
/>
</motion.svg>
) : null}
</AnimatePresence>
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
)
}
export { Checkbox, checkboxVariants }
组件作用
Checkbox 用于在一组互不排斥的选项中选择零项或多项,也适用于同意条款等需要明确提交的布尔选择。全局主色填充、紧凑圆角与路径绘制勾号提供清晰且丝滑的反馈。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "default" | "lg" | default | Physical size of the checkbox. |
| disabled | boolean | — | |
| asChild | boolean | — |
事件
onCheckedChange(checked) 在状态变化后触发,值可能是 true、false 或 "indeterminate"。其余键盘、焦点与表单属性会透传到底层 Radix Root。
拓展使用
使用 checked="indeterminate" 表示一组子选项仅部分选中,横线和勾号都会使用路径动画进入;取消选择时则轻柔缩回。使用 size="sm | default | lg" 调整控件尺寸,文字标签应通过原生 label 与控件关联。