-
-
Notifications
You must be signed in to change notification settings - Fork 459
/
Copy pathuseTocHighlight.tsx
82 lines (68 loc) · 2.14 KB
/
useTocHighlight.tsx
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
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {useState, useRef, useEffect} from 'react';
const TOP_OFFSET = 85;
export function getHeaderAnchors(): HTMLAnchorElement[] {
return Array.prototype.filter.call(
document.getElementsByClassName('mdx-header-anchor'),
function (testElement) {
return (
testElement.parentNode.nodeName === 'H1' ||
testElement.parentNode.nodeName === 'H2' ||
testElement.parentNode.nodeName === 'H3'
);
}
);
}
/**
* Sets up Table of Contents highlighting.
*/
export function useTocHighlight() {
const [currentIndex, setCurrentIndex] = useState<number>(0);
const timeoutRef = useRef<number | null>(null);
useEffect(() => {
function updateActiveLink() {
const pageHeight = document.body.scrollHeight;
const scrollPosition = window.scrollY + window.innerHeight;
const headersAnchors = getHeaderAnchors();
if (scrollPosition >= 0 && pageHeight - scrollPosition <= 0) {
// Scrolled to bottom of page.
setCurrentIndex(headersAnchors.length - 1);
return;
}
let index = -1;
while (index < headersAnchors.length - 1) {
const headerAnchor = headersAnchors[index + 1];
const {top} = headerAnchor.getBoundingClientRect();
if (top >= TOP_OFFSET) {
break;
}
index += 1;
}
setCurrentIndex(Math.max(index, 0));
}
function throttledUpdateActiveLink() {
if (timeoutRef.current === null) {
timeoutRef.current = window.setTimeout(() => {
timeoutRef.current = null;
updateActiveLink();
}, 100);
}
}
document.addEventListener('scroll', throttledUpdateActiveLink);
document.addEventListener('resize', throttledUpdateActiveLink);
updateActiveLink();
return () => {
if (timeoutRef.current != null) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
document.removeEventListener('scroll', throttledUpdateActiveLink);
document.removeEventListener('resize', throttledUpdateActiveLink);
};
}, []);
return {
currentIndex,
};
}