UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

259 lines (222 loc) • 6.65 kB
import { focusManagerLogger } from "../../logger"; export class Tree { constructor(treeLoaded) { this.root = { id: "root", component: null, parent: null, children: [], lastFocusedItem: null, }; this.loadingCounter = 0; this.treeLoaded = treeLoaded; } /** * Find node by id and removes it from tree * @public * @param {String} id of node to remove from tree */ findAndRemoveNode(id) { const node = this.findInTree(id); if (node) { this.removeNode(node); } } /** * Remove node from tree * @private * @param {Object} node to remove. */ removeNode(node) { const index = node.parent.children.indexOf(node); if (index > -1) { node.parent.children.splice(index, 1); } } /** * Adds given node to given parent. If parent childtren is undefined or null it * initialize the array. * @private * @param {Object} node to add * @param {Object} parentNode which node should be added to */ addNodeToParentNode(node, parentNode) { if (!parentNode.children) { parentNode.children = []; } parentNode.children.push(node); node.parent = parentNode; } /** * Update node with provided data from newNode * @private * @param {Object} node to update * @param {Object} newNode node to take data from */ updateNode(currentNode, newNode) { if (!currentNode.component && newNode.component) { this.loadingCounter--; if (this.loadingCounter === 0) { currentNode.component = newNode.component; this.treeLoaded(true, currentNode); } } else if (currentNode.component && !newNode.component) { this.loadingCounter++; if (this.loadingCounter > 0) { currentNode.component = newNode.component; this.treeLoaded(false, currentNode); } } currentNode.component = newNode.component; } /** * Creates temporary parent node for given node. add node as a child of parent node. * @private * @param {Object} node to add to temporarly created parentNode * @returns (Object) temporarly created parentNode */ createTemporaryParentNodeforNode(node) { const parentNode = { id: node.component.props.groupId, component: null, parent: this.root, children: [node], lastFocusedItem: null, }; node.parent = parentNode; this.root.children.push(parentNode); this.loadingCounter++; if (this.loadingCounter > 0) { this.treeLoaded(false, parentNode); } return parentNode; } /** * Adds node to tree. If node does not have parent adds this node to root parent. * If node has parent but parent does not exists in tree structure it create * temporary parent and add it to root. If node has parent and parent exists in tree, * it will be added under prefered parent node. If node exist in the tree it will be * updated with given input node data (parent, component). * @public * @param {Object} node to add */ addNode(node) { const existingNode = this.findInTree(node.id); if (!existingNode) { if (this.hasGroupID(node)) { const existingParentNode = this.findInTree( node.component.props.groupId ); if (existingParentNode) { this.addNodeToParentNode(node, existingParentNode); } else { this.createTemporaryParentNodeforNode(node); } } else { this.addNodeToParentNode(node, this.root); } } else { focusManagerLogger.debug({ message: `Focusable node with ${ node.id } id already exist. Replacing with the new one. ${ this.hasGroupID(node) ? "Make sure that there are no id duplicates inside the " + existingNode.parent.id + " group." : "" }`, }); if (this.hasGroupID(node)) { if (existingNode.parent.id !== node.component.props.groupId) { this.removeNode(existingNode); this.updateNode(existingNode, node); const existingParentNode = this.findInTree( node.component.props.groupId ); if (existingParentNode) { this.addNodeToParentNode(existingNode, existingParentNode); } else { this.createTemporaryParentNodeforNode(existingNode); } } else { this.updateNode(existingNode, node); } } else { this.updateNode(existingNode, node); } } } /** * Check if node has definced component and component has groupId * @private * @param {Object} node to check * @returns True if node has defined groupID * @returns False if node has NOT defined groupID */ hasGroupID(node) { return node.component && node.component.props.groupId; } /** * Add given node to root node * @private * @param {Object} node to add */ pushNodeToRoot(node) { if (node.component) { const findChildNode = this.findInTree(node.component.props.groupId); if (findChildNode) { this.root.children.pop(findChildNode); node.children.push(findChildNode); } } this.root.children.push(node); } /** * Finds item by given id in tree. * @public * @param {String} id of node to search * @returns founded node or null */ findInTree(id) { const retVal = null; return this.findInArray(id, this.root.children, retVal); } findInArray(id, children, retVal) { if (!retVal && children) { retVal = children.find((obj) => obj.id === id); if (!retVal) { children.forEach((child) => { if (child.children) { retVal = this.findInArray(id, child.children, retVal); if (retVal) { return retVal; } } }); } else { return retVal; } } return retVal; } /** * Gets the IDs of all parent groups for a given node id. * @public * @param {String} nodeId - The id of the node to get parent IDs for. * @returns {Array} An array of parent group IDs or an empty array if the node is not found. */ getParentGroupIds(nodeId) { const parentIds = []; let currentNode = this.findInTree(nodeId); if (!currentNode) { return []; // Node not found, return an empty array } // Traverse up the tree, collecting parent IDs while (currentNode.parent && currentNode.parent.id !== "root") { parentIds.push(currentNode.parent.id); currentNode = currentNode.parent; } return parentIds; } }