Files
brk/website_next/learn/details.js
T
2026-06-14 00:46:07 +02:00

116 lines
3.0 KiB
JavaScript

const storagePrefix = "bitview:learn-section";
/** @param {string} id */
function getStorageKey(id) {
return `${storagePrefix}:${id}`;
}
/** @param {HTMLElement} section */
function getDetails(section) {
return /** @type {HTMLDetailsElement} */ (
section.querySelector(":scope > details")
);
}
/** @param {HTMLElement} section */
function getHash(section) {
return `#${section.id}`;
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function getNavDetails(main, section) {
const anchor = main.querySelector(`nav a[href="${getHash(section)}"]`);
const summary = anchor?.parentElement;
return summary?.localName === "summary" ? summary.parentElement : null;
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function syncNavSection(main, section) {
const details = getNavDetails(main, section);
if (details) details.toggleAttribute("open", getDetails(section).open);
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function openSection(main, section) {
let current = section;
while (main.contains(current)) {
const details = current.querySelector(":scope > details");
if (details) {
/** @type {HTMLDetailsElement} */ (details).open = true;
syncNavSection(main, current);
}
const parent = current.parentElement?.closest("section[id]");
if (!parent) break;
current = /** @type {HTMLElement} */ (parent);
}
}
/** @param {HTMLElement} main */
export function initSectionDetails(main) {
const sections = [
...main.querySelectorAll("section[id]:not([data-numbered='false'])"),
].map((section) => /** @type {HTMLElement} */ (section));
for (const section of sections) {
const saved = localStorage.getItem(getStorageKey(section.id));
getDetails(section).open = saved === "1";
syncNavSection(main, section);
}
main.addEventListener("toggle", (event) => {
const details = /** @type {HTMLDetailsElement} */ (event.target);
const section = /** @type {HTMLElement} */ (details.parentElement);
if (!section.matches("article section[id]")) return;
localStorage.setItem(getStorageKey(section.id), details.open ? "1" : "0");
syncNavSection(main, section);
main.dispatchEvent(new Event("sectiontoggle"));
}, { capture: true });
/** @param {string} hash */
function openHash(hash) {
const target = document.getElementById(hash.slice(1));
if (target && main.contains(target)) openSection(main, target);
}
/** @param {string} hash */
function toggleHash(hash) {
const target = document.getElementById(hash.slice(1));
if (!target || !main.contains(target)) return false;
const section = /** @type {HTMLElement} */ (target);
const details = getDetails(section);
if (details.open) {
details.open = false;
syncNavSection(main, section);
return false;
}
openSection(main, section);
return true;
}
if (window.location.hash) openHash(window.location.hash);
return {
openHash,
toggleHash,
};
}