Form groups and layout DSL

v0.0.1

The groups and layout sections of a resource's form block arrange fields into groups, groups into steps, and add a header, a footer and notices. The hierarchy is step, then groups, then fields.

groups.group

Declares a named group of fields. A field may sit in at most one group. It sits at mishka_gervaz.form.groups.group.

Call: group name

Arguments:

ArgumentTypeDefaultDescription
nameatomnoneRequired. Group identifier.

Options:

OptionTypeDefaultDescription
fieldslist(atom)noneRequired. Names of the fields in the group. A field may sit in at most one group.
collapsedbooleanfalseStart collapsed. The value is compiled into the config, but no built-in template or handler reads it.
collapsiblebooleanfalseCan be collapsed by user.
positionanynoneGroup position (integer, :first, :last). The value is compiled into the config, but no built-in template or handler reads it.
visibleboolean | (any -> any)trueStatic or dynamic visibility. fn state -> boolean() end.
restrictedboolean | (any -> any)falseRestrict to master users. true or fn state -> boolean() end.

group ui

How the group is drawn. It sits at mishka_gervaz.form.groups.group.ui.

OptionTypeDefaultDescription
labelString.t | (-> any)noneGroup label.
iconString.tnoneGroup icon. The value is compiled into the config, but no built-in template or handler reads it.
descriptionString.tnoneGroup description. The value is compiled into the config, but no built-in template or handler reads it.
classString.tnoneGroup CSS classes.
header_classString.tnoneGroup header CSS classes. The value is compiled into the config, but no built-in template or handler reads it.
columns1 | 2 | 3 | 4noneNumber of grid columns for this group (overrides global layout columns).
extramap%{}Additional options. The value is compiled into the config, but no built-in template or handler reads it.

layout

Picks the layout mode and the grid. Its entities are the steps and the chrome. It sits at mishka_gervaz.form.layout.

OptionTypeDefaultDescription
columns1 | 2 | 3 | 41Number of grid columns (1-4). Only 1 to 4 change the grid. Any other value gives one column.
mode:standard | :wizard | :tabs:standard:standard draws one page. :wizard shows one step at a time. :tabs needs steps too, and the Standard template draws it one step at a time as well. Free jumping needs navigation :free and a goto_step event, which no built-in element sends. :wizard and :tabs need steps, and :standard forbids them.
navigation:sequential | :free:sequential:sequential enforces the order of the steps. :free lets the reader jump, and is invalid with :wizard.
persistence:none | :ets | :client_token:noneWhere step data is meant to live between steps: :none, :ets or :client_token. The value is compiled into the config, but no built-in template or handler reads it. It is readable with Info.Form.persistence/1.
responsivebooleantrueEnable responsive layout adjustments. The value is compiled into the config, but no built-in template or handler reads it.

step

Declares a step of a wizard or a tabs form. It sits at mishka_gervaz.form.layout.step.

Call: step name

Arguments:

ArgumentTypeDefaultDescription
nameatomnoneRequired. Step identifier.

Options:

OptionTypeDefaultDescription
groupslist(atom)noneRequired. Names of the groups in the step. A group may sit in at most one step.
actionatomnoneValidation action to run when leaving this step. Gervaz does not call it. Navigation goes through the step handler's can_advance?/2.
summarybooleanfalseMarks the step as the review step. At most one step may set it. Gervaz checks when it compiles that only one step sets it. The built-in template does not draw a step differently for it.
on_enter(any -> any)noneCallback invoked when entering the step. Receives state, returns state. Gervaz does not call it. Navigation goes through the step handler's can_advance?/2.
before_leave(any -> any)noneCallback invoked before leaving the step. Return {:halt, state} to block navigation. Gervaz does not call it. Navigation goes through the step handler's can_advance?/2.
after_leave(any -> any)noneCallback invoked after leaving the step. Receives state, returns state. Gervaz does not call it. Navigation goes through the step handler's can_advance?/2.
visibleboolean | (any -> any)trueStatic or dynamic visibility. fn state -> boolean() end. The value is compiled into the config, but no built-in template or handler reads it.

step ui

How the step is drawn. It sits at mishka_gervaz.form.layout.step.ui.

OptionTypeDefaultDescription
labelString.t | (-> any)noneStep label.
iconString.tnoneStep icon. The value is compiled into the config, but no built-in template or handler reads it.
descriptionString.tnoneStep description. The value is compiled into the config, but no built-in template or handler reads it.
classString.tnoneStep CSS classes. The value is compiled into the config, but no built-in template or handler reads it.
header_classString.tnoneStep header CSS classes. The value is compiled into the config, but no built-in template or handler reads it.
extramap%{}Additional options.

