From a8d4d70abe98616cac15ab0bb6f2a97e67e5b708 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 13 Apr 2026 08:38:24 -0500 Subject: [PATCH] live_table polish: border color, control layout, row clicks, pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tailwind source(none) wasn't scanning deps, so shadcn tokens (border-border, bg-muted, text-foreground, ...) used by live_table and sutra_ui weren't generated, leaving `border` to fall back to currentcolor — a heavy dark border around every table in light mode. Add @source directives for deps/live_table/lib and deps/sutra_ui/lib. - Drop the hardcoded width:100% on the outer .select wrapper so the per-page selector's Tailwind w-24 wins instead of stretching across the header and overlapping the search box. - Make whole rows clickable: global click delegator in app.ts forwards tr clicks to the first in the row (the Actions "View" link), skipping inner interactive elements. cursor + hover highlight added. - Replace the default Prev/Next footer with a daisyUI .join/.btn-based pager (MicrowavepropWeb.LiveTableFooter), wired up globally via :live_table defaults so it applies to every live_table at once. --- assets/css/app.css | 2 + assets/css/live_table_compat.css | 26 +++++++++- assets/js/app.ts | 14 +++++ config/config.exs | 5 +- lib/microwaveprop_web/live_table_footer.ex | 59 ++++++++++++++++++++++ 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 lib/microwaveprop_web/live_table_footer.ex diff --git a/assets/css/app.css b/assets/css/app.css index 718bcfe8..2eee35d9 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -8,6 +8,8 @@ @source "../css"; @source "../js"; @source "../../lib/microwaveprop_web"; +@source "../../deps/live_table/lib"; +@source "../../deps/sutra_ui/lib"; /* live_table / sutra_ui compat: maps shadcn tokens live_table uses to daisyUI base colors and provides component CSS for the SutraUI diff --git a/assets/css/live_table_compat.css b/assets/css/live_table_compat.css index 690b8014..b80eccb1 100644 --- a/assets/css/live_table_compat.css +++ b/assets/css/live_table_compat.css @@ -31,11 +31,13 @@ --color-ring: var(--color-primary); } -/* SutraUI Select — used by live_table for the per-page selector. */ +/* SutraUI Select — used by live_table for the per-page selector. + No width: 100% here — Tailwind width utilities (e.g. `w-24`) on the + component must win, otherwise the per-page select overflows the header + row and overlaps the search box. */ *:not(select).select { position: relative; display: inline-flex; - width: 100%; } .select-trigger { @@ -301,6 +303,26 @@ /* live_table outer container should have a visible surface + border. */ #live-table table { background: var(--color-base-100); } +/* Clickable rows: whole-row click delegates to the first in the row + (typically the Actions column's "View" link). The cursor + hover tint + make it feel interactive; the actual navigation is wired up in app.ts. */ +#live-table tbody tr[id] { + cursor: pointer; + transition: background-color 120ms ease; +} + +#live-table tbody tr[id]:hover { + background: color-mix(in oklch, var(--color-primary) 6%, transparent); +} + +#live-table tbody tr#empty-placeholder { + cursor: default; +} + +#live-table tbody tr#empty-placeholder:hover { + background: transparent; +} + /* Filter bar used when filters are configured. */ .filter-bar { display: flex; diff --git a/assets/js/app.ts b/assets/js/app.ts index 1bf46996..c047581c 100644 --- a/assets/js/app.ts +++ b/assets/js/app.ts @@ -77,6 +77,20 @@ const liveSocket = new LiveSocket("/live", Socket, { hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe, CopyLink, RoverMap, BeaconMap, BeaconsListMap, CommaNumber}, }) +// live_table rows are dead on their own — wire up whole-row clicks to +// trigger the first inside that row (typically the Actions "View" +// link). Clicks on interactive elements are ignored so inner links/buttons +// still win. +document.addEventListener("click", (e: MouseEvent) => { + const target = e.target as HTMLElement | null + if (!target) return + if (target.closest("a, button, input, select, label, [role='option'], [role='menuitem']")) return + const tr = target.closest("#live-table tbody tr[id]") as HTMLTableRowElement | null + if (!tr || tr.id === "empty-placeholder") return + const link = tr.querySelector("a[href]") as HTMLAnchorElement | null + if (link) link.click() +}) + // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) diff --git a/config/config.exs b/config/config.exs index a623e4cf..8f10a744 100644 --- a/config/config.exs +++ b/config/config.exs @@ -20,7 +20,10 @@ config :esbuild, config :live_table, app: :microwaveprop, repo: Microwaveprop.Repo, - pubsub: Microwaveprop.PubSub + pubsub: Microwaveprop.PubSub, + defaults: %{ + custom_footer: {MicrowavepropWeb.LiveTableFooter, :render} + } # Configure Elixir's Logger config :logger, :default_formatter, diff --git a/lib/microwaveprop_web/live_table_footer.ex b/lib/microwaveprop_web/live_table_footer.ex new file mode 100644 index 00000000..288ab632 --- /dev/null +++ b/lib/microwaveprop_web/live_table_footer.ex @@ -0,0 +1,59 @@ +defmodule MicrowavepropWeb.LiveTableFooter do + @moduledoc false + use Phoenix.Component + + def render(assigns) do + options = assigns.options + table_options = assigns.table_options + paginate? = get_in(options, ["pagination", "paginate?"]) + mode = get_in(table_options, [:pagination, :mode]) + + assigns = + assigns + |> assign(:show_pagination?, paginate? && mode != :infinite_scroll) + |> assign(:page, parse_page(get_in(options, ["pagination", "page"]))) + |> assign(:has_next_page?, get_in(options, ["pagination", :has_next_page]) || false) + + ~H""" + + """ + end + + defp parse_page(page) when is_integer(page), do: page + defp parse_page(page) when is_binary(page), do: String.to_integer(page) + defp parse_page(_), do: 1 +end