Initial commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* WijiBoard – Hash-based SPA Router
|
||||
* web/js/router.js
|
||||
*
|
||||
* Usage:
|
||||
* Router.register('home', homeModule);
|
||||
* Router.register('stepper-test', stepperModule);
|
||||
* Router.init('#view', '#home'); // outlet element id, default route
|
||||
*
|
||||
* Each section module must export:
|
||||
* { mount(containerEl), unmount() }
|
||||
*/
|
||||
const Router = (() => {
|
||||
const routes = new Map(); // hash → module
|
||||
let outlet = null; // DOM element to render into
|
||||
let current = null; // currently active route key
|
||||
let currentMod = null; // currently mounted module
|
||||
|
||||
// ── Register a route ─────────────────────────────────────────
|
||||
function register(hash, module) {
|
||||
routes.set(hash, module);
|
||||
}
|
||||
|
||||
// ── Navigate to a route ──────────────────────────────────────
|
||||
async function navigate(hash) {
|
||||
const key = hash.replace(/^#/, '');
|
||||
|
||||
if (!routes.has(key)) {
|
||||
console.warn(`[Router] Unknown route: "${key}"`);
|
||||
return;
|
||||
}
|
||||
if (key === current) return; // already there
|
||||
|
||||
// Unmount old section
|
||||
if (currentMod?.unmount) {
|
||||
try { await currentMod.unmount(); } catch (e) { console.error('[Router] unmount error', e); }
|
||||
}
|
||||
|
||||
// Clear outlet
|
||||
outlet.innerHTML = '';
|
||||
|
||||
// Mount new section
|
||||
const mod = routes.get(key);
|
||||
currentMod = mod;
|
||||
current = key;
|
||||
|
||||
try {
|
||||
await mod.mount(outlet);
|
||||
} catch (e) {
|
||||
console.error('[Router] mount error', e);
|
||||
outlet.innerHTML = `<p style="color:var(--accent-red);padding:24px">
|
||||
Failed to load section: ${e.message}</p>`;
|
||||
}
|
||||
|
||||
// Update sidebar active state
|
||||
document.querySelectorAll('.sidebar-nav-item').forEach(el => {
|
||||
el.classList.toggle('active', el.dataset.route === key);
|
||||
});
|
||||
|
||||
// Update URL hash (without re-triggering hashchange)
|
||||
if (location.hash.replace('#','') !== key) {
|
||||
history.replaceState(null, '', `#${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Init: wire up outlet + default route ─────────────────────
|
||||
function init(outletSelector, defaultHash = '#home') {
|
||||
outlet = document.querySelector(outletSelector);
|
||||
if (!outlet) throw new Error(`[Router] Outlet "${outletSelector}" not found`);
|
||||
|
||||
// Listen for hash changes
|
||||
window.addEventListener('hashchange', () => {
|
||||
navigate(location.hash || defaultHash);
|
||||
});
|
||||
|
||||
// Handle initial load
|
||||
const initial = location.hash && location.hash.length > 1
|
||||
? location.hash
|
||||
: defaultHash;
|
||||
|
||||
// Small defer to let the page fully render first
|
||||
requestAnimationFrame(() => navigate(initial));
|
||||
}
|
||||
|
||||
// ── Active route getter ───────────────────────────────────────
|
||||
function getActive() { return current; }
|
||||
|
||||
return { register, navigate, init, getActive };
|
||||
})();
|
||||
|
||||
export default Router;
|
||||
Reference in New Issue
Block a user