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

Single state struct for MishkaGervaz table.

Instead of scattered assigns, all table state is managed in this struct.
This provides:

- Clear state structure
- Easy state updates
- Type safety
- Single source of truth

## Performance Optimization

State is split into two parts:
- `static` - Configuration that never changes (same reference for O(1) comparison)
- Dynamic fields - User interaction state that triggers re-renders

This separation allows LiveView to skip re-rendering static parts (columns, filters, etc.)
when only dynamic state (page, filter_values, etc.) changes.

## Sub-builders

State initialization is composed of sub-builders that can be overridden:

- `ColumnBuilder` - Builds columns from DSL and resource
- `FilterBuilder` - Builds filters from DSL and resource
- `ActionBuilder` - Builds row/bulk actions
- `Presentation` - Resolves UI adapter and templates
- `UrlSync` - Handles URL state synchronization
- `Access` - Handles access control

## User Override

Override the entire state module:

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

      def init(id, resource, user) do
        state = super(id, resource, user)
        %{state | custom_field: :value}
      end
    end

Override specific sub-builders:

    defmodule MyApp.Table.State do
      use MishkaGervaz.Table.Web.State,
        column: MyApp.Table.ColumnBuilder,
        filter: MyApp.Table.FilterBuilder
    end

Or override via DSL:

    mishka_gervaz do
      table do
        state do
          column MyApp.Table.ColumnBuilder
          filter MyApp.Table.FilterBuilder
        end
      end
    end

Override entire state module via DSL:

    mishka_gervaz do
      table do
        state module: MyApp.Table.CustomState
      end
    end

## Helper Functions

Helper functions are available in `MishkaGervaz.Table.Web.State.Helpers` and can be
used when overriding state functions:

    defmodule MyApp.Table.State do
      use MishkaGervaz.Table.Web.State
      alias MishkaGervaz.Table.Web.State.Helpers, as: StateHelpers

      def hydrate_relation_filter_labels(state) do
        # Use helpers in your override
        StateHelpers.hydrate_filter(filter, acc, state)
      end
    end

See `MishkaGervaz.Table.Web.State.Helpers`,
`MishkaGervaz.Table.Web.State.ColumnBuilder`,
`MishkaGervaz.Table.Web.State.FilterBuilder`,
`MishkaGervaz.Table.Web.State.ActionBuilder`,
`MishkaGervaz.Table.Web.State.Presentation`,
`MishkaGervaz.Table.Web.State.UrlSync`,
`MishkaGervaz.Table.Web.State.Access`,
`MishkaGervaz.Table.Web.AutoState`,
`MishkaGervaz.Table.Web.Refresh`,
`MishkaGervaz.Table.Web.UrlSync`.

# `archive_status`

```elixir
@type archive_status() :: :active | :archived
```

# `loading_status`

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

# `loading_type`

```elixir
@type loading_type() :: :initial | :reset | :more
```

# `t`

```elixir
@type t() :: %MishkaGervaz.Table.Web.State{
  archive_status: archive_status(),
  base_path: String.t() | nil,
  current_page_size: pos_integer() | nil,
  current_user: map() | nil,
  dismissed_notices: MapSet.t(),
  excluded_ids: MapSet.t(any()),
  expanded_data: struct() | nil,
  expanded_id: String.t() | nil,
  filter_values: map(),
  has_initial_data?: boolean(),
  has_more?: boolean(),
  loading: loading_status(),
  loading_type: loading_type(),
  master_user?: boolean(),
  page: integer(),
  path_params: map(),
  preload_aliases: %{required(atom()) =&gt; atom()},
  preserved_params: map(),
  records_result: struct(),
  relation_filter_state: %{required(atom()) =&gt; map()},
  saved_active_state: map() | nil,
  saved_archived_state: map() | nil,
  select_all?: boolean(),
  selected_ids: MapSet.t(any()),
  sort_fields: [{atom(), :asc | :desc}],
  static: MishkaGervaz.Table.Web.State.Static.t(),
  supports_archive: boolean(),
  template: module(),
  total_count: integer() | nil,
  total_pages: integer() | nil
}
```

# `apply_url_state`

```elixir
@spec apply_url_state(t(), map() | nil) :: t()
```

# `bidirectional_url_sync?`

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

# `can_modify_record?`

```elixir
@spec can_modify_record?(t(), map()) :: boolean()
```

# `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()]
```

# `hydrate_relation_filter_labels`

```elixir
@spec hydrate_relation_filter_labels(t()) :: t()
```

Hydrate relation filter state with labels for selected values from URL

# `init`

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

# `record_visible?`

```elixir
@spec record_visible?(t(), map()) :: boolean()
```

# `switch_template`

```elixir
@spec switch_template(t(), atom()) :: {:ok, t()} | {:error, :template_not_allowed}
```

# `template_switching_enabled?`

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

# `update`

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

---

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