f20c7e2f63
Hash router with publish, licenses, generate, stats, and settings pages backed by license server admin API. Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
1.7 KiB
JavaScript
74 lines
1.7 KiB
JavaScript
/** @typedef {{ path: string; title: string; render: (root: HTMLElement) => void | (() => void) }} Route */
|
|
|
|
/** @type {Route[]} */
|
|
const routes = [];
|
|
|
|
/** @type {(() => void) | null} */
|
|
let cleanup = null;
|
|
|
|
/** @param {Route} route */
|
|
export function registerRoute(route) {
|
|
routes.push(route);
|
|
}
|
|
|
|
/** @param {string} hashPath */
|
|
export function navigate(hashPath) {
|
|
const path = hashPath.startsWith('#') ? hashPath : `#${hashPath}`;
|
|
if (location.hash !== path) {
|
|
location.hash = path;
|
|
} else {
|
|
renderCurrent();
|
|
}
|
|
}
|
|
|
|
function currentPath() {
|
|
const hash = location.hash.replace(/^#/, '') || '/publish';
|
|
return hash.startsWith('/') ? hash : `/${hash}`;
|
|
}
|
|
|
|
function findRoute() {
|
|
const path = currentPath();
|
|
return routes.find((r) => r.path === path) ?? routes.find((r) => r.path === '/publish');
|
|
}
|
|
|
|
function updateNav() {
|
|
const path = currentPath();
|
|
document.querySelectorAll('.nav a').forEach((a) => {
|
|
const href = a.getAttribute('href') ?? '';
|
|
a.classList.toggle('active', href === `#${path}`);
|
|
});
|
|
}
|
|
|
|
function renderCurrent() {
|
|
const route = findRoute();
|
|
const root = document.getElementById('page-root');
|
|
if (!root || !route) return;
|
|
|
|
if (cleanup) {
|
|
cleanup();
|
|
cleanup = null;
|
|
}
|
|
|
|
root.innerHTML = '';
|
|
document.title = `${route.title} — TTRPG Player Publisher`;
|
|
updateNav();
|
|
|
|
const result = route.render(root);
|
|
if (typeof result === 'function') {
|
|
cleanup = result;
|
|
}
|
|
}
|
|
|
|
export function startRouter() {
|
|
window.addEventListener('hashchange', renderCurrent);
|
|
if (!location.hash) {
|
|
location.hash = '#/publish';
|
|
} else {
|
|
renderCurrent();
|
|
}
|
|
}
|
|
|
|
export function getCurrentPath() {
|
|
return currentPath();
|
|
}
|