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, and this page is available as Markdown at https://ifhover.github.io/leaf-ui/v/0.6.0/components/form.md.
  • 简体中文
  • 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
    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>
      );
    }
    

    扩展用法

    联系人

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

    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'标签与字段的排列方式
    labelWidthnumber | 'auto''auto'数字设置像素宽度;auto 统一使用最宽标签
    labelAlign'left' | 'right''right'标签对齐方向
    disabledbooleanfalse禁用交互。
    refRef<HTMLFormElement>—表单引用

    FormField API

    属性类型默认值说明
    labelReactNode—字段标签
    htmlForstring自动生成与输入控件的 id 关联;一个字段对应一个控件或组
    requiredbooleanfalse要求整体值非空,复合控件只校验一次。
    helpReactNode—显示在控件下方的辅助提示,占据正常布局空间
    errorReactNode—错误提示,优先于 help 展示
    labelWidthnumber—当前字段的标签宽度,单位像素

    类型

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

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

    使用说明

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

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

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

    扩展类型

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

    FormGroupProps API

    属性类型默认值说明
    legendReactNode—字段组名称
    descriptionReactNode—补充说明

    FormListProps API

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

    FormListField API

    属性类型默认值说明
    keystring必填必需。唯一且稳定的标识
    indexnumber必填必需。当前索引,从 0 开始
    namestring必填必需。表单字段名称
    valueT必填必需。受控值

    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

    属性类型默认值说明
    errorsreadonly FormError[]—省略时汇总同一 Form 内字段错误
    titleReactNode—标题内容

    FormError API

    属性类型默认值说明
    fieldIdstring必填必需。关联输入控件 id,点击错误后聚焦
    messageReactNode必填必需。错误内容
    labelReactNode—显示内容

    接入 React Hook Form

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

    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

    属性类型默认值说明
    errorsRecord<string, string>—必需。按字段名组织的错误,可传给 FormField。
    pendingboolean—必需。校验或提交是否进行中。
    setErrorsDispatch<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—操作失败时调用;命令面板会保留并展示错误。
    focusErrorboolean—出现错误时聚焦第一个字段。