Merge pull request #174 from life-itself/fix/toc-behavior
Adjusted ToC behavior on scroll + minor style change
This commit is contained in:
commit
72080c50f5
|
|
@ -1,35 +1,43 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
/* Creates an Intersection Observer to keep track of headings intersecting
|
||||
* a section of the viewport defined by the rootMargin */
|
||||
const getIntersectionObserver = (callback) => {
|
||||
return;
|
||||
};
|
||||
|
||||
const useHeadingsObserver = () => {
|
||||
const [activeHeading, setActiveHeading] = useState("");
|
||||
const [observer, setObserver] = useState(null);
|
||||
|
||||
const activeHeading = "";
|
||||
const timeoutId = null;
|
||||
|
||||
/* Runs only after the first render, in order to preserve the observer
|
||||
* between component rerenderings (e.g. after activeHeading change). */
|
||||
* between component rerenderings. */
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
// entries.forEach((entry) => {
|
||||
// if (entry.isIntersecting) {
|
||||
// setActiveHeading(entry.target.id);
|
||||
// }
|
||||
// });
|
||||
// highlight only the first intersecting heading in the ToC
|
||||
const firstIntersectingHeading = entries.find(
|
||||
(entry) => entry.isIntersecting
|
||||
);
|
||||
if (firstIntersectingHeading) {
|
||||
setActiveHeading(firstIntersectingHeading.target.id);
|
||||
const newActiveHeading = firstIntersectingHeading?.target.id;
|
||||
if (!newActiveHeading || activeHeading === newActiveHeading) {
|
||||
return;
|
||||
}
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
timeoutId = setTimeout(() => {
|
||||
// remove highlight of the previous active heading in the ToC
|
||||
document
|
||||
.querySelector(`.toc-link[href="#${activeHeading}"]`)
|
||||
?.classList.remove("active");
|
||||
|
||||
// add highlight to the new active heading in the ToC
|
||||
document
|
||||
.querySelector(`.toc-link[href="#${newActiveHeading}"]`)
|
||||
?.classList.add("active");
|
||||
|
||||
activeHeading = newActiveHeading;
|
||||
}, 300);
|
||||
},
|
||||
{
|
||||
root: null,
|
||||
threshold: 0.55,
|
||||
rootMargin: "-65px 0% -85% 0%", // 65px is a navbar height
|
||||
}
|
||||
);
|
||||
|
|
@ -41,21 +49,6 @@ const useHeadingsObserver = () => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeHeading) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tocLink = document.querySelector(
|
||||
`.toc-link[href="#${activeHeading}"]`
|
||||
);
|
||||
tocLink.classList.add("active");
|
||||
|
||||
return () => {
|
||||
tocLink.classList.remove("active");
|
||||
};
|
||||
}, [activeHeading]);
|
||||
|
||||
return observer;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -90,19 +90,25 @@ body {
|
|||
@apply p-0;
|
||||
}
|
||||
|
||||
.toc-link {
|
||||
@apply transition-colors;
|
||||
}
|
||||
|
||||
/* link (a) element */
|
||||
.toc-item .toc-link {
|
||||
@apply text-sm text-slate-400 no-underline break-normal
|
||||
.toc-link {
|
||||
@apply transition-colors relative text-sm text-slate-400 no-underline break-normal;
|
||||
}
|
||||
|
||||
.toc-item .toc-link:not(.active) {
|
||||
@apply hover:text-white
|
||||
.toc-link:not(.active) {
|
||||
@apply hover:text-white;
|
||||
}
|
||||
|
||||
.toc-link.active {
|
||||
@apply text-yellow-li
|
||||
@apply text-yellow-li;
|
||||
}
|
||||
|
||||
.toc-link::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
@apply transition-opacity opacity-0 border-l border-yellow-li h-[110%] -top-[5%] -left-[0.5rem];
|
||||
}
|
||||
|
||||
.toc-link.active::before {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue