UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

94 lines (93 loc) 3.69 kB
import { useRef, useCallback, useEffect } from 'react'; function clamp(num, min, max) { return num <= min ? min : num >= max ? max : num; } export default function useDraggable({ onMove, onDrop, getBoundingRect = () => document.body.getBoundingClientRect(), handleSelector, }) { const startRef = useRef(null); const handleRef = useRef(null); const targetRef = useRef(null); const handleRefCallback = useCallback((newNode) => { const oldNode = handleRef.current; if (oldNode) { oldNode.removeEventListener('mousedown', handleMouseDown); } if (newNode) { newNode.addEventListener('mousedown', handleMouseDown); boundInitialPosition(); } handleRef.current = newNode; }, []); useEffect(() => { if (handleSelector) { /** * Element may not be yet attached. * This gives time react to render the node handle. */ setTimeout(() => { const node = targetRef?.current?.querySelector(handleSelector); node && handleRefCallback(node); }, 16); } }, [handleSelector]); const applyTransform = (dx, dy) => { if (!targetRef.current) return; targetRef.current.style.transform = `translate3d(${dx}px, ${dy}px, 0)`; }; const getTranslation = (event) => { if (!startRef.current) return { dx: 0, dy: 0 }; const { pageX, pageY, bounds } = startRef.current; return { dx: clamp(event.pageX - pageX, bounds.left, bounds.right), dy: clamp(event.pageY - pageY, bounds.top, bounds.bottom), }; }; const getTranslationBounds = (targetRect, boundingRect) => { const left = boundingRect.x - targetRect.x; const right = left + boundingRect.width - targetRect.width; const top = boundingRect.y - targetRect.y; const bottom = top + boundingRect.height - targetRect.height; return { left, right, top, bottom }; }; const handleMouseDown = (event) => { if (!targetRef.current) return; event.preventDefault(); document.addEventListener('mouseup', handleMouseUp); document.addEventListener('mousemove', handleMouseMove); const targetRect = targetRef.current.getBoundingClientRect(); const boundingRect = getBoundingRect(); startRef.current = { pageX: event.pageX, pageY: event.pageY, bounds: getTranslationBounds(targetRect, boundingRect), }; }; const handleMouseUp = (event) => { event.preventDefault(); document.removeEventListener('mouseup', handleMouseUp); document.removeEventListener('mousemove', handleMouseMove); const { dx, dy } = getTranslation(event); applyTransform(0, 0); onDrop && onDrop(dx, dy); }; const handleMouseMove = (event) => { event.preventDefault(); const { dx, dy } = getTranslation(event); applyTransform(dx, dy); onMove && onMove(event); }; const boundInitialPosition = () => { if (!targetRef.current) return; const targetRect = targetRef.current.getBoundingClientRect(); const boundingRect = getBoundingRect(); const bounds = getTranslationBounds(targetRect, boundingRect); const dx = clamp(0, bounds.left, bounds.right); const dy = clamp(0, bounds.top, bounds.bottom); if (onDrop && (dx !== 0 || dy !== 0)) onDrop(dx, dy); }; return { handleRef: handleRefCallback, targetRef, applyTransform }; }