Skip to content

Instantly share code, notes, and snippets.

@julenwang
Last active October 22, 2021 09:37
Show Gist options
  • Save julenwang/be6f6d0f5e38d0ba598c121b5ba45b14 to your computer and use it in GitHub Desktop.
Save julenwang/be6f6d0f5e38d0ba598c121b5ba45b14 to your computer and use it in GitHub Desktop.
useArbitraryDrag.ts
import { CSSProperties, MutableRefObject, RefCallback, useCallback, useMemo, useRef, useState } from 'react';
import { useDrag } from 'react-dnd';
import { transformPxToRemString } from '@/utils/doms';
const initOffset = { x: 0, y: 0 };
const useArbitraryDrag = (): {
ref: RefCallback<HTMLElement>;
style: CSSProperties;
} => {
const [offset, setOffset] = useState(initOffset);
const dragElementRef = useRef<HTMLElement>(null) as MutableRefObject<HTMLElement | null>;
const initCoordRef = useRef(new DOMRect());
const [{ differenceOffset, isDragging }, ref] = useDrag(() => ({
type: Symbol('useArbitraryDrag'),
end: (_item, monitor) => {
const initCoord = initCoordRef.current;
const sourceClientOffset = monitor.getSourceClientOffset();
setOffset(() => {
let x = sourceClientOffset!.x;
let y = sourceClientOffset!.y;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + initCoord.width > window.innerWidth) x = window.innerWidth - initCoord.width;
if (y + initCoord.height > window.innerHeight) y = window.innerHeight - initCoord.height;
return { x: x - initCoord!.x, y: y - initCoord!.y };
});
},
collect: (monitor) => ({
differenceOffset: monitor.getDifferenceFromInitialOffset(),
isDragging: monitor.isDragging(),
}),
}));
const refCallback = useCallback(
(element: HTMLElement | null) => {
dragElementRef.current = element;
if (element) {
initCoordRef.current = element.getBoundingClientRect();
}
ref(element);
},
[dragElementRef, ref]
);
const finalCoord = useMemo(() => {
return {
x: isDragging ? `${offset.x + differenceOffset!.x}px` : `${transformPxToRemString(offset.x)}`,
y: isDragging ? `${offset.y + differenceOffset!.y}px` : `${transformPxToRemString(offset.y)}`,
};
}, [differenceOffset, isDragging, offset]);
return useMemo(
() => ({
style: {
transform: `translate(${finalCoord.x},${finalCoord.y})`,
},
ref: refCallback,
}),
[finalCoord.x, finalCoord.y, refCallback]
);
};
export default useArbitraryDrag;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment