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.
This commit is contained in:
parent
1cd2b46cd5
commit
5f2d7e6bbc
4 changed files with 52 additions and 33 deletions
|
|
@ -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 (_) {}
|
||||
|
|
|
|||
|
|
@ -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 += `<div class="sep"></div>
|
||||
<div class="sect-hdr">${icon(n.icon)}<span>${n.label}</span></div>`
|
||||
if (!firstGroup) h += '<div class="sep"></div>'
|
||||
h += `<div class="sect-hdr">${icon(n.icon)}<span>${n.label}</span></div>`
|
||||
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 += `<div class="sep"></div>
|
||||
<button class="row" data-folder="${n.id}">
|
||||
if (!firstGroup) h += '<div class="sep"></div>'
|
||||
firstGroup = false
|
||||
h += `<button class="row" data-folder="${n.id}">
|
||||
${icon(n.icon)}<span class="lbl">${n.label}</span>
|
||||
${/* desktop badge removed — the folder label already conveys it */''}
|
||||
<span class="chev ${o?'open':''}">${icon('chevron')}</span>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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 += `<div class="sep"></div>
|
||||
<div class="sect-hdr">${icon(n.icon)}<span>${n.label}</span></div>`
|
||||
if (!firstGroup) h += '<div class="sep"></div>'
|
||||
h += `<div class="sect-hdr">${icon(n.icon)}<span>${n.label}</span></div>`
|
||||
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 += `<div class="sep"></div>
|
||||
<button class="row" data-folder="${n.id}">
|
||||
if (!firstGroup) h += '<div class="sep"></div>'
|
||||
firstGroup = false
|
||||
h += `<button class="row" data-folder="${n.id}">
|
||||
${icon(n.icon)}<span class="lbl">${n.label}</span>
|
||||
${/* desktop badge removed — the folder label already conveys it */''}
|
||||
<span class="chev ${o?'open':''}">${icon('chevron')}</span>
|
||||
|
|
|
|||
Loading…
Reference in a new issue