defmodule Moon.Light.Table do @moduledoc false use Moon.Light.Component alias Moon.Icon import Moon.Helpers.MakeList, only: [add_index_as: 1] attr(:selected, :any, default: [], doc: "List of selected ids, or just id, or [], or nil") attr(:sort, :any, default: [], doc: "Sorting of the table, format [field:, \"ASC\"|\"DESC\"]. If given, component will sort given items before displaying" ) attr(:items, :any, required: true, doc: "The list of items to be rendered. If item does not have id - than index is used instead. Able to work with streams" ) attr(:row_click, :any, doc: "Event that firset on row click") attr(:row_click_cb, :any, doc: "Callback for generating on_click per row. row and row_id will bi given as parameters" ) attr(:sorting_click, :any, doc: "Sorting stuff") attr(:row_gap, :any, default: "border-spacing-y-1", doc: "Can be used to add any class to the table" ) attr(:row_size, :string, values: ~w(xs sm md lg xl 2xl), default: "md", doc: "Size of the row. not a css class!" ) attr(:is_cell_border, :boolean, default: false, doc: "If cell has border") attr(:is_headless, :boolean, default: false, doc: "If table has column headers or not") attr(:is_zebra_style, :boolean, default: false, doc: "If table is styled to present its rows in automatically alternating colours" ) attr(:row_bg, :any, default: "bg-goku", doc: "Can be used as an additional class for all the rows" ) attr(:selected_bg, :any, default: "bg-beerus", doc: "Can be used as an additional class for selected rows" ) attr(:header_row_class, :any, doc: "Can be used as an additional class for header row") attr(:even_row_bg, :any, default: "bg-transparent", doc: "Can be used as an additional class for even rows in zebra-style table" ) attr(:hover_bg, :any, doc: "Can be used as an additional class for all rows. please use hover:... tailwind's format" ) slot(:cols, generator_prop: :items, doc: "The list of columns defining the Grid") attr(:id, :string, doc: "Just an id for a table tag") attr(:testid, :string, doc: "Data-testid attribute for a table tag") attr(:class, :any, doc: "Additional classes for a table tag") attr(:body_attrs, :map, default: %{}, doc: "Additional attributes for tbody tag") def table(assigns) do ~H""" <%= for {col, col_index} <- Enum.with_index(@cols) do %> <% end %> <%= for {row_index, item} <- stream_data(assigns) do %> <%= for {col, col_index} <- Enum.with_index(@cols) do %> <% end %> <% end %>
<%= col.label %> <.icon :if={col.name && col.sortable} class={[ "text-[1.5em] transition-transform transition-200", "rotate-180": @sort[:"#{col.name}"] != "ASC", "opacity-0": !@sort[:"#{col.name}"] ]} name="arrows_up" />
<%= render_slot(col) %>
""" end def sort_items(items, sort) do import Enum, only: [reverse: 1, reduce: 3, sort_by: 3] reverse(sort) |> reduce(items, fn {field, sort_dir}, list -> sort_by( list, & &1[field], &if sort_dir == "ASC" do &1 < &2 else &1 > &2 end ) end) end defp toggle_sort_dir(sort_dir) do if "#{sort_dir}" == "ASC" do "DESC" else "ASC" end end defp is_selected_item(item, selected) do item[:is_selected] || is_selected(item[:id], selected) end defp is_selected(id, selected) when is_list(selected) do "#{id}" in selected end defp is_selected(id, selected) do "#{id}" == "#{selected}" end defp inter_cell_border() do "after:content-[\"\"] after:absolute after:w-px after:bg-beerus " <> "after:h-3/5 after:bottom-[20%] after:right-0 after:translate-x-[-50%] relative" end defp text_size_classes(row_size) do case row_size do "xs" -> "text-moon-12 py-1 px-2" "sm" -> "text-moon-14 py-1 px-3" "md" -> "text-moon-14 py-2 px-3" "lg" -> "text-moon-14 py-3 px-3" "xl" -> "text-moon-14 py-4 px-3" "2xl" -> "text-moon-14 py-5 px-3" end end defp stream_data(%{items: stream = %Phoenix.LiveView.LiveStream{}}) do stream end defp stream_data(%{items: items, sort: sort}) when is_list(items) do items |> add_index_as() |> sort_items(sort) |> Enum.with_index(&{&2, &1}) end defp dom_id(id, _) when is_binary(id) do id end defp dom_id(id, id2) do "#{id2}-row-#{id}" end end