mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-06 04:54:29 +08:00
Add LiveView table component (#2460)
This commit is contained in:
parent
60d7dd74c3
commit
714417189b
3 changed files with 123 additions and 58 deletions
|
@ -739,4 +739,87 @@ defmodule LivebookWeb.CoreComponents do
|
|||
def hook_prop(value) do
|
||||
Jason.encode!(value)
|
||||
end
|
||||
|
||||
@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>
|
||||
</.table>
|
||||
"""
|
||||
attr :id, :string, required: true
|
||||
attr :rows, :list, required: true
|
||||
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 and :action slots"
|
||||
|
||||
slot :col, required: true do
|
||||
attr :label, :string
|
||||
end
|
||||
|
||||
slot :action, doc: "the slot for showing user actions in the last table column"
|
||||
|
||||
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="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
|
||||
<table class="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
:for={col <- @col}
|
||||
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
|
||||
>
|
||||
<%= col[:label] %>
|
||||
</th>
|
||||
<th
|
||||
:if={@action != []}
|
||||
class="py-3.5 pl-3 pr-5 text-right text-sm font-semibold text-gray-900 sm:pr-7"
|
||||
>
|
||||
<span>Actions</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
id={@id}
|
||||
phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
|
||||
class="divide-y divide-gray-200 bg-white"
|
||||
>
|
||||
<tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-gray-50">
|
||||
<td
|
||||
:for={{col, i} <- Enum.with_index(@col)}
|
||||
phx-click={@row_click && @row_click.(row)}
|
||||
class={["relative p-0", @row_click && "hover:cursor-pointer"]}
|
||||
>
|
||||
<div class="block p-4 sm:px-6">
|
||||
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-gray-100 sm:rounded-l-xl" />
|
||||
<span class={["relative", i == 0 && "text-sm font-medium text-gray-900"]}>
|
||||
<%= render_slot(col, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td :if={@action != []} class="relative p-0">
|
||||
<div class="relative whitespace-nowrap py-4 pl-3 pr-4 text-sm font-medium sm:pr-6 flex justify-end items-center">
|
||||
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-gray-100 sm:rounded-r-xl" />
|
||||
<span :for={action <- @action} class="relative ml-4">
|
||||
<%= render_slot(action, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,66 +19,46 @@ defmodule LivebookWeb.Hub.SecretListComponent do
|
|||
No secrets here... yet!
|
||||
</.no_entries>
|
||||
<div :if={@secrets != []}>
|
||||
<table class="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
|
||||
<.table id="hub-secrets-table" rows={@secrets}>
|
||||
<:col :let={secret} label="Name"><%= secret.name %></:col>
|
||||
<:action :let={secret}>
|
||||
<span class="tooltip left" data-tooltip="Edit">
|
||||
<.link
|
||||
id={"hub-secret-#{secret.name}-edit"}
|
||||
patch={"/#{@edit_path}/#{secret.name}"}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="icon-button"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="py-3.5 pl-3 pr-6 text-right text-sm font-semibold text-gray-900 sm:pr-8"
|
||||
<.remix_icon icon="edit-fill" class="text-lg" />
|
||||
</.link>
|
||||
</span>
|
||||
</:action>
|
||||
<:action :let={secret}>
|
||||
<span class="tooltip left" data-tooltip="Delete">
|
||||
<button
|
||||
id={"hub-secret-#{secret.name}-delete"}
|
||||
type="button"
|
||||
phx-click={
|
||||
JS.push("delete_hub_secret",
|
||||
value: %{
|
||||
name: secret.name,
|
||||
value: secret.value,
|
||||
hub_id: secret.hub_id,
|
||||
deployment_group_id: secret.deployment_group_id,
|
||||
return_to: @return_to
|
||||
},
|
||||
target: @myself
|
||||
)
|
||||
}
|
||||
role="menuitem"
|
||||
class="icon-button"
|
||||
>
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white">
|
||||
<tr :for={secret <- @secrets}>
|
||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
|
||||
<%= secret.name %>
|
||||
</td>
|
||||
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 flex justify-end items-center gap-4 text-sm font-medium sm:pr-6">
|
||||
<span class="tooltip left" data-tooltip="Edit">
|
||||
<.link
|
||||
id={"hub-secret-#{secret.name}-edit"}
|
||||
patch={"/#{@edit_path}/#{secret.name}"}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="icon-button"
|
||||
>
|
||||
<.remix_icon icon="edit-fill" class="text-lg" />
|
||||
</.link>
|
||||
</span>
|
||||
<span class="tooltip left" data-tooltip="Delete">
|
||||
<button
|
||||
id={"hub-secret-#{secret.name}-delete"}
|
||||
type="button"
|
||||
phx-click={
|
||||
JS.push("delete_hub_secret",
|
||||
value: %{
|
||||
name: secret.name,
|
||||
value: secret.value,
|
||||
hub_id: secret.hub_id,
|
||||
deployment_group_id: secret.deployment_group_id,
|
||||
return_to: @return_to
|
||||
},
|
||||
target: @myself
|
||||
)
|
||||
}
|
||||
role="menuitem"
|
||||
class="icon-button"
|
||||
>
|
||||
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
|
||||
</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
|
||||
</button>
|
||||
</span>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -13,9 +13,11 @@
|
|||
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
|
||||
"eini": {:hex, :eini_beam, "2.2.3", "4c809b483b0435789c5924642b81ed1cd6fa7d23f5e2efb3e420522a051fa483", [:rebar3], [], "hexpm", "10381b4cb76b8340b492653dae8f6ab7cb372305906ea196d6a1c070516e7a5f"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
|
||||
"expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"},
|
||||
"floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"},
|
||||
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
||||
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
|
||||
"iso8601": {:hex, :iso8601, "1.3.3", "994aff5dfe760f14a8c4f2d8c3cf500371bf1a8cf309c3c0cb510401064223e0", [:rebar3], [], "hexpm", "bcc7767d691e4d8a26e713f48da51abd951bec4e071ae841f371766f96b46834"},
|
||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||
|
|
Loading…
Add table
Reference in a new issue