ux(rover-planning): table rows are real <a href> links
Each path-table cell now wraps its content in <.link navigate={...}>,
so the row behaves as a native anchor: hovering shows the destination
URL in the browser status bar, right-click 'open in new tab' works,
middle-click works, and the rendered HTML is meaningful to assistive
tech / scrapers / link-validators.
Replaces the phx-click={JS.navigate(...)} pattern, which gave none of
those affordances and required the user to trust that the row was
clickable. Whole-row hover highlight is preserved via a CSS
:has(a:hover) selector so cell-hover still tints the row.
This commit is contained in:
parent
893a02e1e2
commit
9bec5721d0
1 changed files with 64 additions and 22 deletions
|
|
@ -14,7 +14,6 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
|
|||
alias Microwaveprop.RoverPlanning
|
||||
alias Microwaveprop.RoverPlanning.Mission
|
||||
alias MicrowavepropWeb.LocationResolver
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
@impl true
|
||||
def mount(%{"id" => id}, _session, socket) do
|
||||
|
|
@ -329,6 +328,29 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
|
|||
|
||||
defp path_url(_, _), do: nil
|
||||
|
||||
# Wraps a single table cell's content in a real <a> when an href is
|
||||
# available, so the browser shows the destination on hover and
|
||||
# right-click / middle-click work natively. Falls back to a plain
|
||||
# <div> when the row has no path (e.g. compute hasn't started).
|
||||
attr :href, :string, default: nil
|
||||
slot :inner_block, required: true
|
||||
|
||||
defp path_cell(%{href: href} = assigns) when is_binary(href) do
|
||||
~H"""
|
||||
<.link navigate={@href} class="block px-3 py-2 text-inherit no-underline">
|
||||
{render_slot(@inner_block)}
|
||||
</.link>
|
||||
"""
|
||||
end
|
||||
|
||||
defp path_cell(assigns) do
|
||||
~H"""
|
||||
<div class="px-3 py-2">
|
||||
{render_slot(@inner_block)}
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
summary = progress_summary(assigns.paths)
|
||||
|
|
@ -513,38 +535,58 @@ defmodule MicrowavepropWeb.RoverPlanningLive.Show do
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%!-- Each cell wraps its content in <.link>, so the row
|
||||
behaves as a real anchor: native href hover preview,
|
||||
right-click "open in new tab", middle-click, etc. The
|
||||
row-hover highlight is preserved via :has(a:hover) so
|
||||
hovering any cell still tints the whole row. --%>
|
||||
<tr
|
||||
:for={path <- group_paths}
|
||||
phx-click={path_url(@mission, path) && JS.navigate(path_url(@mission, path))}
|
||||
class={[
|
||||
path_url(@mission, path) && "cursor-pointer hover:bg-base-200"
|
||||
path_url(@mission, path) &&
|
||||
"[&:has(a:hover)]:bg-base-200 transition-colors"
|
||||
]}
|
||||
title={path_url(@mission, path) && "Open in Path Calculator"}
|
||||
>
|
||||
<td class="font-mono text-xs">
|
||||
<td class="font-mono text-xs p-0">
|
||||
<% {primary, secondary} = station_lines(path) %>
|
||||
<div>{primary}</div>
|
||||
<div :if={secondary} class="opacity-60">{secondary}</div>
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
<div>{primary}</div>
|
||||
<div :if={secondary} class="opacity-60">{secondary}</div>
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td class="font-mono text-xs">
|
||||
{band_label(path.band_mhz || @mission.band_mhz)}
|
||||
<td class="font-mono text-xs p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{band_label(path.band_mhz || @mission.band_mhz)}
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td>{status_badge(path.status)}</td>
|
||||
<td class="text-xs">
|
||||
{if path.result, do: format_distance(path.result["distance_km"]), else: "—"}
|
||||
<td class="p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{status_badge(path.status)}
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td class="text-xs">
|
||||
{if path.result,
|
||||
do: format_loss(path.result["total_baseline_loss_db"]),
|
||||
else: "—"}
|
||||
<td class="text-xs p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{if path.result, do: format_distance(path.result["distance_km"]), else: "—"}
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td>
|
||||
{if path.result, do: verdict_badge(path.result["verdict"]), else: "—"}
|
||||
<td class="text-xs p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{if path.result,
|
||||
do: format_loss(path.result["total_baseline_loss_db"]),
|
||||
else: "—"}
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td>
|
||||
{if path.result,
|
||||
do: propagation_badge(path.result["propagation_score"]),
|
||||
else: "—"}
|
||||
<td class="p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{if path.result, do: verdict_badge(path.result["verdict"]), else: "—"}
|
||||
</.path_cell>
|
||||
</td>
|
||||
<td class="p-0">
|
||||
<.path_cell href={path_url(@mission, path)}>
|
||||
{if path.result,
|
||||
do: propagation_badge(path.result["propagation_score"]),
|
||||
else: "—"}
|
||||
</.path_cell>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue