Skip to content

Commit 23c94ef

Browse files
Added scroll based menu highlight feature (#1085)
Co-authored-by: Beier (Bill) <bluebill1049@hotmail.com>
1 parent 29c6c58 commit 23c94ef

File tree

1 file changed

+75
-22
lines changed

1 file changed

+75
-22
lines changed

src/components/Menu/Menu.tsx

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,50 @@
1+
import { useEffect, useState } from "react"
12
import Link from "next/link"
2-
import colors from "../../styles/colors"
33
import styles from "./SideMenu.module.css"
44
import typographyStyles from "../../styles/typography.module.css"
5-
import { useRouter } from "next/router"
5+
import colors from "../../styles/colors"
66
import { Pages } from "../../types/types"
77

88
function Menu({ pages = [] }: { pages: Pages }) {
9-
const router = useRouter()
10-
const { asPath: pathname } = router
9+
const [activeSection, setActiveSection] = useState<string>("")
10+
11+
useEffect(() => {
12+
const handleScroll = () => {
13+
const sections = pages.flatMap((page) =>
14+
[page, ...(page.pages || [])].map((p) => ({
15+
id: p.pathname.replace("#", ""),
16+
top:
17+
document.getElementById(p.pathname.replace("#", ""))?.offsetTop ||
18+
0,
19+
}))
20+
)
21+
22+
const currentSection = sections.reduce((acc, section) => {
23+
const { id, top } = section
24+
if (window.scrollY >= top - 100) {
25+
console.log(id)
26+
return id
27+
}
28+
return acc
29+
}, "")
30+
31+
setActiveSection(currentSection)
32+
}
33+
34+
window.addEventListener("scroll", handleScroll)
35+
36+
handleScroll()
37+
38+
return () => window.removeEventListener("scroll", handleScroll)
39+
}, [pages])
40+
41+
const scrollToSection = (sectionId: string, event: React.MouseEvent) => {
42+
event.preventDefault()
43+
const element = document.getElementById(sectionId.replace("#", ""))
44+
if (element) {
45+
element.scrollIntoView({ behavior: "smooth" })
46+
}
47+
}
1148

1249
return (
1350
<aside className={styles.menu}>
@@ -26,41 +63,57 @@ function Menu({ pages = [] }: { pages: Pages }) {
2663

2764
<ul className="scrollArea">
2865
{pages.map((page) => {
29-
const isActive = pathname === page.pathname
66+
const isActive = activeSection === page.pathname.replace("#", "")
67+
const hasActiveChild = page.pages?.some(
68+
(subPage) => activeSection === subPage.pathname.replace("#", "")
69+
)
3070

3171
return (
3272
<li
3373
key={page.pathname}
34-
className={styles.menuItem}
74+
className={`${styles.menuItem} ${
75+
isActive || hasActiveChild ? styles.activeParent : ""
76+
}`}
3577
style={{
3678
display: page?.pages ? "block" : "flex",
3779
}}
3880
>
39-
<code aria-hidden className={styles.code}>{`</>`}</code>
40-
<Link
41-
className={isActive ? styles.isActive : ""}
81+
<code aria-hidden className={styles.code}>
82+
{`</>`}
83+
</code>
84+
<a
4285
href={page.pathname}
86+
className={isActive ? styles.isActive : ""}
87+
onClick={(e) => scrollToSection(page.pathname, e)}
4388
>
4489
{page.name}
45-
</Link>
90+
</a>
4691

4792
{page?.pages && (
4893
<ul>
49-
{page.pages.map((page) => {
50-
const isActive = pathname === page.pathname
94+
{page.pages.map((subPage) => {
95+
const isSubActive =
96+
activeSection === subPage.pathname.replace("#", "")
5197

5298
return (
53-
<li key={page.pathname} className={styles.menuItem}>
54-
<code
55-
aria-hidden
56-
className={styles.code}
57-
>{`</>`}</code>{" "}
58-
<Link
59-
className={isActive ? styles.isActive : ""}
60-
href={page.pathname}
99+
<li
100+
key={subPage.pathname}
101+
className={`${styles.menuItem} ${
102+
isSubActive ? styles.activeItem : ""
103+
}`}
104+
>
105+
<code aria-hidden className={styles.code}>
106+
{`</>`}
107+
</code>
108+
<a
109+
href={subPage.pathname}
110+
className={isSubActive ? styles.isActive : ""}
111+
onClick={(e) =>
112+
scrollToSection(subPage.pathname, e)
113+
}
61114
>
62-
{page.name}
63-
</Link>
115+
{subPage.name}
116+
</a>
64117
</li>
65118
)
66119
})}

0 commit comments

Comments
 (0)