@thi.ng/router
Version:
Generic trie-based router with support for wildcards, route param validation/coercion, auth
84 lines (83 loc) • 2.48 kB
JavaScript
import { equiv } from "@thi.ng/equiv";
import { Router } from "./router.js";
class HTMLRouter extends Router {
currentPath;
popHandler;
hashHandler;
useFragment;
constructor(config) {
super({ prefix: config.useFragment ? "#/" : "/", ...config });
this.useFragment = config.useFragment !== false;
}
start() {
window.addEventListener("popstate", this.handlePopChange());
if (this.useFragment) {
window.addEventListener("hashchange", this.handleHashChange());
}
if (this.opts.initial) {
const route = this.routeForID(this.opts.initial);
this.route(this.format({ id: route.id }), void 0, "replace");
} else {
this.route(this.useFragment ? location.hash : location.pathname);
}
}
release() {
window.removeEventListener("popstate", this.popHandler);
if (this.useFragment) {
window.removeEventListener("hashchange", this.hashHandler);
}
}
/**
* Like {@link Router.route}, but takes additional arg to control if this
* routing operation should manipulate the browser's `history`.
*
* @remarks
* If called from userland, this normally is set to "push" (also the
* default). However, we want to adjust this behavior if called internally.
*
* @param src -
* @param ctx -
* @param mode -
*/
route(src, ctx, mode = "push") {
const old = this.current;
const route = super.route(src, ctx);
if (route && !equiv(route, old)) {
this.currentPath = this.format(route) + (this.useFragment ? "" : location.search);
if (mode === "push") {
history.pushState(this.currentPath, "", this.currentPath);
} else if (mode === "replace") {
history.replaceState(this.currentPath, "", this.currentPath);
}
}
return route;
}
routeTo(route, ctx) {
if (this.useFragment) {
location.hash = route;
}
this.route(route, ctx);
}
handlePopChange() {
return this.popHandler = this.popHandler || ((e) => {
this.route(
e.state || (this.useFragment ? location.hash : location.pathname),
void 0,
"none"
);
}).bind(this);
}
handleHashChange() {
return this.hashHandler = this.hashHandler || ((e) => {
if (!this.current?.redirect) {
const hash = e.newURL.substring(e.newURL.indexOf("#"));
if (hash !== this.currentPath) {
this.route(hash, void 0, "none");
}
}
}).bind(this);
}
}
export {
HTMLRouter
};