115 lines
No EOL
3 KiB
TypeScript
115 lines
No EOL
3 KiB
TypeScript
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 <AnyMenuItem>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 <AnyMenuItem>this.traversalMap.get(path);
|
|
}
|
|
|
|
public getTraversedLabels(): string[] {
|
|
const labels = [];
|
|
for (let i = 0; i < this.currentPath.length; i++) {
|
|
const path = this.currentPath.slice(0, i + 1);
|
|
labels.push(this.getMenuItem(this.stringifyTraversalPath(path)).label);
|
|
}
|
|
|
|
return labels;
|
|
}
|
|
|
|
public static unstringifyTraversalPath(path: StringifiedTraversalPath): TraversalPath {
|
|
return path.split('/');
|
|
}
|
|
|
|
private generateTraversalMap(): TraversalMap {
|
|
const map = new Map<StringifiedTraversalPath, AnyMenuItem>();
|
|
|
|
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('/');
|
|
}
|
|
} |