laif-ds
Version:
Design System di Laif con componenti React basati su principi di Atomic Design
164 lines (136 loc) • 5.17 kB
Markdown
Declarative form builder using `react-hook-form` (with optional Zod). Renders inputs, selects, textareas, and checkboxes from a simple items config, handles validation, and provides a submit button with loading state.
---
```ts
interface FormComposerItem {
label: string;
component: "input" | "select" | "textarea" | "checkbox";
name: string;
defaultValue?: string | boolean | number;
options?: AppSelectOption[]; // only for "select"
disabled?: boolean;
placeholder?: string;
}
```
---
| Prop | Type | Default | Description |
| --------------- | --------------------- | ------------ | -------------------------------------------------- | ----- | --------------------------- |
| `cols` | `"1" | "2" | "3"` | `"2"` | Grid columns for the items. |
| `items` | `FormComposerItem[]` | **required** | Field configuration list. |
| `schema` | `ZodTypeAny` | `undefined` | Optional Zod schema to enable validation/resolver. |
| `defaultValues` | `Record<string, any>` | `undefined` | RHF default values; overrides item `defaultValue`. |
| `submitText` | `string` | `"Invia"` | Submit button label. |
| `onSubmit` | `(data: any) => void` | `undefined` | Called with valid form values. |
| `isSubmitting` | `boolean` | `false` | Controls submit button loading/disabled state. |
---
- **Validation**: If `schema` is provided, a Zod resolver is attached; otherwise native validity is used.
- **Defaults**: Each `item.defaultValue` is merged into RHF defaults, unless `defaultValues` is passed.
- **Components**:
- `component: "input"` → `Input` with `label` and `placeholder`.
- `component: "textarea"` → `Textarea` with `label`.
- `component: "select"` → `AppSelect` with `options`.
- `component: "checkbox"` → `Checkbox` + `Label` inline.
- **Errors**: Zod error messages are rendered under each field.
- **Layout**: Items are placed in a responsive grid with `cols` columns; last item may span full width.
---
```tsx
import { z } from "zod";
import { FormComposer } from "laif-ds";
const formSchema = z.object({
name: z.string().min(2, "Il nome è troppo corto"),
email: z.string().email("Email non valida"),
message: z.string().min(10, "Almeno 10 caratteri"),
select: z.string().min(1, "Seleziona una opzione"),
checkbox: z.boolean().default(false),
});
export function ContactForm() {
return (
<div className="w-[800px]">
<FormComposer
schema={formSchema}
items={[
{
label: "Name",
component: "input",
name: "name",
placeholder: "Inserisci il tuo nome",
defaultValue: "ciao",
},
{
label: "Select",
component: "select",
name: "select",
defaultValue: "1",
options: [
{ value: "1", label: "Option 1" },
{ value: "2", label: "Option 2" },
{ value: "3", label: "Option 3" },
],
},
{
label: "Email",
component: "input",
name: "email",
placeholder: "Inserisci la tua email",
},
{
label: "Checkbox",
component: "checkbox",
name: "checkbox",
defaultValue: true,
},
{
label: "Message",
component: "textarea",
name: "message",
placeholder: "Inserisci il tuo messaggio",
},
]}
onSubmit={(data) => console.log(data)}
/>
</div>
);
}
```
```tsx
import * as React from "react";
import { z } from "zod";
import { FormComposer } from "laif-ds";
const schema = z.object({ name: z.string().min(2), email: z.string().email() });
export function SubmitExample() {
const [values, setValues] = React.useState({});
const [isSubmitting, setSubmitting] = React.useState(false);
const onSubmit = (data: any) => {
setSubmitting(true);
setTimeout(() => setSubmitting(false), 2000);
setValues(data);
};
return (
<div className="w-[800px]">
<FormComposer
schema={schema}
items={[
{ label: "Name", component: "input", name: "name" },
{ label: "Email", component: "input", name: "email" },
]}
onSubmit={onSubmit}
isSubmitting={isSubmitting}
/>
<pre>{JSON.stringify(values, null, 2)}</pre>
</div>
);
}
```
---
- **Extensibility**: Use `startContent`, `endContent`, `iconLeft`, `iconRight` props of `Input` for richer fields.
- **Select**: Provide `options` for `component: "select"` (`AppSelectOption[]`).
- **Accessibility**: Labels are wired via `htmlFor`/`id`; errors use `role="alert"` and `aria-live` when relevant.