From add0a934240df2b84ae82fd168dd6d414e2dd9df Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Thu, 12 Mar 2026 20:12:23 +0100 Subject: [PATCH] [feat](trx-frontend-http): frequency-ordered z-index and hover-to-front for RDS overlay layers Each RDS PS overlay item (position: absolute within the shared #rds-ps-overlay container) now receives a z-index derived from its channel frequency: items are sorted by freq_hz ascending so higher-frequency layers sit on top of lower-frequency ones by default. Hovering any layer temporarily assigns it the maximum z-index (entry count + 10) to bring it to the front; mouseleave restores the frequency-derived default stored in data-default-z. Also reverts the incorrectly applied vchan picker layer changes from the previous commit. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 13 ++++++ .../trx-frontend-http/assets/web/index.html | 1 - .../assets/web/plugins/vchan.js | 45 ------------------- .../trx-frontend-http/assets/web/style.css | 38 ---------------- 4 files changed, 13 insertions(+), 84 deletions(-) diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index 4a6c391..a73fe61 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -1364,6 +1364,12 @@ function renderRdsOverlays() { evt.stopPropagation(); copyRdsPsToClipboard(entry.rds, entry.freq_hz); }); + el.addEventListener("mouseenter", () => { + el.style.zIndex = String(entries.length + 10); + }); + el.addEventListener("mouseleave", () => { + if (el.dataset.defaultZ) el.style.zIndex = el.dataset.defaultZ; + }); rdsPsOverlay.appendChild(el); rdsOverlayEntries.push({ ...entry, el, stackIdx: idx }); }); @@ -1386,6 +1392,10 @@ function positionRdsOverlays() { const count = rdsOverlayEntries.length; const mid = (count - 1) / 2; const stackStepPx = 26; + // Assign z-indices: sort by frequency ascending so higher-frequency layers + // sit on top of lower-frequency ones in the default (non-hover) state. + const sortedByFreq = [...rdsOverlayEntries].sort((a, b) => a.freq_hz - b.freq_hz); + const freqZMap = new Map(sortedByFreq.map((e, i) => [e.id, i + 1])); rdsOverlayEntries.forEach((entry, idx) => { const el = entry.el; if (!el) return; @@ -1399,6 +1409,9 @@ function positionRdsOverlays() { const offsetPx = Math.round((idx - mid) * stackStepPx); el.style.left = `${clamped * width}px`; el.style.top = `calc(50% + ${offsetPx}px)`; + const z = String(freqZMap.get(entry.id) ?? (idx + 1)); + el.style.zIndex = z; + el.dataset.defaultZ = z; }); } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html index bc716b1..131338e 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html @@ -262,7 +262,6 @@
Signal
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js index c3ea228..3e64708 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js @@ -49,50 +49,6 @@ function vchanHandleChannels(data) { } } -function vchanRenderLayers() { - const container = document.getElementById("vchan-freq-layers"); - if (!container) return; - container.innerHTML = ""; - - if (vchanChannels.length === 0) { - container.style.height = "0"; - return; - } - - // Sort by frequency ascending so higher-frequency channels get higher z-index. - const sorted = [...vchanChannels].sort((a, b) => a.freq_hz - b.freq_hz); - - const LAYER_H_PX = 32; - const STEP_PX = 11; // vertical offset between layers so each peeks below the next - const totalH = LAYER_H_PX + (sorted.length - 1) * STEP_PX; - container.style.height = totalH + "px"; - - sorted.forEach((ch, i) => { - const layer = document.createElement("div"); - layer.className = "vchan-freq-layer"; - if (ch.id === vchanActiveId) layer.classList.add("active"); - - layer.style.top = (i * STEP_PX) + "px"; - // Higher frequency → higher index → higher z-index (sits on top by default). - const defaultZ = i + 1; - layer.style.zIndex = defaultZ; - - layer.textContent = `${ch.index}: ${vchanFmtFreq(ch.freq_hz)} ${ch.mode}`; - layer.title = `Ch ${ch.index}: ${vchanFmtFreq(ch.freq_hz)} ${ch.mode} · ${ch.subscribers} subscriber${ch.subscribers !== 1 ? "s" : ""}`; - - // Bring hovered layer to the front; restore on leave. - const maxZ = sorted.length + 10; - layer.addEventListener("mouseenter", () => { layer.style.zIndex = maxZ; }); - layer.addEventListener("mouseleave", () => { layer.style.zIndex = defaultZ; }); - - layer.addEventListener("click", () => { - if (ch.id !== vchanActiveId) vchanSubscribe(ch.id); - }); - - container.appendChild(layer); - }); -} - function vchanRender() { const picker = document.getElementById("vchan-picker"); if (!picker) return; @@ -137,7 +93,6 @@ function vchanRender() { addBtn.addEventListener("click", vchanAllocate); picker.appendChild(addBtn); - vchanRenderLayers(); vchanSyncAccentUI(); } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index 5696ca2..81c8e7f 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -392,44 +392,6 @@ input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem; border-color: var(--vchan-color); box-shadow: inset 3px 0 0 var(--vchan-color); } -/* Frequency layers: absolutely-positioned channel strips stacked by frequency */ -.vchan-freq-layers { - position: relative; - margin-top: 0.5rem; - /* height is set dynamically by vchanRenderLayers() */ -} -.vchan-freq-layer { - position: absolute; - left: 0; - right: 0; - height: 2rem; - display: flex; - align-items: center; - padding: 0 0.6rem; - border-radius: 6px; - background: var(--input-bg); - border: 1px solid var(--border-light); - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 0.82rem; - color: var(--text-muted); - cursor: pointer; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - transition: border-color 0.12s, box-shadow 0.12s; -} -.vchan-freq-layer:hover { - border-color: var(--vchan-color); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35); - color: var(--text); -} -.vchan-freq-layer.active { - background: var(--btn-bg); - color: var(--text); - font-weight: 600; - border-color: var(--vchan-color); - box-shadow: inset 3px 0 0 var(--vchan-color); -} /* Applied to #freq and #spectrum-bw-input when on a virtual channel */ .vchan-ch-active { border-color: var(--vchan-color) !important;