UNPKG

@fluentui/react

Version:

Reusable React components for building web experiences.

301 lines 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeytipTree = void 0; var tslib_1 = require("tslib"); var Utilities_1 = require("../../Utilities"); var KeytipUtils_1 = require("../../utilities/keytips/KeytipUtils"); var KeytipConstants_1 = require("../../utilities/keytips/KeytipConstants"); /** * This class is responsible for handling the parent/child relationships between keytips */ var KeytipTree = /** @class */ (function () { /** * KeytipTree constructor */ function KeytipTree() { this.nodeMap = {}; // Root has no keytipSequence this.root = { id: KeytipConstants_1.KTP_LAYER_ID, children: [], parent: '', keySequences: [], }; this.nodeMap[this.root.id] = this.root; } /** * Add a keytip node to this KeytipTree * * @param keytipProps - Keytip to add to the Tree * @param uniqueID - Unique ID for this keytip * @param persisted - T/F if this keytip should be marked as persisted */ KeytipTree.prototype.addNode = function (keytipProps, uniqueID, persisted) { var fullSequence = this._getFullSequence(keytipProps); var nodeID = (0, KeytipUtils_1.sequencesToID)(fullSequence); // Take off the last item to calculate the parent sequence fullSequence.pop(); // Parent ID is the root if there aren't any more sequences var parentID = this._getParentID(fullSequence); // Create node and add to map var node = this._createNode(nodeID, parentID, [], keytipProps, persisted); this.nodeMap[uniqueID] = node; // Try to add self to parents children var parents = this.getNodes([parentID]); parents.forEach(function (parent) { return parent.children.push(nodeID); }); }; /** * Updates a node in the tree * * @param keytipProps - Keytip props to update * @param uniqueID - Unique ID for this keytip */ KeytipTree.prototype.updateNode = function (keytipProps, uniqueID) { var node = this.nodeMap[uniqueID]; if (node) { var fullSequence = this._getFullSequence(keytipProps); var nodeID_1 = (0, KeytipUtils_1.sequencesToID)(fullSequence); // Take off the last item to calculate the parent sequence fullSequence.pop(); // Parent ID is the root if there aren't any more sequences var parentID = this._getParentID(fullSequence); var prevParent = node.parent; // Fix parent nodes if needed if (prevParent !== parentID) { // If parent has changed, remove child from old parent this._removeChildFromParents(prevParent, node.id); } if (node.id !== nodeID_1) { // If the ID of the node has changed, update node's parent's array of children with new ID var parents = this.getNodes([parentID]); parents.forEach(function (parent) { var index = parent.children.indexOf(node.id); index >= 0 ? (parent.children[index] = nodeID_1) : parent.children.push(nodeID_1); }); } // Update values node.id = nodeID_1; node.keySequences = keytipProps.keySequences; node.overflowSetSequence = keytipProps.overflowSetSequence; node.onExecute = keytipProps.onExecute; node.onReturn = keytipProps.onReturn; node.hasDynamicChildren = keytipProps.hasDynamicChildren; node.hasMenu = keytipProps.hasMenu; node.parent = parentID; node.disabled = keytipProps.disabled; } }; /** * Removes a node from the KeytipTree * * @param sequence - full string of the node to remove */ KeytipTree.prototype.removeNode = function (keytipProps, uniqueID) { var fullSequence = this._getFullSequence(keytipProps); var nodeID = (0, KeytipUtils_1.sequencesToID)(fullSequence); // Take off the last sequence to calculate the parent ID fullSequence.pop(); // Parent ID is the root if there aren't any more sequences this._removeChildFromParents(this._getParentID(fullSequence), nodeID); if (this.nodeMap[uniqueID]) { // Remove the node from the nodeMap delete this.nodeMap[uniqueID]; } }; /** * Searches the currentKeytip's children to exactly match a sequence. Will not match disabled nodes but * will match persisted nodes * * @param keySequence - string to match * @param currentKeytip - The keytip whose children will try to match * @param doc - The document for DOM operations * @returns The node that exactly matched the keySequence, or undefined if none matched */ KeytipTree.prototype.getExactMatchedNode = function (keySequence, currentKeytip, doc) { var _this = this; var theDoc = doc !== null && doc !== void 0 ? doc : (0, Utilities_1.getDocument)(); var possibleNodes = this.getNodes(currentKeytip.children); var matchingNodes = possibleNodes.filter(function (node) { return _this._getNodeSequence(node) === keySequence && !node.disabled; }); // If we found no nodes, we are done if (matchingNodes.length === 0) { return undefined; } // Since the matching nodes all have the same key sequence, // Grab the first one build the correct selector var node = matchingNodes[0]; // If we have exactly one node, return it if (matchingNodes.length === 1) { return node; } // Get the potential target elements based on a selector from the sequences var keySequences = node.keySequences; var overflowSetSequence = node.overflowSetSequence; var fullKeySequences = overflowSetSequence ? (0, KeytipUtils_1.mergeOverflows)(keySequences, overflowSetSequence) : keySequences; var keytipTargetSelector = (0, KeytipUtils_1.ktpTargetFromSequences)(fullKeySequences); var potentialTargetElements = theDoc.querySelectorAll(keytipTargetSelector); // If we have less nodes than the potential target elements, // we won't be able to map element to node, return the first node. // Note, the number of nodes could be more than the number of potential // target elements, if an OverflowSet is involved if (matchingNodes.length < potentialTargetElements.length) { return node; } // Attempt to find the node that corresponds to the first visible/non-hidden element var matchingIndex = Array.from(potentialTargetElements).findIndex(function (element) { var _a; return (0, Utilities_1.isElementVisibleAndNotHidden)(element, (_a = theDoc.defaultView) !== null && _a !== void 0 ? _a : undefined); }); if (matchingIndex !== -1) { return matchingNodes[matchingIndex]; } // We did not find any visible elements associated with any of the nodes. // We may be dealing with a keytip that is a submenu in an OverflowSet. // Worst case, fall back to the first node returned var overflowNode = matchingNodes.find(function (matchingNode) { return matchingNode.hasOverflowSubMenu; }); return overflowNode || node; }; /** * Searches the currentKeytip's children to find nodes that start with the given sequence. Will not match * disabled nodes but will match persisted nodes * * @param keySequence - string to partially match * @param currentKeytip - The keytip whose children will try to partially match * @returns List of tree nodes that partially match the given sequence */ KeytipTree.prototype.getPartiallyMatchedNodes = function (keySequence, currentKeytip) { var _this = this; // Get children that are persisted var possibleNodes = this.getNodes(currentKeytip.children); return possibleNodes.filter(function (node) { return _this._getNodeSequence(node).indexOf(keySequence) === 0 && !node.disabled; }); }; /** * Get the non-persisted children of the give node * If no node is given, will use the 'currentKeytip' * * @param node - Node to get the children for * @returns List of node IDs that are the children of the node */ KeytipTree.prototype.getChildren = function (node) { var _this = this; if (!node) { node = this.currentKeytip; if (!node) { return []; } } var children = node.children; return Object.keys(this.nodeMap).reduce(function (nodes, key) { if (children.indexOf(_this.nodeMap[key].id) >= 0 && !_this.nodeMap[key].persisted) { nodes.push(_this.nodeMap[key].id); } return nodes; }, []); }; /** * Gets all nodes from their IDs * * @param ids - List of keytip IDs * @returns Array of nodes that match the given IDs, can be empty */ KeytipTree.prototype.getNodes = function (ids) { var _this = this; return Object.keys(this.nodeMap).reduce(function (nodes, key) { if (ids.indexOf(_this.nodeMap[key].id) >= 0) { nodes.push(_this.nodeMap[key]); } return nodes; }, []); }; /** * Gets a single node from its ID * * @param id - ID of the node to get * @returns Node with the given ID, if found */ KeytipTree.prototype.getNode = function (id) { var nodeMapValues = (0, Utilities_1.values)(this.nodeMap); return (0, Utilities_1.find)(nodeMapValues, function (node) { return node.id === id; }); }; /** * Tests if the currentKeytip in this.keytipTree is the parent of 'keytipProps' * * @param keytipProps - Keytip to test the parent for * @returns T/F if the currentKeytip is this keytipProps' parent */ KeytipTree.prototype.isCurrentKeytipParent = function (keytipProps) { if (this.currentKeytip) { var fullSequence = tslib_1.__spreadArray([], keytipProps.keySequences, true); if (keytipProps.overflowSetSequence) { fullSequence = (0, KeytipUtils_1.mergeOverflows)(fullSequence, keytipProps.overflowSetSequence); } // Take off the last sequence to calculate the parent ID fullSequence.pop(); // Parent ID is the root if there aren't any more sequences var parentID = fullSequence.length === 0 ? this.root.id : (0, KeytipUtils_1.sequencesToID)(fullSequence); var matchesCurrWithoutOverflow = false; if (this.currentKeytip.overflowSetSequence) { var currKeytipIdWithoutOverflow = (0, KeytipUtils_1.sequencesToID)(this.currentKeytip.keySequences); matchesCurrWithoutOverflow = currKeytipIdWithoutOverflow === parentID; } return matchesCurrWithoutOverflow || this.currentKeytip.id === parentID; } return false; }; KeytipTree.prototype._getParentID = function (fullSequence) { return fullSequence.length === 0 ? this.root.id : (0, KeytipUtils_1.sequencesToID)(fullSequence); }; KeytipTree.prototype._getFullSequence = function (keytipProps) { var fullSequence = tslib_1.__spreadArray([], keytipProps.keySequences, true); if (keytipProps.overflowSetSequence) { fullSequence = (0, KeytipUtils_1.mergeOverflows)(fullSequence, keytipProps.overflowSetSequence); } return fullSequence; }; KeytipTree.prototype._getNodeSequence = function (node) { var fullSequence = tslib_1.__spreadArray([], node.keySequences, true); if (node.overflowSetSequence) { fullSequence = (0, KeytipUtils_1.mergeOverflows)(fullSequence, node.overflowSetSequence); } return fullSequence[fullSequence.length - 1]; }; KeytipTree.prototype._createNode = function (id, parentId, children, keytipProps, persisted) { var _this = this; var keySequences = keytipProps.keySequences, hasDynamicChildren = keytipProps.hasDynamicChildren, overflowSetSequence = keytipProps.overflowSetSequence, hasMenu = keytipProps.hasMenu, onExecute = keytipProps.onExecute, onReturn = keytipProps.onReturn, disabled = keytipProps.disabled, hasOverflowSubMenu = keytipProps.hasOverflowSubMenu; var node = { id: id, keySequences: keySequences, overflowSetSequence: overflowSetSequence, parent: parentId, children: children, onExecute: onExecute, onReturn: onReturn, hasDynamicChildren: hasDynamicChildren, hasMenu: hasMenu, disabled: disabled, persisted: persisted, hasOverflowSubMenu: hasOverflowSubMenu, }; node.children = Object.keys(this.nodeMap).reduce(function (array, nodeMapKey) { if (_this.nodeMap[nodeMapKey].parent === id) { array.push(_this.nodeMap[nodeMapKey].id); } return array; }, []); return node; }; KeytipTree.prototype._removeChildFromParents = function (parentID, childID) { var parents = this.getNodes([parentID]); parents.forEach(function (parent) { var childIndex = parent.children.indexOf(childID); if (childIndex >= 0) { parent.children.splice(childIndex, 1); } }); }; return KeytipTree; }()); exports.KeytipTree = KeytipTree; //# sourceMappingURL=KeytipTree.js.map