Back to Black Site Map Editor
SOURCE / PINNED RELEASE

Made of little things.

Black Site Map Editor

Release
32a57733b7e9…
Author-recorded commit
3f54890f542f…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor

Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/pane-resizer.ts
// Draggable, keyboard-accessible splitters for the workspace side panes.
// Widths live as --left-w / --right-w CSS variables on the workspace grid; the
// canvas ResizeObserver refits the map as the panes change size.
export interface ResizablePane {
    /** Handle element mounted on an edge of the canvas pane. */
    handle: HTMLElement;
    /** Pane element whose grid column the handle controls. */
    pane: HTMLElement;
    /** Width in px matching the CSS fallback, used for resets. */
    defaultWidth: number;
    /** Smallest useful width in px. */
    min: number;
    /** Largest useful width in px. */
    max: number;
}

export function mountPaneResizers(
    workspace: HTMLElement,
    sides: { left: ResizablePane; right: ResizablePane },
) {
    const currentWidth = (side: ResizablePane) => side.pane.getBoundingClientRect().width;
    const cssVariable = (side: ResizablePane) => (side === sides.left ? '--left-w' : '--right-w');
    function setWidth(side: ResizablePane, width: number) {
        const value = Math.round(Math.max(side.min, Math.min(side.max, width)));
        workspace.style.setProperty(cssVariable(side), `${value}px`);
        side.handle.setAttribute('aria-valuenow', String(value));
    }
    for (const side of [sides.left, sides.right]) {
        const handle = side.handle;
        // The left pane grows towards the right edge; the right pane the other way.
        const growsWithRightwardDrag = side === sides.left;
        let dragFrom: { width: number; x: number } | undefined;
        const dragTo = (event: PointerEvent) => {
            if (!dragFrom) return;
            const delta = (event.clientX - dragFrom.x) * (growsWithRightwardDrag ? 1 : -1);
            setWidth(side, dragFrom.width + delta);
        };
        const stop = (event: PointerEvent) => {
            if (!handle.hasPointerCapture(event.pointerId)) return;
            dragFrom = undefined;
            handle.releasePointerCapture(event.pointerId);
            handle.classList.remove('dragging');
            document.body.classList.remove('pane-resizing');
        };
        handle.setAttribute('aria-valuemin', String(side.min));
        handle.setAttribute('aria-valuemax', String(side.max));
        handle.setAttribute('aria-valuenow', String(currentWidth(side) || side.defaultWidth));
        handle.addEventListener('pointerdown', (event) => {
            if (event.button !== 0) return;
            event.preventDefault();
            dragFrom = { width: currentWidth(side), x: event.clientX };
            handle.setPointerCapture(event.pointerId);
            handle.classList.add('dragging');
            document.body.classList.add('pane-resizing');
        });
        handle.addEventListener('pointermove', (event) => {
            if (handle.hasPointerCapture(event.pointerId)) dragTo(event);
        });
        handle.addEventListener('pointerup', stop);
        handle.addEventListener('pointercancel', stop);
        handle.addEventListener('lostpointercapture', () => {
            dragFrom = undefined;
            handle.classList.remove('dragging');
            document.body.classList.remove('pane-resizing');
        });
        handle.addEventListener('dblclick', () => setWidth(side, side.defaultWidth));
        handle.addEventListener('keydown', (event) => {
            if (event.key === 'Home') {
                event.preventDefault();
                setWidth(side, side.defaultWidth);
                return;
            }
            const step = event.shiftKey ? 48 : 16;
            let delta = 0;
            if (event.key === 'ArrowLeft') delta = growsWithRightwardDrag ? -step : step;
            else if (event.key === 'ArrowRight') delta = growsWithRightwardDrag ? step : -step;
            else return;
            event.preventDefault();
            setWidth(side, currentWidth(side) + delta);
        });
    }
}