组件
开关 Switch
带弹簧滑块与完整无障碍语义的二元状态控件。
基础示例
Loading…
pnpm dlx wui@latest add @wui/switch"use client"
import * as React from "react"
import { Switch as SwitchPrimitive } from "radix-ui"
import { motion, useReducedMotion } from "motion/react"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const switchVariants = cva(
"relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-input/80 shadow-inner outline-none transition-[background-color,box-shadow,opacity] duration-300 ease-out focus-visible:ring-[3px] focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary",
{
variants: {
size: {
sm: "h-5 w-9 p-0.5",
default: "h-7 w-12 p-0.5",
lg: "h-8 w-14 p-0.5",
},
},
defaultVariants: { size: "default" },
}
)
const thumbSize = {
sm: { size: 16, travel: 16 },
default: { size: 24, travel: 20 },
lg: { size: 28, travel: 24 },
} as const
export interface SwitchProps
extends Omit<React.ComponentProps<typeof SwitchPrimitive.Root>, "children"> {
/** Physical size of the switch. @default "default" */
size?: "sm" | "default" | "lg"
}
/** A tactile, accessible binary control with a spring-driven thumb. */
function Switch({
className,
size = "default",
checked,
defaultChecked = false,
onCheckedChange,
...props
}: SwitchProps) {
const reduceMotion = useReducedMotion()
const [internalChecked, setInternalChecked] = React.useState(defaultChecked)
const isChecked = checked ?? internalChecked
const metrics = thumbSize[size]
function handleCheckedChange(next: boolean) {
if (checked === undefined) setInternalChecked(next)
onCheckedChange?.(next)
}
return (
<SwitchPrimitive.Root
data-slot="switch"
data-size={size}
className={cn(switchVariants({ size }), className)}
checked={checked}
defaultChecked={defaultChecked}
onCheckedChange={handleCheckedChange}
{...props}
>
<SwitchPrimitive.Thumb asChild>
<motion.span
data-slot="switch-thumb"
className="pointer-events-none block rounded-full bg-background shadow-sm"
style={{ width: metrics.size, height: metrics.size }}
animate={{ x: isChecked ? metrics.travel : 0 }}
transition={
reduceMotion
? { duration: 0 }
: { type: "spring", stiffness: 520, damping: 32, mass: 0.65 }
}
/>
</SwitchPrimitive.Thumb>
</SwitchPrimitive.Root>
)
}
export { Switch, switchVariants }
组件作用
Switch 用于让设置立即在开启与关闭之间切换,例如通知、隐私或显示偏好。它基于 Radix Switch,支持鼠标、触摸、键盘以及表单语义;滑块通过 Motion 弹簧运动,并在 prefers-reduced-motion 下自动静止降级。
不要用 Switch 代替需要“提交/取消”的操作;这类场景应使用 Checkbox 或 Button。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "default" | "lg" | default | Physical size of the switch. |
| disabled | boolean | — | |
| asChild | boolean | — |
事件
onCheckedChange(checked):状态变化后触发。- 原生焦点、键盘与表单属性会继续透传到底层 Radix Root。
拓展使用
尺寸
Loading…
受控用法通过 checked 与 onCheckedChange 管理;非受控用法使用 defaultChecked。