From 047d67d2299f5bbc98ac70922a7e69aa0dcbbaf6 Mon Sep 17 00:00:00 2001 From: you Date: Thu, 19 Mar 2026 21:04:15 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Excel-like=20column=20resize=20=E2=80=94?= =?UTF-8?q?=20drag=20steals=20from=20neighbor,=20percentages=20persist,=20?= =?UTF-8?q?panel=20drag=20reflows=20proportionally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/app.js | 39 ++++++++++++++++++++++++++++++++++----- public/packets.js | 8 ++------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/public/app.js b/public/app.js index dada6178..c023cbda 100644 --- a/public/app.js +++ b/public/app.js @@ -466,6 +466,20 @@ function makeColumnsResizable(tableSelector, storageKey) { if (saved) { try { widths = JSON.parse(saved); } catch { widths = null; } + // Validate: must be array of correct length with values summing to ~100 (percentages) + if (widths && Array.isArray(widths) && widths.length === ths.length) { + const sum = widths.reduce((s, w) => s + w, 0); + if (sum > 90 && sum < 110) { + // Saved percentages — apply directly + table.style.tableLayout = 'fixed'; + table.style.width = '100%'; + ths.forEach((th, i) => { th.style.width = widths[i] + '%'; }); + // Skip measurement, jump to adding handles + addResizeHandles(); + return; + } + } + widths = null; // Force remeasure } if (!widths) { @@ -532,9 +546,13 @@ function makeColumnsResizable(tableSelector, storageKey) { topN.forEach(x => { finalWidths[x.i] += Math.round(surplus * (x.w / topTotal)); }); } - table.style.width = containerW + 'px'; - ths.forEach((th, i) => { th.style.width = finalWidths[i] + 'px'; }); + table.style.width = '100%'; + const totalFinal = finalWidths.reduce((s, w) => s + w, 0); + ths.forEach((th, i) => { th.style.width = (finalWidths[i] / totalFinal * 100) + '%'; }); + addResizeHandles(); + + function addResizeHandles() { // Add resize handles ths.forEach((th, i) => { if (i === ths.length - 1) return; @@ -554,15 +572,25 @@ function makeColumnsResizable(tableSelector, storageKey) { function onMove(e2) { const dx = e2.clientX - startX; const newW = Math.max(30, startW + dx); - th.style.width = newW + 'px'; - table.style.width = (startTableW + (newW - startW)) + 'px'; + // Steal/give space from next column (Excel behavior) + const nextTh = ths[i + 1]; + const nextW = nextTh ? nextTh.offsetWidth : 0; + const delta = newW - th.offsetWidth; + if (nextTh && nextW - delta >= 30) { + th.style.width = newW + 'px'; + nextTh.style.width = (nextW - delta) + 'px'; + } } function onUp() { handle.classList.remove('active'); document.body.style.cursor = ''; document.body.style.userSelect = ''; - const ws = ths.map(t => t.offsetWidth); + // Save as percentages + const tableW = table.offsetWidth; + const ws = ths.map(t => (t.offsetWidth / tableW * 100)); localStorage.setItem(storageKey, JSON.stringify(ws)); + // Re-apply as percentages + ths.forEach((t, j) => { t.style.width = ws[j] + '%'; }); document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); } @@ -571,4 +599,5 @@ function makeColumnsResizable(tableSelector, storageKey) { }); th.appendChild(handle); }); + } // end addResizeHandles } diff --git a/public/packets.js b/public/packets.js index fd6cf097..137b03fc 100644 --- a/public/packets.js +++ b/public/packets.js @@ -38,13 +38,11 @@ const w = Math.max(280, Math.min(window.innerWidth * 0.7, startW - (e2.clientX - startX))); panel.style.width = w + 'px'; panel.style.minWidth = w + 'px'; - // Force left panel and table to match new available width + // Explicitly size left panel so table reflows within it const left = document.getElementById('pktLeft'); - const table = document.getElementById('pktTable'); - if (left && table) { + if (left) { const available = left.parentElement.clientWidth - w; left.style.width = available + 'px'; - table.style.width = available + 'px'; } } function onUp() { @@ -54,9 +52,7 @@ localStorage.setItem(PANEL_WIDTH_KEY, panel.offsetWidth); // Clear forced widths, let flex take over const left = document.getElementById('pktLeft'); - const table = document.getElementById('pktTable'); if (left) left.style.width = ''; - if (table) table.style.width = ''; document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); }