This commit is contained in:
parent
e3ded78ba3
commit
a670600ad7
4 changed files with 124 additions and 9 deletions
|
@ -17,12 +17,16 @@ const minDepth = Math.min(...headings.map((heading) => heading.depth));
|
||||||
<div class="toc-wrapper">
|
<div class="toc-wrapper">
|
||||||
<slot />
|
<slot />
|
||||||
<div class="toc">
|
<div class="toc">
|
||||||
Table of contents
|
<h3 class="title">Table of contents</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{headings.map((heading) => {
|
{headings.map((heading) => {
|
||||||
return (
|
return (
|
||||||
<li style={`padding-left: ${(heading.depth - minDepth) * 10}px;`}>
|
<li>
|
||||||
<a href={`#${heading.slug}`}>{heading.text}</a>
|
<a href={`#${heading.slug}`} id={`${heading.slug}-link`}>
|
||||||
|
<span style={`padding-left: ${(heading.depth - minDepth) * 10}px;`}>
|
||||||
|
{heading.text}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -35,8 +39,86 @@ const minDepth = Math.min(...headings.map((heading) => heading.depth));
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script define:vars={{ toc, headings }}>
|
||||||
document.addEventListener("scroll", (doc, evt) => {
|
if (toc) {
|
||||||
console.log("SHIET");
|
const headingTags = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
||||||
|
const headingsMap = new Map([...headings.map((heading) => [heading.slug, new Set()])]);
|
||||||
|
const reverseHeadingMap = new Map();
|
||||||
|
const linkMap = new Map();
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const visibleElements = new Map();
|
||||||
|
|
||||||
|
// Register links
|
||||||
|
for (const heading of headings) {
|
||||||
|
const link = document.getElementById(`${heading.slug}-link`);
|
||||||
|
const el = document.getElementById(heading.slug);
|
||||||
|
if (link && el) {
|
||||||
|
linkMap.set(heading.slug, link);
|
||||||
|
link.addEventListener("click", (e) => {
|
||||||
|
el.scrollIntoView({ behavior: "smooth" });
|
||||||
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (!visibleElements.has(heading.slug)) {
|
||||||
|
visibleElements.set(heading.slug, new Set());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const target = entry.target;
|
||||||
|
const slug = reverseHeadingMap.get(target);
|
||||||
|
const link = linkMap.get(slug);
|
||||||
|
const associatedEls = visibleElements.get(slug);
|
||||||
|
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
// if it wasn't previously visible
|
||||||
|
// let's make the link active
|
||||||
|
if (associatedEls.size === 0) {
|
||||||
|
console.log("SHIET", link);
|
||||||
|
link.parentNode.classList.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
associatedEls.add(target);
|
||||||
|
} else {
|
||||||
|
// if it was previously visible
|
||||||
|
// check if it's the last element
|
||||||
|
if (associatedEls.size > 0) {
|
||||||
|
if (associatedEls.size === 1) link.parentNode.classList.remove("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (associatedEls.size > 0) {
|
||||||
|
associatedEls.delete(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const postContentEl = document.getElementById("post-content");
|
||||||
|
console.log("content", postContentEl);
|
||||||
|
|
||||||
|
let belongsTo;
|
||||||
|
for (const child of postContentEl.children) {
|
||||||
|
if (headingTags.has(child.tagName.toLowerCase())) {
|
||||||
|
belongsTo = child.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (belongsTo) {
|
||||||
|
const headingSet = headingsMap.get(belongsTo);
|
||||||
|
headingSet.add(child);
|
||||||
|
reverseHeadingMap.set(child, belongsTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("headings map", headingsMap);
|
||||||
|
console.log("reverse", reverseHeadingMap);
|
||||||
|
|
||||||
|
[...headingsMap.values()]
|
||||||
|
.flatMap((x) => [...x])
|
||||||
|
.forEach((x) => {
|
||||||
|
observer.observe(x);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -80,7 +80,7 @@ const datestamp = post.data.date.toLocaleDateString(undefined, {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="post-content">
|
<div class="post-content" id="post-content">
|
||||||
<Content />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
.post-title {
|
.post-title {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
margin-top: 20px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ $tocBreakpoint: variables.$breakpoint + $tocWidth;
|
||||||
.toc-wrapper {
|
.toc-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc {
|
.toc {
|
||||||
|
@ -16,7 +20,35 @@ $tocBreakpoint: variables.$breakpoint + $tocWidth;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 6px;
|
display: block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
// background-color: var(--link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
color: var(--text-color);
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
border-radius: unset;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: unset;
|
||||||
|
border-left-color: var(--link-color);
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
&:first {
|
||||||
|
border: 1px solid orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: var(--link-hover-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue