From 5f2d7e6bbca0a2ef3afbe9f199d9dba57d6d67ab Mon Sep 17 00:00:00 2001 From: doxa-maps Date: Sat, 12 Sep 2026 21:21:45 +0000 Subject: [PATCH] fix(spa,dashboard): kill the top divider + reflow the map when the table collapses - SPA sidebar: suppress the leading section divider on the FIRST nav group. Every section emitted a `.sep` rule before its header, so Prayer Mobilization (the first section) drew a 1px line directly under the header with nothing above it to divide. drawSide() now tracks a firstGroup flag and skips it. - Research dashboard: when the table under the map collapses, resize the map to fill the freed space. The ResizeObserver alone missed the grid reflow at the instant the table cell flips to display:none (v-show), leaving the map canvas at its old size (gray gap). DashboardMapPanel now also resizes on the 'toggleTable' bus event (rAF + a 280ms settle pass), with clean teardown. --- .../dashboards/DashboardMapPanel.vue | 9 ++++ layouts/app-wrapper-material/index.html | 15 ++++-- .../research-dashboard/research-dashboard.js | 46 +++++++++---------- public/index.html | 15 ++++-- 4 files changed, 52 insertions(+), 33 deletions(-) diff --git a/app-profiles/research-dashboard/src/components/dashboards/DashboardMapPanel.vue b/app-profiles/research-dashboard/src/components/dashboards/DashboardMapPanel.vue index d66a9d407..7f51fa428 100644 --- a/app-profiles/research-dashboard/src/components/dashboards/DashboardMapPanel.vue +++ b/app-profiles/research-dashboard/src/components/dashboards/DashboardMapPanel.vue @@ -53,6 +53,7 @@ let added = false // container calls map.resize() on ANY container-size change, so the canvas always fills its cell. // General-correct (not table-specific): also covers window/rail/facet reflow. rAF-coalesced. let containerRO = null +let offToggleTable = null let _resizeRaf = 0 function scheduleMapResize() { if (_resizeRaf) return @@ -209,8 +210,16 @@ onMounted(async () => { containerRO = new ResizeObserver(scheduleMapResize) containerRO.observe(mapContainer.value) } + // Belt-and-suspenders: the RO observes the container, but when the table cell toggles + // display:none (v-show) the grid reflow can land a frame before the observer notices, so + // the 1fr map row grows without the canvas following → the reported gray gap. Resize + // explicitly the moment collapse state flips (rAF now + one delayed pass for the settle). + if (bus && typeof bus.on === 'function') { + offToggleTable = bus.on('toggleTable', () => { scheduleMapResize(); setTimeout(scheduleMapResize, 280) }) + } }) onBeforeUnmount(() => { + try { offToggleTable && offToggleTable() } catch (_) {} offToggleTable = null if (containerRO) { try { containerRO.disconnect() } catch (_) {} containerRO = null } if (_resizeRaf) { cancelAnimationFrame(_resizeRaf); _resizeRaf = 0 } try { destroy && destroy() } catch (_) {} diff --git a/layouts/app-wrapper-material/index.html b/layouts/app-wrapper-material/index.html index 3f37f8b7d..202f7c2bf 100644 --- a/layouts/app-wrapper-material/index.html +++ b/layouts/app-wrapper-material/index.html @@ -675,11 +675,15 @@ function rowHTML(n, cur){ } function drawSide(){ let h = '' + // The `sep` is a divider BETWEEN groups — the first group must not carry a + // leading one (a rule under the header with nothing above it to divide). + let firstGroup = true for (const n of tree){ - if (n.kind === 'item'){ h += rowHTML(n, active); continue } + if (n.kind === 'item'){ h += rowHTML(n, active); firstGroup = false; continue } if (n.kind === 'section'){ - h += `
-
${icon(n.icon)}${n.label}
` + if (!firstGroup) h += '
' + h += `
${icon(n.icon)}${n.label}
` + firstGroup = false // A section's children may be plain items OR a nested 'subgroup' // (e.g. Laborer Mobilization > Research > Research Map). for (const c of n.children){ @@ -694,8 +698,9 @@ function drawSide(){ } if (n.kind === 'folder'){ const o = open.get(n.id) - h += `
-