# Form Layout

## Usage

`FormLayout` arranges fields. It is not a `<form>`. Put the fields in the default slot and choose a direction.

```vue
<script setup lang="ts">
import { FormLayout } from '@astryx-vue/core/FormLayout'
import { TextInput } from '@astryx-vue/core/TextInput'
</script>

<template>
  <FormLayout>
    <TextInput v-model="name" label="Name" />
    <TextInput v-model="email" label="Email" />
  </FormLayout>
</template>
```

`direction` is `vertical` (the default), `horizontal`, or `horizontal-labels`. Vertical stacks fields with a gap of spacing step 4. Horizontal places them in equal columns. Horizontal labels put the label on the left and the control on the right, and collapse to a column below 480px.

`defaultOptionality` is `optional` or `required`. It changes the indicator and `aria-required` on fields that do not set their own `isOptional` or `isRequired`. It does not set the native `required` attribute. When a field sets both, optional wins. Outside a `FormLayout`, `aria-required` stays `isRequired && !isOptional`.

Nested layouts keep their own direction. A horizontal row can sit inside a vertical form.

### Anatomy

- **Form title**: Heading that describes the purpose of the form.
- **Fields**: Input components with labels for collecting user data.
- **Footer**: Contains confirmation buttons such as Submit or Cancel.

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Use horizontal for a short row of related fields, such as a first and last name. |
| Do | Use horizontal-labels when every field has a label and the form is wider than a phone. |
| Don't | Put an unrelated control, such as a page title, in the layout. It participates in the grid. |
| Don't | Expect `defaultOptionality` to block submit. It only sets the indicator and `aria-required`. |

## Examples

### Horizontal

Two fields sit side by side for a pair such as first and last name.

```vue
<script setup lang="ts">
import { FormLayout } from '@astryx-vue/core/FormLayout'
import { TextInput } from '@astryx-vue/core/TextInput'
import { shallowRef } from 'vue'

const first = shallowRef('Jordan')
const last = shallowRef('Rivera')
</script>

<template>
  <FormLayout direction="horizontal" style="width: 100%; max-width: 400px">
    <TextInput v-model="first" label="First Name" />
    <TextInput v-model="last" label="Last Name" />
  </FormLayout>
</template>
```

### Settings form

A settings form places each label beside its input.

```vue
<script setup lang="ts">
import { FormLayout } from '@astryx-vue/core/FormLayout'
import { Selector } from '@astryx-vue/core/Selector'
import { TextArea } from '@astryx-vue/core/TextArea'
import { TextInput } from '@astryx-vue/core/TextInput'
import { shallowRef } from 'vue'

const displayName = shallowRef('Jane Doe')
const email = shallowRef('jane@example.com')
const timezone = shallowRef('America/Los_Angeles')
const bio = shallowRef('')

const timezones = [
  { label: 'Pacific Time', value: 'America/Los_Angeles' },
  { label: 'Eastern Time', value: 'America/New_York' },
  { label: 'UTC', value: 'UTC' },
]
</script>

<template>
  <FormLayout direction="horizontal-labels">
    <TextInput v-model="displayName" label="Display Name" />
    <TextInput v-model="email" label="Email" />
    <Selector v-model="timezone" label="Timezone" :options="timezones" />
    <TextArea v-model="bio" label="Bio" :rows="3" />
  </FormLayout>
</template>
```

### Mixed controls

One form mixes a text input, a selector, and checkboxes.

```vue
<script setup lang="ts">
import { CheckboxList, CheckboxListItem } from '@astryx-vue/core/CheckboxList'
import { FormLayout } from '@astryx-vue/core/FormLayout'
import { Selector } from '@astryx-vue/core/Selector'
import { TextInput } from '@astryx-vue/core/TextInput'
import { shallowRef } from 'vue'

const name = shallowRef('Maya Torres')
const role = shallowRef('editor')
const notifications = shallowRef(['email', 'push'])

const roles = [
  { label: 'Viewer', value: 'viewer' },
  { label: 'Editor', value: 'editor' },
  { label: 'Admin', value: 'admin' },
]
</script>

<template>
  <FormLayout>
    <TextInput v-model="name" label="Full Name" />
    <Selector v-model="role" label="Role" :options="roles" />
    <CheckboxList v-model="notifications" label="Notifications">
      <CheckboxListItem label="Email" value="email" />
      <CheckboxListItem label="SMS" value="sms" />
      <CheckboxListItem label="Push" value="push" />
    </CheckboxList>
  </FormLayout>
</template>
```

### Nested address

An address form mixes a vertical stack with horizontal rows for grouped fields.

```vue
<script setup lang="ts">
import { FormLayout } from '@astryx-vue/core/FormLayout'
import { TextInput } from '@astryx-vue/core/TextInput'
import { shallowRef } from 'vue'

const first = shallowRef('Priya')
const last = shallowRef('Sharma')
const email = shallowRef('priya.sharma@example.com')
const city = shallowRef('San Francisco')
const state = shallowRef('CA')
const zip = shallowRef('94105')
</script>

<template>
  <FormLayout>
    <FormLayout direction="horizontal">
      <TextInput v-model="first" label="First Name" />
      <TextInput v-model="last" label="Last Name" />
    </FormLayout>
    <TextInput v-model="email" label="Email" />
    <FormLayout direction="horizontal">
      <TextInput v-model="city" label="City" />
      <TextInput v-model="state" label="State" />
      <TextInput v-model="zip" label="ZIP" />
    </FormLayout>
  </FormLayout>
</template>
```

## Form Layout Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `direction` | `'vertical' \| 'horizontal' \| 'horizontal-labels'` | `'vertical'` | How the fields are arranged. `horizontal` uses equal columns. `horizontal-labels` puts the label on the left and stacks below 480px. |
| `defaultOptionality` | `'optional' \| 'required'` | - | Default for fields that do not set `isOptional` or `isRequired`. It changes the indicator and `aria-required`. It does not set the native `required` attribute. When a field sets both, optional wins. |

## Slots

| Name | Description |
| --- | --- |
| `default` | The fields. A nested `FormLayout` keeps its own direction. |

## Native attributes

`FormLayout` renders a `<div>`, not a `<form>`.

The following go to that element:

- HTML attributes.
- `aria-*` attributes.
- `data-*` attributes.
- `class`.
- `style`.

`class` is merged. The root sets `data-direction`.
