Files
leonpan-assets/src/component/FileManager/Dnd/useDndScrolling.ts

73 lines
2.1 KiB
TypeScript
Raw Normal View History

2025-10-19 13:31:11 +00:00
import { useRef } from "react";
import { throttle } from "lodash";
const threshold = 0.1;
const useDragScrolling = (containers: string[]) => {
const isScrolling = useRef(false);
const targets = containers.map((id) => document.querySelector(id) as HTMLElement);
const rects = useRef<DOMRect[]>([]);
const goDown = (target: HTMLElement) => {
return () => {
target.scrollTop += 5;
const { offsetHeight, scrollTop, scrollHeight } = target;
const isScrollEnd = offsetHeight + scrollTop >= scrollHeight;
if (isScrolling.current && !isScrollEnd) {
window.requestAnimationFrame(goDown(target));
}
};
};
const goUp = (target: HTMLElement) => {
return () => {
target.scrollTop -= 5;
if (isScrolling.current && target.scrollTop > 0) {
window.requestAnimationFrame(goUp(target));
}
};
};
const onDragOver = (event: MouseEvent) => {
// detect if mouse is in any rect
rects.current.forEach((rect, index) => {
if (event.clientX < rect.left || event.clientX > rect.right) {
isScrolling.current = false;
return;
}
const height = rect.bottom - rect.top;
if (event.clientY > rect.top && event.clientY < rect.top + threshold * height) {
isScrolling.current = true;
window.requestAnimationFrame(goUp(targets[index]));
} else if (event.clientY < rect.bottom && event.clientY > rect.bottom - threshold * height) {
isScrolling.current = true;
window.requestAnimationFrame(goDown(targets[index]));
} else {
isScrolling.current = false;
}
});
};
const throttleOnDragOver = throttle(onDragOver, 300);
const addEventListenerForWindow = () => {
rects.current = targets.map((t) => t.getBoundingClientRect());
window.addEventListener("dragover", throttleOnDragOver, false);
};
const removeEventListenerForWindow = () => {
window.removeEventListener("dragover", throttleOnDragOver, false);
isScrolling.current = false;
};
return {
addEventListenerForWindow,
removeEventListenerForWindow,
};
};
export default useDragScrolling;