LanternUI.Components.Command (LanternUI v0.8.2)

Copy Markdown View Source

Command palette (⌘K dialog) — a modal combobox over a listbox of actions.

There is no Fluxon equivalent; the structure mirrors shadcn's Base UI Command (input → list → groups → items → empty), translated onto Lantern's dialog runtime and tokens.

Filtering is the consumer's job

The component never filters its own children. It renders exactly the items it is handed and reports the query upward, so a LiveView can answer from a database, a search index, or memory:

<.command id="cmd-k" on_search="command_search" on_select="command_select">
  <.command_group label="Tickets">
    <.command_item :for={t <- @results} value={t.id}>
      {t.title}
      <:shortcut>{t.number}</:shortcut>
    </.command_item>
  </.command_group>
  <.command_empty :if={@results == []}>No matches for "{@query}"</.command_empty>
</.command>

def handle_event("command_search", %{"query" => q}, socket), do: ...
def handle_event("command_select", %{"value" => v}, socket), do: ...

Events, and why they are not phx- attributes

The LanternCommand hook pushes on_search / on_select itself. Nothing in this component carries a phx-change, phx-submit, or a <form> element, so mounting the palette in a global app shell cannot make a host app's own element("form") / form[phx-change=...] test selectors ambiguous. Event names default to the command_* namespace for the same reason.

Payloads:

  • on_search → %{"query" => query} (debounced by debounce, in ms)
  • on_select → %{"value" => value} — pushed only for items that do not carry their own phx-click, so per-item bindings are never double-fired.

Opening and closing

Shares the dialog contract with LanternUI.Components.Modal:

<.button phx-click={LanternUI.open_dialog("cmd-k")}>Search…</.button>
{:noreply, LanternUI.open_dialog(socket, "cmd-k")}

plus the built-in ⌘K / Ctrl+K hotkey (hotkey="k", set hotkey={nil} to disable). Focus is trapped while open and restored to the previously focused element on close. Escape and a backdrop click close by default.

Keyboard

↑/↓ move the highlight, Home/End jump, Enter activates the highlighted item, Escape closes. Focus stays in the input; the active item is published with aria-activedescendant (this is a combobox, not the roving DOM focus used by menu/select).

Supporting parts

Use command_separator/1 between groups or related results. Use command_shortcut/1 for a standalone keyboard hint; an item's :shortcut slot is the usual choice for a hint attached to one item. command_group/1, command_item/1, and command_empty/1 cover grouped results, selectable rows, and the empty state.

<.command_separator />
<.command_shortcut>Ctrl+K</.command_shortcut>

Summary

Functions

command(assigns)

Attributes

  • id (:string) (required) - Stable DOM id used by open_dialog/close_dialog.

  • open (:boolean) - Render already open (server-driven palettes). Defaults to false.

  • label (:string) - Accessible name for the dialog and the results listbox. Defaults to "Command palette".

  • placeholder (:string) - Search input placeholder. Defaults to "Type a command or search…".

  • target (:any) - LiveComponent to send on_search/on_select to, as @myself.

    Required when the palette is rendered from INSIDE a LiveComponent: without it the hook pushes to the parent LiveView, which then has to define handlers it has no business owning. A global palette in an app shell is almost always a LiveComponent, so this is the normal case rather than an edge one.

    Defaults to nil.

  • on_search (:string) - LiveView event pushed with %{"query" => query}. nil disables searching. Defaults to "command_search".

  • on_select (:string) - LiveView event pushed with %{"value" => value} when an item without its own phx-click is activated. nil disables it. Defaults to "command_select".

  • debounce (:integer) - Search debounce in milliseconds. Defaults to 200.

  • hotkey (:string) - Key that opens the palette with Meta/Ctrl. nil disables the global hotkey. Defaults to "k".

  • loading (:boolean) - Show the loading row above the results. Defaults to false.

  • search_on_open (:boolean) - Push on_search with the current query when the palette opens. Defaults to true.

  • close_on_esc (:boolean) - Close when Escape is pressed. Defaults to true.

  • close_on_outside_click (:boolean) - Close when the backdrop is clicked. Defaults to true.

  • close_on_select (:boolean) - Close the palette after an item is chosen. Defaults to true.

  • class (:any) - Extra classes on the dialog panel. Defaults to nil.

  • container_class (:any) - Extra classes on the overlay root. Defaults to nil.

  • backdrop_class (:any) - Extra classes on the dimmed backdrop. Defaults to nil.

  • list_class (:any) - Extra classes on the scrollable results list. Defaults to nil.

  • Global attributes are accepted. Arbitrary HTML/phx-* attributes passed through to the overlay root.

Slots

  • inner_block (required) - Results: groups, items, separators, empty state.
  • footer - Sticky footer under the results (hints, counts).

command_empty(assigns)

Attributes

  • class (:any) - Extra classes merged onto the empty state. Defaults to nil.
  • Global attributes are accepted. Arbitrary HTML/phx-* attributes passed through.

Slots

  • inner_block (required) - Empty-state copy.

command_group(assigns)

Attributes

  • id (:any) - Element id; auto-generated when omitted. Defaults to nil.
  • label (:string) - Section heading rendered above the items. Defaults to nil.
  • class (:any) - Extra classes merged onto the group. Defaults to nil.
  • Global attributes are accepted. Arbitrary HTML/phx-* attributes passed through.

Slots

  • inner_block (required) - Items belonging to this group.

command_item(assigns)

Attributes

  • id (:any) - Element id; auto-generated when omitted. Defaults to nil.
  • value (:any) - Value sent as %{"value" => value} when the item is selected. Defaults to nil.
  • disabled (:boolean) - Render the row unselectable. Defaults to false.
  • class (:any) - Extra classes merged onto the row. Defaults to nil.
  • Global attributes are accepted. Arbitrary HTML/phx-* attributes. An item with its own phx-click does not also push the palette's on_select event. Supports all globals plus: ["phx-click", "phx-target", "phx-value-id"].

Slots

  • icon - Leading icon or avatar.
  • description - Secondary line under the label.
  • shortcut - Trailing keyboard hint or badge.
  • inner_block (required) - Item label.

command_separator(assigns)

Attributes

  • class (:any) - Extra classes merged onto the separator. Defaults to nil.
  • Global attributes are accepted. Arbitrary HTML/phx-* attributes passed through.

command_shortcut(assigns)

Attributes

  • class (:any) - Extra classes merged onto the shortcut hint. Defaults to nil.
  • Global attributes are accepted. Arbitrary HTML/phx-* attributes passed through.

Slots

  • inner_block (required) - Keyboard hint text, e.g. ⌘K.