import * as plugins from './smartrouter.plugins.js'; import { QueryParams } from './smartrouter.classes.queryparams.js'; import { type ISelectionOption, SelectionDimension } from './smartrouter.classes.selectiondimension.js'; const routeLog = (message: string) => { console.log(`%c[Router]%c ${message}`, 'color: rgb(255, 105, 100);', 'color: inherit'); }; export interface IRouterOptions { debug?: boolean; } export type THandlerFunction = (routeArg: IRouteInfo) => Promise; export interface IRouteInfo { path: string; index: number; params: { [key: string]: string }; queryParams: { [key: string]: string }; } /** * Router */ export class SmartRouter { public options: IRouterOptions = { debug: false, }; public queryParams = new QueryParams(); /** * the routes we are handling */ public routes: Array<{ matchFunction: plugins.pathToRegExp.MatchFunction; handler: THandlerFunction; }> = []; /** * Creates an instance of Router. */ constructor(optionsArg: IRouterOptions) { // lets set the router options this.options = { ...this.options, ...optionsArg, }; // lets subscribe to route changes window.addEventListener('popstate', (popStateEventArg) => { popStateEventArg.preventDefault(); this._handleRouteState(); }); } /** * Push route state to history stack */ public async pushUrl(url: string = '/', state: any = {}) { if (url !== window.location.pathname) { window.history.pushState(state, window.document.title, url); } else { window.history.replaceState(state, window.document.title, url); } await this._handleRouteState(); } /** * Attach route with handler * @param {string|RegExp} routeArg * @param {function} handlerArg */ public on(routeArg: string, handlerArg: THandlerFunction) { const routeObject = { matchFunction: plugins.pathToRegExp.match(routeArg), handler: handlerArg, }; this.routes.push(routeObject); const removeFunction = () => { this.routes.splice(this.routes.indexOf(routeObject), 1); }; return removeFunction; } /** * Apply routes handler to current route */ async _handleRouteState() { const currentLocation = window.location.pathname; // lets find all wanted routes. const wantedRoutes = this.routes.filter((routeArg) => { return !!routeArg.matchFunction(currentLocation); }); for (const wantedRoute of wantedRoutes) { const routeResult = wantedRoute.matchFunction(currentLocation); wantedRoute.handler({ ...(routeResult.valueOf() as Object), queryParams: this.queryParams.getAllAsObject(), // TODO check wether entries is supported in typings } as IRouteInfo); // not waiting here } } public selectionDimensionsMap = new plugins.lik.ObjectMap(); public async createSelectionDimension(optionsArg: { routeArg: string, keyArg: string, options: ISelectionOption[], peers?: SelectionDimension[], }) { const selectionDimension = new SelectionDimension(); selectionDimension.dimensionKey = optionsArg.keyArg; selectionDimension.dimensionPeers = optionsArg.peers; selectionDimension.selectionOptions = optionsArg.options; await this.selectionDimensionsMap.findOneAndRemove(async dimensionArg => dimensionArg.dimensionKey === optionsArg.keyArg); this.selectionDimensionsMap.addMappedUnique(optionsArg.keyArg, selectionDimension); } }