header

A title and description drawn above the fields. It sits at mishka_gervaz.form.layout.header.

OptionTypeDefaultDescription
titleString.t | (-> any) | (any -> any)noneHeader title. String, fn -> _ end, or fn state -> _ end.
descriptionString.t | (-> any) | (any -> any)noneHeader description. String, fn -> _ end, or fn state -> _ end.
iconString.tnoneHeroicon name (e.g., "hero-shield-check").
classString.tnoneCSS classes for the header wrapper.
render(any -> any) | (any, any -> any)noneCustom HEEx render. fn assigns -> ~H"..." end or fn assigns, state -> ~H"..." end.
extramap%{}Additional template-specific options. The value is compiled into the config, but no built-in template or handler reads it.
visibleboolean | (any -> any)trueStatic or dynamic visibility. fn state -> boolean() end.
restrictedboolean | (any -> any)falseRestrict to master users. true or fn state -> boolean() end.

footer

Static content drawn below the submit row. It sits at mishka_gervaz.form.layout.footer.

OptionTypeDefaultDescription
contentString.t | (-> any) | (any -> any)noneFooter content. String, fn -> _ end, or fn state -> _ end.
classString.tnoneCSS classes for the footer wrapper.
render(any -> any) | (any, any -> any)noneCustom HEEx render. fn assigns -> ~H"..." end or fn assigns, state -> ~H"..." end.
extramap%{}Additional template-specific options. The value is compiled into the config, but no built-in template or handler reads it.
visibleboolean | (any -> any)trueStatic or dynamic visibility. fn state -> boolean() end.
restrictedboolean | (any -> any)falseRestrict to master users. true or fn state -> boolean() end.

notice

A static alert or banner with a position and an optional binding. It sits at mishka_gervaz.form.layout.notice.

Call: notice name

Arguments:

ArgumentTypeDefaultDescription
nameatomnoneRequired. Unique notice identifier (used for dismiss tracking).

Options:

OptionTypeDefaultDescription
positionany:form_topWhere the notice is drawn. Valid atoms and tuples are listed below the table. The default is :form_top.
type:info | :warning | :error | :success | :neutral:infoVisual style: :info, :warning, :error, :success, or :neutral.
titleString.t | (-> any) | (any -> any)noneNotice title. String, fn -> _ end, or fn state -> _ end.
contentString.t | (-> any) | (any -> any)noneNotice body. String, fn -> _ end, or fn state -> _ end.
iconString.tnoneHeroicon name.
dismissiblebooleanfalseWhether the user can dismiss this notice.
bind_tonil | :validation | :uploads | :dirtynoneDraws the notice only while a condition holds. See the list below the table. Combined with show_when using AND.
show_when(any -> any)noneCustom predicate fn state -> boolean() end. Combined with bind_to (AND).
only_stepslist(atom)noneScopes the notice to these steps in wizard and tabs mode. The names must exist.
render(any -> any) | (any, any -> any)noneCustom HEEx render. fn assigns -> ~H"..." end or fn assigns, state -> ~H"..." end.
visibleboolean | (any -> any)trueStatic or dynamic visibility. fn state -> boolean() end.
restrictedboolean | (any -> any)falseRestrict to master users. true or fn state -> boolean() end.

Valid position values:

PositionWhere the notice is drawn
:form_topInside the form element, above the header. The default.
:before_header, :after_headerAround the form header.
:before_groupsBefore the groups.
:before_fieldsAccepted, and the Standard template never draws it. Use :before_groups.
:before_submitAbove the submit and cancel row.
:form_bottomBelow the submit row, inside the form.
:form_footerBelow the form footer. The last slot.
{:before_group, name}, {:after_group, name}Beside a declared group.

Valid bind_to values:

ValueThe notice shows while
:validationThe form has errors.
:uploadsAny registered upload has errors. Nothing in the built-in code sets upload errors, so this notice does not show.
:dirtyThe form has unsaved changes.

notice ui

Classes for the notice. It sits at mishka_gervaz.form.layout.notice.ui.

OptionTypeDefaultDescription
classString.tnoneCSS classes for the notice wrapper.
extramap%{}Additional template-specific options. The value is compiled into the config, but no built-in template or handler reads it.

Layout rules

  • Steps are required in :wizard and :tabs modes and forbidden in :standard mode.
  • Gervaz reports navigation :free with :wizard when it compiles. Use :tabs for free navigation.
  • Every group named in a step must exist, and a group may sit in at most one step.
  • At most one step may set summary true.
  • Notice names are unique within the form.