-
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathindex.ts
More file actions
50 lines (44 loc) · 1.52 KB
/
index.ts
File metadata and controls
50 lines (44 loc) · 1.52 KB
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
import React, { useState, useLayoutEffect } from "react";
import throttle from "lodash/fp/throttle";
export interface useScrollSpyParams {
activeSectionDefault?: number;
offsetPx?: number;
sectionElementRefs: React.RefObject<HTMLElement>[];
throttleMs?: number;
scrollingElement?: React.RefObject<HTMLElement>;
}
export default ({
activeSectionDefault = 0,
offsetPx = 0,
scrollingElement,
sectionElementRefs = [],
throttleMs = 100,
}: useScrollSpyParams): number | undefined => {
const [activeSection, setActiveSection] = useState(activeSectionDefault);
const handle = throttle(throttleMs, () => {
let currentSectionId = activeSection;
for (let i = 0; i < sectionElementRefs.length; i += 1) {
const section = sectionElementRefs[i].current;
// Needs to be a valid DOM Element
if (!section || !(section instanceof Element)) continue;
// GetBoundingClientRect returns values relative to viewport
if (section.getBoundingClientRect().top + offsetPx < 0) {
currentSectionId = i;
continue;
}
// No need to continue loop, if last element has been detected
break;
}
setActiveSection(currentSectionId);
});
useLayoutEffect(() => {
const scrollable = scrollingElement?.current ?? window;
scrollable.addEventListener("scroll", handle);
// Run initially
handle();
return () => {
scrollable.removeEventListener("scroll", handle);
};
}, [sectionElementRefs, offsetPx, scrollingElement, handle]);
return activeSection;
};