defmodule Moon.Design.Table do @moduledoc false use Moon.StatelessComponent alias Moon.Icon import Moon.Helpers.MakeList, only: [add_index_as: 1] @doc "List of selected ids, or just id, or [], or nil" prop(selected, :any, default: []) @doc "Sorting of the table, format [field:, \"ASC\"|\"DESC\"]. If given, component will sort given items before displaying" prop(sort, :keyword, default: []) @doc "The list of items to be rendered. If item does not have id - than index is used instead. Able to work with streams" prop(items, :generator, required: true) @doc "Event that firset on row click" prop(row_click, :event) @doc "Callback for generating on_click per row. row and row_id will bi given as parameters" prop(row_click_cb, :any) @doc "Sorting stuff" prop(sorting_click, :event) @doc "Can be used to add any class to the table" prop(row_gap, :css_class, default: "border-spacing-y-1") @doc "Size of the row. not a css class!" prop(row_size, :string, values!: ~w(xs sm md lg xl 2xl), default: "md") @doc "If cell has border" prop(is_cell_border, :boolean, default: false) @doc "If table has column headers or not" prop(is_headless, :boolean, default: false) @doc "If table is styled to present its rows in automatically alternating colours" prop(is_zebra_style, :boolean, default: false) @doc "Can be used as an additional class for all the rows" prop(row_bg, :css_class, default: "bg-goku") @doc "Can be used as an additional class for selected rows" prop(selected_bg, :css_class, default: "bg-beerus") @doc "Can be used as an additional class for header row" prop(header_row_class, :css_class) @doc "Can be used as an additional class for even rows in zebra-style table" prop(even_row_bg, :css_class, default: "bg-transparent") @doc "Can be used as an additional class for all rows. please use hover:... tailwind's format" prop(hover_bg, :css_class) @doc "The list of columns defining the Grid" slot(cols, generator_prop: :items) @doc "Just an id for a table tag" prop(id, :string) @doc "Data-testid attribute for a table tag" prop(testid, :string) @doc "Additional classes for a table tag" prop(class, :css_class) @doc "Additional attributes for tbody tag" prop(body_attrs, :map, default: %{}) def render(assigns) do ~F""" {#for {col, col_index} <- Enum.with_index(@cols)} {/for} {#for {row_index, item} <- stream_data(assigns)} {#for {col, col_index} <- Enum.with_index(@cols)} {/for} {/for}
{col.label}
<#slot {col} generator_value={item} />
""" end @doc "Sorts items by sort given" 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 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) defp is_selected(id, selected) when is_list(selected), do: "#{id}" in selected defp is_selected(id, selected), do: "#{id}" == "#{selected}" 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 defp dom_id(id, id2), do: "#{id2}-row-#{id}" end