> Leaf UI 0.6.0 · Package: @ifhover/leaf-ui · Source: b30c3d3a8fa9c5e15d0d0522cebfbd33f60c6b06
> Browser documentation: https://ifhover.github.io/leaf-ui/v/0.6.0/components/form.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms-full.txt.

# Form 表单

组织字段、标签和校验提示，收集表单数据。

## 提交与校验


*姓名

*工作邮箱

用于接收项目通知

*所属团队

提交重置

basic.tsx

复制代码

```tsx lineNumbers file="../../src/examples/form/basic.tsx"
import { Button, Flex, Form, FormField, Input, Select, Text } from '@ifhover/leaf-ui';

import { useState } from 'react';

export function FormBasic() {
  const [result, setResult] = useState('');
  return (
    <Form
      onSubmit={(event) => {
        event.preventDefault();
        const data = new FormData(event.currentTarget);
        setResult(`${data.get('name')} · ${data.get('email')} · ${data.get('team')}`);
      }}
      onReset={() => setResult('')}
    >
      <FormField label="姓名" required>
        <Input name="name" placeholder="你的姓名" />
      </FormField>
      <FormField label="工作邮箱" required help="用于接收项目通知">
        <Input name="email" type="email" placeholder="hello@example.com" />
      </FormField>
      <FormField label="所属团队" required>
        <Select
          name="team"
          options={[
            { value: 'design', label: '设计团队' },
            { value: 'engineering', label: '工程团队' },
          ]}
        />
      </FormField>
      <FormField>
        <Flex gap="md" align="center" wrap justify="center">
          <Button type="submit">提交</Button>
          <Button type="reset" variant="outline">
            重置
          </Button>
        </Flex>
      </FormField>
      {result && (
        <output>
          <Text tone="muted">{result}</Text>
        </output>
      )}
    </Form>
  );
}
```

展开代码

## 标签宽度


自动标签宽度切换长标签

姓名

工作邮箱

label-width.tsx

复制代码

```tsx lineNumbers file="../../src/examples/form/label-width.tsx"
import { Button, Flex, Form, FormField, Input, Space, Switch } from '@ifhover/leaf-ui';

import { useState } from 'react';

export function FormLabelWidth() {
  const [automatic, setAutomatic] = useState(true);
  const [extra, setExtra] = useState(false);
  return (
    <Space direction="vertical" align="stretch">
      <Flex gap="md" align="center" wrap justify="center">
        <Switch checked={automatic} onChange={(event) => setAutomatic(event.target.checked)}>
          自动标签宽度
        </Switch>
        <Button variant="outline" onClick={() => setExtra(!extra)}>
          切换长标签
        </Button>
      </Flex>
      <Form labelWidth={automatic ? 'auto' : 110}>
        <FormField label="姓名">
          <Input name="name" />
        </FormField>
        <FormField label="工作邮箱">
          <Input name="email" />
        </FormField>
        {extra && (
          <FormField label="紧急联系人电话号码">
            <Input name="emergency" />
          </FormField>
        )}
      </Form>
    </Space>
  );
}
```

展开代码

## 扩展用法


联系人添加、移除或调整联系人顺序。

*联系人 1

移除

添加联系人

检查表单重置

extensions.tsx

复制代码

```tsx lineNumbers file="../../src/examples/form/extensions.tsx"
import { Button, Flex, Form, FormErrorSummary, FormField, FormGroup, FormList, Input, Space } from '@ifhover/leaf-ui';

export function ExtensionDemo() {
  return (
    <Form layout="vertical" onSubmit={(e) => e.preventDefault()}>
      <FormErrorSummary />
      <FormGroup legend="联系人" description="添加、移除或调整联系人顺序。">
        <FormList name="contacts" defaultValue={['']}>
          {(fields, ops) => (
            <>
              {fields.map((field) => (
                <FormField key={field.key} label={`联系人 ${field.index + 1}`} required>
                  <Flex gap="sm" align="start">
                    <Input
                      style={{ flex: 1, minWidth: 0 }}
                      name={`${field.name}.email`}
                      type="email"
                      value={field.value}
                      onChange={(e) => ops.update(field.index, e.target.value)}
                    />
                    <Button variant="ghost" onClick={() => ops.remove(field.index)}>
                      移除
                    </Button>
                    {field.index > 0 && (
                      <Button
                        variant="ghost"
                        onClick={() => ops.move(field.index, field.index - 1)}
                      >
                        上移
                      </Button>
                    )}
                  </Flex>
                </FormField>
              ))}
              <Button variant="outline" onClick={() => ops.add('')}>
                添加联系人
              </Button>
            </>
          )}
        </FormList>
      </FormGroup>
      <Space>
        <Button type="submit">检查表单</Button>
        <Button type="reset" variant="outline">
          重置
        </Button>
      </Space>
    </Form>
  );
}
```

