# Data entry — which pattern for which job

Which editing shape a job takes, and the laws that hold inside each. Which entry draws it is
[catalog.md](./catalog.md); its props are `dist/<module>.d.ts`.

## The decision table

| The job | The shape |
|---|---|
| Edit a field of an existing record, in place | the control's own `onSave` |
| Fill a NEW record, or a draft the reader may abandon | `FormField` fieldset, in a dialog or a page |
| Pick one of a known set | by option count — see below |
| Find an existing thing or make one | the `Combobox` family |
| Add repeated structured rows | `ChargeLines`, a priced band |
| Move a record to its next stage | a handoff: a stage transition, never a message |
| Attach files | `InlineFiles` on a row, `FilesEditor` for a record's files (its bar where the section owns verbs), a `MarkStack` of them on a line or in a cell |

**The right-input-per-field law.** A field's control follows its DATA type — a date takes a date
editor, a member a member editor, a bounded set a choice control. A text box that
accepts anything is what a field falls back to when nobody decided what it holds.

**A date editor's value is a canonical ISO string; `@lotics/ui/date_picker_value` reads and builds
one.** A second copy of that conversion disagrees on the edges it exists for.

**A dialog is a draft, so it does not derive the ERROR per keystroke**: validate on commit, or the
reader is corrected while still typing.

## A field that saves itself — the preferred way to edit an existing record

**ONE AXIS: who owns the draft.** A form owns it and commits in its footer (`onValueChange`); a
FIELD owns it and writes it itself (`onSave` — async, saving and a refusal in place, an
`AbortError` silent, Escape reverts, blur or Enter commits, a same-gesture act awaiting it).
One component, one sheet, one anatomy either way; flipping an
instance remounts it.

**Everything a record states is changed among its facts.** A contact reached from under the name
acts; it does not correct.

**The box does not move, because nothing swaps** — the control is the value at rest and under the
caret. An editable link keeps its DOOR as its own target: one per destination — a many-link's in
each row's chip — none where it points at nothing.

**`numberOfLines` declares what ENTER means**, and wrapping does not: one line commits on Enter,
more makes Enter a newline and the commit a blur or a button.

**The gate cannot see a CAPTURED object**: an act takes an ID and re-reads the row, or it commits
against a value that has since changed. A field counts what it saved as current until `value`
catches up, so a lagging parent does not flicker the old value back.

**THE FRAME IS THE PERMISSION** — an editable value rests as its control, framed before it is
touched, on a form and a record alike; a read-only value is its text, boxless.

**With `actions` the SURFACE owns the box** — a form's number or date field as one that saves
itself — and the control surrenders its own: one box, never a field inside a field. Pass the verb
unconditionally and `disabled` it when idle, or the field resizes as the value fills. A date holds
its width: under a label over it, its verbs end the label's line.

**`clearable` means the reader may EMPTY the field** — the ✕ on text, the named empty ROW on a
select, a date's calendar Clear, and an emptied date or number committing rather than reverting. A
record fact's `required` withholds them all.

**A quantity states its `unit`** — inside the box after the figure; "12,5 t" typed into kg is
12 500. `format` is money or a percent: what the box prints AND reads back.

**A NOTE IS ALWAYS MULTI-LINE, and its reserve is a FLOOR** — three lines at rest holding anything
or nothing, growing with the prose, never a fixed tall box. In a table the CELL reserves the WIDTH
too: the editor never states its own, or a long value re-lays the column. **So does the read-only
spelling**, or a note reads whole only where the reader may edit it.

**A picker over a select FIELD keeps the field's colours** — hand it the field (`fieldOptions`);
mapping its options drops them silently. The badge is OPT-IN (`badge: "tonal" | "dot"`) because the
colours have to MEAN something: assigned BY POSITION they paint an in-transit order red.

## Choosing a CHOICE control — by the set, never taste

The set's size and cardinality answer for the form, the record and the register alike; the `form` column is wherever a choice is asked — a create, an act's asks:

| Set | `form` | `record` |
|---|---|---|
| single, to 12 | `select` — resting on the chosen option's dot | `select` |
| single, 13+ | `combobox` — type to narrow | `combobox` |
| multi, ≤6 | `chips` — `OptionToggles multi` | `combobox` |
| multi, 7+ | `combobox` | `combobox` |
| one option, or none arrived yet | `select` — never a strip of one, or chips over nothing | `select` |

