import {AnyMenuItem, MenuItemType} from "./MenuRenderer.types"; import {TraversalMap, StringifiedTraversalPath, TraversalPath, TraversalKey} from "./MenuTraversal.types"; import {InvalidTraversalRouteError} from "./InvalidTraversalRouteError"; export class MenuTraversal { private readonly traversalMap: TraversalMap private currentPath: TraversalPath = []; public get path() { return this.currentPath; } public get stringifiedPath(): StringifiedTraversalPath { return this.stringifyTraversalPath(this.currentPath); } public get currentMenuItem(): AnyMenuItem { const path = this.stringifiedPath; if (!this.traversalMap.has(path)) { throw new InvalidTraversalRouteError(this.currentPath); } return this.traversalMap.get(path); } public get isRoot(): boolean { return this.currentPath.length === 0; } constructor( private readonly menu: AnyMenuItem[], private readonly rootLabel: string = "", private readonly rootDescription: string = '' ) { this.traversalMap = this.generateTraversalMap(); } public travelForward(next: TraversalKey) { const nextPath = [ ...this.currentPath, next ]; if (!this.traversalMap.has(this.stringifyTraversalPath(nextPath))) { throw new InvalidTraversalRouteError(nextPath); } this.currentPath = nextPath; } public travelBack() { this.currentPath.pop(); } public getMenuItem(path: StringifiedTraversalPath): AnyMenuItem { if (!this.traversalMap.has(path)) { throw new InvalidTraversalRouteError([path]); } return this.traversalMap.get(path); } public static unstringifyTraversalPath(path: StringifiedTraversalPath): TraversalPath { return path.split('/'); } private generateTraversalMap(): TraversalMap { const map = new Map(); map.set('', { traversalKey: '', label: this.rootLabel, description: this.rootDescription, type: MenuItemType.Collection, children: this.menu }); const that = this; function traversePath(path: TraversalPath, menuItem: AnyMenuItem) { path = [...path, menuItem.traversalKey]; map.set(that.stringifyTraversalPath(path), menuItem); if (menuItem.type !== MenuItemType.Collection) { return; } menuItem.children.forEach((nextMenuItem) => { traversePath(path, nextMenuItem); }) } this.menu.forEach((menuItem) => { traversePath([], menuItem); }) return map; } private stringifyTraversalPath(path: TraversalPath): StringifiedTraversalPath { return path.join('/'); } }