项目文件夹

文件
2026-07-13 21:36:22 +08:00

193 行
4.9 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 表单与输入
## 目录
- 表单使用 FieldGroup + Field
- InputGroup 需配合 InputGroupInput/InputGroupTextarea
- 输入框内的按钮使用 InputGroup + InputGroupAddon
- 选项集合(2–7 个选项)使用 ToggleGroup
- FieldSet + FieldLegend 用于对相关字段进行分组
- 字段校验与禁用状态
---
## 表单使用 FieldGroup + Field
始终使用 `FieldGroup` + `Field` —— 切勿使用 `space-y-*` 的原始 `div`
```tsx
<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`
**错误示例:**
```tsx
<InputGroup>
<Input placeholder="搜索..." />
</InputGroup>
```
**正确示例:**
```tsx
import { InputGroup, InputGroupInput } from "@/components/ui/input-group"
<InputGroup>
<InputGroupInput placeholder="搜索..." />
</InputGroup>
```
---
## 输入框内的按钮使用 InputGroup + InputGroupAddon
切勿通过自定义定位将 `Button` 直接放在 `Input` 内部或紧邻其旁。
**错误示例:**
```tsx
<div className="relative">
<Input placeholder="搜索..." className="pr-10" />
<Button className="absolute right-0 top-0" size="icon">
<SearchIcon />
</Button>
</div>
```
**正确示例:**
```tsx
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` 组件并自行管理激活状态。
**错误示例:**
```tsx
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>
```
**正确示例:**
```tsx
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` 使用,创建带标签的切换组:
```tsx
<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](./base-vs-radix.md#togglegroup)。
---
## FieldSet + FieldLegend 用于对相关字段进行分组
对相关的复选框、单选按钮或开关,使用 `FieldSet` + `FieldLegend` —— 而不是在 `div` 中加一个标题:
```tsx
<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` 用于设置控件的样式。
```tsx
// 无效状态
<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`