Changeset 0.25.2 (#162)

This commit is contained in:
2026-03-08 22:36:56 +00:00
parent b3f8b747dd
commit 6c484fa7f8
13 changed files with 240 additions and 151 deletions

View File

@@ -345,115 +345,99 @@ function _createHandle(direction, splitEl, index, surfaceId, splitId) {
splitEl.appendChild(handle);
}
// Drag logic
let startX, startY, leftEl, rightEl, startLeftBasis, startRightBasis;
// Track adjacent panes for dblclick reset
let lastLeftEl = null, lastRightEl = null;
const onMouseDown = (e) => {
e.preventDefault();
const allPanes = Array.from(splitEl.querySelectorAll(':scope > .pane, :scope > .pane-split'));
// Find the two panes adjacent to this handle
const handleIdx = Array.from(splitEl.children).indexOf(handle);
leftEl = splitEl.children[handleIdx - 1];
rightEl = splitEl.children[handleIdx + 1];
if (!leftEl || !rightEl) return;
initDragResize(handle, {
direction,
onStart(_clientPos) {
// Find the two panes adjacent to this handle
const handleIdx = Array.from(splitEl.children).indexOf(handle);
const leftEl = splitEl.children[handleIdx - 1];
const rightEl = splitEl.children[handleIdx + 1];
if (!leftEl || !rightEl) return false;
startX = e.clientX;
startY = e.clientY;
// Use current rendered size (matches what flex layout computed)
startLeftBasis = leftEl.getBoundingClientRect().width;
startRightBasis = rightEl.getBoundingClientRect().width;
lastLeftEl = leftEl;
lastRightEl = rightEl;
// Immediately lock the non-flex panes to their current sizes
// so the first move delta doesn't cause a visual snap
if (leftEl.dataset.paneFlex !== '1') {
leftEl.style.flexBasis = startLeftBasis + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
}
if (rightEl.dataset.paneFlex !== '1') {
rightEl.style.flexBasis = startRightBasis + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
}
const isHoriz = direction === 'horizontal';
const dim = isHoriz ? 'width' : 'height';
const startLeftBasis = leftEl.getBoundingClientRect()[dim];
const startRightBasis = rightEl.getBoundingClientRect()[dim];
handle.classList.add('pane-handle--active');
document.body.style.cursor = direction === 'horizontal' ? 'col-resize' : 'row-resize';
document.body.style.userSelect = 'none';
// Lock non-flex panes to current rendered size so the first
// move delta doesn't cause a visual snap
if (leftEl.dataset.paneFlex !== '1') {
leftEl.style.flexBasis = startLeftBasis + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
}
if (rightEl.dataset.paneFlex !== '1') {
rightEl.style.flexBasis = startRightBasis + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
};
handle.classList.add('pane-handle--active');
const onMouseMove = (e) => {
const delta = direction === 'horizontal'
? e.clientX - startX
: e.clientY - startY;
return { leftEl, rightEl, startLeftBasis, startRightBasis };
},
onMove(delta, ctx) {
const { leftEl, rightEl, startLeftBasis, startRightBasis } = ctx;
const leftIsFlex = leftEl.dataset.paneFlex === '1';
const rightIsFlex = rightEl.dataset.paneFlex === '1';
const isHoriz = direction === 'horizontal';
const dim = isHoriz ? 'width' : 'height';
const maxSize = splitEl.getBoundingClientRect()[dim] * 0.6;
const leftIsFlex = leftEl.dataset.paneFlex === '1';
const rightIsFlex = rightEl.dataset.paneFlex === '1';
// Cap: no single fixed pane should exceed 60% of container
const maxSize = splitEl.getBoundingClientRect().width * 0.6;
if (leftIsFlex) {
// Only adjust the right (fixed) pane; left flex absorbs remainder
const newRight = Math.min(maxSize, Math.max(50, startRightBasis - delta));
rightEl.style.flexBasis = newRight + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
} else if (rightIsFlex) {
// Only adjust the left (fixed) pane; right flex absorbs remainder
const newLeft = Math.min(maxSize, Math.max(50, startLeftBasis + delta));
leftEl.style.flexBasis = newLeft + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
} else {
// Both fixed — adjust both (original behavior)
const newLeft = Math.max(50, startLeftBasis + delta);
const newRight = Math.max(50, startRightBasis - delta);
leftEl.style.flexBasis = newLeft + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
rightEl.style.flexBasis = newRight + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
}
};
const onMouseUp = () => {
handle.classList.remove('pane-handle--active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
// Persist sizes
_persistSizes(surfaceId, splitEl);
};
handle.addEventListener('mousedown', onMouseDown);
if (leftIsFlex) {
const newRight = Math.min(maxSize, Math.max(50, startRightBasis - delta));
rightEl.style.flexBasis = newRight + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
} else if (rightIsFlex) {
const newLeft = Math.min(maxSize, Math.max(50, startLeftBasis + delta));
leftEl.style.flexBasis = newLeft + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
} else {
const newLeft = Math.max(50, startLeftBasis + delta);
const newRight = Math.max(50, startRightBasis - delta);
leftEl.style.flexBasis = newLeft + 'px';
leftEl.style.flexGrow = '0';
leftEl.style.flexShrink = '0';
rightEl.style.flexBasis = newRight + 'px';
rightEl.style.flexGrow = '0';
rightEl.style.flexShrink = '0';
}
},
onEnd(_ctx) {
handle.classList.remove('pane-handle--active');
_persistSizes(surfaceId, splitEl);
},
});
// Double-click resets to default
handle.addEventListener('dblclick', () => {
if (leftEl) {
if (leftEl.dataset.paneFlex === '1') {
leftEl.style.flex = '1'; leftEl.style.flexBasis = ''; leftEl.style.flexGrow = ''; leftEl.style.flexShrink = '';
} else {
leftEl.style.flexBasis = ''; leftEl.style.flexGrow = ''; leftEl.style.flexShrink = '';
}
}
if (rightEl) {
if (rightEl.dataset.paneFlex === '1') {
rightEl.style.flex = '1'; rightEl.style.flexBasis = ''; rightEl.style.flexGrow = ''; rightEl.style.flexShrink = '';
} else {
rightEl.style.flexBasis = ''; rightEl.style.flexGrow = ''; rightEl.style.flexShrink = '';
}
}
_resetPaneSize(lastLeftEl);
_resetPaneSize(lastRightEl);
_persistSizes(surfaceId, splitEl);
});
return { handle, splitEl, index, direction };
}
/** Reset a pane element to its default flex sizing. */
function _resetPaneSize(el) {
if (!el) return;
if (el.dataset.paneFlex === '1') {
el.style.flex = '1';
}
el.style.flexBasis = '';
el.style.flexGrow = '';
el.style.flexShrink = '';
}
// ── Persistence ─────────────────────────────
function _persistSizes(surfaceId, splitEl) {