UNPKG

@thi.ng/router

Version:

Generic trie-based router with support for wildcards, route param validation/coercion, auth

84 lines (83 loc) 2.48 kB
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 };