router-dom
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -31,4 +31,6 @@ export default class Router { | ||
path: string; | ||
children?: Array<RouteParam>; | ||
} | ||
interface Route extends RouteBasic { | ||
isChildOf?: Route; | ||
path: RegExp; | ||
@@ -35,0 +37,0 @@ originalPath: string; |
import { pathToRegexp, match } from "path-to-regexp"; | ||
import { render, html, hydro, $, $$ } from "hydro-js"; | ||
import { render, html, hydro, $, $$, setReuseElements } from "hydro-js"; | ||
let router; | ||
@@ -13,6 +13,21 @@ const outletSelector = "[data-outlet]"; | ||
//@ts-expect-error | ||
router.doRouting(location.pathname, e); | ||
router.doRouting(location.pathname + location.search, e); | ||
}); | ||
export default class Router { | ||
constructor(routes, options = {}) { | ||
// Handle nested routes | ||
const length = routes.length - 1; | ||
for (let i = length; i >= 0; i--) { | ||
const route = routes[i]; | ||
if (route.children) { | ||
route.children.forEach((child, idx) => { | ||
routes.splice(i + idx, 0, { | ||
...child, | ||
path: `${route.path}/${child.path}`, | ||
isChildOf: route, | ||
}); | ||
}); | ||
Reflect.deleteProperty(route, "children"); | ||
} | ||
} | ||
const newRoutes = routes.map((route) => { | ||
@@ -59,3 +74,3 @@ return { | ||
} | ||
async doRouting(to = location.pathname, e) { | ||
async doRouting(to = location.pathname + location.search, e) { | ||
dispatchEvent(new Event("beforeRouting")); | ||
@@ -94,22 +109,18 @@ const from = this.oldRoute ?? to; | ||
// Handle template / element | ||
if (route?.templateUrl) { | ||
let cacheObj = fetchCache.get(route); | ||
if (!fetchCache.has(route) || cacheObj?.promise === null) { | ||
cacheObj.controller?.abort(); | ||
const data = await fetch(route.templateUrl); | ||
if (!cacheObj) { | ||
cacheObj = { | ||
html: await data.text(), | ||
}; | ||
fetchCache.set(route, cacheObj); | ||
} | ||
else { | ||
cacheObj.html = await data.text(); | ||
} | ||
if (!!route.isChildOf) { | ||
setReuseElements(false); | ||
const parent = route.isChildOf; | ||
if (parent.templateUrl) { | ||
handleTemplate(parent, outletSelector); | ||
} | ||
Reflect.deleteProperty(cacheObj, "controller"); | ||
render(html `<div data-outlet>${await cacheObj.html}</div>`, outletSelector, false); | ||
else if (parent.element) { | ||
render(html `<div data-outlet>${parent.element}</div>`, outletSelector, false); | ||
} | ||
setReuseElements(true); | ||
} | ||
if (route?.templateUrl) { | ||
handleTemplate(route, $(outletSelector).querySelector(outletSelector) ?? outletSelector); | ||
} | ||
else if (route?.element) { | ||
render(html `<div data-outlet>${route?.element}</div>`, outletSelector, false); | ||
render(html `<div data-outlet>${route?.element}</div>`, $(outletSelector).querySelector(outletSelector) ?? outletSelector, false); | ||
} | ||
@@ -137,3 +148,3 @@ else { | ||
go(path, state, params = "") { | ||
this.oldRoute = location.pathname; | ||
this.oldRoute = location.pathname + location.search; | ||
const newPath = base + path + params; | ||
@@ -242,1 +253,19 @@ // Only navigate when the path differs | ||
}).observe(document.body, { childList: true, subtree: true }); | ||
async function handleTemplate(route, where) { | ||
let cacheObj = fetchCache.get(route); | ||
if (!fetchCache.has(route) || cacheObj?.promise === null) { | ||
cacheObj.controller?.abort(); | ||
const data = await fetch(route.templateUrl); | ||
if (!cacheObj) { | ||
cacheObj = { | ||
html: await data.text(), | ||
}; | ||
fetchCache.set(route, cacheObj); | ||
} | ||
else { | ||
cacheObj.html = await data.text(); | ||
} | ||
} | ||
Reflect.deleteProperty(cacheObj, "controller"); | ||
render(html `<div data-outlet>${await cacheObj.html}</div>`, where, false); | ||
} |
{ | ||
"name": "router-dom", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "A lightweight router for everyone", | ||
@@ -23,3 +23,3 @@ "type": "module", | ||
"update": "npx npm-check-updates -u && npx typesync && npm i && npm outdated", | ||
"test": "tsc && esbuild dist/router.js --bundle --format=esm --outfile=src/bundle.js && wtr src/test*.html --node-resolve --playwright --browsers chromium firefox webkit && node -e \"fs.rmSync('src/bundle.js')\"" | ||
"test": "tsc && esbuild dist/router.js --bundle --format=esm --outfile=src/bundle.js && wtr src/tests/*.html --node-resolve --playwright --browsers chromium firefox webkit && node -e \"fs.rmSync('src/bundle.js')\"" | ||
}, | ||
@@ -26,0 +26,0 @@ "author": "Fabian Krutsch <f.krutsch@gmx.de> (https://krutsch.netlify.app/)", |
@@ -7,7 +7,7 @@ # router-dom | ||
> - library agnostic. | ||
> - simple: define your routes, start to listen to route changes. | ||
> - simple: define your routes, start to listen global event and to route changes. | ||
> - base href support. | ||
> - opt-in errorHandler and formHandler. | ||
> - support in all modern browsers. | ||
> - RegExp Routes | ||
> - RegExp and nested Routes | ||
@@ -58,3 +58,6 @@ ## Demo | ||
The router class takes an array with at least one entry. Only the path is mandatory. Either a template or and element will be rendered in your element with attribute `data-outlet`. The second argument is the optional object options: it can take a general errorHandler and a formHandler. If there is a formHandler, form submits will handled via attributes on the form element and fetch. | ||
The router class takes an array with at least one entry. Only the path is mandatory.<br> | ||
Either a template or and element will be rendered in your element with attribute `data-outlet`.<br> | ||
You can also specifiy one-level of children.<br> | ||
The second argument is the optional object options: it can take a general errorHandler and a formHandler. If there is a formHandler, form submits will handled via attributes on the form element and fetch. | ||
@@ -61,0 +64,0 @@ ```js |
import type { MatchResult } from "path-to-regexp"; | ||
import { pathToRegexp, match } from "path-to-regexp"; | ||
import { render, html, hydro, $, $$ } from "hydro-js"; | ||
import { render, html, hydro, $, $$, setReuseElements } from "hydro-js"; | ||
@@ -16,3 +16,3 @@ let router: Router; | ||
//@ts-expect-error | ||
router.doRouting(location.pathname, e); | ||
router.doRouting(location.pathname + location.search, e); | ||
}); | ||
@@ -26,2 +26,18 @@ | ||
constructor(routes: [RouteParam, ...RouteParam[]], options: Options = {}) { | ||
// Handle nested routes | ||
const length = routes.length - 1; | ||
for (let i = length; i >= 0; i--) { | ||
const route = routes[i]; | ||
if (route.children) { | ||
route.children.forEach((child, idx) => { | ||
routes.splice(i + idx, 0, { | ||
...child, | ||
path: `${route.path}/${child.path}`, | ||
isChildOf: route, | ||
} as RouteParam); | ||
}); | ||
Reflect.deleteProperty(route, "children"); | ||
} | ||
} | ||
const newRoutes = routes.map((route) => { | ||
@@ -74,3 +90,6 @@ return { | ||
private async doRouting(to: string = location.pathname, e?: PopStateEvent) { | ||
private async doRouting( | ||
to: string = location.pathname + location.search, | ||
e?: PopStateEvent | ||
) { | ||
dispatchEvent(new Event("beforeRouting")); | ||
@@ -116,23 +135,22 @@ const from = this.oldRoute ?? to; | ||
// Handle template / element | ||
if (route?.templateUrl) { | ||
let cacheObj = fetchCache.get(route); | ||
if (!fetchCache.has(route) || cacheObj?.promise === null) { | ||
cacheObj!.controller?.abort(); | ||
const data = await fetch(route.templateUrl); | ||
if (!cacheObj) { | ||
cacheObj = { | ||
html: await data.text(), | ||
}; | ||
fetchCache.set(route, cacheObj); | ||
} else { | ||
cacheObj.html = await data.text(); | ||
} | ||
if (!!route.isChildOf) { | ||
setReuseElements(false); | ||
const parent = route.isChildOf!; | ||
if (parent.templateUrl) { | ||
handleTemplate(parent, outletSelector); | ||
} else if (parent.element) { | ||
render( | ||
html`<div data-outlet>${parent.element}</div>`, | ||
outletSelector, | ||
false | ||
); | ||
} | ||
Reflect.deleteProperty(cacheObj!, "controller"); | ||
setReuseElements(true); | ||
} | ||
render( | ||
html`<div data-outlet>${await cacheObj!.html}</div>`, | ||
outletSelector, | ||
false | ||
if (route?.templateUrl) { | ||
handleTemplate( | ||
route, | ||
$(outletSelector)!.querySelector(outletSelector) ?? outletSelector | ||
); | ||
@@ -142,3 +160,3 @@ } else if (route?.element) { | ||
html`<div data-outlet>${route?.element}</div>`, | ||
outletSelector, | ||
$(outletSelector)!.querySelector(outletSelector) ?? outletSelector, | ||
false | ||
@@ -166,3 +184,3 @@ ); | ||
go(path: string, state: LooseObject, params = "") { | ||
this.oldRoute = location.pathname; | ||
this.oldRoute = location.pathname + location.search; | ||
const newPath = base + path + params; | ||
@@ -286,2 +304,22 @@ | ||
async function handleTemplate(route: Route, where: string | Element) { | ||
let cacheObj = fetchCache.get(route); | ||
if (!fetchCache.has(route) || cacheObj?.promise === null) { | ||
cacheObj!.controller?.abort(); | ||
const data = await fetch(route.templateUrl!); | ||
if (!cacheObj) { | ||
cacheObj = { | ||
html: await data.text(), | ||
}; | ||
fetchCache.set(route, cacheObj); | ||
} else { | ||
cacheObj.html = await data.text(); | ||
} | ||
} | ||
Reflect.deleteProperty(cacheObj!, "controller"); | ||
render(html`<div data-outlet>${await cacheObj!.html}</div>`, where, false); | ||
} | ||
const enum cycles { | ||
@@ -301,4 +339,6 @@ leave = "leave", | ||
path: string; | ||
children?: Array<RouteParam>; | ||
} | ||
interface Route extends RouteBasic { | ||
isChildOf?: Route; | ||
path: RegExp; | ||
@@ -305,0 +345,0 @@ originalPath: string; |
54928
13
649
104