Renders a single-row key-binding bar, typically anchored to the bottom of a screen.
Bindings are {key_label, description} tuples displayed as styled [key] action
pairs separated by a configurable separator string. When no :bindings list is
provided the widget calls keybindings/0 on the currently active screen module
automatically.
Each binding is clickable: hovering highlights the binding, and clicking dispatches the associated key event as if the user pressed that key.
Component tag
Tag :footer, built by Drafter.App as {:footer, opts}:
footer(opts)There is no positional argument; every prop comes from opts. :app_module
is supplied by the renderer.
Options
:bindings- list of{key, description}tuples. Defaultnil, in which case the widget callskeybindings/0on the active screen module, falling back to:app_module, and then to[]when neither exports it:separator-String.t/0placed between binding pairs. Default" ":style- style map applied to description text. Defaultnil, which uses the computed:footertheme style:key_style- style map applied to key label text. Defaultnil, which uses the computed:footer:keypart style:app_module- module used for theme resolution and as the fallback source ofkeybindings/0, passed by the renderer as:__app_module__. Defaultnil
update/2 re-reads every option, and passing bindings: nil explicitly restores
the "ask the active screen" behaviour. All of them are live-updatable through the
component tree.
Widget value
Drafter.get_widget_value/1 is not implemented for this widget and returns nil.
Events
The footer is not focusable and handles no keys. A hover moves the highlight to
the binding under the pointer, and a mouse release on a binding sends the
corresponding key event through Drafter.Event.Manager. A label of one byte
becomes a {:char, codepoint} event, a label in the built-in table becomes its
{:key, atom} event, a label with + becomes {:key, key, modifiers}, and any
other label is downcased and converted to an atom.
Usage
footer(bindings: [{"q", "Quit"}, {"Tab", "Focus next"}, {"Enter", "Select"}])
footer()
Summary
Functions
The registry tag for this widget.
Turns the {:footer, opts} element into a props map for mount/1.
Callback implementation for Drafter.Widget.handle_event/2.
Moves the highlight to the binding spanning column x, counted from the left edge
of the widget.
Dispatches the key event for the binding under column x.
Builds the footer state from props. :hovered_index always starts at nil.
Always 1: the footer occupies a single row.
Draws the binding bar as a single strip padded or cropped to rect.width.
Callback implementation for Drafter.Widget.unmount/1.
Folds fresh props into state, re-reading :bindings, :style, :key_style,
:separator and :app_module. :hovered_index is left alone.
Returns mount_props unchanged, so a re-render passes every option through to
update/2.
Types
Functions
@spec component_tag() :: :footer
The registry tag for this widget.
iex> Drafter.Widget.Footer.component_tag()
:footer
@spec from_component_opts( term(), keyword() ) :: Drafter.Widget.props()
Turns the {:footer, opts} element into a props map for mount/1.
The positional argument is ignored. :__app_module__ becomes :app_module.
iex> Drafter.Widget.Footer.from_component_opts(nil, bindings: [{"q", "Quit"}])
%{bindings: [{"q", "Quit"}], separator: " ", style: nil, key_style: nil, app_module: nil}
Callback implementation for Drafter.Widget.handle_event/2.
Moves the highlight to the binding spanning column x, counted from the left edge
of the widget.
Returns {:ok, state} with the new :hovered_index, which is nil when x
falls on a separator or past the last binding, or {:noreply, state} when the
index has not changed.
Dispatches the key event for the binding under column x.
On a hit the event is sent through Drafter.Event.Manager.send_event/1, exactly
as if the key had been pressed, and {:ok, state} is returned. A release on a
separator or past the last binding returns {:noreply, state}.
@spec mount(Drafter.Widget.props()) :: t()
Builds the footer state from props. :hovered_index always starts at nil.
iex> f = Drafter.Widget.Footer.mount(%{bindings: [{"q", "Quit"}]})
iex> {f.bindings, f.separator, f.style, f.key_style, f.hovered_index}
{[{"q", "Quit"}], " ", nil, nil, nil}
@spec preferred_height( term(), keyword() ) :: pos_integer()
Always 1: the footer occupies a single row.
@spec render(t(), Drafter.Widget.rect()) :: [Drafter.Draw.Strip.t()]
Draws the binding bar as a single strip padded or cropped to rect.width.
Each binding takes " key " followed by " description", with :separator
between pairs and none after the last. The hovered binding is drawn with
reverse: true. rect.height is not consulted.
Callback implementation for Drafter.Widget.unmount/1.
@spec update(Drafter.Widget.props(), t()) :: t()
Folds fresh props into state, re-reading :bindings, :style, :key_style,
:separator and :app_module. :hovered_index is left alone.
:bindings is taken whenever the key is present, so bindings: nil restores the
active-screen lookup rather than being ignored.
iex> f = Drafter.Widget.Footer.mount(%{bindings: [{"q", "Quit"}]})
iex> Drafter.Widget.Footer.update(%{bindings: nil}, f).bindings
nil
@spec update_props_from_mount(Drafter.Widget.props(), t(), keyword()) :: Drafter.Widget.props()
Returns mount_props unchanged, so a re-render passes every option through to
update/2.