helpers/navigation/nav_tree.js (56 lines of code) (raw):
import { Node } from './nav_node';
export class NavTree {
constructor(items) {
this.nodes = [];
this.initNodes(items);
}
get topLevelNodes() {
return this.nodes.filter((node) => node.depth === 0);
}
/**
* Creates a flat list of Node instances representing the nav tree.
*
* @param {array} items The nav items to be added to the list.
* @param {Node} parent The items' parent if any.
* @param {number} depth The current depth-level in the tree.
*/
initNodes(items = [], parent = null, depth = 0) {
if (!items.length) {
return;
}
items.forEach((item) => {
const node = new Node({ ...item, parent, depth });
this.nodes.push(node);
parent?.addChild(node);
this.initNodes(item.items, node, depth + 1);
});
}
/**
* Activates a node based on a route.
*
* @param {String} route The route to be matched with a node.
*/
activateNodeWithRoute(route = '') {
if (!route) {
return;
}
const fragments = route.replace(/^\//, '').split('/').reverse().filter(Boolean);
const activeNode = this.getNodeWithPaths(fragments);
if (!activeNode) {
return;
}
activeNode.activate();
}
/**
* Attempts to find the node that matches a list of paths.
* Since, multiple nodes could have the same path, we also check all of the matching node's
* parents to make sure the whole branch matches the paths list.
*
* @param {Array} paths Paths to be matched against.
* @returns {Node} The matched node.
*/
getNodeWithPaths(paths = []) {
const [path, ...rest] = paths;
let leaves = this.nodes.filter((node) => node.path === path);
const removeSimilarLeaves = (nodes, remainingPaths) => {
if (!remainingPaths.length) {
return;
}
const [currentPath, ...currentRemainingPaths] = remainingPaths;
nodes.forEach((node) => {
const { parent } = node;
if (parent) {
if (parent.path === currentPath) {
removeSimilarLeaves([parent], currentRemainingPaths);
} else if (parent.path === undefined) {
removeSimilarLeaves([parent], remainingPaths);
} else {
leaves = leaves.filter(({ id }) => id !== node.id);
}
}
});
};
removeSimilarLeaves(leaves, rest);
return leaves?.[0];
}
}