# FormComposer

## Overview

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.

---

## Types

### FormComposerItem

```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;
}
```

---

## Props

| 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.     |

---

## Behavior

- **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.

---

## Examples

### With Zod Schema

```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>
  );
}
```

### Submit State

```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>
  );
}
```

---

## Notes

- **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.
