cannery/lib/cannery_web/components/table_component.ex
shibao cfbec3189c
All checks were successful
continuous-integration/drone/push Build is passing
display used-up date on used-up ammo
2022-11-07 21:01:57 -05:00

87 lines
2.8 KiB
Elixir

defmodule CanneryWeb.Components.TableComponent do
@moduledoc """
Livecomponent that presents a resortable table
It takes the following required assigns:
- `:columns`: An array of maps containing the following keys
- `:label`: A gettext'd or otherwise user-facing string label for the
column. Can be nil
- `:key`: An atom key used for sorting
- `:class`: Extra classes to be applied to the column element, if desired.
Optional
- `:sortable`: If false, will prevent the user from sorting with it.
Optional
- `:values`: An array of maps containing data for each row. Each map is
string-keyed with the associated column key to the following values:
- A single element, like string, integer or Phoenix.LiveView.Rendered
object, like returned from the ~H sigil
- A tuple, containing a custom value used for sorting, and the displayed
content.
"""
use CanneryWeb, :live_component
alias Phoenix.LiveView.Socket
@impl true
@spec update(
%{
required(:columns) =>
list(%{
required(:label) => String.t() | nil,
required(:key) => atom() | nil,
optional(:class) => String.t(),
optional(:sortable) => false
}),
required(:rows) =>
list(%{
(key :: atom()) => any() | {custom_sort_value :: String.t(), value :: any()}
}),
optional(any()) => any()
},
Socket.t()
) :: {:ok, Socket.t()}
def update(%{columns: columns, rows: rows} = assigns, socket) do
initial_key = columns |> List.first() |> Map.get(:key)
rows = rows |> Enum.sort_by(fn row -> row |> Map.get(initial_key) end, :asc)
socket =
socket
|> assign(assigns)
|> assign(columns: columns, rows: rows, last_sort_key: initial_key, sort_mode: :asc)
{:ok, socket}
end
@impl true
def handle_event(
"sort_by",
%{"sort-key" => key},
%{assigns: %{rows: rows, last_sort_key: last_sort_key, sort_mode: sort_mode}} = socket
) do
key = key |> String.to_existing_atom()
sort_mode =
case {key, sort_mode} do
{^last_sort_key, :asc} -> :desc
{^last_sort_key, :desc} -> :asc
{_new_sort_key, _last_sort_mode} -> :asc
end
rows = rows |> sort_by_custom_sort_value_or_value(key, sort_mode)
{:noreply, socket |> assign(last_sort_key: key, sort_mode: sort_mode, rows: rows)}
end
defp sort_by_custom_sort_value_or_value(rows, key, sort_mode) do
rows
|> Enum.sort_by(
fn row ->
case row |> Map.get(key) do
{custom_sort_key, _value} -> custom_sort_key
value -> value
end
end,
sort_mode
)
end
end