fix(contacts/map): align line colors with sidebar legend

The polylines were sourcing from --band-* CSS vars (a separate, theme-aware
design palette) while the legend swatches rendered from the Elixir @band_colors
map, so the two diverged for every band — e.g. 10 GHz showed lime on the map
and emerald in the legend. Emit `data-band-color` on each band checkbox and
have the JS hook read from there, making the legend the single source of truth.
Drops the now-redundant theme observer / restyleLines plumbing.
This commit is contained in:
Graham McIntire 2026-05-05 13:56:02 -05:00
parent 90cc554c4f
commit 18680b35e5
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 13 additions and 58 deletions

View file

@ -40,7 +40,6 @@ interface ContactsMapHook extends ViewHook {
map: L.Map
delegatedChange: (e: Event) => void
delegatedClick: (e: Event) => void
themeObserver: MutationObserver | null
initMap(this: ContactsMapHook): void
buildLines(this: ContactsMapHook): void
syncBandLayers(this: ContactsMapHook): void
@ -49,7 +48,6 @@ interface ContactsMapHook extends ViewHook {
updateCount(this: ContactsMapHook, count: number): void
wireControls(this: ContactsMapHook): void
unwireControls(this: ContactsMapHook): void
restyleLines(this: ContactsMapHook): void
}
function inDateRange(tsDate: string, startDate: string, endDate: string): boolean {
@ -60,48 +58,26 @@ function inDateRange(tsDate: string, startDate: string, endDate: string): boolea
return true
}
// Amateur-band palette. Nine of these map to the design system's --band-*
// CSS vars (defined in assets/css/app.css); the higher mm-wave bands above
// 47 GHz fall back to hard-coded hues since the design set only spans up
// to 6mm. CSS vars are resolved once per page via getComputedStyle because
// Leaflet writes the color onto the SVG `stroke` attribute, which does not
// evaluate `var()` references.
const BAND_VAR: Record<number, string> = {
1296: "--band-23cm",
2304: "--band-13cm",
3400: "--band-9cm",
5760: "--band-6cm",
10000: "--band-3cm",
24000: "--band-1-25cm",
47000: "--band-6mm",
}
const BAND_FALLBACK: Record<number, string> = {
68000: "#dc2626",
75000: "#c026d3",
122000: "#db2777",
134000: "#e11d48",
241000: "#b91c1c",
}
// Band colors are sourced from the legend swatches in the sidebar — each
// band checkbox carries `data-band-color` set server-side from
// `Microwaveprop.ContactMapLive`'s @band_colors map. Reading from the DOM
// keeps the legend the single source of truth: change the Elixir map and
// both the swatch and the polyline update in lockstep.
let BAND_COLORS: Record<number, string> | null = null
function resolveBandColors(): Record<number, string> {
if (BAND_COLORS) return BAND_COLORS
const styles = getComputedStyle(document.documentElement)
const resolved: Record<number, string> = { ...BAND_FALLBACK }
for (const [band, cssVar] of Object.entries(BAND_VAR)) {
const value = styles.getPropertyValue(cssVar).trim()
if (value) resolved[Number(band)] = value
const resolved: Record<number, string> = {}
const inputs = document.querySelectorAll<HTMLInputElement>("input[data-band][data-band-color]")
for (const inp of inputs) {
const band = parseInt(inp.dataset.band || "", 10)
const color = inp.dataset.bandColor
if (Number.isFinite(band) && color) resolved[band] = color
}
BAND_COLORS = resolved
return resolved
}
function invalidateBandColors(): void {
BAND_COLORS = null
}
function bandLabel(mhz: number): string {
if (mhz >= 1000) {
const ghz = mhz / 1000
@ -125,7 +101,6 @@ export const ContactsMap = {
this.bandGroups = new Map()
this.lines = []
this.dotLayer = L.layerGroup()
this.themeObserver = null
// Callsign filter still roundtrips through LiveView (debounced) — it's
// cross-cutting and fires rarely. Band toggles are purely client-side.
@ -144,18 +119,6 @@ export const ContactsMap = {
this.wireControls()
// Re-color polylines when the theme toggles. Leaflet writes `stroke` as
// an SVG attribute (which does not resolve CSS vars), so we re-read the
// resolved --band-* values and call setStyle on each line.
this.themeObserver = new MutationObserver(() => {
invalidateBandColors()
this.restyleLines()
})
this.themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"]
})
requestAnimationFrame(() => this.initMap())
fetch(fetchUrl)
@ -174,16 +137,6 @@ export const ContactsMap = {
destroyed(this: ContactsMapHook) {
this.unwireControls()
if (this.themeObserver) {
this.themeObserver.disconnect()
this.themeObserver = null
}
},
restyleLines(this: ContactsMapHook) {
for (const entry of this.lines) {
entry.line.setStyle({ color: bandColor(entry.band) })
}
},
wireControls(this: ContactsMapHook) {

View file

@ -202,6 +202,7 @@ defmodule MicrowavepropWeb.ContactMapLive do
type="checkbox"
checked
data-band={band}
data-band-color={band_color(band)}
style={"accent-color:#{band_color(band)}"}
/>
<span
@ -325,6 +326,7 @@ defmodule MicrowavepropWeb.ContactMapLive do
type="checkbox"
checked
data-band={band}
data-band-color={band_color(band)}
style={"accent-color:#{band_color(band)}"}
/>
<span