展开代码

## Form API

| 属性           | 类型                           | 默认值            | 说明                     |
| ------------ | ---------------------------- | -------------- | ---------------------- |
| `layout`     | `'horizontal' \| 'vertical'` | `'horizontal'` | 标签与字段的排列方式             |
| `labelWidth` | `number \| 'auto'`           | `'auto'`       | 数字设置像素宽度；auto 统一使用最宽标签 |
| `labelAlign` | `'left' \| 'right'`          | `'right'`      | 标签对齐方向                 |
| `disabled`   | `boolean`                    | `false`        | 禁用交互。                  |
| `ref`        | `Ref<HTMLFormElement>`       | —              | 表单引用                   |

## FormField API

| 属性           | 类型          | 默认值     | 说明                        |
| ------------ | ----------- | ------- | ------------------------- |
| `label`      | `ReactNode` | —       | 字段标签                      |
| `htmlFor`    | `string`    | 自动生成    | 与输入控件的 id 关联；一个字段对应一个控件或组 |
| `required`   | `boolean`   | `false` | 要求整体值非空，复合控件只校验一次。        |
| `help`       | `ReactNode` | —       | 显示在控件下方的辅助提示，占据正常布局空间     |
| `error`      | `ReactNode` | —       | 错误提示，优先于 help 展示          |
| `labelWidth` | `number`    | —       | 当前字段的标签宽度，单位像素            |

## 类型

定义组件属性或组装变量时可导入相关类型。

```ts
import type { FormProps, FormFieldProps } from '@ifhover/leaf-ui';
```

## 使用说明

默认自动计算同一 Form 中最宽的标签。标签文本、字体或可见字段变化时更新；窄屏和 vertical 布局按纵向排列。支持 onSubmit、onReset、action、method 等表单属性。

help 和错误提示会占据正常布局空间，并与下一项保留表单间距；文字换行后自然增加高度。

用 FormData 读取控件上的 name。简单校验使用 required、type、minLength 等属性，错误提示跟随字段显示；业务与服务端错误通过 error 传入。设置 noValidate 可以接管校验。FormData 返回字符串，不会自动转换为数字或嵌套对象，未勾选复选框不会提交 false。受控字段的重置由应用处理。

## 扩展类型

```ts
import type { FormGroupProps, FormListProps, FormListField, FormListOperations, FormErrorSummaryProps, FormError } from '@ifhover/leaf-ui';
```

## FormGroupProps API

| 属性            | 类型          | 默认值 | 说明    |
| ------------- | ----------- | --- | ----- |
| `legend`      | `ReactNode` | —   | 字段组名称 |
| `description` | `ReactNode` | —   | 补充说明  |

## FormListProps API

