Skip to content

Commit 44abdea

Browse files
committed
feature(web): smart scroll on drag POC
1 parent df6bde8 commit 44abdea

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

packages/web/src/views/Calendar/components/Grid/MainGrid/MainGrid.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { MainGridEvents } from "./MainGridEvents";
2121
import { MainGridColumns } from "../Columns/MainGridColumns";
2222
import { selectIsDrafting } from "@web/ducks/events/selectors/draft.selectors";
2323
import { isRightClick } from "@web/common/utils/mouse/mouse.util";
24+
import { useDragEventSmartScroll } from "@web/views/Calendar/hooks/grid/useDragEventSmartScroll";
2425

2526
interface Props {
2627
dateCalcs: DateCalcs;
@@ -40,7 +41,6 @@ export const MainGrid: FC<Props> = ({
4041
weekProps,
4142
}) => {
4243
const dispatch = useAppDispatch();
43-
4444
const { component } = weekProps;
4545
const { isCurrentWeek, week, weekDays } = component;
4646
const isDrafting = useAppSelector(selectIsDrafting);
@@ -74,6 +74,8 @@ export const MainGrid: FC<Props> = ({
7474
);
7575
};
7676

77+
useDragEventSmartScroll(mainGridRef);
78+
7779
return (
7880
<StyledMainGrid id={ID_GRID_MAIN} ref={mainGridRef}>
7981
<MainGridColumns
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { selectDraft } from "@web/ducks/events/selectors/draft.selectors";
2+
import { useAppSelector } from "@web/store/store.hooks";
3+
import { MutableRefObject, useEffect, useState, useRef } from "react";
4+
5+
const SCROLL_SPEED = 10;
6+
const EDGE_THRESHOLD = 50;
7+
8+
export const useDragEventSmartScroll = (
9+
mainGridRef: MutableRefObject<HTMLDivElement | null>,
10+
) => {
11+
const draftEvent = useAppSelector(selectDraft);
12+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
13+
const scrollRef = useRef<number | null>(null);
14+
15+
useEffect(() => {
16+
if (!draftEvent) return;
17+
18+
const updateMousePosition = (event: MouseEvent) => {
19+
setMousePosition({ x: event.clientX, y: event.clientY });
20+
};
21+
22+
window.addEventListener("mousemove", updateMousePosition);
23+
24+
return () => {
25+
window.removeEventListener("mousemove", updateMousePosition);
26+
};
27+
}, [draftEvent]);
28+
29+
useEffect(() => {
30+
if (!draftEvent || !mainGridRef.current) return;
31+
32+
const container = mainGridRef.current;
33+
34+
const scrollIfNeeded = () => {
35+
if (!container) return;
36+
37+
const { top, bottom } = container.getBoundingClientRect();
38+
const { y } = mousePosition;
39+
40+
let scrollAmount = 0;
41+
42+
const isAtTop = container.scrollTop === 0;
43+
const isAtBottom =
44+
container.scrollTop + container.clientHeight >= container.scrollHeight;
45+
46+
if (y < top + EDGE_THRESHOLD && !isAtTop) {
47+
scrollAmount = -SCROLL_SPEED;
48+
} else if (y > bottom - EDGE_THRESHOLD && !isAtBottom) {
49+
scrollAmount = SCROLL_SPEED;
50+
}
51+
52+
if (scrollAmount !== 0) {
53+
container.scrollTop += scrollAmount;
54+
scrollRef.current = requestAnimationFrame(scrollIfNeeded);
55+
} else {
56+
scrollRef.current = null;
57+
}
58+
};
59+
60+
if (!scrollRef.current) {
61+
scrollRef.current = requestAnimationFrame(scrollIfNeeded);
62+
}
63+
64+
return () => {
65+
if (scrollRef.current) {
66+
cancelAnimationFrame(scrollRef.current);
67+
scrollRef.current = null;
68+
}
69+
};
70+
}, [mousePosition, draftEvent]);
71+
};

0 commit comments

Comments
 (0)