add table component
This commit is contained in:
		
							
								
								
									
										87
									
								
								lib/cannery_web/components/table_component.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								lib/cannery_web/components/table_component.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | 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`: 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 CanneryWeb, :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 | ||||||
							
								
								
									
										46
									
								
								lib/cannery_web/components/table_component.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/cannery_web/components/table_component.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | <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> | ||||||
		Reference in New Issue
	
	Block a user