| 属性             | 类型                                                                                                                                               | 默认值  | 说明                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ---- | ------------------- |
| `name`         | `string`                                                                                                                                         | 必填   | 必需。表单字段名称           |
| `value`        | `readonly T[]`                                                                                                                                   | —    | 受控值                 |
| `defaultValue` | `readonly T[]`                                                                                                                                   | `[]` | 初始值；非受控表单 reset 时恢复 |
| `onChange`     | `(values: T[]) => void`                                                                                                                          | —    | 值变化回调               |
| `itemKey`      | `(value: T) => string`                                                                                                                           | —    | 提取稳定的项目标识           |
| `children`     | `(fields: readonly `[`FormListField`](#formlistfield-api)`<T>[], operations: `[`FormListOperations`](#formlistoperations-api)`<T>) => ReactNode` | 必填   | 必需。内容或下级项目          |

## FormListField API

| 属性      | 类型       | 默认值 | 说明             |
| ------- | -------- | --- | -------------- |
| `key`   | `string` | 必填  | 必需。唯一且稳定的标识    |
| `index` | `number` | 必填  | 必需。当前索引，从 0 开始 |
| `name`  | `string` | 必填  | 必需。表单字段名称      |
| `value` | `T`      | 必填  | 必需。受控值         |

## FormListOperations API

| 属性       | 类型                                             | 默认值 | 说明                   |
| -------- | ---------------------------------------------- | --- | -------------------- |
| `add`    | `(value: T, index?: number) => void`           | 必填  | 必需。插入字段，省略 index 时追加 |
| `remove` | `(index: number \| readonly number[]) => void` | 必填  | 必需。移除一个或多个位置         |
| `move`   | `(from: number, to: number) => void`           | 必填  | 必需。移动字段并保留稳定 key     |
| `update` | `(index: number, value: T) => void`            | 必填  | 必需。更新字段值             |

## FormErrorSummaryProps API

| 属性       | 类型                                           | 默认值 | 说明                 |
| -------- | -------------------------------------------- | --- | ------------------ |
| `errors` | `readonly `[`FormError`](#formerror-api)`[]` | —   | 省略时汇总同一 Form 内字段错误 |
| `title`  | `ReactNode`                                  | —   | 标题内容               |

## FormError API

| 属性        | 类型          | 默认值 | 说明                   |
| --------- | ----------- | --- | -------------------- |
| `fieldId` | `string`    | 必填  | 必需。关联输入控件 id，点击错误后聚焦 |
| `message` | `ReactNode` | 必填  | 必需。错误内容              |
| `label`   | `ReactNode` | —   | 显示内容                 |

## 接入 React Hook Form

在应用中安装 react-hook-form。原生输入使用 register；onChange 返回业务值的组件使用 Controller。Leaf Form 负责布局，表单库负责状态与校验，使用 noValidate 接管校验。FormList 的点号 name 只是字段名，不会自动生成嵌套对象；使用 React Hook Form 管理数组时优先使用 useFieldArray。

```tsx
import { useForm, Controller } from 'react-hook-form';
import { Form, FormField, FormErrorSummary, Input, Select, Button } from '@ifhover/leaf-ui';

export function AccountForm() {
  const { register, control, handleSubmit, formState: { errors } } = useForm({ defaultValues: { email: '', role: '' } });
  return <Form noValidate onSubmit={handleSubmit(values => console.log(values))}>
    <FormErrorSummary />
    <FormField label="Email" error={errors.email?.message}>
      <Input {...register('email', { required: 'Enter an email' })} />
    </FormField>
    <Controller name="role" control={control} rules={{ required: 'Choose a role' }} render={({ field, fieldState }) =>
      <FormField label="Role" error={fieldState.error?.message}>
        <Select {...field} options={[{ value: 'editor', label: 'Editor' }]} />
      </FormField>}
    />
    <Button type="submit">Save</Button>
  </Form>;
}
```

## FormValidationApi

| 属性             | 类型                                                     | 默认值 | 说明                             |
| -------------- | ------------------------------------------------------ | --- | ------------------------------ |
| `errors`       | `Record<string, string>`                               | —   | 必需。按字段名组织的错误，可传给 FormField。    |
| `pending`      | `boolean`                                              | —   | 必需。校验或提交是否进行中。                 |
| `setErrors`    | `Dispatch<SetStateAction<Record<string, string>>>`     | —   | 必需。设置服务端或业务错误。                 |
| `handleSubmit` | `(event: FormEvent<HTMLFormElement>) => Promise<void>` | —   | 必需。绑定到 Form.onSubmit。          |
| `handleReset`  | `(event: FormEvent<HTMLFormElement>) => void`          | —   | 必需。绑定到 Form.onReset，取消请求并清除错误。 |
| `cancel`       | `() => void`                                           | —   | 必需。取消当前请求。                     |
| `clearErrors`  | `(name?: string) => void`                              | —   | 必需。清除一个或全部错误并取消旧请求。            |

## FormValidationOptions

| 属性           | 类型                                                                                                      | 默认值 | 说明                                |
| ------------ | ------------------------------------------------------------------------------------------------------- | --- | --------------------------------- |
| `validate`   | `( data: FormData, signal: AbortSignal, ) => Record<string, string> \| Promise<Record<string, string>>` | —   | 必需。接收 FormData 和取消信号，返回按字段名组织的错误。 |
| `onSubmit`   | `(data: FormData, signal: AbortSignal) => void \| Promise<void>`                                        | —   | 校验通过后提交；应响应取消信号。                  |
| `onError`    | `(error: unknown) => void`                                                                              | —   | 操作失败时调用；命令面板会保留并展示错误。             |
| `focusError` | `boolean`                                                                                               | —   | 出现错误时聚焦第一个字段。                     |
