chore: moved component and added mix_quiet to justfile
This commit is contained in:
parent
8d1d04fa05
commit
c3b33b55a5
2 changed files with 3 additions and 0 deletions
|
|
@ -1,172 +0,0 @@
|
|||
defmodule MvWeb.Components.FieldVisibilityDropdownComponent do
|
||||
@moduledoc """
|
||||
LiveComponent for managing field visibility in the member overview.
|
||||
|
||||
Provides an accessible dropdown menu where users can select/deselect
|
||||
which member fields and custom fields are visible in the table.
|
||||
|
||||
## Props
|
||||
- `:all_fields` - List of all available fields
|
||||
- `:custom_fields` - List of CustomField resources
|
||||
- `:selected_fields` - Map field_name → boolean
|
||||
- `:id` - Component ID
|
||||
|
||||
## Events sent to parent:
|
||||
- `{:field_toggled, field, value}`
|
||||
- `{:fields_selected, map}`
|
||||
"""
|
||||
|
||||
use MvWeb, :live_component
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# UPDATE
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign_new(:open, fn -> false end)
|
||||
|> assign_new(:all_fields, fn -> [] end)
|
||||
|> assign_new(:custom_fields, fn -> [] end)
|
||||
|> assign_new(:selected_fields, fn -> %{} end)
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RENDER
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
all_fields = assigns.all_fields || []
|
||||
custom_fields = assigns.custom_fields || []
|
||||
|
||||
all_items =
|
||||
Enum.map(member_fields(all_fields), fn field ->
|
||||
%{
|
||||
value: field_to_string(field),
|
||||
label: format_field_label(field)
|
||||
}
|
||||
end) ++
|
||||
Enum.map(custom_fields(all_fields), fn field ->
|
||||
%{
|
||||
value: field,
|
||||
label: format_custom_field_label(field, custom_fields)
|
||||
}
|
||||
end)
|
||||
|
||||
assigns = assign(assigns, :all_items, all_items)
|
||||
|
||||
# LiveComponents require a static HTML element as root, not a function component
|
||||
~H"""
|
||||
<div>
|
||||
<.dropdown_menu
|
||||
id="field-visibility-menu"
|
||||
icon="hero-adjustments-horizontal"
|
||||
button_label={gettext("Columns")}
|
||||
items={@all_items}
|
||||
checkboxes={true}
|
||||
selected={@selected_fields}
|
||||
open={@open}
|
||||
show_select_buttons={true}
|
||||
phx_target={@myself}
|
||||
/>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# EVENTS (matching the Core Component API)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@impl true
|
||||
def handle_event("toggle_dropdown", _params, socket) do
|
||||
{:noreply, assign(socket, :open, !socket.assigns.open)}
|
||||
end
|
||||
|
||||
def handle_event("close_dropdown", _params, socket) do
|
||||
{:noreply, assign(socket, :open, false)}
|
||||
end
|
||||
|
||||
# toggle single item
|
||||
def handle_event("select_item", %{"item" => item}, socket) do
|
||||
current = Map.get(socket.assigns.selected_fields, item, true)
|
||||
updated = Map.put(socket.assigns.selected_fields, item, !current)
|
||||
|
||||
send(self(), {:field_toggled, item, !current})
|
||||
{:noreply, assign(socket, :selected_fields, updated)}
|
||||
end
|
||||
|
||||
# select all
|
||||
def handle_event("select_all", _params, socket) do
|
||||
all =
|
||||
socket.assigns.all_fields
|
||||
|> Enum.map(&field_to_string/1)
|
||||
|> Enum.map(&{&1, true})
|
||||
|> Enum.into(%{})
|
||||
|
||||
send(self(), {:fields_selected, all})
|
||||
{:noreply, assign(socket, :selected_fields, all)}
|
||||
end
|
||||
|
||||
# select none
|
||||
def handle_event("select_none", _params, socket) do
|
||||
none =
|
||||
socket.assigns.all_fields
|
||||
|> Enum.map(&field_to_string/1)
|
||||
|> Enum.map(&{&1, false})
|
||||
|> Enum.into(%{})
|
||||
|
||||
send(self(), {:fields_selected, none})
|
||||
{:noreply, assign(socket, :selected_fields, none)}
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# HELPERS (with defensive nil guards)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
defp member_fields(nil), do: []
|
||||
|
||||
defp member_fields(fields) do
|
||||
Enum.filter(fields, fn field ->
|
||||
is_atom(field) ||
|
||||
(is_binary(field) && not String.starts_with?(field, "custom_field_"))
|
||||
end)
|
||||
end
|
||||
|
||||
defp custom_fields(nil), do: []
|
||||
|
||||
defp custom_fields(fields) do
|
||||
Enum.filter(fields, fn field ->
|
||||
is_binary(field) && String.starts_with?(field, "custom_field_")
|
||||
end)
|
||||
end
|
||||
|
||||
defp field_to_string(field) when is_atom(field), do: Atom.to_string(field)
|
||||
defp field_to_string(field) when is_binary(field), do: field
|
||||
|
||||
defp format_field_label(field) do
|
||||
field
|
||||
|> field_to_string()
|
||||
|> String.replace("_", " ")
|
||||
|> String.split()
|
||||
|> Enum.map(&String.capitalize/1)
|
||||
|> Enum.join(" ")
|
||||
end
|
||||
|
||||
defp format_custom_field_label(field_string, custom_fields) do
|
||||
case String.trim_leading(field_string, "custom_field_") do
|
||||
"" ->
|
||||
field_string
|
||||
|
||||
id ->
|
||||
case Enum.find(custom_fields, fn cf -> to_string(cf.id) == id end) do
|
||||
nil -> gettext("Custom Field %{id}", id: id)
|
||||
custom_field -> custom_field.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue