صندوق القائمة · الأنماط
عناصر متدفقة وتحديد يتحكم به الخادم.
Stream
Apple
Banana
Cherry
<div class="flex flex-col gap-3 w-full max-w-xl">
<div class="flex flex-wrap gap-2">
<.action phx-click="add_item" class="button button--sm button--accent">
<.heroicon name="hero-plus" /> Add item
</.action>
<.action phx-click="reset" class="button button--sm button--alert">
Reset
</.action>
</div>
<.listbox class="listbox" items={Corex.List.new(@items_list)}>
<:label>Choose an item</:label>
<:empty>No items</:empty>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
</div>
defmodule MyAppWeb.ListboxStreamDemoLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
initial = [
%{value: "1", label: "Apple"},
%{value: "2", label: "Banana"},
%{value: "3", label: "Cherry"}
]
socket =
socket
|> stream_configure(:items, dom_id: &("listbox:stream-listbox:item:" <> to_string(&1.value)))
|> stream(:items, initial)
|> assign(:items_list, initial)
|> assign(:next_id, 4)
if connected?(socket) do
Process.send_after(self(), :add_timestamp_item, 3_000)
end
{:ok, socket}
end
@impl true
def handle_info(:add_timestamp_item, socket) do
Process.send_after(self(), :add_timestamp_item, 10_000)
id = to_string(socket.assigns.next_id)
time =
DateTime.utc_now()
|> DateTime.truncate(:second)
|> DateTime.to_time()
|> Time.to_string()
item = %{value: id, label: "Item " <> id <> " @ " <> time}
{:noreply,
socket
|> stream_insert(:items, item)
|> assign(:items_list, socket.assigns.items_list ++ [item])
|> assign(:next_id, socket.assigns.next_id + 1)}
end
@impl true
def handle_event("add_item", _params, socket) do
id = to_string(socket.assigns.next_id)
item = %{value: id, label: "Item " <> id}
{:noreply,
socket
|> stream_insert(:items, item)
|> assign(:items_list, socket.assigns.items_list ++ [item])
|> assign(:next_id, socket.assigns.next_id + 1)}
end
@impl true
def handle_event("reset", _params, socket) do
initial = [
%{value: "1", label: "Apple"},
%{value: "2", label: "Banana"},
%{value: "3", label: "Cherry"}
]
{:noreply,
socket
|> stream(:items, initial, reset: true)
|> assign(:items_list, initial)
|> assign(:next_id, 4)}
end
@impl true
def render(assigns) do
~H"""
<div class="flex flex-col gap-3 w-full max-w-xl">
<div class="flex flex-wrap gap-2">
<.action phx-click="add_item" class="button button--sm button--accent">
<.heroicon name="hero-plus" /> Add item
</.action>
<.action phx-click="reset" class="button button--sm button--alert">
Reset
</.action>
</div>
<.listbox id="stream-listbox" class="listbox" items={Corex.List.new(@items_list)}>
<:label>Choose an item</:label>
<:empty>No items</:empty>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
</div>
"""
end
end
Stream مجمّع
Europe
France
Germany
Asia
Japan
<div class="flex flex-col gap-3 w-full max-w-xl">
<div class="flex flex-wrap gap-2">
<.action
phx-click="add_to_group"
phx-value-group="Europe"
class="button button--sm button--accent"
>
<.heroicon name="hero-plus" /> Add to Europe
</.action>
<.action
phx-click="add_to_group"
phx-value-group="Asia"
class="button button--sm button--accent"
>
<.heroicon name="hero-plus" /> Add to Asia
</.action>
<.action phx-click="reset_grouped" class="button button--sm button--alert">
Reset
</.action>
</div>
<.listbox
class="listbox"
items={Corex.List.new(@grouped_items_list)}
>
<:label>Choose a country</:label>
<:empty>No items</:empty>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
</div>
defmodule MyAppWeb.ListboxStreamGroupedDemoLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
initial = [
%{value: "g1", label: "France", group: "Europe"},
%{value: "g2", label: "Japan", group: "Asia"},
%{value: "g3", label: "Germany", group: "Europe"}
]
socket =
socket
|> stream_configure(:grouped_items, dom_id: &("listbox:stream-grouped-listbox:item:" <> to_string(&1.value)))
|> stream(:grouped_items, initial)
|> assign(:grouped_items_list, initial)
|> assign(:next_grouped_id, 4)
{:ok, socket}
end
@impl true
def handle_event("add_to_group", %{"group" => group}, socket) do
n = socket.assigns.next_grouped_id
id = "g" <> Integer.to_string(n)
item = %{value: id, label: "Item " <> Integer.to_string(n), group: group}
{:noreply,
socket
|> stream_insert(:grouped_items, item)
|> assign(:grouped_items_list, socket.assigns.grouped_items_list ++ [item])
|> assign(:next_grouped_id, n + 1)}
end
@impl true
def handle_event("reset_grouped", _params, socket) do
initial = [
%{value: "g1", label: "France", group: "Europe"},
%{value: "g2", label: "Japan", group: "Asia"},
%{value: "g3", label: "Germany", group: "Europe"}
]
{:noreply,
socket
|> stream(:grouped_items, initial, reset: true)
|> assign(:grouped_items_list, initial)
|> assign(:next_grouped_id, 4)}
end
@impl true
def render(assigns) do
~H"""
<div class="flex flex-col gap-3 w-full max-w-xl">
<div class="flex flex-wrap gap-2">
<.action
phx-click="add_to_group"
phx-value-group="Europe"
class="button button--sm button--accent"
>
<.heroicon name="hero-plus" /> Add to Europe
</.action>
<.action
phx-click="add_to_group"
phx-value-group="Asia"
class="button button--sm button--accent"
>
<.heroicon name="hero-plus" /> Add to Asia
</.action>
<.action phx-click="reset_grouped" class="button button--sm button--alert">
Reset
</.action>
</div>
<.listbox
id="stream-grouped-listbox"
class="listbox"
items={Corex.List.new(@grouped_items_list)}
>
<:label>Choose a country</:label>
<:empty>No items</:empty>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
</div>
"""
end
end
مُتحكَّم (القيمة)
France
Belgium
Germany
Netherlands
Switzerland
Austria
value: ["fra", "bel"]
<.listbox
class="listbox"
items={
Corex.List.new([
%{label: "France", value: "fra"},
%{label: "Belgium", value: "bel"},
%{label: "Germany", value: "deu"},
%{label: "Netherlands", value: "nld"},
%{label: "Switzerland", value: "che"},
%{label: "Austria", value: "aut"}
])
}
selection_mode="multiple"
controlled
value={@listbox_controlled_value}
on_value_change="listbox_patterns_controlled_value"
>
<:label>Choose countries</:label>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
<div class="w-full min-w-0">
<p class="text-sm text-ink-muted font-mono break-all text-center">
value: {inspect(@listbox_controlled_value)}
</p>
</div>
defmodule MyAppWeb.ListboxControlledDemoLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :listbox_controlled_value, ["fra", "bel"])}
end
@impl true
def handle_event("listbox_patterns_controlled_value", %{"value" => value}, socket)
when is_list(value) do
{:noreply, assign(socket, :listbox_controlled_value, value)}
end
@impl true
def render(assigns) do
~H"""
<div class="flex flex-col gap-3 w-full items-center">
<div class="w-full max-w-md">
<.listbox
id="listbox-patterns-controlled-field"
class="listbox"
items={
Corex.List.new([
%{label: "France", value: "fra"},
%{label: "Belgium", value: "bel"},
%{label: "Germany", value: "deu"},
%{label: "Netherlands", value: "nld"},
%{label: "Switzerland", value: "che"},
%{label: "Austria", value: "aut"}
])
}
selection_mode="multiple"
controlled
value={@listbox_controlled_value}
on_value_change="listbox_patterns_controlled_value"
>
<:label>Choose countries</:label>
<:item_indicator><.heroicon name="hero-check" /></:item_indicator>
</.listbox>
</div>
<div class="w-full min-w-0" id="listbox-patterns-controlled-state">
<p class="text-sm text-ink-muted font-mono break-all text-center">
value: {inspect(@listbox_controlled_value)}
</p>
</div>
</div>
"""
end
end