import { URLPattern } from "@supersoniks/concorde/utils"; export type RouteArgs = { propertyMap?: object; query?: object; hash?: string; }; export class Route { private route: string = ""; constructor(name?: keyof RoutesData) { if (!name) { return; } this.route = Routes.get(name); } extract() { const pattern = new URLPattern("(/)*" + this.route); const currentUrl = document.location.pathname + document.location.hash; const matches = pattern.match(currentUrl); return matches; } /** * @deprecated * @see matchesCurrentPath * @returns {boolean} */ matchesLocation() { return this.matchesCurrentPath(); } matchesCurrentPath() { return this.extract() !== null; } removeQuery() { const url = new URL(this.route, window.location.origin); url.search = ""; this.route = url.href.replace(window.location.origin, ""); return this; } withoutQuery() { return this.clone().removeQuery(); } clone() { const clonedRoute = new Route(); clonedRoute.route = this.route; return clonedRoute; } fill(properties: Object) { this.route = new URLPattern(this.route).stringify(properties); return this; } query(query: Object) { const url = new URL(this.route, window.location.origin); Object.entries(query).forEach(([key, value]) => { url.searchParams.set(key, value); }); this.route = url.href.replace(window.location.origin, ""); return this; } hash(hash: string) { const url = new URL(this.route, window.location.origin); url.hash = hash; this.route = url.href.replace(window.location.origin, ""); return this; } appendPath(pathname?: string) { if (!pathname) { return this; } const url = new URL(this.route, window.location.origin); url.pathname = url.pathname.replace(/\/$/, ""); if (!pathname.startsWith("/")) { pathname = "/" + pathname; } url.pathname += pathname; this.route = url.href.replace(window.location.origin, ""); return this; } toString() { return this.route; } valueOf() { return this.route; } hasHash() { const url = new URL(this.route, window.location.origin); return url.hash !== ""; } getHash() { const url = new URL(this.route, window.location.origin); return url.hash; } hasSameBaseAs(route?: Route) { if (!route) { return false; } const url = new URL(this.route, window.location.origin); const url2 = new URL(route.route, window.location.origin); return url.origin + url.pathname === url2.origin + url2.pathname; } goTo() { window.location.href = this.route; } } export class Routes { private static routes = {}; static register( routes: Partial, baseUrlOrOpt: { baseUrl?: string; prefix?: string } | string = "", prefix = "", ) { const newRoutes: Partial = {}; Object.keys(routes).forEach((key) => { const name = key as keyof RouteData & string; let route = routes[name] as string; let baseUrl = ""; if (baseUrlOrOpt) { if (typeof baseUrlOrOpt === "string") { baseUrl = baseUrlOrOpt; } else { baseUrl = baseUrlOrOpt.baseUrl || ""; prefix = prefix || baseUrlOrOpt.prefix || ""; } } if (baseUrl && !route.includes(baseUrl)) { route = baseUrl + "/" + route; route = route?.replace(/\/+/g, "/"); route = route?.replace(/\/#/g, "#"); } newRoutes[(prefix + name) as keyof RouteData] = route as RouteData[keyof RouteData]; }); Routes.routes = { ...Routes.routes, ...newRoutes } as Partial; } static get>( name: keyof RouteData, args?: RouteArgs, ) { const routes = Routes.routes as Partial; let route = routes[name] || ""; if (args?.propertyMap) { route = new URLPattern(route).stringify(args.propertyMap); } const url = new URL( route, route.startsWith("#") ? window.location.href : window.location.origin, ); if (args?.query) { Object.entries(args.query).forEach(([key, value]) => { url.searchParams.set(key, value); }); } if (args?.hash) { url.hash = args.hash; } return url.href.replace(window.location.origin, ""); } static fillURLPattern(route: string, properties: Object) { return new URLPattern(route).stringify(properties); } } export const route = (name?: keyof RouteData) => { return new Route(name); };