@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
JavaScript
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 };
}