UNPKG

@selenite/commons

Version:

This typescript package provides a set of frequently used utilities, types and svelte actions for building projects with Typescript and Svelte.

186 lines (185 loc) 6.8 kB
import { Vector2D } from '../datastructure'; import { PointerDownWatcher, posFromClient } from '../utils'; const resizeHandleMap = new Map(); const enteredNodes = new Set(); let active; function globalOnpointermove(e) { const pos = posFromClient(e); for (const [node, { onpointerenter, onpointerleave, threshold: m }] of resizeHandleMap) { const rect = node.getBoundingClientRect(); if (pos.x >= rect.left - m && pos.x <= rect.right + m && pos.y >= rect.top - m && pos.y <= rect.bottom + m) { if (enteredNodes.has(node)) continue; enteredNodes.add(node); // console.debug('Entered nodes', Array.from(enteredNodes)); if (!active || active === node) { active = node; onpointerenter(e); } } else { if (!enteredNodes.has(node)) continue; enteredNodes.delete(node); // console.debug('Entered nodes', Array.from(enteredNodes)); if (active === node) { onpointerleave(e); if (PointerDownWatcher.instance.isPointerDown) { return; } active = enteredNodes.values().next().value; if (active) { resizeHandleMap.get(active)?.onpointerenter(e); } } } } } const defaultThreshold = 5; export function resizable(node, params = {}) { function onpointerenter() { document.addEventListener('pointermove', onpointermove, { passive: true }); removePointermoveCleanup?.(); } let removePointermoveCleanup; function onpointerleave() { // Wait for pointer up to stop resizing removePointermoveCleanup = PointerDownWatcher.instance.subscribe((down) => { if (!down) { document.removeEventListener('pointermove', onpointermove); removePointermoveCleanup?.(); document.documentElement.style.cursor = ''; document.body.style.pointerEvents = ''; currentSide = null; } }); } let resetLastPosCleanup; let currentSide = null; let lastPos; let baseWidth; let baseHeight; function onpointermove(e) { if (enteredNodes.size > 1) return; const pointerdownWatcher = PointerDownWatcher.instance; const pointerdown = pointerdownWatcher.isPointerDown; if (pointerdown && currentSide === null) return; if (pointerdown && currentSide !== null) { if (!resetLastPosCleanup) { resetLastPosCleanup = pointerdownWatcher.subscribe((down) => { if (!down) { lastPos = undefined; baseHeight = undefined; baseWidth = undefined; resetLastPosCleanup?.(); resetLastPosCleanup = undefined; params.onresizeend?.(); } }); } const rect = node.getBoundingClientRect(); if (!baseWidth) { baseWidth = rect.width; } if (!baseHeight) { baseHeight = rect.height; } const downPos = pointerdownWatcher.pos; if (!downPos) return; // Dragging if (!lastPos) { lastPos = downPos; } const pos = posFromClient(e); // const offset = Vector2D.subtract(pos, lastPos); // const totalOffset = Vector2D.subtract(pos, downPos); lastPos = pos; document.body.style.userSelect = 'none'; document.body.style.pointerEvents = 'none'; let width = rect.width; let height = rect.height; if (currentSide.includes('e')) { width = pos.x - rect.left; } else if (currentSide.includes('w')) { width = rect.right - pos.x; } if (currentSide.includes('s')) { height = pos.y - rect.top; } else if (currentSide.includes('n')) { height = rect.bottom - pos.y; } node.style.width = `${width}px`; node.style.height = `${height}px`; params.onresize?.({ side: currentSide, node, event: e, height, width }); return; } document.body.style.userSelect = ''; document.body.style.pointerEvents = ''; const all = params.sides?.all ?? (params.sides?.top === undefined && params.sides?.left === undefined && params.sides?.bottom === undefined && params.sides?.right === undefined); const top = params.sides?.top ?? all; const left = params.sides?.left ?? all; const bottom = params.sides?.bottom ?? all; const right = params.sides?.right ?? all; const threshold = params.threshold ?? defaultThreshold; const pos = posFromClient(e); const rect = node.getBoundingClientRect(); const dx = pos.x - rect.left; const dy = pos.y - rect.top; const w = rect.width; const h = rect.height; const m = threshold; const previousSide = currentSide; let verticalLetter = ''; let horizontalLetter = ''; if (left && dx < m) { horizontalLetter = 'w'; } else if (right && dx > w - m) { horizontalLetter = 'e'; } if (top && dy < m) { verticalLetter = 'n'; } else if (bottom && dy > h - m) { verticalLetter = 's'; } const candidate = verticalLetter + horizontalLetter; currentSide = candidate.length > 0 ? candidate : null; if (currentSide !== previousSide) { document.documentElement.style.cursor = currentSide !== null ? `${currentSide}-resize` : ''; } } if (resizeHandleMap.size === 0) { document.addEventListener('pointermove', globalOnpointermove, { passive: true }); } resizeHandleMap.set(node, { onpointerenter, onpointerleave, get threshold() { return params.threshold ?? defaultThreshold; } }); return { destroy() { resizeHandleMap.delete(node); if (resizeHandleMap.size === 0) { document.removeEventListener('pointermove', globalOnpointermove); } }, update(newParams) { params = newParams; } }; }