import type { Action } from "svelte/action"; // port of the appview's `fragments/resizable` ResizablePanel: a grip that drags // a sibling panel's width, and resets it on double click export interface ResizableOptions { // the panel this grip sizes; looked up lazily so it can mount after the grip target: () => HTMLElement | null | undefined; // `before` when the panel sits left of the grip, `after` when it sits right direction?: "before" | "after"; min?: number; max?: number; } export const resizable: Action = (node, options) => { let current = options; let startX = 0; let startWidth = 0; let panel: HTMLElement | null | undefined; const move = (event: MouseEvent) => { if (!panel) return; const delta = current.direction === "after" ? startX - event.clientX : event.clientX - startX; const width = startWidth + delta; if (width < (current.min ?? 100) || width > (current.max ?? Infinity)) return; panel.style.width = `${width}px`; panel.style.flexShrink = "0"; }; const up = () => { node.classList.remove("resizing"); document.body.style.cursor = ""; document.body.style.userSelect = ""; document.removeEventListener("mousemove", move); document.removeEventListener("mouseup", up); }; const down = (event: MouseEvent) => { panel = current.target(); if (!panel) return; event.preventDefault(); node.classList.add("resizing"); document.body.style.cursor = "col-resize"; document.body.style.userSelect = "none"; startX = event.clientX; startWidth = panel.offsetWidth; document.addEventListener("mousemove", move); document.addEventListener("mouseup", up); }; const reset = (event: MouseEvent) => { const element = current.target(); if (!element) return; event.preventDefault(); element.style.width = ""; element.style.flexShrink = ""; }; node.addEventListener("mousedown", down); node.addEventListener("dblclick", reset); return { update: (next: ResizableOptions) => (current = next), destroy: () => { node.removeEventListener("mousedown", down); node.removeEventListener("dblclick", reset); up(); } }; };