Default form template for MishkaGervaz.
Implements MishkaGervaz.Form.Behaviours.Template and serves as the
out-of-the-box renderer for every mishka_gervaz form do … end block.
Custom templates inherit from this module via use MishkaGervaz.Form.Behaviours.Template, which delegates the four
optional callbacks (render_loading/1, render_field/1,
render_group/1, render_step_indicator/1) here.
Layout modes
:standard— single-page form, fields rendered in their declared groups (or flat when no groups are declared).:wizard— multi-step form with a sequential step indicator and step-aware submit / next / previous buttons.:tabs— same step structure as:wizardbut with free navigation between tabs.
Mode selection is read from @state.static.layout_mode. The render
pipeline (render/1) dispatches notices (render_notices_at/2),
the form header / footer chrome, the body (render_groups/1 or
render_current_step_groups/1), upload sections, and submit buttons
in a fixed order so notice positioning stays predictable.
Two-axis composition
- Template (this module) — where things go: groups, steps, notices, submit area.
- UI adapter (e.g.
MishkaGervaz.UIAdapters.Tailwind) — how things look: every concrete element (field_group,alert,button,step_indicator, …) is dispatched throughMishkaGervaz.Helpers.dynamic_component/1, which forwards to the adapter declared on the resource.
Field-type dispatch
render_field/1 delegates per-type rendering to modules under
MishkaGervaz.Form.Types.Field.* (e.g.
MishkaGervaz.Form.Types.Field.Hidden,
MishkaGervaz.Form.Types.Field.Relation). Built-in types are wired
through render_input/4; custom types implement
MishkaGervaz.Form.Behaviours.FieldType and are referenced directly
in the field DSL (field :foo, MyApp.FieldTypes.Color).
Input scoping
render_input/4 puts :table_id — the UI adapter's name for the scope a control belongs to —
on the base assigns of every field, alongside the :id taken from the Phoenix form field. The
adapter's searchable controls (search_select, load_more_select, multi_select, combobox,
string_list_input) build their wrapper id from that scope plus the field name, so two forms on
one page may share a field name without their ids colliding.
render_string_list_input/4 sets the same scope itself, since it builds its assigns from the
template assigns rather than from that base.
Upload progress
render_upload_entries/1 sets its track width with an inline style="width: N%" rather than a
Tailwind width class, as does MishkaGervaz.UIAdapters.Tailwind.upload_progress/1.
See MishkaGervaz.Form.Behaviours.Template,
MishkaGervaz.Form.Behaviours.FieldType,
MishkaGervaz.Behaviours.UIAdapter,
MishkaGervaz.Helpers, and
MishkaGervaz.Form.Web.UploadHelpers.
Summary
Functions
Draws state.form_errors, the messages of a failed save that no field shows, as one :error
alert above the submit row. Draws nothing when there are none.
The form while its record loads: the UI adapter's spinner over a "Loading…" label, in a box at
least 280px tall, marked data-role="gervaz-form-loading".
Functions
Draws state.form_errors, the messages of a failed save that no field shows, as one :error
alert above the submit row. Draws nothing when there are none.
The form while its record loads: the UI adapter's spinner over a "Loading…" label, in a box at
least 280px tall, marked data-role="gervaz-form-loading".