Lumi/src/web/public/lumi-page.js
2026-07-22 10:20:32 +02:00

35 lines
1.5 KiB
JavaScript

(() => {
const registrations = new Map();
let mounted = [];
function register(name, definition) {
if (!name || !definition) throw new TypeError("LumiPage.register requires a name and mount definition.");
registrations.set(name, typeof definition === "function" ? { mount: definition } : definition);
}
function unmount() {
for (const item of mounted.splice(0).reverse()) {
try { item.controller.abort(); } catch {}
try { item.teardown?.(); } catch (error) { console.error(`Lumi page teardown failed for ${item.name}.`, error); }
}
window.dispatchEvent(new CustomEvent("lumi:page-unmounted"));
}
function mount(root = document) {
unmount();
for (const [name, definition] of registrations) {
const selector = definition.selector || `[data-lumi-page~="${CSS.escape(name)}"]`;
const target = root.matches?.(selector) ? root : root.querySelector?.(selector);
if (!target || typeof definition.mount !== "function") continue;
const controller = new AbortController();
const context = { root: target, signal: controller.signal, pageRoot: root };
const result = definition.mount(target, context);
mounted.push({ name, controller, teardown: typeof result === "function" ? result : result?.destroy?.bind(result) });
}
window.dispatchEvent(new CustomEvent("lumi:page-mounted", { detail: { root } }));
}
window.LumiPage = Object.freeze({ register, mount, unmount });
document.addEventListener("DOMContentLoaded", () => mount(document), { once: true });
})();