4.9 KiB
4.9 KiB
表单与输入
目录
- 表单使用 FieldGroup + Field
- InputGroup 需配合 InputGroupInput/InputGroupTextarea
- 输入框内的按钮使用 InputGroup + InputGroupAddon
- 选项集合(2–7 个选项)使用 ToggleGroup
- FieldSet + FieldLegend 用于对相关字段进行分组
- 字段校验与禁用状态
表单使用 FieldGroup + Field
始终使用 FieldGroup + Field —— 切勿使用 space-y-* 的原始 div:
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">邮箱</FieldLabel>
<Input id="email" type="email" />
</Field>
<Field>
<FieldLabel htmlFor="password">密码</FieldLabel>
<Input id="password" type="password" />
</Field>
</FieldGroup>
在设置页面中使用 Field orientation="horizontal"。对于视觉上隐藏的标签,使用 FieldLabel className="sr-only"。
选择表单控件:
- 简单文本输入 →
Input - 带预定义选项的下拉框 →
Select - 可搜索下拉框 →
Combobox - 原生 HTML 选择(无 JS)→
native-select - 布尔切换 →
Switch(用于设置)或Checkbox(用于表单) - 少数选项中的单选 →
RadioGroup - 在 2–5 个选项之间切换 →
ToggleGroup+ToggleGroupItem - OTP/验证码 →
InputOTP - 多行文本 →
Textarea
InputGroup 需配合 InputGroupInput/InputGroupTextarea
切勿在 InputGroup 内部使用原始的 Input 或 Textarea。
错误示例:
<InputGroup>
<Input placeholder="搜索..." />
</InputGroup>
正确示例:
import { InputGroup, InputGroupInput } from "@/components/ui/input-group"
<InputGroup>
<InputGroupInput placeholder="搜索..." />
</InputGroup>
输入框内的按钮使用 InputGroup + InputGroupAddon
切勿通过自定义定位将 Button 直接放在 Input 内部或紧邻其旁。
错误示例:
<div className="relative">
<Input placeholder="搜索..." className="pr-10" />
<Button className="absolute right-0 top-0" size="icon">
<SearchIcon />
</Button>
</div>
正确示例:
import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group"
<InputGroup>
<InputGroupInput placeholder="搜索..." />
<InputGroupAddon>
<Button size="icon">
<SearchIcon data-icon="inline-start" />
</Button>
</InputGroupAddon>
</InputGroup>
选项集合(2–7 个选项)使用 ToggleGroup
不要手动循环 Button 组件并自行管理激活状态。
错误示例:
const [selected, setSelected] = useState("daily")
<div className="flex gap-2">
{["daily", "weekly", "monthly"].map((option) => (
<Button
key={option}
variant={selected === option ? "default" : "outline"}
onClick={() => setSelected(option)}
>
{option}
</Button>
))}
</div>
正确示例:
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
<ToggleGroup spacing={2}>
<ToggleGroupItem value="daily">每日</ToggleGroupItem>
<ToggleGroupItem value="weekly">每周</ToggleGroupItem>
<ToggleGroupItem value="monthly">每月</ToggleGroupItem>
</ToggleGroup>
结合 Field 使用,创建带标签的切换组:
<Field orientation="horizontal">
<FieldTitle id="theme-label">主题</FieldTitle>
<ToggleGroup aria-labelledby="theme-label" spacing={2}>
<ToggleGroupItem value="light">浅色</ToggleGroupItem>
<ToggleGroupItem value="dark">深色</ToggleGroupItem>
<ToggleGroupItem value="system">跟随系统</ToggleGroupItem>
</ToggleGroup>
</Field>
注意:
defaultValue和type/multiple属性在 base 与 radix 之间存在差异。详见 base-vs-radix.md。
FieldSet + FieldLegend 用于对相关字段进行分组
对相关的复选框、单选按钮或开关,使用 FieldSet + FieldLegend —— 而不是在 div 中加一个标题:
<FieldSet>
<FieldLegend variant="label">偏好设置</FieldLegend>
<FieldDescription>请选择所有适用的选项。</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox id="dark" />
<FieldLabel htmlFor="dark" className="font-normal">深色模式</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
字段校验与禁用状态
两个属性都需要 —— data-invalid/data-disabled 用于设置字段的样式(标签、描述),而 aria-invalid/disabled 用于设置控件的样式。
// 无效状态
<Field data-invalid>
<FieldLabel htmlFor="email">邮箱</FieldLabel>
<Input id="email" aria-invalid />
<FieldDescription>邮箱地址无效。</FieldDescription>
</Field>
// 禁用状态
<Field data-disabled>
<FieldLabel htmlFor="email">邮箱</FieldLabel>
<Input id="email" disabled />
</Field>
适用于所有控件:Input、Textarea、Select、Checkbox、RadioGroupItem、Switch、Slider、NativeSelect、InputOTP。