WIP PWA for Grain
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add History API router

+67
+67
src/router.js
··· 1 + class Router { 2 + #routes = new Map(); 3 + #outlet = null; 4 + #currentComponent = null; 5 + 6 + register(path, componentTag) { 7 + this.#routes.set(path, componentTag); 8 + return this; 9 + } 10 + 11 + connect(outlet) { 12 + this.#outlet = outlet; 13 + window.addEventListener('popstate', () => this.#navigate()); 14 + this.#navigate(); 15 + return this; 16 + } 17 + 18 + push(path) { 19 + if (location.pathname !== path) { 20 + history.pushState(null, '', path); 21 + this.#navigate(); 22 + } 23 + } 24 + 25 + replace(path) { 26 + if (location.pathname !== path) { 27 + history.replaceState(null, '', path); 28 + this.#navigate(); 29 + } 30 + } 31 + 32 + #navigate() { 33 + const path = location.pathname; 34 + let componentTag = this.#routes.get(path); 35 + 36 + // Try to match dynamic routes 37 + if (!componentTag) { 38 + for (const [routePath, tag] of this.#routes) { 39 + if (routePath.includes(':')) { 40 + const regex = new RegExp( 41 + '^' + routePath.replace(/:[\w]+/g, '([^/]+)') + '$' 42 + ); 43 + if (regex.test(path)) { 44 + componentTag = tag; 45 + break; 46 + } 47 + } 48 + } 49 + } 50 + 51 + // Fallback to wildcard route 52 + if (!componentTag) { 53 + componentTag = this.#routes.get('*'); 54 + } 55 + 56 + if (this.#outlet && componentTag) { 57 + // Only re-render if component changed 58 + if (this.#currentComponent !== componentTag) { 59 + this.#outlet.innerHTML = ''; 60 + this.#outlet.appendChild(document.createElement(componentTag)); 61 + this.#currentComponent = componentTag; 62 + } 63 + } 64 + } 65 + } 66 + 67 + export const router = new Router();