136 lines
3.6 KiB
JavaScript
136 lines
3.6 KiB
JavaScript
(() => {
|
|
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
|
|
|
|
function setupToc() {
|
|
const headings = $$(".post-content h2[id], .post-content h3[id]");
|
|
const links = $$("#TableOfContents a");
|
|
if (!headings.length || !links.length) return;
|
|
|
|
let queued = false;
|
|
|
|
function update() {
|
|
const y = window.scrollY + window.innerHeight * 0.35;
|
|
let activeId = headings[0].id;
|
|
|
|
for (const heading of headings) {
|
|
if (heading.offsetTop > y) break;
|
|
activeId = heading.id;
|
|
}
|
|
|
|
for (const link of links) {
|
|
link.classList.toggle(
|
|
"active",
|
|
decodeURIComponent(link.hash.slice(1)) === activeId
|
|
);
|
|
}
|
|
|
|
queued = false;
|
|
}
|
|
|
|
function queue() {
|
|
if (queued) return;
|
|
queued = true;
|
|
window.requestAnimationFrame(update);
|
|
}
|
|
|
|
window.addEventListener("scroll", queue, { passive: true });
|
|
window.addEventListener("resize", queue);
|
|
update();
|
|
}
|
|
|
|
function setupPostTags() {
|
|
const list = document.querySelector("[data-post-list]");
|
|
if (!list) return;
|
|
|
|
const rows = $$(".post-row", list);
|
|
const links = $$("[data-tag]");
|
|
const status = document.querySelector("[data-tag-filter-status]");
|
|
const clear = status?.querySelector("a");
|
|
|
|
function setUrl(tag) {
|
|
const url = new URL(window.location.href);
|
|
if (tag) {
|
|
url.searchParams.set("tag", tag);
|
|
} else {
|
|
url.searchParams.delete("tag");
|
|
}
|
|
window.history.pushState(null, "", `${url.pathname}${url.search}`);
|
|
}
|
|
|
|
function applyTag(tag) {
|
|
for (const link of links) {
|
|
link.classList.toggle("active", link.dataset.tag === tag);
|
|
}
|
|
|
|
let visible = 0;
|
|
for (const row of rows) {
|
|
const tags = JSON.parse(row.dataset.tags || "[]");
|
|
const matched = !tag || tags.includes(tag);
|
|
row.hidden = !matched;
|
|
if (matched) visible += 1;
|
|
}
|
|
|
|
if (status) {
|
|
const name = status.querySelector("[data-tag-filter-name]");
|
|
if (name) name.textContent = tag ? `[${tag}]` : "";
|
|
status.hidden = !tag;
|
|
status.classList.toggle("is-empty", visible === 0);
|
|
}
|
|
}
|
|
|
|
for (const link of links) {
|
|
link.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
const nextTag = link.classList.contains("active") ? null : link.dataset.tag;
|
|
applyTag(nextTag);
|
|
setUrl(nextTag);
|
|
});
|
|
}
|
|
|
|
if (clear) {
|
|
clear.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
applyTag(null);
|
|
setUrl(null);
|
|
});
|
|
}
|
|
|
|
applyTag(new URLSearchParams(window.location.search).get("tag"));
|
|
}
|
|
|
|
function setupClickableRows() {
|
|
const rows = $$("[data-row-href]");
|
|
|
|
function openRow(row) {
|
|
const href = row.dataset.rowHref;
|
|
if (href) window.location.href = href;
|
|
}
|
|
|
|
for (const row of rows) {
|
|
row.addEventListener("click", (event) => {
|
|
if (event.target.closest("a")) return;
|
|
openRow(row);
|
|
});
|
|
|
|
row.addEventListener("keydown", (event) => {
|
|
if (event.key !== "Enter" && event.key !== " ") return;
|
|
event.preventDefault();
|
|
openRow(row);
|
|
});
|
|
|
|
row.addEventListener("pointerdown", (event) => {
|
|
if (event.target.closest(".item-links a")) return;
|
|
row.classList.add("is-pressed");
|
|
});
|
|
|
|
for (const eventName of ["pointerup", "pointercancel", "pointerleave", "blur"]) {
|
|
row.addEventListener(eventName, () => row.classList.remove("is-pressed"));
|
|
}
|
|
}
|
|
}
|
|
|
|
setupToc();
|
|
setupPostTags();
|
|
setupClickableRows();
|
|
})();
|