Skip to content
Lenso UI

Form 表单

用于表单校验与提交处理的包裹组件

用法

import { Form } from '@lenso/ui';

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6, e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e, Apache-2.0.import { Check } from "@gravity-ui/icons";import { Button, Description, FieldError, Form, Input, Label, TextField } from "@lenso/ui";import type { FormEvent } from "react";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  form: { display: "flex", flexDirection: "column", width: 384, maxWidth: "100%", gap: 16 },  actions: { display: "flex", gap: 8 },});
export function Basic() {  function onSubmit(event: FormEvent<HTMLFormElement>) {    event.preventDefault();    const data = Object.fromEntries(new FormData(event.currentTarget).entries());    alert(`Form submitted with: ${JSON.stringify(data, null, 2)}`);  }  return (    <Form xstyle={styles.form} onSubmit={onSubmit}>      <TextField        name="email"        validate={(value) =>          /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(String(value))            ? null            : "Please enter a valid email address"        }      >        <Label required>Email</Label>        <Input required type="email" placeholder="[email protected]" />        <FieldError />      </TextField>      <TextField        name="password"        validate={(value) => {          if (String(value).length < 8) return "Password must be at least 8 characters";          if (!/[A-Z]/.test(String(value)))            return "Password must contain at least one uppercase letter";          if (!/[0-9]/.test(String(value))) return "Password must contain at least one number";          return null;        }}      >        <Label required>Password</Label>        <Input required minLength={8} type="password" placeholder="Enter your password" />        <Description>Must be at least 8 characters with 1 uppercase and 1 number</Description>        <FieldError />      </TextField>      <div {...stylex.props(styles.actions)}>        <Button type="submit">          <Check aria-hidden="true" />          Submit        </Button>        <Button type="reset" variant="secondary">          Reset        </Button>      </div>    </Form>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

组件结构

import {Form, Button} from '@lenso/ui';
export default () => (  <Form>    {/* Form fields go here */}    <Button type="submit"/>    <Button type="reset"/>  </Form>)

示例

渲染函数

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6, e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e, Apache-2.0.import { Check } from "@gravity-ui/icons";import { Button, Description, FieldError, Form, Input, Label, TextField } from "@lenso/ui";import type { FormEvent } from "react";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  form: { display: "flex", width: 384, maxWidth: "100%", flexDirection: "column", gap: 16 },  actions: { display: "flex", gap: 8 },});
export function RenderFunction() {  function onSubmit(event: FormEvent<HTMLFormElement>) {    event.preventDefault();    const data = Object.fromEntries(new FormData(event.currentTarget).entries());    alert(`Form submitted with: ${JSON.stringify(data, null, 2)}`);  }  return (    <Form      xstyle={styles.form}      render={(props) => <form {...props} data-custom="foo" />}      onSubmit={onSubmit}    >      <TextField        name="email"        validate={(value) =>          /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(String(value))            ? null            : "Please enter a valid email address"        }      >        <Label required>Email</Label>        <Input required type="email" placeholder="[email protected]" />        <FieldError />      </TextField>      <TextField        name="password"        validate={(value) => {          const password = String(value);          if (password.length < 8) return "Password must be at least 8 characters";          if (!/[A-Z]/.test(password)) return "Password must contain at least one uppercase letter";          if (!/[0-9]/.test(password)) return "Password must contain at least one number";          return null;        }}      >        <Label required>Password</Label>        <Input required minLength={8} type="password" placeholder="Enter your password" />        <Description>Must be at least 8 characters with 1 uppercase and 1 number</Description>        <FieldError />      </TextField>      <div {...stylex.props(styles.actions)}>        <Button type="submit">          <Check aria-hidden="true" />          Submit        </Button>        <Button type="reset" variant="secondary">          Reset        </Button>      </div>    </Form>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义样式

Tailwind CSS

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6, e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e, Apache-2.0.import { Button, Form, Input, Label, TextField } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  form: {    display: "flex",    width: 320,    maxWidth: "100%",    flexDirection: "column",    gap: 12,    borderRadius: 12,    borderWidth: 1,    borderStyle: "solid",    borderColor: "color-mix(in oklch, var(--border) 80%, transparent)",    backgroundColor: "var(--surface)",    padding: 16,    boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",  },  input: { backgroundColor: "var(--field-background)" },  button: { width: "100%" },});
export function CustomStyles() {  return (    <Form xstyle={styles.form} onSubmit={(event) => event.preventDefault()}>      <TextField name="email">        <Label required>Work email</Label>        <Input required type="email" xstyle={styles.input} placeholder="[email protected]" />      </TextField>      <Button xstyle={styles.button} type="submit">        Continue      </Button>    </Form>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

全局 CSS

要自定义表单布局与间距,可在 <Form> 上使用 className prop,或通过 @layer components 添加项目级类。 了解更多。

Form 渲染原生 <form> 元素,聚焦校验与提交。@lenso/tokens 中无专用 BEM 类——从全局 CSS 应用容器样式,控件级自定义请使用字段组件。

@layer components {  .form-layout {    @apply flex flex-col gap-4 rounded-xl border border-border bg-surface p-4 shadow-sm;  }}
<Form className="form-layout" onSubmit={handleSubmit}>  {/* TextField, Input, Button, etc. */}</Form>

分组字段与共享布局请使用 Fieldset,并定位 .fieldset、.fieldset__legend 等相关类。单个控件请参阅 TextField、Input、Label、FieldError 的 Global CSS 部分。

样式参考

HeroUI 对在 @lenso/tokens 中提供样式的组件遵循 BEM 方法论。

Form 渲染原生 <form> 元素,无专用 BEM 类。通过 className 应用布局、间距与表面样式。字段外观与校验状态来自 TextField、Input、Label、Description、FieldError 等子组件。结构化多字段布局请与 Fieldset 组合使用。

API 参考

Form

Form 组件是 React Aria Form 原语的包裹层,提供表单校验与提交处理能力。

Prop类型默认值描述
actionstring | FormHTMLAttributes['action']-提交表单数据的 URL
classNamestring-应用于 form 元素的 Tailwind CSS 类
childrenReact.ReactNode-表单内容(字段、按钮等)
encType'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain'-表单数据提交的编码类型
method'get' | 'post'-提交表单时使用的 HTTP 方法
onInvalid(event: FormEvent<HTMLFormElement>) => void-表单校验失败时调用。默认聚焦第一个无效字段。使用 preventDefault() 可自定义聚焦行为
onReset(event: FormEvent<HTMLFormElement>) => void-表单重置时调用
onSubmit(event: FormEvent<HTMLFormElement>) => void-表单提交时调用
target'_self' | '_blank' | '_parent' | '_top'-提交后显示响应的位置
validationBehavior'native' | 'aria''native'使用原生 HTML 校验还是 ARIA 校验。native 阻止提交,aria 实时显示错误
validationErrorsValidationErrors-按字段名映射的服务端校验错误。立即显示,用户修改字段时清除
aria-labelstring-表单的无障碍标签
aria-labelledbystring-标注表单的元素 ID。提供时创建 form landmark
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, undefined>-使用自定义 render 函数覆盖默认 DOM 元素

Form Validation

Form 组件集成 React Aria 校验系统,支持:

  • 使用内置 HTML5 校验属性(required、minLength、pattern 等)
  • 在 TextField 组件上提供自定义校验函数
  • 使用 FieldError 组件展示校验错误
  • 在正确校验后处理表单提交
  • 通过 validationErrors prop 提供服务端校验错误

Validation Behavior

validationBehavior prop 控制校验展示方式:

  • native(默认):使用原生 HTML 校验,有错误时阻止提交
  • aria:使用 ARIA 属性校验,用户输入时实时显示错误,不阻止提交

可在表单级或单个字段级设置此行为。

Form Submission

表单可通过多种方式提交:

  • 传统提交:设置 action prop 提交到 URL
  • JavaScript 处理:使用 onSubmit 处理表单数据
  • FormData API:在 submit 处理函数中使用 FormData API 访问表单数据

FormData 示例:

function handleSubmit(e: FormEvent<HTMLFormElement>) {  e.preventDefault();  const formData = new FormData(e.currentTarget);  const data = Object.fromEntries(formData);  console.log('Form data:', data);}

Integration with Form Fields

Form 组件与 HeroUI 表单字段组件无缝协作:

  • TextField:带标签与校验的文本输入
  • Checkbox:布尔选择
  • RadioGroup:多选一
  • Switch:开关控件
  • Button:提交与重置操作

所有字段组件放在 Form 内时会自动集成 Form 的校验与提交行为。

Advanced Usage

更高级用法包括:

  • 自定义校验上下文
  • 表单 context provider
  • 与第三方库集成
  • 校验错误时的自定义焦点管理

请参阅 React Aria Form 文档。

无障碍

使用 React Aria 组件时,表单默认可访问。主要特性包括:

  • 原生 <form> 元素语义
  • 使用 aria-label 或 aria-labelledby 创建 form landmark
  • 校验错误时自动焦点管理
  • 使用 validationBehavior="aria" 时的 ARIA 校验属性

相关案例

See upstream Form showcases. Product showcases are not part of the local component runtime.

相关组件