Suggestions while you type, in groups (lessons, pull requests, tasks), with the part that matched in bold
and an optional last row that asks the AI. The server owns the search: the field sends event (debounced by
debounce ms) with the text, and the caller answers with groups; this component draws the field and the
list and moves the active row with ↑ ↓, opens it with Enter and runs the AI row with ⌘ + Enter (or Ctrl +
Enter). Esc closes the list, typing opens it again. The list also closes when the focus leaves the whole
component, a moment after, so the click that chose a row still lands.
A group is %{label: "Lessons", items: [%{id: ..., label: ..., to: path, hint: "30", tone: :warn}]}; each
group is a role="group" named by its label. A row goes where to (a LiveView navigation), patch or
href says (or sends event with its id); hint is a small mono badge, tone colors it, and
hint_style: :plain draws it as bare mono text in the tone, with no background (the number of a pull request
or an issue: #142). The AI row sends ask_event with the typed text
in the q parameter (phx-value-q). highlight/2 splits a label around the query, ignoring case and
accents, for the bold part.
While the server searches, loading shows loading_label (with the loading_icon slot, if any) instead of the
"nothing found" text, keeps the rows it has, and sets aria-busy on the list. What a query that finds nothing
says is empty, or the empty_state slot when it needs more than text (a link to create the thing); the slot
gets the query.
The field tells assistive technology the list is open only when there is a row in it: not for a query that
found nothing and has no AI row. Icons are slots (search_icon, with a ⌕ when there is none) and the
key caps are DevactionUI.Kbd: ask_kbd is the key of the AI row, written by the caller (⌘↵ on a Mac,
Ctrl ↵ elsewhere).
Summary
Functions
Splits label around the first place query appears, ignoring case and accents:
highlight("Ecto.Multi", "ecto") is [match: "Ecto", text: ".Multi"]. No match, or a blank query, is the
whole label as text.
One row of the list, for DevactionUI.Autocomplete and DevactionUI.CommandPalette: a link when the item has
to (a LiveView navigation), patch or href, and a button that sends event with the item's id (as
id) when it has event. It is a role="option" the hook can move over.
Functions
@spec autocomplete(map()) :: Phoenix.LiveView.Rendered.t()
Attributes
id(:string) (required)name(:string) - Defaults to"q".value(:string) - Defaults to"".label(:string) (required) - what is being searched, for assistive technology.placeholder(:string) - Defaults tonil.event(:string) (required) - the event the field sends with the text, debounced.debounce(:integer) - milliseconds the field waits before sending. Defaults to150.groups(:list) -%{label, items: [%{id, label, to | patch | href, hint, tone}]}. Defaults to[].empty(:string) (required) - shown when a query finds nothing.loading(:boolean) - the server is searching: showsloading_labeland marks the list busy. Defaults tofalse.loading_label(:string) - what the list says whileloading. Defaults tonil.ask_label(:string) - the text of the AI row, e.g.Ask the AI about "ecto"; no row when nil. Defaults tonil.ask_event(:string) - the event the AI row sends with the text, asq. Defaults tonil.ask_kbd(:string) - the key of the AI row, as a key cap:⌘↵; none when nil. Defaults tonil.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
search_icon- the magnifier in the field.loading_icon- turning icon beforeloading_label.empty_state- replaces theemptytext; gets the query.
Splits label around the first place query appears, ignoring case and accents:
highlight("Ecto.Multi", "ecto") is [match: "Ecto", text: ".Multi"]. No match, or a blank query, is the
whole label as text.
@spec option(map()) :: Phoenix.LiveView.Rendered.t()
One row of the list, for DevactionUI.Autocomplete and DevactionUI.CommandPalette: a link when the item has
to (a LiveView navigation), patch or href, and a button that sends event with the item's id (as
id) when it has event. It is a role="option" the hook can move over.
Attributes
id(:string) (required)item(:map) (required) -%{to | patch | href}goes there;%{event}sends it with the item'sid.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)