docs(ui): expand bilingual component guides
This commit is contained in:
@@ -1,37 +1,96 @@
|
||||
---
|
||||
title: 组合表单
|
||||
description: 组合 Field、Input 和 Button,同时让领域状态继续由应用管理。
|
||||
title: 组合资料表单
|
||||
description: 用 Field、Input、Select 和 Button 组合可访问表单,同时把状态留在应用层。
|
||||
order: 30
|
||||
toc:
|
||||
- id: create-the-form
|
||||
title: 创建表单
|
||||
- id: complete-example
|
||||
title: 完整示例
|
||||
- id: validation
|
||||
title: 表单验证
|
||||
title: 验证状态
|
||||
- id: controlled-select
|
||||
title: 受控 Select
|
||||
- id: styling
|
||||
title: 样式
|
||||
title: 样式覆盖
|
||||
---
|
||||
|
||||
## 创建表单 {#create-the-form}
|
||||
## 完整示例 {#complete-example}
|
||||
|
||||
```tsx
|
||||
import * as React from "react"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { Field, FieldError, FieldLabel } from "@workspace/ui/components/field"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldError,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
} from "@workspace/ui/components/field"
|
||||
import { Input } from "@workspace/ui/components/input"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@workspace/ui/components/select"
|
||||
|
||||
;<form onSubmit={saveProfile}>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="display-name">显示名称</FieldLabel>
|
||||
<Input id="display-name" name="displayName" />
|
||||
<FieldError>{errors.displayName}</FieldError>
|
||||
</Field>
|
||||
<Button type="submit">保存资料</Button>
|
||||
</form>
|
||||
export function ProfileForm() {
|
||||
const [role, setRole] = React.useState("editor")
|
||||
const [error, setError] = React.useState("")
|
||||
|
||||
function submit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
const data = new FormData(event.currentTarget)
|
||||
setError(data.get("displayName") ? "" : "请输入显示名称")
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={submit} className="max-w-md space-y-6">
|
||||
<FieldGroup>
|
||||
<Field data-invalid={Boolean(error)}>
|
||||
<FieldLabel htmlFor="display-name">显示名称</FieldLabel>
|
||||
<Input
|
||||
id="display-name"
|
||||
name="displayName"
|
||||
aria-invalid={Boolean(error)}
|
||||
aria-describedby="display-name-error"
|
||||
/>
|
||||
<FieldDescription>其他成员会看到这个名称。</FieldDescription>
|
||||
<FieldError id="display-name-error">{error}</FieldError>
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<FieldLabel htmlFor="role">角色</FieldLabel>
|
||||
<Select
|
||||
value={role}
|
||||
onValueChange={(value) => setRole(value ?? "editor")}
|
||||
>
|
||||
<SelectTrigger id="role">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="viewer">只读</SelectItem>
|
||||
<SelectItem value="editor">编辑者</SelectItem>
|
||||
<SelectItem value="admin">管理员</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
|
||||
<Button type="submit">保存资料</Button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 表单验证 {#validation}
|
||||
## 验证状态 {#validation}
|
||||
|
||||
UI 包负责渲染验证状态,但不会指定表单库或 Schema。请在应用边界连接原生表单数据、React 状态或表单框架。
|
||||
UI 包不选择验证库。应用负责产生错误,再把同一状态连接到 `Field data-invalid`、输入控件的 `aria-invalid` 和 `FieldError`。多个 Schema 错误可通过 `FieldError errors={errors}` 传入,它会去重后渲染列表。
|
||||
|
||||
## 样式 {#styling}
|
||||
## 受控 Select {#controlled-select}
|
||||
|
||||
受支持的语义变化使用组件变体,局部布局使用 `className`。需要合并条件类名或消费者覆盖时,使用 `@workspace/ui/lib/utils` 中的 `cn`。
|
||||
本地 Select 基于 Base UI,使用 `value` 与 `onValueChange`。`SelectTrigger` 的箭头由组件自己渲染,只需放入 `SelectValue`;不要再添加第二个 Chevron。弹层默认与 trigger 对齐并使用其宽度,可通过 `SelectContent` 的 `side`、`align`、`sideOffset` 与 `alignItemWithTrigger` 调整。
|
||||
|
||||
## 样式覆盖 {#styling}
|
||||
|
||||
先使用组件已有的 `variant`、`size` 和结构部件,再用 `className` 处理页面布局。组件内部使用 `cn` 合并 class,因此消费端的 Tailwind class 可以覆盖默认 utility。跨多个页面重复的覆盖应沉淀为新 variant 或复合组件,而不是复制长 class 字符串。
|
||||
|
||||
Reference in New Issue
Block a user