Skip to content

Commit

Permalink
Active state on TOC
Browse files Browse the repository at this point in the history
  • Loading branch information
JSerZANP committed May 5, 2024
1 parent 2a1c318 commit 7bceb03
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
73 changes: 72 additions & 1 deletion examples/web/components/TOC.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,76 @@
"use client";
import { useEffect, useRef } from "react";
import styles from "./TOC.module.css";
import { debounce } from "./debouce";
import { filterNonNull } from "./filterNonNull";

export function TOC({ children }: { children: React.ReactNode }) {
return <div className={styles.toc}>{children}</div>;
const refAside = useRef<HTMLDivElement>(null);

useEffect(() => {
const aside = refAside.current;
if (aside == null) return;
const list = filterNonNull(
Array.from(aside.querySelectorAll("li")).map((li) => {
const id = li
.querySelector("a")
?.getAttribute("href")
?.replace("#", "");
if (id == null) return null;
const el = document.getElementById(decodeURIComponent(id));
return {
id,
li,
el,
};
})
);
const highlight = () => {
console.log("highlight");
const scrollTop = window.scrollY;
const active = list.findLast(({ el }, i) => {
if (el == null) return i === 0;
return el.offsetTop <= scrollTop + 100;
});
list.forEach((item) => {
const { li, el, id } = item;
// if el is empty, try to fetch it again
// because the target might just show up
if (el == null) {
item.el = document.getElementById(id);
}

// if el is still empty, disable it
if (el == null && id != "") {
li.classList.remove("enabled");
} else {
li.classList.add("enabled");
}

li.classList.remove("active");
});

if (active != null) {
active.li.classList.add("active");
}
};

const debouncedHighlight = debounce(highlight, 100);

window.addEventListener("scroll", debouncedHighlight, {
passive: true,
});

debouncedHighlight();

return () => {
window.removeEventListener("scroll", debouncedHighlight);
};
}, []);

return (
<div ref={refAside} className={styles.toc}>
{children}
</div>
);
}
10 changes: 10 additions & 0 deletions examples/web/components/debouce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function debounce(func: (...args: unknown[]) => unknown, wait = 1000) {
let timer: number = 0;

return function (this: any, ...args: unknown[]) {
clearTimeout(timer);
timer = window.setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
3 changes: 3 additions & 0 deletions examples/web/components/filterNonNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function filterNonNull<T>(arr: Array<T>): Array<NonNullable<T>> {
return arr.filter((item): item is NonNullable<T> => Boolean(item));
}

0 comments on commit 7bceb03

Please sign in to comment.