StatifierBlocks.Editor.Field (StatifierBlocks v0.8.0)

Copy Markdown View Source

One config field, dispatching on the closed field-type set (ADR-0005 decision 9, ADR-0002 decision 7).

The set is closed precisely so this renderer can be total, and the mapping is the record's, unchanged:

Field typeRendering
:stringsingle-line text input
:integernumber input, step 1
:booleancheckbox
{:select, choices}select, choices in declared order
:expressionsingle-line source input
:durationone text control; predicator duration strings primary, with on-screen examples
{:list, t}repeatable rows of t's renderer, with add and remove

:duration's row is decision 9 as amended 2026-08-29. One text control, not a value/unit pair and not a pair with an escape hatch beside it: the compound control could not spell PT1H30M at all, and a control plus an escape hatch is two ways to say one thing with a rule about which wins. Predicator duration strings are primary with the examples on screen, ISO-8601 stays accepted, and an empty field omits the key.

What the typed text means is StatifierBlocks.DurationInput's, not this module's - it is a function of the text alone, so it is asserted with LiveView absent. Where an omitted key is omitted is StatifierBlocks.Editor.ConfigForm's, which owns where a decoded value is written. This module renders the control and shows the refusal.

The refusal shown beneath a :duration is the inline check, and it is earlier than decision 9's gate rather than a second one: the gate still decides what reaches the document, and the inline sentence tells the author which spelling they are failing while they are still typing it. Nothing here is stored - the stored form is the author's string verbatim, byte for byte.

A required field is marked with the word, not with an asterisk (parity item 1.9). An asterisk is a convention that has to be learned from a legend the editor does not have, it is read aloud as "star", and it is one character wide next to a label it is easy to miss. Required says the same thing to a reader and to a screen reader, and it is the field record's own required? that decides - never a key name and never a type.

Two control types carry a placeholder, and neither is chosen by key or by type name: an :expression says what kind of thing belongs in it, and a :duration shows the spelling it stores. A bare :string says nothing, because there is nothing a type as wide as "string" can suggest - which is the rule, and it is what makes the two that do say something the editor's own presentation rather than a special case.

Whether a block type may declare a placeholder on its own fields is a question this module deliberately does not answer: ADR-0002 decision 7 closes the field TYPE set, not the keys of a field record, so admitting one is a widening of that record and belongs to whoever amends it.

:expression renders as a plain source input here. Predicator source is statifier-ui's subject (sui-bob, sui-ADR-0006), and decision 9 records a richer affordance as a deferral, so this component accepts an expression_component override for exactly that seam.

This module is a renderer, not a gate. Nothing here decides whether a value is acceptable: validate_config/1 does, through StatifierBlocks.Edit.check_config/3, which is why an unparseable integer reaches the draft config as the string the author typed rather than being silently coerced or dropped.

Summary

Functions

Decodes one field's slice of a form's params back into a config value, dispatching on the same closed type set the renderer does.

One field: its label, its control, and its own findings (decision 11).

The DOM id for a field's control. Part of decision 7's DOM contract.

The form param name a field's control posts under.

Functions

decode(arg1, raw)

Decodes one field's slice of a form's params back into a config value, dispatching on the same closed type set the renderer does.

Total, and deliberately non-coercing at the edges: an integer field whose input does not parse yields the string the author typed, so validate_config/1 reports it and decision 9's gate keeps it out of the document. Coercing to zero here would silently discard the author's intent and commit a value they never asked for.

field(assigns)

One field: its label, its control, and its own findings (decision 11).

Attributes

  • field (StatifierBlocks.ViewModel.Field) (required)
  • target (:any) (required)
  • class (:string) - Defaults to nil.
  • expression_component (:any) - Override for :expression, per ADR-0005 decision 9's seam. Receives the same assigns. Defaults to nil.

input_id(arg)

The DOM id for a field's control. Part of decision 7's DOM contract.

input_name(arg)

@spec input_name(StatifierBlocks.ViewModel.Field.t()) :: String.t()

The form param name a field's control posts under.