Match /map sidebar layout on /weather
Right-hand dark sidebar holds the layer selector, description, navigation links, and auth menu. Mobile floating panel mirrors the pattern used on /map so users get the same controls in both views.
This commit is contained in:
parent
0a8c779783
commit
291214033a
1 changed files with 202 additions and 43 deletions
|
|
@ -213,30 +213,151 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
|||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="relative w-screen h-screen overflow-hidden">
|
||||
<%!-- Full-page map --%>
|
||||
<div
|
||||
id="weather-map"
|
||||
phx-hook="WeatherMap"
|
||||
phx-update="ignore"
|
||||
data-weather={@initial_data_json}
|
||||
data-layers={Jason.encode!(@layers)}
|
||||
data-selected-layer={@selected_layer}
|
||||
class="absolute inset-0 z-0"
|
||||
>
|
||||
<div class="flex w-screen h-screen overflow-hidden">
|
||||
<%!-- Map container --%>
|
||||
<div class="relative flex-1 min-w-0">
|
||||
<%!-- Full-page map --%>
|
||||
<div
|
||||
id="weather-map"
|
||||
phx-hook="WeatherMap"
|
||||
phx-update="ignore"
|
||||
data-weather={@initial_data_json}
|
||||
data-layers={Jason.encode!(@layers)}
|
||||
data-selected-layer={@selected_layer}
|
||||
class="absolute inset-0 z-0"
|
||||
>
|
||||
</div>
|
||||
|
||||
<%!-- Mobile-only floating controls --%>
|
||||
<div
|
||||
id="weather-controls"
|
||||
class="md:hidden absolute top-2 left-12 z-[1000] flex flex-col gap-2 max-w-[calc(100vw-4rem)]"
|
||||
>
|
||||
<div
|
||||
data-theme="dark"
|
||||
class="bg-neutral text-neutral-content shadow rounded-box border border-base-300 p-2 flex flex-col gap-2"
|
||||
>
|
||||
<div class="font-bold text-sm leading-tight px-1 flex items-center justify-between gap-2">
|
||||
<div class="min-w-0">
|
||||
<span>NTMS</span>
|
||||
<div class="font-normal text-xs opacity-70">Weather Map</div>
|
||||
<div class="font-normal text-[10px] opacity-60">
|
||||
<%= if @valid_time do %>
|
||||
{Calendar.strftime(@valid_time, "%Y-%m-%d %H:%M UTC")}
|
||||
<% else %>
|
||||
No data available
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 shrink-0">
|
||||
<div
|
||||
id="weather-utc-clock-mobile"
|
||||
phx-hook="UtcClock"
|
||||
class="font-mono text-xs opacity-70 tabular-nums"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
id="weather-mobile-panel-toggle"
|
||||
class="btn btn-xs btn-square btn-ghost"
|
||||
onclick="document.getElementById('weather-panel-extras').classList.toggle('hidden')"
|
||||
>
|
||||
<.icon name="hero-bars-3" class="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="weather-panel-extras" class="hidden flex-col gap-2">
|
||||
<%!-- Layer pill buttons grouped --%>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<div :for={{group, layers} <- group_layers(@layers)}>
|
||||
<div class="text-[10px] font-semibold opacity-50 uppercase tracking-wider px-1 mb-0.5">
|
||||
{group}
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<button
|
||||
:for={layer <- layers}
|
||||
phx-click="select_layer"
|
||||
phx-value-layer={layer.id}
|
||||
class={[
|
||||
"btn btn-xs rounded-full",
|
||||
if(@selected_layer == layer.id, do: "btn-primary", else: "btn-ghost")
|
||||
]}
|
||||
>
|
||||
{layer.label}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-[11px] opacity-70 px-1 leading-snug">
|
||||
{layer_description(@layers, @selected_layer)}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1 border-t border-base-300 pt-2">
|
||||
<.link navigate="/map" class="btn btn-xs btn-ghost justify-start">
|
||||
Propagation Map
|
||||
</.link>
|
||||
<.link navigate="/path" class="btn btn-xs btn-ghost justify-start">
|
||||
Path Calculator
|
||||
</.link>
|
||||
<.link navigate="/beacons" class="btn btn-xs btn-ghost justify-start">
|
||||
Beacons
|
||||
</.link>
|
||||
<.link navigate="/algo" class="btn btn-xs btn-ghost justify-start">
|
||||
Scoring Algorithm
|
||||
</.link>
|
||||
<.link navigate="/submit" class="btn btn-xs btn-ghost justify-start">
|
||||
Submit a Contact
|
||||
</.link>
|
||||
<.link navigate="/contacts" class="btn btn-xs btn-ghost justify-start">
|
||||
Contact Training Data
|
||||
</.link>
|
||||
<.link navigate="/contacts/map" class="btn btn-xs btn-ghost justify-start">
|
||||
Contact Map
|
||||
</.link>
|
||||
<.link navigate="/about" class="btn btn-xs btn-ghost justify-start">
|
||||
About
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- Point detail panel --%>
|
||||
<div
|
||||
id="weather-detail-panel"
|
||||
phx-update="ignore"
|
||||
class={[
|
||||
"bg-neutral text-neutral-content shadow-lg border border-base-300 overflow-hidden overflow-y-auto z-[1001]",
|
||||
"fixed bottom-0 left-0 right-0 max-h-[50vh] rounded-t-2xl",
|
||||
"md:absolute md:top-auto md:right-auto md:bottom-4 md:left-3 md:max-h-[70vh] md:w-80 md:rounded-box"
|
||||
]}
|
||||
style="display:none;"
|
||||
>
|
||||
</div>
|
||||
|
||||
<%!-- Sidebar expand button (visible when sidebar is collapsed) --%>
|
||||
<button
|
||||
id="weather-sidebar-expand"
|
||||
class="hidden md:hidden absolute top-3 right-3 z-[1000] btn btn-sm btn-neutral shadow-lg"
|
||||
onclick="document.getElementById('weather-sidebar').style.display='flex';this.style.display='none';window.dispatchEvent(new Event('sidebar-toggle'));"
|
||||
>
|
||||
<.icon name="hero-bars-3" class="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<%!-- Top-left control panel --%>
|
||||
<%!-- Desktop right sidebar --%>
|
||||
<div
|
||||
id="weather-controls"
|
||||
class="absolute top-2 left-12 z-[1000] flex flex-col gap-2 max-w-[calc(100vw-4rem)] md:left-14 md:top-3 md:max-w-none"
|
||||
id="weather-sidebar"
|
||||
data-theme="dark"
|
||||
class="hidden md:flex flex-col w-56 shrink-0 h-full bg-neutral text-neutral-content border-l border-base-300 overflow-hidden"
|
||||
>
|
||||
<div class="bg-base-100/90 shadow rounded-box border border-base-300 p-2 md:p-3 flex flex-col gap-2">
|
||||
<div class="p-3 flex flex-col gap-2 shrink-0 overflow-y-auto flex-1">
|
||||
<div class="font-bold text-sm leading-tight px-1 flex items-center justify-between gap-2">
|
||||
<div class="min-w-0">
|
||||
<span class="hidden md:inline">HRRR Weather Data</span>
|
||||
<span class="md:hidden">Weather</span>
|
||||
<div class="font-normal text-xs opacity-70">
|
||||
<span>NTMS</span>
|
||||
<div class="font-normal text-xs opacity-70">Weather Map</div>
|
||||
<div class="font-normal text-[10px] opacity-60">
|
||||
<%= if @valid_time do %>
|
||||
{Calendar.strftime(@valid_time, "%Y-%m-%d %H:%M UTC")}
|
||||
<% else %>
|
||||
|
|
@ -244,11 +365,21 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="weather-utc-clock"
|
||||
phx-hook="UtcClock"
|
||||
class="font-mono text-xs opacity-70 tabular-nums shrink-0"
|
||||
>
|
||||
<div class="flex items-center gap-1.5 shrink-0">
|
||||
<div
|
||||
id="weather-utc-clock-desktop"
|
||||
phx-hook="UtcClock"
|
||||
class="font-mono text-xs opacity-70 tabular-nums"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
id="weather-sidebar-collapse"
|
||||
class="btn btn-xs btn-square btn-ghost"
|
||||
title="Collapse sidebar"
|
||||
onclick="document.getElementById('weather-sidebar').style.display='none';document.getElementById('weather-sidebar-expand').style.display='flex';window.dispatchEvent(new Event('sidebar-toggle'));"
|
||||
>
|
||||
<.icon name="hero-chevron-right" class="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -275,30 +406,58 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
|||
</div>
|
||||
|
||||
<%!-- Layer description --%>
|
||||
<div class="text-xs opacity-70 px-1 leading-snug">
|
||||
<div class="text-[11px] opacity-70 px-1 leading-snug">
|
||||
{layer_description(@layers, @selected_layer)}
|
||||
</div>
|
||||
|
||||
<%!-- Links --%>
|
||||
<div class="flex flex-col gap-1 border-t border-base-300 pt-2">
|
||||
<.link navigate="/map" class="btn btn-xs btn-ghost justify-start">
|
||||
Propagation Prediction Map
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%!-- Navigation --%>
|
||||
<ul class="menu menu-xs border-t border-base-300 pt-2 px-0">
|
||||
<li>
|
||||
<.link navigate="/map">Propagation Map</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/path">Path Calculator</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/beacons">Beacons</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/algo">Scoring Algorithm</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/submit">Submit a Contact</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/contacts">Contact Training Data</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/contacts/map">Contact Map</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link navigate="/about">About</.link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<%!-- Point detail panel --%>
|
||||
<div
|
||||
id="weather-detail-panel"
|
||||
phx-update="ignore"
|
||||
class={[
|
||||
"bg-neutral text-neutral-content shadow-lg border border-base-300 overflow-hidden overflow-y-auto z-[1001]",
|
||||
"fixed bottom-0 left-0 right-0 max-h-[50vh] rounded-t-2xl",
|
||||
"md:absolute md:top-3 md:bottom-auto md:left-auto md:right-auto md:max-h-[60vh] md:max-w-sm md:rounded-box"
|
||||
]}
|
||||
style="display:none;"
|
||||
>
|
||||
<%!-- Auth --%>
|
||||
<ul class="menu menu-xs border-t border-base-300 pt-2 px-0">
|
||||
<%= if @current_scope && @current_scope.user do %>
|
||||
<li class="menu-title text-xs opacity-70">{@current_scope.user.callsign}</li>
|
||||
<li>
|
||||
<.link href={~p"/users/settings"}>Settings</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link href={~p"/users/log-out"} method="delete">Log out</.link>
|
||||
</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<.link href={~p"/users/register"}>Register</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link href={~p"/users/log-in"}>Log in</.link>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue