Prima.Listbox (Prima v0.3.0)
View SourceA single-select listbox component for use as a form input.
Unlike Prima.Dropdown (an action menu, role="menu"), Listbox is a value
picker (role="listbox") — selecting an option updates a hidden form field
and the displayed value, similar to a native <select>.
Quick Start
<.listbox id="fruit-listbox" name="fruit" value={@selected_fruit}>
<.listbox_trigger id="fruit-listbox-trigger">
<.listbox_value>{@selected_fruit || "Select a fruit..."}</.listbox_value>
</.listbox_trigger>
<.listbox_options id="fruit-listbox-options" class="min-w-[var(--reference-width)]">
<.listbox_option id="fruit-option-apple" value="apple">Apple</.listbox_option>
<.listbox_option id="fruit-option-banana" value="banana">Banana</.listbox_option>
</.listbox_options>
</.listbox>Form Integration
Listbox renders a hidden <input> with the given name. Selecting an option
updates the input's value and dispatches bubbling input and change events,
so a parent form's phx-change fires exactly like it would for a native form
field:
<form phx-change="form_changed">
<.listbox id="fruit-listbox" name="fruit" value={@selected_fruit}>
...
</.listbox>
</form>
def handle_event("form_changed", %{"fruit" => fruit}, socket) do
{:noreply, assign(socket, selected_fruit: fruit)}
endDisplayed Value
The listbox value is rendered by the caller (so the initial page load is always correct — no flash of placeholder text), and updated instantly on the client when an option is picked, ahead of any server round-trip:
<.listbox_trigger id="fruit-listbox-trigger">
<.listbox_value>{@selected_fruit || "Select a fruit..."}</.listbox_value>
</.listbox_trigger>Disabling the Listbox
Pass disabled={true} to listbox to disable the entire field. Its hidden
input is disabled in the server-rendered HTML, excluding the value from form
submission while preserving it for when the field is enabled again.
On mount and LiveView updates, the JS hook applies native disabled to the
trigger button, preventing interaction and removing it from the tab order.
Disabling an open listbox closes it.
For styling, the listbox wrapper receives data-disabled in the
server-rendered HTML. Style the trigger through that ancestor, e.g. with
Tailwind's in-data-disabled: variant. Avoid styling the trigger with
disabled: - the native attribute is only applied once the hook mounts, so
the disabled styling would flicker in after the initial page load.
<.listbox id="fruit-listbox" name="fruit" value={@selected_fruit} disabled={@locked?}>
<.listbox_trigger id="fruit-listbox-trigger" class="in-data-disabled:opacity-50">
<.listbox_value>{@selected_fruit || "Select a fruit..."}</.listbox_value>
</.listbox_trigger>
<.listbox_options id="fruit-listbox-options" class="min-w-[var(--reference-width)]">
<.listbox_option id="fruit-option-apple" value="apple">Apple</.listbox_option>
</.listbox_options>
</.listbox>Setting the Value from Client-Side Code
Prima.Listbox.JS.set_value/2 sets the value without a server round-trip.
It updates the hidden input, the displayed value and the selection markers:
<button type="button" phx-click={Prima.Listbox.JS.set_value("fruit-listbox", "apple")}>
Reset
</button>Like setting .value on a native <select>, this dispatches no events by
default, so a parent form's phx-change doesn't fire. Pass notify: true to
also dispatch input and change, exactly like a user selection:
Prima.Listbox.JS.set_value("fruit-listbox", "apple", notify: true)Without notify, the server never learns the new value. If this listbox
re-renders (its value or slot content changes), LiveView restores the
server's value. Re-renders elsewhere on the page don't affect it.
Disabled options can be set, since disabled only blocks user picks. A value
that matches no option, including "", is ignored.
From plain JavaScript, dispatch the underlying event on the listbox element:
document.getElementById("fruit-listbox").dispatchEvent(
new CustomEvent("prima:listbox:set-value", { detail: { value: "apple" } })
)
Summary
Functions
An individual selectable option within a listbox.
The listbox options.
The trigger button for a listbox.
The displayed value within a listbox trigger.
Functions
Attributes
id(:string) (required)name(:string) (required)value(:any) - Defaults tonil.disabled(:boolean) - Defaults tofalse.- Global attributes are accepted.
Slots
inner_block(required)
An individual selectable option within a listbox.
Attributes
id(required) - Unique identifier, required for ARIA relationshipsvalue(required) - The value submitted when this option is selecteddisplay- The text shown inlistbox_valuewhen selected (defaults tovalue)disabled- Boolean to mark the option as unselectable (default: false)
Attributes
id(:string) (required)value(:any) (required)display(:string) - Defaults tonil.class(:string) - Defaults to"".disabled(:boolean) - Defaults tofalse.- Global attributes are accepted.
Slots
inner_block(required)
The listbox options.
CSS variables
--reference-width: Width of the trigger, or the element selected byreference. Updates automatically while open.
Apply min-width: var(--reference-width) for a minimum width, or
width: var(--reference-width) for an exact match.
Attributes
id(:string) (required)transition_enter(:any) - Defaults tonil.transition_leave(:any) - Defaults tonil.class(:string) - Defaults to"".reference(:string) - Defaults tonil.placement(:string) - Defaults to"bottom-start". Must be one of"top","top-start","top-end","right","right-start","right-end","bottom","bottom-start","bottom-end","left","left-start", or"left-end".flip(:boolean) - Defaults totrue.offset(:integer) - Defaults to4.- Global attributes are accepted.
Slots
inner_block(required)
The trigger button for a listbox.
Render a listbox_value within the trigger so the JS hook can update the
displayed value without changing other content such as icons.
Examples
<.listbox_trigger id="fruit-listbox-trigger">
<.listbox_value>{@selected_fruit || "Select a fruit..."}</.listbox_value>
<svg class="h-5 w-5" ...>...</svg>
</.listbox_trigger>Accessible Naming
Labelling is up to the caller, as with a native <select>. The trigger's
content is its accessible name (via aria-labelledby). If the trigger only
shows the current value, put a visually hidden label next to it so that both
the meaning and the value are announced:
<.listbox_trigger id="role-listbox-trigger">
<span class="sr-only">Role:</span>
<.listbox_value>{@selected_role}</.listbox_value>
</.listbox_trigger>To disable the field, pass disabled={true} to the parent listbox.
Its JS hook synchronizes this button's native disabled attribute. Style the
disabled state of this button via an in-data-disabled: Tailwind variant.
Attributes
id(:string) (required)class(:string) - Defaults to"".- Global attributes are accepted.
Slots
inner_block(required)
The displayed value within a listbox trigger.
The JS hook updates this content when an option is selected without changing other trigger content.
Attributes
class(:string) - Defaults to"".- Global attributes are accepted.
Slots
inner_block(required)