forked from nodejs/nodejs.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnavigation.tsx
More file actions
75 lines (66 loc) · 2.37 KB
/
navigation.tsx
File metadata and controls
75 lines (66 loc) · 2.37 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
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
import React, { useState, useRef } from 'react';
import NavigationSection from './navigation-section';
import { NavigationSectionData, NavigationSectionItem } from '../types';
import { isSmallScreen } from '../util/isScreenWithinWidth';
import { scrollTo, calcNavScrollParams } from '../util/scrollTo';
interface Props {
sections: NavigationSectionData;
currentSlug: string;
}
const Navigation = ({ sections, currentSlug }: Props): JSX.Element => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [hasScrolled, setHasScrolled] = useState<boolean>(false);
const navElement = useRef<HTMLElement | null>(null);
const toggle = (): void => setIsOpen(!isOpen);
const onItemClick = (): void => {
if (isSmallScreen()) {
toggle();
}
};
const autoScroll = async (height: number): Promise<void> => {
if (isOpen && !hasScrolled && navElement.current) {
const { newScrollPos, scrollWindow, scrollTime } = calcNavScrollParams(height, navElement.current);
try {
await scrollTo(newScrollPos, scrollWindow, scrollTime);
setHasScrolled(true);
} catch (e) {
// TODO: follow up with appropriate error logging if any
setHasScrolled(false);
}
}
};
const className = isOpen ? 'side-nav side-nav--open' : 'side-nav';
const readSections: Set<NavigationSectionItem['slug']> = new Set();
// Assume section items up to the one currently open have been read. Track
// their unique slugs in `readSections` set.
Object.keys(sections).some((sectionKey): boolean => {
let isCurrentSlug = false;
sections[sectionKey].some((sectionItem): boolean => {
isCurrentSlug = sectionItem.slug === currentSlug;
if (!isCurrentSlug) {
readSections.add(sectionItem.slug);
}
return isCurrentSlug;
});
return isCurrentSlug;
});
return (
<nav className={className} ref={navElement}>
<button type="button" className="side-nav__open" onClick={toggle}>
Menu
</button>
{Object.keys(sections).map((sectionKey: string): JSX.Element[] => (
<NavigationSection
key={sectionKey}
title={sectionKey}
section={sections[sectionKey]}
currentSlug={currentSlug}
onItemClick={onItemClick}
readSections={readSections}
autoScroll={autoScroll}
/>
))}
</nav>
);
};
export default Navigation;