add table component
This commit is contained in:
parent
3cb723b9e4
commit
fd4fdcc36b
87
lib/lokal_web/components/table_component.ex
Normal file
87
lib/lokal_web/components/table_component.ex
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
defmodule LokalWeb.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`: A string 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 LokalWeb, :live_component
|
||||||
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
@spec update(
|
||||||
|
%{
|
||||||
|
required(:columns) =>
|
||||||
|
list(%{
|
||||||
|
required(:label) => String.t() | nil,
|
||||||
|
required(:key) => String.t() | nil,
|
||||||
|
optional(:class) => String.t(),
|
||||||
|
optional(:sortable) => false
|
||||||
|
}),
|
||||||
|
required(:rows) =>
|
||||||
|
list(%{
|
||||||
|
(key :: String.t()) => 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: key, sort_mode: sort_mode}} = socket
|
||||||
|
) do
|
||||||
|
sort_mode = if sort_mode == :asc, do: :desc, else: :asc
|
||||||
|
rows = rows |> sort_by_custom_sort_value_or_value(key, sort_mode)
|
||||||
|
{:noreply, socket |> assign(sort_mode: sort_mode, rows: rows)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(
|
||||||
|
"sort_by",
|
||||||
|
%{"sort-key" => key},
|
||||||
|
%{assigns: %{rows: rows}} = socket
|
||||||
|
) do
|
||||||
|
rows = rows |> sort_by_custom_sort_value_or_value(key, :asc)
|
||||||
|
{:noreply, socket |> assign(last_sort_key: key, sort_mode: :asc, 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
|
52
lib/lokal_web/components/table_component.html.heex
Normal file
52
lib/lokal_web/components/table_component.html.heex
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black">
|
||||||
|
<table class="min-w-full table-auto text-center bg-white">
|
||||||
|
<thead class="border-b border-primary-600">
|
||||||
|
<tr>
|
||||||
|
<%= for %{key: key, label: label} = column <- @columns do %>
|
||||||
|
<%= if column |> Map.get(:sortable, true) do %>
|
||||||
|
<th class={"p-2 #{column[:class]}"}>
|
||||||
|
<span
|
||||||
|
class="cursor-pointer"
|
||||||
|
phx-click="sort_by"
|
||||||
|
phx-value-sort-key={key}
|
||||||
|
phx-target={@myself}
|
||||||
|
>
|
||||||
|
<span class="underline"><%= label %></span>
|
||||||
|
<%= if @last_sort_key == key do %>
|
||||||
|
<%= case @sort_mode do %>
|
||||||
|
<% :asc -> %>
|
||||||
|
<i class="fas fa-sm fa-chevron-down"></i>
|
||||||
|
<% :desc -> %>
|
||||||
|
<i class="fas fa-sm fa-chevron-up"></i>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<i class="fas fa-sm fa-chevron-up opacity-0"></i>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<% else %>
|
||||||
|
<th class={"p-2 #{column[:class]}"}>
|
||||||
|
<%= label %>
|
||||||
|
</th>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<%= for values <- @rows do %>
|
||||||
|
<tr>
|
||||||
|
<%= for %{key: key} = value <- @columns do %>
|
||||||
|
<td class={"p-2 #{value[:class]}"}>
|
||||||
|
<%= case values |> Map.get(key) do %>
|
||||||
|
<% {_custom_sort_value, value} -> %>
|
||||||
|
<%= value %>
|
||||||
|
<% value -> %>
|
||||||
|
<%= value %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<% end %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user