// a prose page's list follows the section you're reading. clicking one (or following a #link)
// picks it and lights it up in the page, and that pick holds even when the page can't scroll
// it to the top, until you scroll yourself.
import { tree } from "./tree";
const list = document.querySelector<HTMLElement>(".pane:not(.cfg-pane) > [data-tree]");
if (list) setup(list);
function setup(list: HTMLElement) {
const outline = tree(list);
const keys = [...list.querySelectorAll<HTMLElement>("[data-to]")].map((a) => a.dataset.to!).filter(Boolean);
const headings = keys.map((k) => document.getElementById(k)).filter((h): h is HTMLElement => !!h);
let pinned: string | null = null;
let tick = 0;
const spy = () => {
tick = 0;
if (pinned !== null) return;
const line = window.innerHeight * 0.3;
let cur = "";
for (const h of headings) if (h.getBoundingClientRect().top < line) cur = h.id;
outline.select(cur);
};
const pin = (key: string) => {
pinned = key;
outline.select(key);
const h = key && document.getElementById(key);
if (!h) return;
h.classList.remove("flash");
void h.offsetWidth;
h.classList.add("flash");
};
const unpin = () => {
if (pinned === null) return;
pinned = null;
spy();
};
window.addEventListener("scroll", () => (tick ||= requestAnimationFrame(spy)), { passive: true });
// only the reader's own scrolling lets the page take the list back
window.addEventListener("wheel", unpin, { passive: true });
window.addEventListener("touchmove", unpin, { passive: true });
window.addEventListener("keydown", (e) => {
if (["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(e.key) && !list.contains(e.target as Node)) unpin();
});
// a press on the page's scrollbar lands on <html>
window.addEventListener("mousedown", (e) => e.target === document.documentElement && unpin());
window.addEventListener("hashchange", () => pin(decodeURIComponent(location.hash.slice(1))));
list.addEventListener("click", (e) => {
const a = (e.target as HTMLElement).closest<HTMLAnchorElement>("a[data-to]");
if (!a) return;
if (a.dataset.to === "") {
e.preventDefault();
history.pushState(null, "", location.pathname);
window.scrollTo({ top: 0 });
}
// the hash may not change (clicking the same entry twice), so pin here too
pin(a.dataset.to!);
});
if (location.hash) pin(decodeURIComponent(location.hash.slice(1)));
else spy();
}
export {};