export type RouteId = 'editor' | 'cards' | 'reset'; type Render = (route: RouteId, root: HTMLElement) => void | (() => void); export function matchRoute(pathname: string): RouteId { const path = pathname.replace(/\/+$/, '') || '/'; if (path === '/karten') return 'cards'; if (path === '/passwort-neu') return 'reset'; return 'editor'; } // Beendet den laufenden Router (Seiten-Cleanup, Listener); null, solange keiner läuft. let stopCurrent: (() => void) | null = null; let renderCurrent: (() => void) | null = null; export function navigate(path: string): void { const url = new URL(path, location.href); const target = url.pathname + url.search + url.hash; if (target !== location.pathname + location.search + location.hash) { history.pushState(null, '', target); } renderCurrent?.(); } export function startRouter(render: Render): void { stopCurrent?.(); const root = document.getElementById('app-root'); if (!root) throw new Error('#app-root fehlt'); let cleanup: void | (() => void); const renderPage = () => { cleanup?.(); cleanup = undefined; cleanup = render(matchRoute(location.pathname), root); }; const onClick = (event: MouseEvent) => { if (event.defaultPrevented || event.button !== 0) return; if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return; const anchor = (event.target as Element | null)?.closest('a[data-link]'); if (!anchor || (anchor.target && anchor.target !== '_self')) return; const url = new URL(anchor.href, location.href); if (url.origin !== location.origin) return; event.preventDefault(); navigate(url.pathname + url.search + url.hash); }; document.addEventListener('click', onClick); window.addEventListener('popstate', renderPage); renderCurrent = renderPage; stopCurrent = () => { document.removeEventListener('click', onClick); window.removeEventListener('popstate', renderPage); cleanup?.(); cleanup = undefined; renderCurrent = null; stopCurrent = null; }; renderPage(); }