Shared helpers for MishkaGervaz.Form.Web.State.
A module that overrides init/3 (via use MishkaGervaz.Form.Web.State)
can call any helper here without redefining it; the macro itself
imports them as StateHelpers.
Two functional groups:
- Config getters (
get_layout_mode/1,get_uploads/1,get_submit/1, …) — pull a typed value out of the runtimeInfo.config(resource)map with sensible defaults. - Step helpers (
groups_for_step/3) and access (mode_allowed?/3,resolve_access/1) — the bits of logic the macro and external callers (e.g.Form.Web.Live) both need.
mode_allowed?/3 — :restricted semantics
The :restricted field on a source map (or a per-mode entry in
:access_rules) accepts two shapes, and they behave differently:
restricted: true— applies the master gate. The mode is allowed only whenstate.master_user?.restricted: fn state -> boolean end— the function is the final word; the master gate is not layered on top. Returningtruemeans the user is restricted and the mode is denied, returningfalseallows the mode unconditionally.
Use the boolean form unless the decision needs the whole state.
See MishkaGervaz.Form.Web.State,
MishkaGervaz.Form.Web.State.Access.Default, and
MishkaGervaz.Form.Web.Live.
Summary
Functions
Whether field is drawn read-only and left out of what the form saves.
The stream name derived from a resource when identity do stream_name … end is omitted.
The form's layout do footer … end map, or nil when none is declared.
The form's layout do header … end map, or nil when none is declared.
The form's lifecycle hooks as a map, or %{} when the resource declares none.
The grid width of the form, defaulting to a single column.
The form's layout mode, defaulting to :standard when the resource declares none.
How a wizard or tabs form moves between steps — :sequential (the default) or :free.
Every layout do notice … end declared on the resource, in declaration order.
The resolved submit configuration — the three buttons, their position and shared ui.
The uploads do upload … end entries declared on the resource, or [].
The groups belonging to one wizard or tabs step, in the order they were declared.
Whether the current reader may open the form in mode (:create or :update).
The access module used to gate form modes.
Whether the action state.form saves with accepts field or takes it as an argument.
Functions
Whether field is drawn read-only and left out of what the form saves.
True when the field declares readonly true or readonly fn state -> … end says so, or when
taken_by_action?/2 is false.
if field_readonly?(field, state), do: Map.delete(params, to_string(field.name)), else: params
The stream name derived from a resource when identity do stream_name … end is omitted.
iex> generate_stream_name(MyApp.Blog.Post)
:post_form_stream
The form's layout do header … end map, or nil when none is declared.
The form's lifecycle hooks as a map, or %{} when the resource declares none.
@spec get_layout_columns(map()) :: 1 | 2 | 3 | 4
The grid width of the form, defaulting to a single column.
A group may override it for itself with ui do columns … end.
@spec get_layout_mode(map()) :: :standard | :wizard | :tabs
The form's layout mode, defaulting to :standard when the resource declares none.
Every layout do notice … end declared on the resource, in declaration order.
The resolved submit configuration — the three buttons, their position and shared ui.
Falls back to a full default block rather than nil, so a template can read
submit[:create][:label] without guarding first.
The uploads do upload … end entries declared on the resource, or [].
The groups belonging to one wizard or tabs step, in the order they were declared.
Returns every group when the step is unknown, which is what makes a :standard form — and a
step name that no longer exists — render rather than come back empty.
groups_for_step(state.static.groups, state.static.steps, state.current_step)
Whether the current reader may open the form in mode (:create or :update).
Checks, in order: a per-mode access rule, a global access fn mode, state -> … end gate,
then source do restricted true end. A form with no source at all is allowed. A per-mode
rule written access :update, false denies that mode to everyone.
if mode_allowed?(state.static.source, :create, state), do: DataLoader.new_record(socket, state)
The access module used to gate form modes.
Whether the action state.form saves with accepts field or takes it as an argument.
A virtual field is always taken. When state.form is not an AshPhoenix.Form, or its action
is not on its resource, the answer is true and the field is left as declared.