/
table.ex
155 lines (134 loc) · 4.54 KB
/
table.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
defmodule PetalComponents.Table do
use Phoenix.Component
import PetalComponents.Avatar
@doc ~S"""
Renders a table with generic styling.
## Examples
<.table id="users" rows={@users}>
<:col :let={user} label="id"><%= user.id %></:col>
<:col :let={user} label="username"><%= user.username %></:col>
<:empty_state>No data here yet</:empty_state>
</.table>
"""
attr :id, :string
attr :class, :string, default: "", doc: "CSS class"
attr :rows, :list, default: [], doc: "the list of rows to render"
attr :row_id, :any, default: nil, doc: "the function for generating the row id"
attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
attr :row_item, :any,
default: &Function.identity/1,
doc: "the function for mapping each row before calling the :col slot"
slot :col do
attr :label, :string
attr :class, :string
attr :row_class, :string
end
slot :empty_state,
doc: "A message to show when the table is empty, to be used together with :col" do
attr :row_class, :string
end
attr :rest, :global, include: ~w(colspan rowspan)
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
assigns = assign_new(assigns, :id, fn -> "table_#{Ecto.UUID.generate()}" end)
~H"""
<table class={["pc-table", @class]} {@rest}>
<%= if length(@col) > 0 do %>
<thead>
<.tr>
<.th :for={col <- @col} class={col[:class]}><%= col[:label] %></.th>
</.tr>
</thead>
<tbody id={@id} phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}>
<%= if length(@empty_state) > 0 do %>
<.tr class="hidden only:table-row">
<.td
:for={empty_state <- @empty_state}
colspan={length(@col)}
class={empty_state[:row_class]}
>
<%= render_slot(empty_state) %>
</.td>
</.tr>
<% end %>
<.tr
:for={row <- @rows}
id={@row_id && @row_id.(row)}
class={"group #{if @row_click, do: "pc-table__tr--row-click", else: ""}"}
>
<.td
:for={{col, i} <- Enum.with_index(@col)}
phx-click={@row_click && @row_click.(row)}
class={"#{if @row_click, do: "pc-table__td--row-click", else: ""} #{if i == 0, do: "pc-table__td--first-col", else: ""} #{if col[:row_class], do: col[:row_class], else: ""}"}
>
<%= render_slot(col, @row_item.(row)) %>
</.td>
</.tr>
</tbody>
<% else %>
<%= render_slot(@inner_block) %>
<% end %>
</table>
"""
end
attr(:class, :string, default: "", doc: "CSS class")
attr(:rest, :global, include: ~w(colspan rowspan))
slot(:inner_block, required: false)
def th(assigns) do
~H"""
<th class={["pc-table__th", @class]} {@rest}>
<%= render_slot(@inner_block) %>
</th>
"""
end
attr(:class, :string, default: "", doc: "CSS class")
attr(:rest, :global)
slot(:inner_block, required: false)
def tr(assigns) do
~H"""
<tr class={["pc-table__tr", @class]} {@rest}>
<%= render_slot(@inner_block) %>
</tr>
"""
end
attr(:class, :string, default: "", doc: "CSS class")
attr(:rest, :global, include: ~w(colspan headers rowspan))
slot(:inner_block, required: false)
def td(assigns) do
~H"""
<td class={["pc-table__td", @class]} {@rest}>
<%= render_slot(@inner_block) %>
</td>
"""
end
attr(:class, :any, default: "", doc: "CSS class")
attr(:label, :string, default: nil, doc: "Adds a label your user, e.g name")
attr(:sub_label, :string, default: nil, doc: "Adds a sub-label your to your user, e.g title")
attr(:rest, :global)
attr(:avatar_assigns, :map,
default: nil,
doc: "if using an avatar, this map will be passed to the avatar component as props"
)
def user_inner_td(assigns) do
~H"""
<div class={@class} {@rest}>
<div class="pc-table__user-inner-td">
<%= if @avatar_assigns do %>
<.avatar {@avatar_assigns} />
<% end %>
<div class="pc-table__user-inner-td__inner">
<div class="pc-table__user-inner-td__label">
<%= @label %>
</div>
<div class="pc-table__user-inner-td__sub-label">
<%= @sub_label %>
</div>
</div>
</div>
</div>
"""
end
end