**NO FIELD IS EDITED WITH A SEGMENTED CONTROL** — it puts options that do not apply on screen; a
`ChoiceStrip` segment switches what a VIEW shows (a period, a layout). A record never lays a choice out as pills. It rests as a list field holding the value, a set's picks as chips inside
it. A yes/no is a `Switch`. `onSearchChange` hands the query to the caller: the rows
that came back ARE the answer. **A chip earns an ICON only when the set differs in KIND.**

**"No selection" is the PLACEHOLDER, never an option whose value is the empty string** — but a strip
has no placeholder, so an OPTIONAL one leads with the pack's none. A REQUIRED choice offers none,
strip or list.

**A choice that GATES the form is usually the answer "don't ask".** Derive it, or ask once at the
top and remember it.

## A value rendered TWICE is the bug — not the resident editor

Only the prose a PERSON wrote is editable. **An EMPTY value still shows its FIELD** — absence is
information, and a field that vanishes when unset cannot be filled.

**A record's facts are the worked case** — a read-only fact is the register's reading, `onSave`
rests it as its control, and every fact handed over is drawn: a field the screen has no place for is not
passed, never folded. No Edit act, no mode, nothing to disagree.

## Forms

Fields lay out on `Stack columns={{ minWidth }}`, which reflows on the CONTAINER so no breakpoint
is named; a field whose value is longer than a peer's takes the whole row with `FormField span`.

**A form grid states its COLUMN gutter alone** — `gap="none" gapX={16}`: the row gutter is
`FormField`'s own trailing rhythm, and one knob for both axes either pays it twice or closes the
columns.

**A field's gloss sits UNDER its control**, the warning and the error after it: every field then
has the SAME block above its control, so a row of paired controls is level by construction.

**A form marks its OPTIONAL fields** (`FormField.optional`) and leaves the required ones unmarked,
so the mark means one thing on every form. A demanded field is refused empty either way.

**Browser autofill is suppressed on search and kept on forms.** A control that owns a suggestion
list must suppress the browser's, or two lists open over each other.

## Line items and ledgers

A priced band is `ChargeLines`: create → preview → edit, and its closing row takes the pack's
"Total" rather than echoing the `heading` it sits under. **Money is ONE token**: it breaks neither
before the currency nor mid-figure. A line is edited inside the DOCUMENT it belongs to.

## Handoff — a stage transition, never a message

Two shapes, decided by whether the receiving side is the same entity.

**(A) Same entity** — the record moves along its own ladder, on the rung that names the move
(`composition.md` § The ladder). **ROWS TICK ONE AT A TIME**, and **A CHECKLIST REPORTS; IT DOES
NOT COLLECT** — a row that asks for a value is a field. **A status reports what is OUTSTANDING,
never what is settled.**

**(B) Different entity** — the handoff creates a record on the other side, and the two are linked.
The sending screen shows what was sent, not a copy of the receiving screen.


## Lifecycle

**Lifecycle status is ASYMMETRIC by phase** — the control offers the stages reachable from where
the record IS, never the whole vocabulary.

## Files

**Cardinality is a property of the FIELD, and it changes the verb**: "Add file" against "Replace
file". **Nothing to attach TO yet is a real state**, and it is a sentence, not a disabled control.

### Intake — files land THREE ways, on every surface that holds files

Three paths, ONE named door. The drag and the paste come from one drop target wrapping the region,
empty or not. The named door follows what the section HOLDS: nothing, and it is the empty line, the
heading standing its verb down; something, and it is the heading's Add and the body draws none — a
shortfall STATES, it does not act.

`accept` is matched against the file's type and its extension. A paste cannot carry a filename, so
a pasted image is named by the surface; it routes to the focused region first, then to the
innermost registered sink. **Make the two invisible paths discoverable** — say
so in the empty state.

### The `FilesEditor` compound

The root owns what a host cannot re-implement — selection, upload state, the viewer, the drop zone,
each file's ⋯: Open and Download always, Rename (the extension kept), Replace and a confirmed Remove
where the host passes them. A picture is a tile, a document a row saying its size and day. A list of
files takes its value's width; holding files, its add is its field's acts (`FormFieldActs`) — the END
of the label's line over it, of its own first line beside a label, or the host's heading (`addAt="host"`).
On a line a document is its type's glyph and its name; a set, one glyph and how many.

### Gated file CRUD is a PATTERN, composed locally, not a sealed kit component

`useSelectionMode()` for the mode, `shareOrDownloadFiles(files, { title, credentials })` for the
egress, `rotateImageToBlob(url, degrees)` for a correction.
