# `Ascii.Pieces.FormControls`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/pieces/form_controls.ex#L1)

A terminal style form being filled in, field by field.

A settings form drawn the way a terminal app draws one. As upstream
draws it, a focus marker tabs down through a text input, a select, radio
buttons, a checkbox, a switch and a slider, changes each, saves, then
changes them back. Give it `fields` and it draws your form instead, for
a reader to fill in with keys and clicks.

## Options

Give them to `Ascii.new/2` as `options:`, and new ones while it plays
with `Ascii.put_options/2` (or a Phoenix player's `options`): the form
is drawn anew from them. Give only what you mean to change: what a reader
has filled in is in its options already, and giving back older values
would undo keys typed since.

  * `fields` - your form, a list of maps, each with a `type` and a
    `label`, and its type's own keys. Default `[]`: upstream's tour.
    * `"text"` - an input holding `text`.
    * `"select"` - one of `choices`, the one at index `choice`.
    * `"radio"` - `choices` as radio buttons, `choice` the one set.
    * `"checkbox"` - `choices` as checkboxes, `checked` a boolean for
      each; with no `choices`, one box.
    * `"switch"` - `on` or off.
    * `"slider"` - `value` from `min` to `max` (0 to 100 by default), in
      steps of `step` (1; 0 for a hundredth of the range), with its
      `unit` after it (`"%"`, `" ms"`).

    A field with no `type` is a text field; one of a type it does not
    know is its label alone.
  * `buttons` - the buttons' names, at the right under the fields.
    Default `["cancel", "save"]`.
  * `focus` - the field, or after them the button, in focus, from 0;
    `-1` for none. Default `0`.
  * `status` - a note at the left of the buttons, such as `"saved"`.
    Default `""`.
  * `title` - the form's name, in its top edge. Default `"preferences"`.
  * `cols` - the frame's width, 32 to 200; 54 by default.

All but `title` are for a form of your own: the tour is always 54 by 16.
A form's fields take two rows each, so its frame is
`2 * length(fields) + 4` rows. Controls are cut at the right edge, and
buttons that do not fit left out; a focused text field shows the end of
its text, so the caret stays in view. Control characters in its strings
are shown as spaces.

## Colours

In one ink, or a gradient, with `:ink` and `:gradient` (see
`Ascii.Ink`); part by part with `:parts` (see `Ascii.Parts`),
`parts: true` taking the colours suggested here:

  * `frame` - the border, grey; and the controls' own, `frame.input`,
    `frame.select` (with its arrow) and `frame.menu`, the tour's open
    list.
  * `title` - the form's name in the border.
  * `focus` - the marker at the focused field or button (or the list's
    chosen item, or a row's checkbox that keys tick), yellow.
  * `label` - a field's name; `label.focus`, the focused one's, yellow.
  * `field` - what a field holds: its text, the choices' names, on or
    off.
  * `cursor` - a text field's caret, yellow.
  * `checked`, `unchecked` - radio buttons' and checkboxes' marks, green
    and grey.
  * `bar`, `knob`, `track` - a switch's and a slider's filled cells,
    blue; the knob; and the empty cells and the switch's ends, grey.
  * `value`, `unit` - a slider's reading.
  * `button` - a button; `button.focus`, the focused one, yellow.
  * `status` - a form's note; `status.ok`, the tour's saved note, green.
  * `key`, `hint` - the keys along the bottom, yellow, and what they do,
    grey.

## Keys and clicks

A form of your own takes keys and clicks with
`Ascii.handle_event/2`, changing `fields` and `focus`, from which the
app reads what was filled in and where the focus is:

  * Tab and back tab move the focus round the fields and buttons; up and
    down move it and stop at the ends; home and end go to the first and
    last; enter on a field goes on to the next.
  * A text field in focus takes printable characters, up to 256, and
    backspace.
  * Space steps a select or radio buttons on, flips a switch, and ticks
    the checkbox marked `>`.
  * Left and right step a select or radio buttons, move the checkbox
    mark, turn a switch off and on, move a slider a step (page up and
    page down, ten), and move along the buttons.
  * Enter or space on a button is the app's: it is ignored, and `focus`
    says which button.
  * A click takes the focus to the field or button it lands on, and
    picks the radio button, ticks the checkbox, flips the switch or sets
    the slider where it lands; on a select already in focus, it steps it
    on.

The tour takes none. In Phoenix, give the player `interactive` (and
`notify` to be told of each key and click); there Tab is the page's, to
move on from the form, so up and down move between fields. In
ExRatatui, `Ascii.ExRatatui.handle_event/3`; in a terminal,
`mix ascii.play form-controls --option 'fields=[{"label": "name"}]' --interactive`.

## Screen readers

`Ascii.describe/1` reads the form, each field's label and value and
where the focus is, as "account: email ada@example.com, plan free,
volume 40%; focus on plan"; past 160 characters it ends ", and N more".
Playing its tour, it gives the piece's name and note.

## Example

```elixir
fields = [
  %{type: "text", label: "email", text: "ada@example.com"},
  %{type: "radio", label: "plan", choices: ["free", "team"], choice: 1},
  %{type: "slider", label: "volume", value: 40, unit: "%"}
]

form = Ascii.new!("form-controls", options: %{title: "account", fields: fields}, parts: true)
{:ok, form} = Ascii.handle_event(form, {:key, "tab"})
{:ok, form} = Ascii.handle_event(form, {:key, "left"})
Enum.at(form.options.fields, 1).choice
#=> 0

Ascii.describe(form)
#=> "account: email ada@example.com, plan free, volume 40%; focus on plan"
```

```heex
<Ascii.Phoenix.live_player id="account" piece="form-controls" options={%{title: "account", fields: @fields}} parts interactive notify />
```

Ported from `src/pieces/form-controls.ts` in bas3line/ascii (MIT).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
