Skip to content

Commit

Permalink
add table component
Browse files Browse the repository at this point in the history
  • Loading branch information
woylie committed Dec 12, 2023
1 parent 9ca2f4f commit 4b41cae
Showing 1 changed file with 145 additions and 0 deletions.
145 changes: 145 additions & 0 deletions lib/doggo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,151 @@ defmodule Doggo do
"""
end

@doc """
Renders a simple table.
## Examples
<.table id="pets" rows={@pets}>
<:col :let={p} label="name"><%= p.name %></:col>
<:col :let={p} label="age"><%= p.age %></:col>
</.table>
"""
attr :id, :string, required: true

attr :items, :list,
required: true,
doc: "The list of items to be displayed in rows."

attr :caption, :string,
default: nil,
doc: "Content for the `<caption>` element."

attr :row_id, :any,
default: nil,
doc: """
Overrides the default function that retrieves the row ID from a stream item.
"""

attr :row_click, JS,
default: nil,
doc: """
Sets the `phx-click` function attribute for each row `td`. Expects to be a
function that receives a row item as an argument. This does not add the
`phx-click` attribute to the `action` slot.
Example:
```elixir
row_click={&JS.navigate(~p"/users/\#{&1}")}
```
"""

attr :row_item, :any,
default: &Function.identity/1,
doc: """
This function is called on the row item before it is passed to the :col
and :action slots.
"""

slot :col,
required: true,
doc: """
For each column to render, add one `<:col>` element.
```elixir
<:col :let={pet} label="Name" field={:name} col_style="width: 20%;">
<%= pet.name %>
</:col>
```
Any additional assigns will be added as attributes to the `<td>` elements.
""" do
attr :label, :any, doc: "The content for the header column."

attr :col_attrs, :string,
doc: """
If set, a `<colgroup>` element is rendered and the attributes are added
to the `<col>` element of the respective column.
"""
end

slot :action,
doc: """
The slot for showing user actions in the last table column. These columns
do not receive the `row_click` attribute.
```elixir
<:action :let={user}>
<.link navigate={~p"/users/\#{user}"}>Show</.link>
</:action>
```
""" do
attr :label, :string, doc: "The content for the header column."

attr :col_attrs, :string,
doc: """
If set, a `<colgroup>` element is rendered and the attributes are added
to the `<col>` element of the respective column.
"""
end

slot :foot,
default: nil,
doc: """
You can optionally add a `foot`. The inner block will be rendered inside
a `tfoot` element.
<Flop.Phoenix.table>
<:foot>
<tr><td>Total: <span class="total"><%= @total %></span></td></tr>
</:foot>
</Flop.Phoenix.table>
"""

def table(assigns) do
assigns =
with %{rows: %Phoenix.LiveView.LiveStream{}} <- assigns do
assign(assigns, row_id: assigns.row_id || fn {id, _item} -> id end)
end

~H"""
<div class="table-container">
<table id={@id}>
<caption :if={@caption}><%= @caption %></caption>
<colgroup :if={
Enum.any?(@col, & &1[:col_attrs]) or Enum.any?(@action, & &1[:col_attrs])
}>
<col :for={col <- @col} {col[:col_attrs] || []} />
<col :for={action <- @action} {action[:col_attrs] || []} />
</colgroup>
<thead>
<tr>
<th :for={col <- @col}><%= col[:label] %></th>
<th :for={action <- @action}><%= action[:label] %></th>
</tr>
</thead>
<tbody
id={@id <> "-tbody"}
phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
>
<tr :for={row <- @rows} id={@row_id && @row_id.(row)}>
<td :for={col <- @col} phx-click={@row_click && @row_click.(row)}>
<%= render_slot(col, @row_item.(row)) %>
</td>
<td :for={action <- @action}>
<%= render_slot(action, @row_item.(row)) %>
</td>
</tr>
</tbody>
<tfoot :if={@foot != []}><%= render_slot(@foot) %></tfoot>
</table>
</div>
"""
end

@doc """
Renders a drawer with a `brand`, `top`, and `bottom` slot.
Expand Down

0 comments on commit 4b41cae

Please sign in to comment.