Uikit. Components. Live. Select
(elixir_uikit v0.9.0)
Copy Markdown
A searchable single/multi select ("power select") live component. Render it
through Uikit.Components.uk_power_select/1. Needs the PowerSelect hook.
The component only owns transient UI state (the current search string). All domain state — the available options and the selected values — is owned by the caller and passed in as assigns.
Assigns
:id(required) — unique DOM id, also included in every message so the caller can distinguish between multiple selects on one page.:options— list of values to show in the dropdown. The dropdown is open while this list is non-empty or the search input has text, which offers anAdd "..."row for the typed text. Defaults to[].:multiple—true(the default) for a multi select,falsefor a single select.:selected— the selected values, rendered as removable chips: a list for a multi select, a single value ornilfor a single select. Defaults to[].:attr— when:options/:selectedcontain maps or structs, the key to read from each entry. It is used as the display label, to check whether an option is selected, and as the value sent in the component's messages. Whennil(the default), entries are treated as plain values. Example: withattr={:name}and options like%{id: 1, name: "foobar"}, the component renders"foobar"and messages carry"foobar".:gettext_backend—{backend, domain}of the consuming app, e.g.gettext_backend={{MyAppWeb.Gettext, "uikit"}}. Whennil(the default), the English texts are used as-is.
Gettext
The component's texts are translated at runtime with
Gettext.dgettext/4 through the backend and domain injected with
:gettext_backend, in the backend's current locale. mix gettext.extract
does not pick them up — copy priv/gettext/uikit.pot of this library into
the app's priv/gettext and translate it from there.
Messages
Domain events are not handled by the component itself. Instead they are
sent to the parent LiveView process, which reacts in handle_info/2 and
feeds updated :options/:selected assigns back down:
{:power_select, id, {:search, query}}— the user typed in the search input. The caller is expected to look up matching options and re-assign:options.{:power_select, id, {:select, value}}— the user picked a value from the dropdown, or the typed text from theAdd "..."row. The caller should add it to:selected(replace it for a single select) and usually clear:options.{:power_select, id, {:remove, value}}— the user removed a chip. The caller should drop the value from:selected(set it tonilfor a single select).{:power_select, id, :reset}— the user clicked or tabbed out of the select, or pressed Escape. The component clears its search string; the caller should clear:options.
The search input has the id "<id>-input", point a <label for> at it.
Example
Options and selections are usually maps (e.g. Ecto/Ash records). Pass
:attr so the component knows which key to display and to send in its
messages — the caller then resolves that value back to the full record:
<.uk_power_select
id="fruit-select"
options={@fruit_options}
selected={@fruits}
attr={:name}
/>
def handle_info({:power_select, "fruit-select", {:search, query}}, socket) do
# e.g. a DB query returning [%{id: 1, name: "apple"}, ...]
{:noreply, assign(socket, :fruit_options, search_fruits(query))}
end
def handle_info({:power_select, "fruit-select", {:select, name}}, socket) do
case Enum.find(socket.assigns.fruit_options, &(&1.name == name)) do
nil ->
{:noreply, assign(socket, :fruit_options, [])}
fruit ->
{:noreply,
socket
|> update(:fruits, &Enum.uniq(&1 ++ [fruit]))
|> assign(:fruit_options, [])}
end
end
def handle_info({:power_select, "fruit-select", {:remove, name}}, socket) do
{:noreply, update(socket, :fruits, &Enum.reject(&1, fn f -> f.name == name end))}
end
def handle_info({:power_select, "fruit-select", :reset}, socket) do
{:noreply, assign(socket, :fruit_options, [])}
end