/** * Drag-to-resize Svelte action. Attaches pointerdown to the host element * (a thin handle on the inner edge of a sidebar) and writes the new width * back via the supplied setter. The pointer is captured so the drag keeps * tracking when the cursor leaves the handle. * * edge: 'right' — handle on the right edge of the panel; drag right widens * edge: 'left' — handle on the left edge of the panel; drag left widens * * Usage: *
view.leftSidebarWidth, setWidth: setLeftSidebarWidth }} /> */ export interface ResizableParams { edge: 'right' | 'left'; getWidth: () => number; setWidth: (px: number) => void; } export function resizable(node: HTMLElement, initial: ResizableParams) { let params = initial; let pointerId = -1; let startX = 0; let startWidth = 0; function onDown(e: PointerEvent) { if (e.button !== 0) return; pointerId = e.pointerId; startX = e.clientX; startWidth = params.getWidth(); node.setPointerCapture(pointerId); document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; node.addEventListener('pointermove', onMove); node.addEventListener('pointerup', onUp); node.addEventListener('pointercancel', onUp); } function onMove(e: PointerEvent) { if (e.pointerId !== pointerId) return; const dx = e.clientX - startX; const delta = params.edge === 'right' ? dx : -dx; params.setWidth(startWidth + delta); } function onUp(e: PointerEvent) { if (pointerId === -1) return; try { node.releasePointerCapture(pointerId); } catch { // Pointer may already be released; ignore. } pointerId = -1; document.body.style.cursor = ''; document.body.style.userSelect = ''; node.removeEventListener('pointermove', onMove); node.removeEventListener('pointerup', onUp); node.removeEventListener('pointercancel', onUp); } function onDoubleClick() { // Reset to the current default-ish midpoint. Callers can override by // providing their own dblclick handler; we just stop pointer events // from leaking up so the page underneath doesn't react. } node.addEventListener('pointerdown', onDown); node.addEventListener('dblclick', onDoubleClick); return { update(next: ResizableParams) { params = next; }, destroy() { node.removeEventListener('pointerdown', onDown); node.removeEventListener('dblclick', onDoubleClick); } }; }