# SilkeForm

A form container component that manages form state, validation, and submission. It automatically connects child form fields by their `name` prop, handling value binding and error propagation.

## Features

- **Automatic state management**: Child fields connect via `name` prop
- **Type-safe**: Generic type support for form values
- **Validation**: Built-in validation with error tracking
- **Submit handling**: Prevents default form submission
- **External errors**: Inject errors from API responses
- **Nested objects**: Support for nested form structures

## Basic Usage

```js
const [formData, setFormData] = React.useState({
  username: '',
  email: '',
});

<SilkeForm value={formData} onChange={setFormData}>
  <SilkeTextField name="username" label="Username" required />
  <SilkeTextField name="email" label="Email" />
  <SilkeButton type="submit" label="Submit" />
</SilkeForm>;
```

## With Submit Handler

Handle form submission with validation status:

```js
const [formData, setFormData] = React.useState({ name: '', agree: false });
const [submitted, setSubmitted] = React.useState(null);

<SilkeBox column gap="m">
  <SilkeForm
    value={formData}
    onChange={setFormData}
    onSubmit={(data, isValid) => {
      if (isValid) setSubmitted(data);
    }}
  >
    <SilkeTextField name="name" label="Your name" required />
    <SilkeCheckbox name="agree" label="I agree to terms" required />
    <SilkeButton type="submit" label="Submit" />
  </SilkeForm>
  {submitted && (
    <SilkeBox bg="success-10" pad="s" rounded>
      <SilkeText>Submitted: {JSON.stringify(submitted)}</SilkeText>
    </SilkeBox>
  )}
</SilkeBox>;
```

## Tracking Errors

Monitor validation errors with `onErrors`:

```js
const [formData, setFormData] = React.useState({ email: '' });
const [errors, setErrors] = React.useState({});

<SilkeBox column gap="m">
  <SilkeForm value={formData} onChange={setFormData} onErrors={setErrors}>
    <SilkeTextField name="email" label="Email" required />
  </SilkeForm>
  {Object.keys(errors).length > 0 && (
    <SilkeBox bg="error-10" pad="s" rounded>
      <SilkeText size="s">Errors: {JSON.stringify(errors)}</SilkeText>
    </SilkeBox>
  )}
</SilkeBox>;
```

## External Errors

Inject errors from API responses (e.g., server-side validation):

```js
const [formData, setFormData] = React.useState({ username: 'taken_name' });
const [externalErrors, setExternalErrors] = React.useState({});

<SilkeBox column gap="m">
  <SilkeForm value={formData} onChange={setFormData} errors={externalErrors}>
    <SilkeTextField name="username" label="Username" />
    <SilkeBox gap="s">
      <SilkeButton
        label="Simulate API Error"
        onClick={() => setExternalErrors({ username: 'Username already taken' })}
      />
      <SilkeButton label="Clear Error" kind="secondary" onClick={() => setExternalErrors({})} />
    </SilkeBox>
  </SilkeForm>
</SilkeBox>;
```

## Mixed Field Types

Combine different field types in a single form:

```js
const [formData, setFormData] = React.useState({
  name: '',
  role: '',
  notifications: false,
});
const roleOptions = [
  { value: 'admin', label: 'Administrator' },
  { value: 'user', label: 'User' },
  { value: 'guest', label: 'Guest' },
];

<SilkeForm value={formData} onChange={setFormData}>
  <SilkeTextField name="name" label="Full Name" required />
  <SilkeSelectField name="role" label="Role" items={roleOptions} required />
  <SilkeCheckbox name="notifications" label="Enable email notifications" />
  <SilkeButton type="submit" label="Save Settings" />
</SilkeForm>;
```

## Nested Objects

Use `SilkeObjectField` for nested form structures:

```js
const [formData, setFormData] = React.useState({
  user: { firstName: '', lastName: '' },
  address: { city: '', country: '' },
});

<SilkeForm value={formData} onChange={setFormData}>
  <SilkeObjectField name="user">
    <SilkeBox gap="s">
      <SilkeTextField name="firstName" label="First Name" />
      <SilkeTextField name="lastName" label="Last Name" />
    </SilkeBox>
  </SilkeObjectField>
  <SilkeObjectField name="address">
    <SilkeBox gap="s">
      <SilkeTextField name="city" label="City" />
      <SilkeTextField name="country" label="Country" />
    </SilkeBox>
  </SilkeObjectField>
  <SilkeButton type="submit" label="Submit" />
</SilkeForm>;
```

