@itwin/itwinui-react
Version:
A react component library for iTwinUI
106 lines (105 loc) • 3.93 kB
JavaScript
import * as React from 'react';
import {
getTranslateValuesFromElement,
getWindow,
} from '../functions/index.js';
import { useEventListener } from './useEventListener.js';
import { useResizeObserver } from './useResizeObserver.js';
let getContainerRect = (containerRef) => {
let containerRect = containerRef?.current?.getBoundingClientRect();
return {
top: containerRect?.top ?? 0,
right: containerRect?.right ?? getWindow()?.innerWidth ?? 0,
bottom: containerRect?.bottom ?? getWindow()?.innerHeight ?? 0,
left: containerRect?.left ?? 0,
};
};
export const useDragAndDrop = (elementRef, containerRef, enabled = true) => {
let grabOffsetX = React.useRef(0);
let grabOffsetY = React.useRef(0);
let translateX = React.useRef(void 0);
let translateY = React.useRef(void 0);
let containerRectRef = React.useRef(getContainerRect(containerRef));
let adjustTransform = React.useCallback(() => {
if (!elementRef.current || !enabled) return;
let { top, right, bottom, left } =
elementRef.current?.getBoundingClientRect();
let [newTranslateX, newTranslateY] = getTranslateValuesFromElement(
elementRef.current,
);
containerRectRef.current = getContainerRect(containerRef);
if (bottom > containerRectRef.current.bottom)
newTranslateY -= bottom - containerRectRef.current.bottom;
if (top < containerRectRef.current.top)
newTranslateY += containerRectRef.current.top - top;
if (right > containerRectRef.current.right)
newTranslateX -= right - containerRectRef.current.right;
if (left < containerRectRef.current.left)
newTranslateX += containerRectRef.current.left - left;
translateX.current = newTranslateX;
translateY.current = newTranslateY;
elementRef.current.style.transform = `translate(${newTranslateX}px, ${newTranslateY}px)`;
}, [containerRef, elementRef, enabled]);
let [resizeRef, resizeObserver] = useResizeObserver(adjustTransform);
resizeRef(containerRef?.current);
React.useEffect(
() => () => {
resizeObserver?.disconnect();
},
[resizeObserver],
);
useEventListener(
'resize',
() => {
adjustTransform();
if (null != translateX.current && null != translateY.current)
setTransform(
`translate(${translateX.current}px, ${translateY.current}px)`,
);
},
getWindow(),
);
let [transform, setTransform] = React.useState('');
let onPointerMove = React.useRef((event) => {
if (!elementRef.current) return;
let newTranslateX = event.clientX - grabOffsetX.current;
let newTranslateY = event.clientY - grabOffsetY.current;
elementRef.current.style.transform = `translate(${newTranslateX}px, ${newTranslateY}px)`;
adjustTransform();
});
let originalUserSelect = React.useRef('');
let onPointerDown = React.useCallback(
(e) => {
if (!elementRef.current || 0 !== e.button || !enabled) return;
let [x, y] = getTranslateValuesFromElement(elementRef.current);
grabOffsetX.current = e.clientX - x;
grabOffsetY.current = e.clientY - y;
originalUserSelect.current = elementRef.current.style.userSelect;
elementRef.current.style.userSelect = 'none';
let ownerDocument = elementRef.current.ownerDocument || document;
ownerDocument.addEventListener('pointermove', onPointerMove.current);
ownerDocument.addEventListener(
'pointerup',
() => {
setTransform(
`translate(${translateX.current}px, ${translateY.current}px)`,
);
ownerDocument.removeEventListener(
'pointermove',
onPointerMove.current,
);
if (elementRef.current)
elementRef.current.style.userSelect = originalUserSelect.current;
},
{
once: true,
},
);
},
[elementRef, enabled],
);
return {
onPointerDown,
transform,
};
};