Ascii.Pieces.FormControls (Ascii v0.3.0)

Copy Markdown View Source

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

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