[site/ToC][s]: smooth scroll behavior
This commit is contained in:
parent
41cf2c33de
commit
e63bec7fd2
|
|
@ -1,35 +1,43 @@
|
||||||
import { useState, useEffect } from "react";
|
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 useHeadingsObserver = () => {
|
||||||
const [activeHeading, setActiveHeading] = useState("");
|
|
||||||
const [observer, setObserver] = useState(null);
|
const [observer, setObserver] = useState(null);
|
||||||
|
|
||||||
|
const activeHeading = "";
|
||||||
|
const timeoutId = null;
|
||||||
|
|
||||||
/* Runs only after the first render, in order to preserve the observer
|
/* Runs only after the first render, in order to preserve the observer
|
||||||
* between component rerenderings (e.g. after activeHeading change). */
|
* between component rerenderings. */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
// entries.forEach((entry) => {
|
// highlight only the first intersecting heading in the ToC
|
||||||
// if (entry.isIntersecting) {
|
|
||||||
// setActiveHeading(entry.target.id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
const firstIntersectingHeading = entries.find(
|
const firstIntersectingHeading = entries.find(
|
||||||
(entry) => entry.isIntersecting
|
(entry) => entry.isIntersecting
|
||||||
);
|
);
|
||||||
if (firstIntersectingHeading) {
|
const newActiveHeading = firstIntersectingHeading?.target.id;
|
||||||
setActiveHeading(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;
|
||||||
|
}, 250);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
root: null,
|
root: null,
|
||||||
threshold: 0.55,
|
|
||||||
rootMargin: "-65px 0% -85% 0%", // 65px is a navbar height
|
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;
|
return observer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,19 +90,25 @@ body {
|
||||||
@apply p-0;
|
@apply p-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc-link {
|
|
||||||
@apply transition-colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* link (a) element */
|
/* link (a) element */
|
||||||
.toc-item .toc-link {
|
.toc-link {
|
||||||
@apply text-sm text-slate-400 no-underline break-normal
|
@apply transition-colors relative text-sm text-slate-400 no-underline break-normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc-item .toc-link:not(.active) {
|
.toc-link:not(.active) {
|
||||||
@apply hover:text-white
|
@apply hover:text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc-link.active {
|
.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