${detail.score}/100
${tier.label}
@@ -550,13 +569,19 @@ export const PropagationMap = {
const merged = { ...basic, ...this.bandInfo, factors: null }
this.detailPanel.innerHTML = buildLoadingHTML(merged)
this.detailPanel.style.display = "block"
+ this.positionDetailPanel()
this.pushEvent("point_detail", { lat: e.latlng.lat, lon: e.latlng.lng })
}
})
- // Detail panel below sidebar instead of map popup
+ // Detail panel — bottom sheet on mobile, positioned next to sidebar on desktop
this.detailPanel = document.getElementById("detail-panel")
this.viewshedLoading = false
+
+ if (this.detailPanel) {
+ L.DomEvent.disableClickPropagation(this.detailPanel)
+ L.DomEvent.disableScrollPropagation(this.detailPanel)
+ }
this.lastDetail = null
this.handleEvent("point_detail", (detail) => {
@@ -565,6 +590,7 @@ export const PropagationMap = {
this.lastDetail = merged
this.detailPanel.innerHTML = buildPopupHTML(merged, this.viewshedLoading)
this.detailPanel.style.display = "block"
+ this.positionDetailPanel()
// Draw propagation reach polygon based on contiguous good cells
if (this.gridLookup && this.clickedLatLng) {
@@ -679,27 +705,46 @@ export const PropagationMap = {
}
})
- // Legend
- const legend = L.control({ position: "bottomright" })
+ // Legend — compact on mobile, moved to top-right to avoid timeline overlap
+ const legend = L.control({ position: isMobile() ? "topright" : "bottomright" })
legend.onAdd = () => {
const div = L.DomUtil.create("div")
- div.style.cssText = "background:#fff;color:#333;padding:10px 14px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:13px;line-height:2;"
+ const mobile = isMobile()
const rows = [
- ["#00ffa3", "Excellent (80-100)"],
- ["#7dffd4", "Good (65-79)"],
- ["#ffe566", "Marginal (50-64)"],
- ["#ff9044", "Poor (33-49)"],
- ["#ff4f4f", "Negligible (0-32)"]
+ ["#00ffa3", "Excellent", "80+"],
+ ["#7dffd4", "Good", "65"],
+ ["#ffe566", "Marginal", "50"],
+ ["#ff9044", "Poor", "33"],
+ ["#ff4f4f", "Negligible", "0"]
]
- div.innerHTML = "
Propagation" +
- rows.map(([c, label]) =>
- `
${label}`
+ if (mobile) {
+ div.style.cssText = "background:#fff;color:#333;padding:4px 6px;border-radius:6px;box-shadow:0 1px 4px rgba(0,0,0,0.3);font-size:10px;line-height:1.6;"
+ div.innerHTML = rows.map(([c, , score]) =>
+ `
${score}`
).join("
")
+ } else {
+ div.style.cssText = "background:#fff;color:#333;padding:10px 14px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:13px;line-height:2;"
+ div.innerHTML = "
Propagation" +
+ rows.map(([c, label, score]) =>
+ `
${label} (${score === "80+" ? "80-100" : score === "0" ? "0-32" : score + "-" + (parseInt(score) === 65 ? "79" : parseInt(score) === 50 ? "64" : "49")})`
+ ).join("
")
+ }
return div
}
legend.addTo(this.map)
},
+ positionDetailPanel() {
+ if (!this.detailPanel || isMobile()) return
+ // On desktop, position the panel below the control panel
+ const controls = document.getElementById("map-controls")
+ if (controls) {
+ const rect = controls.getBoundingClientRect()
+ this.detailPanel.style.left = rect.left + "px"
+ this.detailPanel.style.top = (rect.bottom + 8) + "px"
+ }
+ },
+
renderScores(scores) {
this.scores = scores
if (this.scoreOverlay) {
@@ -879,6 +924,12 @@ export const PropagationMap = {
}
})
+ const mobile = isMobile()
+ const btnPad = mobile ? "3px 5px" : "4px 8px"
+ const btnMinW = mobile ? "32px" : "38px"
+ const btnFont = mobile ? "10px" : "11px"
+ const subFont = mobile ? "8px" : "9px"
+
const buttons = items.map(t => {
const bg = t.isSelected ? "#00ffa3" : (t.isPast ? "rgba(255,255,255,0.08)" : "rgba(255,255,255,0.15)")
const fg = t.isSelected ? "#000" : (t.isPast ? "rgba(255,255,255,0.4)" : "#fff")
@@ -886,17 +937,19 @@ export const PropagationMap = {
const weight = t.isSelected ? "700" : "400"
const utcLabel = `${t.dt.getUTCHours().toString().padStart(2, "0")}:00`
return `
`
+ ">${t.label}
${utcLabel}`
}).join("")
+ const labelText = mobile ? "Forecast" : "Propagation Forecast"
+
this.timelineEl.style.display = "block"
this.timelineEl.innerHTML = `
-
-
Propagation Forecast
+
+ ${labelText}
${buttons}
`
diff --git a/lib/microwaveprop_web/live/map_live.ex b/lib/microwaveprop_web/live/map_live.ex
index 3281576f..5ff86c08 100644
--- a/lib/microwaveprop_web/live/map_live.ex
+++ b/lib/microwaveprop_web/live/map_live.ex
@@ -248,7 +248,7 @@ defmodule MicrowavepropWeb.MapLive do
@impl true
def render(assigns) do
~H"""
-
+
<%!-- Full-page map --%>
<%!-- Top-left control panel --%>
-
-
-
-
- North Texas Microwave Society
+
+
+
+
+
North Texas Microwave Society
+
NTMS
Propagation Map
-
+
+
+
+
- <%!-- Band selector --%>
+ <%!-- Band selector (always visible) --%>
{selected_label(@bands, @selected_band)}
@@ -305,64 +319,71 @@ defmodule MicrowavepropWeb.MapLive do
- <%!-- Grid toggle --%>
-
+ <%!-- Secondary controls (collapsed on mobile by default) --%>
+
+
- <%!-- Point detail panel (populated by JS on click, ignored by LiveView patches) --%>
-
-
+ <%!-- Point detail panel — bottom sheet on mobile, sidebar on desktop --%>
+
<%!-- Bottom timeline bar --%>