## Using SilkeFormFields

Wrap fields in `SilkeFormFields` for consistent spacing:

```js
const [formData, setFormData] = React.useState({});

<SilkeForm value={formData} onChange={setFormData}>
  <SilkeFormFields>
    <SilkeTextField name="field1" label="Field 1" />
    <SilkeTextField name="field2" label="Field 2" />
    <SilkeTextField name="field3" label="Field 3" />
  </SilkeFormFields>
  <SilkeButton type="submit" label="Submit" />
</SilkeForm>;
```

## Live Form Data Preview

```js
const [formData, setFormData] = React.useState({
  title: '',
  description: '',
  priority: 'medium',
  isPublic: false,
});
const priorityOptions = [
  { value: 'low', label: 'Low' },
  { value: 'medium', label: 'Medium' },
  { value: 'high', label: 'High' },
];

<SilkeBox gap="l">
  <SilkeBox flex>
    <SilkeForm value={formData} onChange={setFormData}>
      <SilkeFormFields>
        <SilkeTextField name="title" label="Title" required />
        <SilkeTextField name="description" label="Description" />
        <SilkeSelectField name="priority" label="Priority" items={priorityOptions} />
        <SilkeCheckbox name="isPublic" label="Make public" />
      </SilkeFormFields>
    </SilkeForm>
  </SilkeBox>
  <SilkeBox flex>
    <SilkeCodeSnippet code={JSON.stringify(formData, null, 2)} />
  </SilkeBox>
</SilkeBox>;
```

## Props

| Prop        | Type                                          | Default | Description                                      |
| ----------- | --------------------------------------------- | ------- | ------------------------------------------------ |
| `value`     | `T`                                           | `{}`    | Current form values                              |
| `onChange`  | `(value: T, valid?: boolean) => void`         | -       | Called on any field change                       |
| `onSubmit`  | `(value: T, valid?: boolean) => void`         | -       | Called on form submission                        |
| `onErrors`  | `(errors: ErrorMap<T>) => void`               | -       | Called when error map changes                    |
| `errors`    | `{ [key in keyof T]?: string }`               | -       | External errors to inject                        |
| `className` | `string`                                      | -       | Additional CSS class names                       |
| `styles`    | `React.CSSProperties`                         | -       | Inline styles                                    |

Also accepts all `SilkeBox` props (except `tag`, `onChange`, `onSubmit`).

## SilkeFormFields

A wrapper component that applies consistent vertical spacing between form fields:

```typescript
<SilkeFormFields>
  {children}
</SilkeFormFields>
```

Renders children in a column with `gap="l"` spacing.

## Types

```typescript
interface SilkeFormProps<T extends { [key: string]: any }> {
  children?: React.ReactNode;
  value?: T;
  errors?: { [key in keyof T]?: string };
  onChange?: (change: T, valid?: boolean) => void;
  onSubmit?: (form: T, valid?: boolean) => void;
  onErrors?: (errors: ErrorMap<T>) => void;
}

interface FormFieldProps<T = any> {
  name?: string;
  label?: React.ReactNode;
  value?: T;
  error?: string;
  readOnly?: boolean;
  disabled?: boolean;
  placeholder?: string;
  onChange?: (change: T) => void;
  onError?: (message?: string) => void;
  onFocus?: (e: FocusEvent) => void;
  onBlur?: (e: FocusEvent) => void;
  validator?: FormValidator<FormFieldProps<T>>;
}

type ErrorMap<T> = { [key in keyof T]?: string };
```

## Form Field Connection

Form fields connect to SilkeForm through their `name` prop:
1. The form provides a context with the current value and change handlers
2. Fields with a `name` prop automatically receive their value from `form.value[name]`
3. Changes from fields update the form's state automatically
4. Validation errors are tracked by field name
