# `MishkaGervaz.Form.Web.State`
[🔗](https://github.com/mishka-group/mishka_gervaz/blob/v0.0.1-alpha.3/lib/mishka_gervaz/form/web/state.ex#L1)

Single state struct for a MishkaGervaz form LiveView.

All per-request form state lives on `t:t/0`. Instead of scattering values
across LiveView assigns, every consumer of the form pipeline reads from
and writes to this struct, giving:

- One clearly-typed shape (`t:t/0` and `t:Static.t/0`).
- One place to thread updates (`update/2`).
- One source of truth for events, the renderer, and tests.

## Performance split

State is partitioned into two halves:

- `static` (`t:Static.t/0`) — configuration that never changes after
  `init/3`. Same struct reference for the lifetime of the form, which
  lets LiveView skip re-rendering nodes that depend only on it.
- Dynamic fields — user-interaction state (form values, current step,
  relation options, errors, …) that drive re-renders.

## Sub-builders

`init/3` composes its work from five overridable sub-builder modules.
The DSL (`state do … end`) can override any of them per-resource, and
the `use` macro accepts the same set as compile-time options.

- `MishkaGervaz.Form.Web.State.FieldBuilder` — resolved field configs.
- `MishkaGervaz.Form.Web.State.GroupBuilder` — group layout.
- `MishkaGervaz.Form.Web.State.StepBuilder` — wizard / tabs step plan.
- `MishkaGervaz.Form.Web.State.Presentation` — UI adapter, template,
  theme, features, debounce.
- `MishkaGervaz.Form.Web.State.Access` — master gate, action mapping,
  preload selection.

## Override patterns

Override the entire state module:

    defmodule MyApp.Form.State do
      use MishkaGervaz.Form.Web.State

      def init(id, resource, user) do
        state = super(id, resource, user)
        MishkaGervaz.Form.Web.State.update(state, mode: :update)
      end
    end

Use `update/2` (or `struct/2`) to mutate fields — the struct shape is
fixed and there is no `:custom_field`. To carry your own data, attach
it to `state.static.config` (read-only, set at build time) or stage it
on `state.field_values`.

Override specific sub-builders:

    defmodule MyApp.Form.State do
      use MishkaGervaz.Form.Web.State,
        field: MyApp.Form.FieldBuilder,
        group: MyApp.Form.GroupBuilder
    end

Or override via DSL:

    mishka_gervaz do
      form do
        state do
          field MyApp.Form.FieldBuilder
          group MyApp.Form.GroupBuilder
        end
      end
    end

Override entire state module via DSL:

    mishka_gervaz do
      form do
        state module: MyApp.Form.CustomState
      end
    end

See `MishkaGervaz.Form.Web.State.Helpers` (shared utilities exposed to
the macro and to user overrides), `MishkaGervaz.Form.Web.Live`,
`MishkaGervaz.Form.Web.Events`, `MishkaGervaz.Form.Web.DataLoader`,
`MishkaGervaz.Form.Behaviours.Template`, and the table-side counterpart
`MishkaGervaz.Table.Web.State`.

# `form_mode`

```elixir
@type form_mode() :: :create | :update
```

# `loading_status`

```elixir
@type loading_status() :: :initial | :loading | :loaded | :error | :denied
```

# `t`

```elixir
@type t() :: %MishkaGervaz.Form.Web.State{
  combobox_options: %{required(atom()) =&gt; [{String.t(), String.t()}]},
  current_step: atom() | nil,
  current_user: map() | nil,
  defaults: map() | nil,
  dirty?: boolean(),
  dismissed_notices: MapSet.t(),
  errors: map(),
  existing_files: %{required(atom()) =&gt; [map()]},
  field_values: map(),
  form: Phoenix.HTML.Form.t() | nil,
  form_errors: [String.t()],
  loading: loading_status(),
  master_user?: boolean(),
  mode: form_mode(),
  preload_aliases: %{required(atom()) =&gt; atom()},
  relation_options: map(),
  static: MishkaGervaz.Form.Web.State.Static.t(),
  step_states: %{required(atom()) =&gt; :pending | :active | :completed | :error},
  upload_state: map(),
  wizard_history: [atom()]
}
```

# `current_step_fields`

```elixir
@spec current_step_fields(t()) :: [map()]
```

# `current_step_groups`

```elixir
@spec current_step_groups(t()) :: [map()]
```

# `default_init`

```elixir
@spec default_init(String.t(), module(), map() | nil) :: t()
```

# `get_action`

```elixir
@spec get_action(t(), atom()) :: atom()
```

# `get_preloads`

```elixir
@spec get_preloads(t()) :: [atom()]
```

# `init`

```elixir
@spec init(String.t(), module(), map() | nil) :: t()
```

# `multi_step?`

```elixir
@spec multi_step?(t()) :: boolean()
```

# `tabs_mode?`

```elixir
@spec tabs_mode?(t()) :: boolean()
```

# `update`

```elixir
@spec update(t(), keyword() | map()) :: t()
```

# `wizard_mode?`

```elixir
@spec wizard_mode?(t()) :: boolean()
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
