-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
use-scroll.ts
101 lines (88 loc) · 2.68 KB
/
use-scroll.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { type RefObject, useCallback, useEffect, useState } from 'react';
type ScrollPosition = {
x: number;
y: number;
};
/**
* Custom hook for tracking and controlling the scroll position of an element.
*
* This hook provides two main functionalities:
* 1. Tracks the current scroll position (`x` and `y` coordinates) of a specified element.
* 2. Provides a function to programmatically scroll the element to specific coordinates (`x`, `y`).
*
* @param ref A reference to the HTML element whose scroll position you want to track or control.
*
* @returns A tuple containing:
* - The current scroll position object, containing `x` (horizontal scroll) and `y` (vertical scroll).
* - A function `scrollTo` that allows you to scroll the element to a specified position.
*
* Example usage:
* ```tsx
* import { useScroll } from "@codiume/hooks";
*
* function Demo() {
* const ref = useRef<HTMLDivElement>(null);
* const [{ x, y }, scrollTo] = useScroll(ref);
*
* return (
* <div>
* <p>Scroll position: x: {x}, y: {y}</p>
* <button onClick={() => scrollTo({ y: 0 })}>Scroll to top</button>
* <div
* ref={ref}
* style={{ height: "300px", width: "300px", overflow: "auto" }}
* >
* <div style={{ height: "1000px", width: "1000px" }}>Scroll me!</div>
* </div>
* </div>
* );
* }
* ```
*
* In this example:
* - `ref` is used to track a scrollable container's position.
* - The scroll position (`x`, `y`) is shown on the screen.
* - The `scrollTo` function allows you to scroll the container to a specific position (`y: 0` in this case).
*/
export function useScroll(ref: RefObject<HTMLElement>) {
const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({
x: 0,
y: 0
});
const scrollTo = useCallback(
(
{ x, y }: Partial<ScrollPosition>,
options: ScrollOptions = { behavior: 'smooth' }
) => {
const el = ref.current;
if (!el) return;
const scrollOptions: ScrollToOptions = { ...options };
if (typeof x === 'number') {
scrollOptions.left = x;
}
if (typeof y === 'number') {
scrollOptions.top = y;
}
el.scrollTo(scrollOptions);
},
[ref]
);
useEffect(() => {
const el = ref.current;
if (!el) return;
const abortController = new AbortController();
const handler = () =>
setScrollPosition({
x: el.scrollLeft,
y: el.scrollTop
});
el.addEventListener('scroll', handler, {
passive: true,
signal: abortController.signal
});
return () => {
abortController.abort();
};
}, [ref]);
return [scrollPosition, scrollTo] as const;
}