| |
| const routes = []; |
| let cleanup = null; |
| let view = null; |
| |
| export function route(re, make, nav) { routes.push({ re, make, nav }); } |
| |
| export function go(path, replace = false) { |
| if (path === location.pathname + location.search) return; |
| history[replace ? 'replaceState' : 'pushState'](null, '', path); |
| mount(); |
| } |
| |
| export function mount() { |
| view ||= document.getElementById('view'); |
| if (cleanup) { try { cleanup(); } catch {} cleanup = null; } |
| view.replaceChildren(); |
| const p = location.pathname; |
| for (const r of routes) { |
| const m = p.match(r.re); |
| if (!m) continue; |
| for (const a of document.querySelectorAll('[data-nav]')) a.classList.toggle('on', a.dataset.nav === r.nav); |
| cleanup = r.make(view, ...m.slice(1)) || null; |
| return; |
| } |
| history.replaceState(null, '', '/'); |
| mount(); |
| } |
| |
| |
| document.addEventListener('click', e => { |
| const a = e.target.closest('a[data-link]'); |
| if (!a || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; |
| e.preventDefault(); |
| go(a.getAttribute('href')); |
| }); |
| window.addEventListener('popstate', mount); |