Packages
moon
2.79.5
2.90.5
retired
2.90.4
2.90.3
2.90.2
2.90.1
2.90.0
2.89.4
2.89.1
2.89.0
2.88.0
2.87.11
2.87.10
2.87.9
2.87.8
2.87.7
2.87.6
2.87.5
2.87.4
2.87.3
2.87.2
2.87.1
2.87.0
2.86.1
2.86.0
2.85.1
2.85.0
2.84.0
2.83.0
2.82.0
2.81.4
2.81.3
2.81.2
2.81.1
2.81.0
2.80.2
2.80.1
2.80.0
2.79.13
2.79.12
2.79.11
2.79.10
2.79.9
2.79.8
2.79.7
2.79.6
2.79.5
2.79.4
2.79.3
2.79.2
2.79.1
2.79.0
2.78.4
2.78.3
2.78.2
2.78.1
2.78.0
2.77.0
2.76.5
2.76.4
2.76.3
2.76.2
2.76.1
2.76.0
2.75.0
2.74.1
2.74.0
2.73.8
2.73.7
2.73.6
2.73.5
2.73.4
2.73.3
2.73.2
2.73.1
2.73.0
2.72.5
2.72.4
2.72.3
2.72.2
2.72.1
2.72.0
2.71.1
2.71.0
2.70.0
2.69.2
2.69.1
2.69.0
2.68.11
2.68.10
Components-based design system written in elixir
Current section
Files
Jump to
Current section
Files
lib/moon/components/table.ex
defmodule Moon.Components.Table do
@moduledoc false
use Moon.StatelessComponent
alias Moon.Components.Table.Paging
alias Moon.Icon
# import Moon.Helpers.MergeClass
@doc "Params from LiveView mount(params, session, socket) that must be passed on"
prop(limit, :integer, default: 10)
prop(offset, :integer, default: 0)
prop(selected, :string)
prop(sort_key, :any)
prop(sort_dir, :any, default: "ASC")
@doc "The list of items to be rendered"
prop(items, :generator, required: true)
prop(row_click, :event)
prop(paging_click, :event)
prop(sorting_click, :event)
prop(paging_info, :any)
prop(class, :css_class)
prop(row_class, :css_class)
prop(id, :string)
prop(even_row_class, :css_class, default: "bg-goku")
prop(has_no_cell_borders, :boolean, default: false)
@doc "The list of columns defining the Grid"
slot(cols, generator_prop: :items)
def render(assigns) do
~F"""
<div class="w-full grid gap-4" {=@id}>
{#if @paging_info}
<Paging paging_info={@paging_info} paging_click={@paging_click} limit={@limit} offset={@offset} />
{/if}
<div class={merge(["w-full overflow-x-scroll", @class])}>
<table class="border-collapse text-sm min-w-full">
<thead>
<tr class="text-trunks">
{#for col <- @cols}
<th
class={merge([
"py-2 px-4 text-left font-normal",
["cursor-pointer": col.name && col.sortable],
col.width
])}
:on-click={(col.sortable && @sorting_click) || nil}
:values={"sort-key": col.name, "sort-dir": toggle_sort_dir(@sort_dir)}
data-testid={"sort-column-#{col.name}"}
>
{col.label}
{#if col.sortable && "#{@sort_key}" == "#{col.name}"}
<Icon class="text-[1.5em]" name="arrows_up" :if={"#{@sort_dir}" == "ASC"} />
<Icon class="text-[1.5em]" name="arrows_down" :if={"#{@sort_dir}" == "DESC"} />
{/if}
</th>
{/for}
</tr>
</thead>
<tbody>
{#for {item, row_index} <- Enum.with_index(@items)}
<tr
class={merge(
"#{@even_row_class}": @selected != "#{item.id}" && rem(row_index, 2) == 0,
"bg-gohan-120": @selected == "#{item.id}",
"#{@row_class}": true
)}
:on-click={@row_click}
:values={selected: item.id}
data-testid={"row-#{row_index}"}
>
{#for {col, col_index} <- Enum.with_index(@cols)}
<td
class={merge([
"p-4",
["#{inter_cell_border()}": !@has_no_cell_borders && col_index < Enum.count(@cols) - 1],
col.width
])}
data-testid={"row-#{row_index}-col-#{col_index}"}
>
<#slot {col} generator_value={item} />
</td>
{/for}
</tr>
{/for}
</tbody>
</table>
</div>
</div>
"""
end
defp toggle_sort_dir(sort_dir) do
sort_dir = "#{sort_dir}"
if sort_dir == "ASC" do
"DESC"
else
"ASC"
end
end
defp inter_cell_border() do
"after:content-[\"\"] after:absolute after:w-px after:bg-beerus " <>
"after:h-2/5 after:bottom-[30%] after:right-0 after:translate-x-[-50%] relative"
end
end