A boolean toggle widget that renders an X mark inside a box next to an optional label.
The checked state is toggled by pressing Space, Enter, or clicking the widget. The
:on_change callback receives the new boolean value after each toggle.
Component tag
Tag :checkbox, built by Drafter.App as {:checkbox, label, opts}:
checkbox(label, opts)The positional argument becomes :label. The checked state goes through
the binding layer: passing bind: :some_key reads it from that app-state key
and writes the new value back on every toggle, and :on_change is built from
the same binding.
Options
:label-String.t/0displayed to the right of the box. Default"". Supplied positionally through thecheckbox/2element:checked-boolean/0initial checked state. Defaultfalse:bind- app-state key atom for two-way binding of the checked state. Defaultnil:on_change-(boolean() -> term())called with the new value after each toggle. Defaultnil. An exception raised inside it is caught and ignored:style-map/0of style overrides. Default%{}. An:app_modulekey inside this map selects the theme used for the box and label colours:focused-boolean/0initial focus flag, read bymount/1. Defaultfalse:class- accepted by the element and normalised into a:classesprop, butmount/1andupdate/2both ignore it, so it has no effect
update/2 accepts :label, :checked, :focused, :style and :on_change and
silently drops every other key. Through the component tree only :on_change and,
when :bind is set, :checked are re-applied on a re-render — :label and
:style are effectively mount-only there.
Widget value
Drafter.get_widget_value/1 returns the checked boolean/0.
Key bindings
Enter and Space with no modifiers toggle the checkbox, as does a mouse
release. The same keys with modifiers are ignored.
Usage
checkbox("Remember me", checked: false, on_change: fn checked -> IO.inspect(checked) end)
Summary
Functions
The registry tag for this widget.
Turns the {:checkbox, label, opts} element into a props map for mount/1.
Handles events directly instead of going through Drafter.Widget.EventRouter.
Builds the checkbox state from props.
Always 1: the checkbox occupies a single row.
Draws the indicator and the label into rect.
Callback implementation for Drafter.Widget.unmount/1.
Folds fresh props into state.
Narrows the props a re-render feeds to update/2.
Types
Functions
@spec component_tag() :: :checkbox
The registry tag for this widget.
iex> Drafter.Widget.Checkbox.component_tag()
:checkbox
@spec from_component_opts( term(), keyword() ) :: Drafter.Widget.props()
Turns the {:checkbox, label, opts} element into a props map for mount/1.
label is the positional argument. The checked state comes from :bind read
against opts[:__app_state__], falling back to opts[:checked] and then false,
and :on_change is the binding's writer. The emitted :classes key is not read
by mount/1.
iex> props = Drafter.Widget.Checkbox.from_component_opts("Agree", checked: true)
iex> {props.label, props.checked, props.style, props.classes}
{"Agree", true, %{}, []}
@spec handle_event(Drafter.Event.t() | atom(), t()) :: {:ok, t()} | {:noreply, t()}
Handles events directly instead of going through Drafter.Widget.EventRouter.
:activate, {:key, :enter}, {:key, :" "} and {:mouse, %{type: :mouse_up}}
flip :checked and call :on_change with the new value. :hover and :unhover
set and clear :hovered; {:focus} sets both :focused and :hovered, and
{:blur} clears both. Everything else, including a key event carrying modifiers,
returns {:noreply, state}.
iex> cb = Drafter.Widget.Checkbox.mount(%{label: "Agree"})
iex> {:ok, toggled} = Drafter.Widget.Checkbox.handle_event({:key, :" "}, cb)
iex> toggled.checked
true
iex> cb = Drafter.Widget.Checkbox.mount(%{label: "Agree"})
iex> {:ok, focused} = Drafter.Widget.Checkbox.handle_event({:focus}, cb)
iex> {focused.focused, focused.hovered}
{true, true}
iex> cb = Drafter.Widget.Checkbox.mount(%{label: "Agree"})
iex> Drafter.Widget.Checkbox.handle_event({:key, :enter, [:shift]}, cb) |> elem(0)
:noreply
@spec mount(Drafter.Widget.props()) :: t()
Builds the checkbox state from props.
:hovered always starts at false.
iex> cb = Drafter.Widget.Checkbox.mount(%{label: "Remember me", checked: true})
iex> {cb.label, cb.checked, cb.focused, cb.hovered}
{"Remember me", true, false, false}
iex> cb = Drafter.Widget.Checkbox.mount(%{})
iex> {cb.label, cb.checked, cb.style, cb.on_change}
{"", false, %{}, nil}
@spec preferred_height( term(), keyword() ) :: pos_integer()
Always 1: the checkbox occupies a single row.
@spec render(t() | Drafter.Widget.props(), Drafter.Widget.rect()) :: [ Drafter.Draw.Strip.t() ]
Draws the indicator and the label into rect.
Accepts either a t/0 or a raw props map, which is mounted first. The indicator
takes the first three columns and the label the rest; a rect narrower than four
columns leaves no room for the label. Returns one strip per row of rect.height,
with only the first carrying content.
Callback implementation for Drafter.Widget.unmount/1.
@spec update(Drafter.Widget.props(), t()) :: t()
Folds fresh props into state.
Only :label, :checked, :focused, :style and :on_change are applied; any
other key in props is dropped without error.
iex> cb = Drafter.Widget.Checkbox.mount(%{label: "Agree"})
iex> updated = Drafter.Widget.Checkbox.update(%{checked: true, nonsense: 1}, cb)
iex> {updated.checked, updated.label}
{true, "Agree"}
@spec update_props_from_mount(Drafter.Widget.props(), t(), keyword()) :: Drafter.Widget.props()
Narrows the props a re-render feeds to update/2.
Always passes :on_change and :classes, and adds :checked only when opts
carries a :bind. :label and :style are deliberately left out, so a re-render
does not overwrite them.