UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

106 lines (105 loc) 3.93 kB
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, }; };