StatifierBlocks.Editor.Field (StatifierBlocks v0.1.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
:durationstructured value/unit control emitting an ISO-8601 string
{:list, t}repeatable rows of t's renderer, with add and remove

:duration emits a string rather than a number because ADR-0001 decision 6 forbids floats in config and "1.5 hours" has to be PT1H30M. The control exists so the author does not have to know that. A stored duration this module cannot parse is not discarded: it falls back to a plain text input carrying the original string, on the same principle as decision 12 - the editor never loses data it did not author.

: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).

An ISO-8601 duration from a whole number of one unit. The inverse of parse_duration/1 on everything parse_duration/1 accepts.

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.

An ISO-8601 duration as {count, unit} in the largest unit that divides it evenly, or nil for anything this control cannot represent - a value with a month or year component, a fractional one, or a string that is not a duration at all. nil is what routes the value to the raw text fallback rather than to the structured control, which is how a duration the editor did not author survives being rendered.

The unit names the :duration control offers, largest last.

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.

format_duration(n, arg2)

@spec format_duration(non_neg_integer(), String.t()) :: String.t()

An ISO-8601 duration from a whole number of one unit. The inverse of parse_duration/1 on everything parse_duration/1 accepts.

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.

parse_duration(value)

@spec parse_duration(term()) :: {non_neg_integer(), String.t()} | nil

An ISO-8601 duration as {count, unit} in the largest unit that divides it evenly, or nil for anything this control cannot represent - a value with a month or year component, a fractional one, or a string that is not a duration at all. nil is what routes the value to the raw text fallback rather than to the structured control, which is how a duration the editor did not author survives being rendered.

units()

@spec units() :: [String.t()]

The unit names the :duration control offers, largest last.