fix: Excel-like column resize — drag steals from neighbor, percentages persist, panel drag reflows proportionally

This commit is contained in:
you
2026-03-19 21:04:15 +00:00
parent 4ed043a0ff
commit 047d67d229
2 changed files with 36 additions and 11 deletions
+34 -5
View File
@@ -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
}
+2 -6
View File
@@ -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);
}