laif-ds
Version:
Design System di Laif con componenti React basati su principi di Atomic Design
138 lines (109 loc) • 4.94 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.