UNPKG

almus-d3-graph

Version:

React component to build interactive and configurable graphs with d3 effortlessly

224 lines (193 loc) 9.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /** * @module Graph/collapse-helper * @description * Offers a series of methods that allow graph to perform the necessary operations to * create the collapsible behavior. * * Developer notes - collapsing nodes and maintaining state on links matrix. * * User interaction flow (for a collapsible graph) * 1. User clicks node * 2. All leaf connections of that node are not rendered anymore * 3. User clicks on same node * 4. All leaf connections of that node are rendered * * Internal react-d3-graph flow * 1. User clicks node * 2. Compute leaf connections for clicked node (rootNode, root as in 'root' of the event) * 3. Update connections matrix (based on 2.) * 4. Update d3Links array with toggled connections (based on 2.) */ /** * For directed graphs. * Check based on node degrees whether it is a leaf node or not. * @param {number} inDegree - the in degree for a given node. * @param {number} outDegree - the out degree for a given node. * @returns {boolean} based on the degrees tells whether node is leaf or not. * @memberof Graph/collapse-helper */ function _isLeafDirected(inDegree, outDegree) { return inDegree <= 1 && outDegree < 1; } /** * For not directed graphs. * Check based on node degrees whether it is a leaf node or not. * @param {number} inDegree - the in degree for a given node. * @param {number} outDegree - the out degree for a given node. * @returns {boolean} based on the degrees tells whether node is leaf or not. * @memberof Graph/collapse-helper */ function _isLeafNotDirected(inDegree, outDegree) { return inDegree <= 1 && outDegree <= 1; } /** * Given in and out degree tells whether degrees indicate a leaf or non leaf scenario. * @param {string} nodeId - The id of the node to get the cardinality of. * @param {Object.<string, number>} linksMatrix - An object containing a matrix of connections of the nodes. * @param {boolean} directed - whether graph in context is directed or not. * @returns {boolean} flag that indicates whether node is leaf or not. * @memberof Graph/collapse-helper */ function _isLeaf(nodeId, linksMatrix, directed) { var _computeNodeDegree = computeNodeDegree(nodeId, linksMatrix), inDegree = _computeNodeDegree.inDegree, outDegree = _computeNodeDegree.outDegree; return directed ? _isLeafDirected(inDegree, outDegree) : _isLeafNotDirected(inDegree, outDegree); } /** * Calculates degree (in and out) of some provided node. * @param {string|number} nodeId - the id of the node whom degree we want to compute. * @param {Object.<string, Object>} linksMatrix - an object containing a matrix of connections of the graph, for each nodeId, * there is an object that maps adjacent nodes ids (string) and their values (number). * @returns {Object.<string, number>} returns object containing in and out degree of the node: * - inDegree: number * - outDegree: number * @memberof Graph/collapse-helper */ function computeNodeDegree(nodeId) { var linksMatrix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return Object.keys(linksMatrix).reduce(function (acc, source) { if (!linksMatrix[source]) { return acc; } var currentNodeConnections = Object.keys(linksMatrix[source]); return currentNodeConnections.reduce(function (_acc, target) { if (nodeId === source) { return _extends({}, _acc, { outDegree: _acc.outDegree + linksMatrix[nodeId][target] }); } if (nodeId === target) { return _extends({}, _acc, { inDegree: _acc.inDegree + linksMatrix[source][nodeId] }); } return _acc; }, acc); }, { inDegree: 0, outDegree: 0 }); } /** * Given a node id we want to calculate the list of leaf connections * @param {string} rootNodeId - node who's leafs we want to calculate. * @param {Object.<string, Object>} linksMatrix - an object containing a matrix of connections of the graph, for each nodeId, * there is an object that maps adjacent nodes ids (string) and their values (number). * @param {Object} config - same as {@link #graphrenderer|config in renderGraph}. * @param {boolean} config.directed - tells whether linksMatrix represents a directed graph or not. * @returns {Array.<Object.<string, string>>} a list of leaf connections. * What is a leaf connection? A leaf connection is a link between some node A and other node B * where A has id equal to rootNodeId and B has inDegree 1 and outDegree 0 (or outDegree 1 but the connection is with A). * @memberof Graph/collapse-helper */ function getTargetLeafConnections(rootNodeId) { var linksMatrix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _ref = arguments[2]; var directed = _ref.directed; var rootConnectionsNodesIds = Object.keys(linksMatrix[rootNodeId]); return rootConnectionsNodesIds.reduce(function (leafConnections, target) { if (_isLeaf(target, linksMatrix, directed)) { leafConnections.push({ source: rootNodeId, target: target }); } return leafConnections; }, []); } /** * Given a node and the connections matrix, check if node should be displayed * NOTE: this function is meant to be used under the `collapsible` toggle, meaning * that the `isNodeVisible` actually is checking visibility on collapsible graphs. * If you think that this code is confusing and could potentially collide (🤞) with #_isLeaf * always remember that *A leaf can, through time, be both a visible or an invisible node!*. * * @param {string} nodeId - The id of the node to get the cardinality of * @param {Object.<string, Object>} nodes - an object containing all nodes mapped by their id. * @param {Object.<string, number>} linksMatrix - An object containing a matrix of connections of the nodes. * @returns {boolean} flag that indicates whether node should or not be displayed. * @memberof Graph/collapse-helper */ function isNodeVisible(nodeId, nodes, linksMatrix) { var _computeNodeDegree2 = computeNodeDegree(nodeId, linksMatrix), inDegree = _computeNodeDegree2.inDegree, outDegree = _computeNodeDegree2.outDegree; var orphan = !!nodes[nodeId]._orphan; return inDegree > 0 || outDegree > 0 || orphan; } /** * Updates d3Links by toggling given connections * @param {Array.<Object>} d3Links - An array containing all the d3 links. * @param {Array.<Object.<string, string>>} connectionMatrix - connections to toggle. * @returns {Array.<Object>} updated d3Links. * @memberof Graph/collapse-helper */ function toggleLinksConnections(d3Links, connectionMatrix) { return d3Links.map(function (d3Link) { var source = d3Link.source, target = d3Link.target; var sourceId = source.id || source; var targetId = target.id || target; // connectionMatrix[sourceId][targetId] can be 0 or non existent var connection = connectionMatrix && connectionMatrix[sourceId] && connectionMatrix[sourceId][targetId]; return connection ? _extends({}, d3Link, { isHidden: false }) : _extends({}, d3Link, { isHidden: true }); }); } /** * Update matrix given array of connections to toggle. * @param {Object.<string, Object>} linksMatrix - an object containing a matrix of connections of the graph, for each nodeId, * there is an object that maps adjacent nodes ids (string) and their values (number). * @param {Array.<Object.<string, string>>} connections - connections to toggle on matrix. * @param {Object} config - same as {@link #graphrenderer|config in renderGraph}. * @param {boolean} config.directed - tells whether linksMatrix represents a directed graph or not. * @returns {Object.<string, Object>} updated linksMatrix * @memberof Graph/collapse-helper */ function toggleLinksMatrixConnections(linksMatrix, connections, _ref2) { var directed = _ref2.directed; return connections.reduce(function (newMatrix, link) { if (!newMatrix[link.source]) { newMatrix[link.source] = {}; } if (!newMatrix[link.source][link.target]) { newMatrix[link.source][link.target] = 0; } var newConnectionValue = newMatrix[link.source][link.target] === 0 ? 1 : 0; newMatrix[link.source][link.target] = newConnectionValue; if (!directed) { newMatrix[link.target][link.source] = newConnectionValue; } return newMatrix; }, _extends({}, linksMatrix)); } exports.computeNodeDegree = computeNodeDegree; exports.getTargetLeafConnections = getTargetLeafConnections; exports.isNodeVisible = isNodeVisible; exports.toggleLinksConnections = toggleLinksConnections; exports.toggleLinksMatrixConnections = toggleLinksMatrixConnections;