Skip to content

Instantly share code, notes, and snippets.

@boriscy
Last active January 11, 2025 18:05
Show Gist options
  • Save boriscy/ad19ffbca9ff750f7aecd0cd16499339 to your computer and use it in GitHub Desktop.
Save boriscy/ad19ffbca9ff750f7aecd0cd16499339 to your computer and use it in GitHub Desktop.
defmodule PollaWeb.GroupsLive.Members do
use PollaWeb, :live_view
alias Polla.Groups
def mount(params, _session, socket) do
group = Groups.get_by_slug(params["slug"])
socket =
assign(socket, group: group)
|> assign(:typeahead_id, "search_members_typeahead")
{:ok, socket}
end
def handle_info({:search_user, params}, socket) do
users = Groups.find_users(%{group_id: params["group_id"], value: params["value"]})
send_update(Polla.TypeaheadLiveComponent,
id: socket.assigns.typeahead_id,
items: users
)
{:noreply, socket}
end
def render(assigns) do
~H"""
<h1>{@group.name}</h1>
<.live_component
module={Polla.TypeaheadLiveComponent}
id={@typeahead_id}
title={gettext("Type to search")}
placeholder={gettext("Type to search for a user")}
on_search={:search_user}
on_select={:select_user}
>
<:extra_args>
<.input type="hidden" name="group_id" value={@group.id} />
</:extra_args>
<:item :let={user}>
<div>
<div>
{user.name}
</div>
<div class="text-primary/80 text-sm">
{user.email}
</div>
</div>
</:item>
</.live_component>
"""
end
end
defmodule Polla.TypeaheadLiveComponent do
use PollaWeb, :live_component
slot :item, required: true
slot :extra_args, required: false
@impl true
def mount(socket) do
{:ok, assign(socket, :items, [])}
end
@impl true
def update(%{items: items} = _assigns, socket) do
{:ok, assign(socket, items: items)}
end
@impl true
def update(assigns, socket) do
{:ok,
socket
|> assign(assigns)
|> assign_new(:form, fn ->
to_form(%{})
end)
|> assign_new(:value, fn ->
nil
end)}
end
@impl true
def handle_event("validate", params, socket) do
send(self(), {socket.assigns.on_search, params})
{:noreply, assign(socket, :params, params)}
end
@impl true
def handle_event("select", %{"id" => id}, socket) do
send(self(), {socket.assigns.on_select, id})
{:noreply, socket}
end
@impl true
def render(assigns) do
~H"""
<div>
<.simple_form for={@form} phx-target={@myself} phx-change="validate" autocomplete="off">
<div class="space-y-6">
<.input field={@form[:value]} type="text" placeholder={@placeholder} phx-debounce={120} />
<%= if @extra_args do %>
{render_slot(@extra_args)}
<% end %>
<ul>
<li
:for={item <- @items}
tabindex="0"
class="border-b py-1 last:border-none dark:border-zinc-700 cursor-pointer focus:outline-none focus:ring focus:ring-blue-300 focus:border-blue-500"
phx-click="select"
phx-value-id={item.id}
phx-target={@myself}
>
<div class="py-2 rounded hover:bg-zinc-100 dark:hover:bg-zinc-700">
{render_slot(@item, item)}
</div>
</li>
</ul>
<div :if={@value && Enum.empty?(@items)}>
{gettext("No results")}
</div>
</div>
</.simple_form>
</div>
"""
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment