1 import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';
2 import { useEffect, useState } from 'react';
5 * This hook will allow any element to be scrolled by dragging it
9 export function useDragToScroll(ref: RefObject<HTMLElement>, { defaultCursor } = { defaultCursor: 'default' }) {
10 let initialPosition = { scrollTop: 0, scrollLeft: 0, mouseX: 0, mouseY: 0 };
12 // We check if element is scrollable or not. So we only show the grab if it can be grab
13 const [isScrollable, setIsScrollable] = useState(false);
14 const scrollHeight = ref?.current?.scrollHeight;
15 const clientHeight = ref?.current?.clientHeight;
18 if (scrollHeight && clientHeight) {
19 setIsScrollable(scrollHeight > clientHeight);
21 }, [scrollHeight, clientHeight]);
27 ref.current.style.cursor = isScrollable ? 'grab' : defaultCursor;
30 const handleMouseMove = (event: MouseEvent) => {
34 ref.current.scrollTop = initialPosition.scrollTop - (event.clientY - initialPosition.mouseY);
35 ref.current.scrollLeft = initialPosition.scrollLeft - (event.clientX - initialPosition.mouseX);
38 const handleMouseUp = () => {
40 ref.current.style.cursor = isScrollable ? 'grab' : defaultCursor;
43 document.removeEventListener('mousemove', handleMouseMove);
44 document.removeEventListener('mouseup', handleMouseUp);
47 const onMouseDown = (event: ReactMouseEvent) => {
48 if (!ref.current || !isScrollable) {
51 // Prevent image default drop event
52 event.preventDefault();
54 scrollLeft: ref.current.scrollLeft,
55 scrollTop: ref.current.scrollTop,
56 mouseX: event.clientX,
57 mouseY: event.clientY,
60 ref.current.style.cursor = 'grabbing';
61 ref.current.style.userSelect = 'none';
63 document.addEventListener('mousemove', handleMouseMove);
64 document.addEventListener('mouseup', handleMouseUp);
68 document.removeEventListener('mousemove', handleMouseMove);
69 document.removeEventListener('mouseup', handleMouseUp);
72 return { onMouseDown };