MishkaGervaz.Table.Web.State (MishkaGervaz v0.0.1-alpha.2)

Copy Markdown View Source

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.

Summary

Functions

See MishkaGervaz.Table.Web.State.Default.apply_url_state/2.

See MishkaGervaz.Table.Web.State.Default.bidirectional_url_sync?/1.

See MishkaGervaz.Table.Web.State.Default.can_modify_record?/2.

See MishkaGervaz.Table.Web.State.Default.default_init/3.

See MishkaGervaz.Table.Web.State.Default.get_action/2.

See MishkaGervaz.Table.Web.State.Default.get_preloads/1.

Hydrate relation filter state with labels for selected values from URL

See MishkaGervaz.Table.Web.State.Default.init/3.

See MishkaGervaz.Table.Web.State.Default.record_visible?/2.

See MishkaGervaz.Table.Web.State.Default.switch_template/2.

See MishkaGervaz.Table.Web.State.Default.template_switching_enabled?/1.

See MishkaGervaz.Table.Web.State.Default.update/2.

Types

archive_status()

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

loading_status()

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

loading_type()

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

t()

@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()) => atom()},
  preserved_params: map(),
  records_result: struct(),
  relation_filter_state: %{required(atom()) => 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
}

Functions

apply_url_state(state, url_state)

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

See MishkaGervaz.Table.Web.State.Default.apply_url_state/2.

bidirectional_url_sync?(state)

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

See MishkaGervaz.Table.Web.State.Default.bidirectional_url_sync?/1.

can_modify_record?(state, record)

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

See MishkaGervaz.Table.Web.State.Default.can_modify_record?/2.

default_init(id, resource, current_user)

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

See MishkaGervaz.Table.Web.State.Default.default_init/3.

get_action(state, action_type)

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

See MishkaGervaz.Table.Web.State.Default.get_action/2.

get_preloads(state)

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

See MishkaGervaz.Table.Web.State.Default.get_preloads/1.

hydrate_relation_filter_labels(state)

@spec hydrate_relation_filter_labels(t()) :: t()

Hydrate relation filter state with labels for selected values from URL

init(id, resource, current_user)

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

See MishkaGervaz.Table.Web.State.Default.init/3.

record_visible?(state, record)

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

See MishkaGervaz.Table.Web.State.Default.record_visible?/2.

switch_template(state, template)

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

See MishkaGervaz.Table.Web.State.Default.switch_template/2.

template_switching_enabled?(state)

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

See MishkaGervaz.Table.Web.State.Default.template_switching_enabled?/1.

update(state, updates)

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

See MishkaGervaz.Table.Web.State.Default.update/2.