@wix/design-system
Version:
@wix/design-system
100 lines • 3.7 kB
JavaScript
import { useEffect, useRef, useCallback } from 'react';
import { useDragLayer } from 'react-dnd';
const SCROLL_ZONE_SIZE = 20;
const SCROLL_SPEED = 10;
const useDragLayerAutoScroll = (containerRef) => {
const { isDragging, currentOffset } = useDragLayer(monitor => ({
isDragging: monitor.isDragging(),
currentOffset: monitor.getClientOffset(),
}));
const animationFrame = useRef(null);
const getScrollContainer = useCallback(() => {
const startNode = containerRef && containerRef.current;
if (!startNode) {
return window;
}
// The virtualized container is itself the scroller, so check the ref node
// before walking up. Non-virtualized renders an unscrolled wrapper here,
// so the walk falls through to an ancestor scroller (or window) as before.
const isScrollable = (el) => {
const { overflowY } = window.getComputedStyle(el);
return ((overflowY === 'auto' || overflowY === 'scroll') &&
el.scrollHeight > el.clientHeight);
};
if (isScrollable(startNode)) {
return startNode;
}
let node = startNode.parentElement;
while (node && node !== document.body) {
if (isScrollable(node)) {
return node;
}
node = node.parentElement;
}
return window;
}, [containerRef]);
const scroll = useCallback(() => {
if (!isDragging || !currentOffset) {
animationFrame.current = requestAnimationFrame(scroll);
return;
}
const container = getScrollContainer();
if (!container) {
animationFrame.current = requestAnimationFrame(scroll);
return;
}
const { y } = currentOffset;
let scrollChange = 0;
if (container === window) {
const viewportHeight = window.innerHeight;
if (y < SCROLL_ZONE_SIZE) {
scrollChange = -SCROLL_SPEED;
}
else if (y > viewportHeight - SCROLL_ZONE_SIZE) {
scrollChange = SCROLL_SPEED;
}
if (scrollChange !== 0) {
window.scrollBy(0, scrollChange);
}
animationFrame.current = requestAnimationFrame(scroll);
}
else if (container instanceof HTMLElement) {
const rect = container.getBoundingClientRect();
if (y < rect.top + SCROLL_ZONE_SIZE) {
scrollChange = -SCROLL_SPEED;
}
else if (y > rect.bottom - SCROLL_ZONE_SIZE) {
scrollChange = SCROLL_SPEED;
}
if (scrollChange !== 0) {
container.scrollTop += scrollChange;
}
animationFrame.current = requestAnimationFrame(scroll);
}
}, [isDragging, currentOffset, getScrollContainer]);
useEffect(() => {
if (isDragging) {
animationFrame.current = requestAnimationFrame(scroll);
}
else {
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
animationFrame.current = null;
}
}
return () => {
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
animationFrame.current = null;
}
};
}, [isDragging, scroll]);
return null;
};
export { useDragLayerAutoScroll };
const AutoScroller = ({ containerRef }) => {
useDragLayerAutoScroll(containerRef);
return null;
};
export default AutoScroller;
//# sourceMappingURL=AutoScroller.js.map