export default class Router { constructor(options = {}) { this.routes = new Map(); this.currentRoute = null; this.basePath = options.basePath || '/'; this.transitionEnabled = true; this.logger = options.logger || null; this.appState = options.appState || null; this._setupListeners(); } register(path, handler, metadata = {}) { this.routes.set(path, { handler, metadata }); return this; } async navigate(path, state = {}, skipTransition = false) { try { const route = this._matchRoute(path); if (!route) { this.logger?.warn(`No route found for: ${path}`); return; } if (this.currentRoute?.path === path) return; this.appState?.setState({ isLoading: true }); const performTransition = () => { if (this.transitionEnabled && !skipTransition && document.startViewTransition) { document.startViewTransition(() => { this._executeRoute(path, route, state); }).finished.then(() => { this.appState?.setState({ isLoading: false }); }).catch(err => { this.logger?.error('View transition failed', err); this.appState?.setState({ isLoading: false }); }); } else { this._executeRoute(path, route, state); this.appState?.setState({ isLoading: false }); } }; window.history.pushState(state, '', `${this.basePath}${path}`); performTransition(); } catch (error) { this.logger?.error(`Navigation error: ${error.message}`); this.appState?.setState({ isLoading: false }); } } _executeRoute(path, route, state) { const root = document.getElementById('app-root'); if (!root) return; root.innerHTML = ''; this.currentRoute = { path, ...route }; try { const result = route.handler(state); if (result instanceof HTMLElement) { root.appendChild(result); } else if (typeof result === 'string') { root.innerHTML = result; } this.appState?.setState({ currentPage: path }); window.dispatchEvent(new CustomEvent('route-changed', { detail: { path, state } })); } catch (error) { this.logger?.error(`Route handler failed for ${path}`, error); root.innerHTML = '

Error loading page

'; } } _matchRoute(path) { if (this.routes.has(path)) { return this.routes.get(path); } for (const [routePath, route] of this.routes.entries()) { const regex = this._pathToRegex(routePath); if (regex.test(path)) { return route; } } return null; } _pathToRegex(path) { const pattern = path .replace(/\//g, '\\/') .replace(/:(\w+)/g, '(?<$1>[^/]+)') .replace(/\*/g, '.*'); return new RegExp(`^${pattern}$`); } _setupListeners() { window.addEventListener('popstate', (event) => { const path = window.location.pathname.replace(this.basePath, '') || '/'; this.navigate(path, event.state, true); }); document.addEventListener('click', (event) => { const link = event.target.closest('a[data-nav]'); if (link) { event.preventDefault(); const path = link.getAttribute('href') || link.getAttribute('data-nav'); this.navigate(path); } }); } back() { window.history.back(); } forward() { window.history.forward(); } }