Initial website deployment
All checks were successful
Deploy Website / build-and-deploy (push) Successful in 1m44s
All checks were successful
Deploy Website / build-and-deploy (push) Successful in 1m44s
This commit is contained in:
135
static/js/site.js
Normal file
135
static/js/site.js
Normal file
@@ -0,0 +1,135 @@
|
||||
(() => {
|
||||
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();
|
||||
})();
|
||||
Reference in New Issue
Block a user