Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / components / hooks / useDragToScroll.ts
blob9ee1861df11e4809b8ef54b0f1869477ccec855e
1 import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';
2 import { useEffect, useState } from 'react';
4 /**
5  * This hook will allow any element to be scrolled by dragging it
6  * @param ref
7  * @param defaultCursor
8  */
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;
17     useEffect(() => {
18         if (scrollHeight && clientHeight) {
19             setIsScrollable(scrollHeight > clientHeight);
20         }
21     }, [scrollHeight, clientHeight]);
23     useEffect(() => {
24         if (!ref.current) {
25             return;
26         }
27         ref.current.style.cursor = isScrollable ? 'grab' : defaultCursor;
28     }, [isScrollable]);
30     const handleMouseMove = (event: MouseEvent) => {
31         if (!ref.current) {
32             return;
33         }
34         ref.current.scrollTop = initialPosition.scrollTop - (event.clientY - initialPosition.mouseY);
35         ref.current.scrollLeft = initialPosition.scrollLeft - (event.clientX - initialPosition.mouseX);
36     };
38     const handleMouseUp = () => {
39         if (ref.current) {
40             ref.current.style.cursor = isScrollable ? 'grab' : defaultCursor;
41         }
43         document.removeEventListener('mousemove', handleMouseMove);
44         document.removeEventListener('mouseup', handleMouseUp);
45     };
47     const onMouseDown = (event: ReactMouseEvent) => {
48         if (!ref.current || !isScrollable) {
49             return;
50         }
51         // Prevent image default drop event
52         event.preventDefault();
53         initialPosition = {
54             scrollLeft: ref.current.scrollLeft,
55             scrollTop: ref.current.scrollTop,
56             mouseX: event.clientX,
57             mouseY: event.clientY,
58         };
60         ref.current.style.cursor = 'grabbing';
61         ref.current.style.userSelect = 'none';
63         document.addEventListener('mousemove', handleMouseMove);
64         document.addEventListener('mouseup', handleMouseUp);
65     };
67     useEffect(() => {
68         document.removeEventListener('mousemove', handleMouseMove);
69         document.removeEventListener('mouseup', handleMouseUp);
70     }, []);
72     return { onMouseDown };