react-d3-graph
Advanced tools
Comparing version 0.4.0 to 1.0.0
# Change Log | ||
## [1.0.0](https://github.com/danielcaldas/react-d3-graph/tree/1.0.0) (2017-12-02) | ||
[Full Changelog](https://github.com/danielcaldas/react-d3-graph/compare/0.4.0...1.0.0) | ||
**Closed issues:** | ||
- How can I get onMouseOverLink event? [\#25](https://github.com/danielcaldas/react-d3-graph/issues/25) | ||
**Fixed bugs:** | ||
- Click one node but another one moves [\#41](https://github.com/danielcaldas/react-d3-graph/issues/41) | ||
**Merged pull requests:** | ||
- Fix/tests coverage [\#44](https://github.com/danielcaldas/react-d3-graph/pull/44) ([danielcaldas](https://github.com/danielcaldas)) | ||
- Fix/on drag node handler [\#42](https://github.com/danielcaldas/react-d3-graph/pull/42) ([danielcaldas](https://github.com/danielcaldas)) | ||
- Feature/on mouse over and out link [\#40](https://github.com/danielcaldas/react-d3-graph/pull/40) ([danielcaldas](https://github.com/danielcaldas)) | ||
- Set proper defaults for Graph component config [\#39](https://github.com/danielcaldas/react-d3-graph/pull/39) ([danielcaldas](https://github.com/danielcaldas)) | ||
- Fix semantics mouse over methods in Graph component [\#38](https://github.com/danielcaldas/react-d3-graph/pull/38) ([danielcaldas](https://github.com/danielcaldas)) | ||
## [0.4.0](https://github.com/danielcaldas/react-d3-graph/tree/0.4.0) (2017-11-11) | ||
@@ -20,6 +38,10 @@ [Full Changelog](https://github.com/danielcaldas/react-d3-graph/compare/0.3.0...0.4.0) | ||
**Implemented enhancements and Fixed bugs:** | ||
**Implemented enhancements:** | ||
- Squeezing if "staticGraph": true [\#24](https://github.com/danielcaldas/react-d3-graph/issues/24), props to [scoutrul](https://github.com/scoutrul) for reporting this issue. | ||
- Squeezing if "staticGraph": true [\#24](https://github.com/danielcaldas/react-d3-graph/issues/24) | ||
**Fixed bugs:** | ||
- Squeezing if "staticGraph": true [\#24](https://github.com/danielcaldas/react-d3-graph/issues/24) | ||
**Merged pull requests:** | ||
@@ -79,2 +101,2 @@ | ||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* | ||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* |
@@ -15,3 +15,3 @@ 'use strict'; | ||
* These properties are marked with 🚅🚅🚅.<br/> | ||
* ⭐ **tip** *to achieve smoother interactions you may want to set **staticGraph** to **true** *<br/> | ||
* ⭐ **tip** *to achieve smoother interactions you may want to provide a toggle to set **staticGraph** to **true** *<br/> | ||
* <br/> | ||
@@ -39,4 +39,6 @@ * **Note about granularity**<br/> | ||
* @param {number} [height=400] - the height of the (svg) area where the graph will be rendered. | ||
* @param {boolean} [highlightBehavior=false] - 🚅🚅🚅 when user mouse hovers a node that node and adjacent common | ||
* connections will be highlighted. All the remaining nodes and links assume opacity value equal to **highlightOpacity**. | ||
* @param {boolean} [nodeHighlightBehavior=false] - 🚅🚅🚅 when user mouse hovers a node that node and adjacent common | ||
* connections will be highlighted (depending on the *highlightDegree* value). All the remaining nodes and links assume opacity value equal to **highlightOpacity**. | ||
* @param {boolean} [linkHighlightBehavior=false] - 🚅🚅🚅 when the user mouse hovers some link that link and the correspondent nodes will be highlighted, this is a similar behavior | ||
* to *nodeHighlightBehavior* but for links <small>(just for historical reference this property was introduced in **v1.0.0**)</small>. | ||
* @param {number} [highlightDegree=1] - **Possible values: 0, 1 or 2**. This value represents the range of the | ||
@@ -47,3 +49,3 @@ * highlight behavior when some node is highlighted. If the value is set to **0** only the selected node will be | ||
* @param {number} [highlightOpacity=1] - this value is used to highlight nodes in the network. The lower | ||
* the value the more the less highlighted nodes will be visible (related to *highlightBehavior*). | ||
* the value the more the less highlighted nodes will be visible (related to *nodeHighlightBehavior*). | ||
* @param {number} [maxZoom=8] - max zoom that can be performed against the graph. | ||
@@ -92,3 +94,3 @@ * @param {number} [minZoom=0.1] - min zoom that can be performed against the graph. | ||
* want the node to keep its color in highlighted state). | ||
* @param {number} [node.highlightFontSize=10] - fontSize in highlighted state. | ||
* @param {number} [node.highlightFontSize=8] - fontSize in highlighted state. | ||
* @param {string} [node.highlightFontWeight='normal'] - fontWeight in highlighted state. | ||
@@ -108,3 +110,7 @@ * @param {string} [node.highlightStrokeColor='SAME'] - strokeColor in highlighted state. | ||
* ``` | ||
* @param {number} [link.strokeWidth=1.5] - strokeWidth for all links. | ||
* @param {number} [link.strokeWidth=1.5] - strokeWidth for all links. By default the actual value is obtain by the | ||
* following expression: | ||
* ```javascript | ||
* link.strokeWidth * (1 / transform); // transform is a zoom delta Δ value | ||
* ``` | ||
* @param {string} [link.highlightColor='#d3d3d3'] - links' color in highlight state. | ||
@@ -115,3 +121,3 @@ * | ||
* const myConfig = { | ||
* highlightBehavior: true, | ||
* nodeHighlightBehavior: true, | ||
* node: { | ||
@@ -132,7 +138,8 @@ * color: 'lightgreen', | ||
height: 400, | ||
highlightBehavior: true, | ||
highlightDegree: 1, | ||
highlightOpacity: 0.1, | ||
highlightOpacity: 1, | ||
linkHighlightBehavior: false, | ||
maxZoom: 8, | ||
minZoom: 0.5, | ||
minZoom: 0.1, | ||
nodeHighlightBehavior: false, | ||
panAndZoom: false, | ||
@@ -149,3 +156,3 @@ staticGraph: false, | ||
renderLabel: true, | ||
size: 80, | ||
size: 200, | ||
strokeColor: 'none', | ||
@@ -152,0 +159,0 @@ strokeWidth: 1.5, |
@@ -66,2 +66,3 @@ 'use strict'; | ||
* @param {string} highlightedNode - same as {@link #buildGraph|highlightedNode in buildGraph}. | ||
* @param {Object} highlightedLink - same as {@link #buildGraph|highlightedLink in buildGraph}. | ||
* @param {number} transform - value that indicates the amount of zoom transformation. | ||
@@ -71,3 +72,3 @@ * @returns {Object} returns an object that aggregates all props for creating respective Link component instance. | ||
*/ | ||
function _buildLinkProps(source, target, nodes, links, config, linkCallbacks, highlightedNode, transform) { | ||
function _buildLinkProps(source, target, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform) { | ||
var x1 = nodes[source] && nodes[source].x || 0; | ||
@@ -78,4 +79,3 @@ var y1 = nodes[source] && nodes[source].y || 0; | ||
var opacity = config.link.opacity; | ||
var mainNodeParticipates = void 0; | ||
var mainNodeParticipates = false; | ||
@@ -94,4 +94,10 @@ switch (config.highlightDegree) { | ||
if (highlightedNode) { | ||
opacity = mainNodeParticipates && nodes[source].highlighted && nodes[target].highlighted ? config.link.opacity : config.highlightOpacity; | ||
var reasonNode = mainNodeParticipates && nodes[source].highlighted && nodes[target].highlighted; | ||
var reasonLink = source === (highlightedLink && highlightedLink.source) && target === (highlightedLink && highlightedLink.target); | ||
var highlight = reasonNode || reasonLink; | ||
var opacity = config.link.opacity; | ||
if (highlightedNode || highlightedLink && highlightedLink.source) { | ||
opacity = highlight ? config.link.opacity : config.highlightOpacity; | ||
} | ||
@@ -101,11 +107,11 @@ | ||
if (mainNodeParticipates && nodes[source].highlighted && nodes[target].highlighted) { | ||
if (highlight) { | ||
stroke = config.link.highlightColor === _const2.default.KEYWORDS.SAME ? config.link.color : config.link.highlightColor; | ||
} | ||
var linkValue = links[source][target] || links[target][source] || 1; | ||
var strokeWidth = config.link.strokeWidth * (1 / transform); | ||
if (config.link.semanticStrokeWidth) { | ||
var linkValue = links[source][target] || links[target][source] || 1; | ||
strokeWidth += linkValue * strokeWidth / 10; | ||
@@ -125,3 +131,5 @@ } | ||
opacity: opacity, | ||
onClickLink: linkCallbacks.onClickLink | ||
onClickLink: linkCallbacks.onClickLink, | ||
onMouseOverLink: linkCallbacks.onMouseOverLink, | ||
onMouseOutLink: linkCallbacks.onMouseOutLink | ||
}; | ||
@@ -138,2 +146,3 @@ } | ||
* @param {string} highlightedNode - same as {@link #buildGraph|highlightedNode in buildGraph}. | ||
* @param {Object} highlightedLink - same as {@link #buildGraph|highlightedLink in buildGraph}. | ||
* @param {number} transform - value that indicates the amount of zoom transformation. | ||
@@ -143,3 +152,3 @@ * @returns {Object[]} returns the generated array of Link components. | ||
*/ | ||
function _buildNodeLinks(nodeId, nodes, links, config, linkCallbacks, highlightedNode, transform) { | ||
function _buildNodeLinks(nodeId, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform) { | ||
var linksComponents = []; | ||
@@ -157,3 +166,3 @@ | ||
var key = '' + nodeId + _const2.default.COORDS_SEPARATOR + target; | ||
var props = _buildLinkProps(source, target, nodes, links, config, linkCallbacks, highlightedNode, transform); | ||
var props = _buildLinkProps(source, target, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform); | ||
@@ -169,24 +178,2 @@ linksComponents.push(_react2.default.createElement(_link2.default, _extends({ key: key }, props))); | ||
/** | ||
* Get the correct node opacity in order to properly make decisions based on context such as currently highlited node. | ||
* @param {Object} node - the node object for whom we will generate properties. | ||
* @param {string} highlightedNode - same as {@link #buildGraph|highlightedNode in buildGraph}. | ||
* @param {Object} config - same as {@link #buildGraph|config in buildGraph}. | ||
* @returns {number} the opacity value for the given node. | ||
* @memberof Graph/helper | ||
*/ | ||
function _getNodeOpacity(node, highlightedNode, config) { | ||
var opacity = void 0; | ||
if (highlightedNode && config.highlightDegree === 0) { | ||
opacity = node.id === highlightedNode && node.highlighted ? config.node.opacity : config.highlightOpacity; | ||
} else if (highlightedNode) { | ||
opacity = node.highlighted ? config.node.opacity : config.highlightOpacity; | ||
} else { | ||
opacity = config.node.opacity; | ||
} | ||
return opacity; | ||
} | ||
/** | ||
* Build some Node properties based on given parameters. | ||
@@ -197,2 +184,3 @@ * @param {Object} node - the node object for whom we will generate properties. | ||
* @param {string} highlightedNode - same as {@link #buildGraph|highlightedNode in buildGraph}. | ||
* @param {Object} highlightedLink - same as {@link #buildGraph|highlightedLink in buildGraph}. | ||
* @param {number} transform - value that indicates the amount of zoom transformation. | ||
@@ -202,8 +190,8 @@ * @returns {Object} returns object that contain Link props ready to be feeded to the Link component. | ||
*/ | ||
function _buildNodeProps(node, config, nodeCallbacks, highlightedNode, transform) { | ||
var opacity = _getNodeOpacity(node, highlightedNode, config); | ||
function _buildNodeProps(node, config, nodeCallbacks, highlightedNode, highlightedLink, transform) { | ||
var highlight = node.highlighted || node.id === (highlightedLink && highlightedLink.source) || node.id === (highlightedLink && highlightedLink.target); | ||
var opacity = _getNodeOpacity(node, highlightedNode, highlightedLink, config); | ||
var fill = node.color || config.node.color; | ||
if (node.highlighted && config.node.highlightColor !== _const2.default.KEYWORDS.SAME) { | ||
if (highlight && config.node.highlightColor !== _const2.default.KEYWORDS.SAME) { | ||
fill = config.node.highlightColor; | ||
@@ -214,3 +202,3 @@ } | ||
if (node.highlighted && config.node.highlightStrokeColor !== _const2.default.KEYWORDS.SAME) { | ||
if (highlight && config.node.highlightStrokeColor !== _const2.default.KEYWORDS.SAME) { | ||
stroke = config.node.highlightStrokeColor; | ||
@@ -221,5 +209,5 @@ } | ||
var nodeSize = node.size || config.node.size; | ||
var fontSize = node.highlighted ? config.node.highlightFontSize : config.node.fontSize; | ||
var fontSize = highlight ? config.node.highlightFontSize : config.node.fontSize; | ||
var dx = fontSize * t + nodeSize / 100 + 1.5; | ||
var strokeWidth = node.highlighted ? config.node.highlightStrokeWidth : config.node.strokeWidth; | ||
var strokeWidth = highlight ? config.node.highlightStrokeWidth : config.node.strokeWidth; | ||
@@ -234,3 +222,3 @@ return { | ||
dx: dx, | ||
fontWeight: node.highlighted ? config.node.highlightFontWeight : config.node.fontWeight, | ||
fontWeight: highlight ? config.node.highlightFontWeight : config.node.fontWeight, | ||
id: node.id, | ||
@@ -251,2 +239,124 @@ label: node[config.node.labelProperty] || node.id, | ||
/** | ||
* Get the correct node opacity in order to properly make decisions based on context such as currently highlighted node. | ||
* @param {Object} node - the node object for whom we will generate properties. | ||
* @param {string} highlightedNode - same as {@link #buildGraph|highlightedNode in buildGraph}. | ||
* @param {Object} highlightedLink - same as {@link #buildGraph|highlightedLink in buildGraph}. | ||
* @param {Object} config - same as {@link #buildGraph|config in buildGraph}. | ||
* @returns {number} the opacity value for the given node. | ||
* @memberof Graph/helper | ||
*/ | ||
function _getNodeOpacity(node, highlightedNode, highlightedLink, config) { | ||
var highlight = node.highlighted || node.id === (highlightedLink && highlightedLink.source) || node.id === (highlightedLink && highlightedLink.target); | ||
var someNodeHighlighted = !!(highlightedNode || highlightedLink && highlightedLink.source && highlightedLink.target); | ||
var opacity = void 0; | ||
if (someNodeHighlighted && config.highlightDegree === 0) { | ||
opacity = highlight ? config.node.opacity : config.highlightOpacity; | ||
} else if (someNodeHighlighted) { | ||
opacity = highlight ? config.node.opacity : config.highlightOpacity; | ||
} else { | ||
opacity = config.node.opacity; | ||
} | ||
return opacity; | ||
} | ||
/** | ||
* Receives a matrix of the graph with the links source and target as concrete node instances and it transforms it | ||
* in a lightweight matrix containing only links with source and target being strings representative of some node id | ||
* and the respective link value (if non existent will default to 1). | ||
* @param {Object[]} graphLinks - an array of all graph links but all the links contain the source and target nodes | ||
* objects. | ||
* @returns {Object.<string, Object>} 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). | ||
* @memberof Graph/helper | ||
*/ | ||
function _initializeLinks(graphLinks) { | ||
return graphLinks.reduce(function (links, l) { | ||
var source = l.source.id || l.source; | ||
var target = l.target.id || l.target; | ||
if (!links[source]) { | ||
links[source] = {}; | ||
} | ||
if (!links[target]) { | ||
links[target] = {}; | ||
} | ||
// @TODO: If the graph is directed this should be adapted | ||
links[source][target] = links[target][source] = l.value || 1; | ||
return links; | ||
}, {}); | ||
} | ||
/** | ||
* Method that initialize graph nodes provided by rd3g consumer and adds additional default mandatory properties | ||
* that are optional for the user. Also it generates an index mapping, this maps nodes ids the their index in the array | ||
* of nodes. This is needed because d3 callbacks such as node click and link click return the index of the node. | ||
* @param {Object[]} graphNodes - the array of nodes provided by the rd3g consumer. | ||
* @returns {Object} returns the nodes ready to be used within rd3g with additional properties such as x, y | ||
* and highlighted values. | ||
* @memberof Graph/helper | ||
*/ | ||
function _initializeNodes(graphNodes) { | ||
var nodes = {}; | ||
var n = graphNodes.length; | ||
for (var i = 0; i < n; i++) { | ||
var node = graphNodes[i]; | ||
node.highlighted = false; | ||
if (!node.hasOwnProperty('x')) { | ||
node['x'] = 0; | ||
} | ||
if (!node.hasOwnProperty('y')) { | ||
node['y'] = 0; | ||
} | ||
nodes[node.id.toString()] = node; | ||
} | ||
return nodes; | ||
} | ||
/** | ||
* Some integrity validations on links and nodes structure. If some validation fails the function will | ||
* throw an error. | ||
* @param {Object} data - Same as {@link #initializeGraphState|data in initializeGraphState}. | ||
* @memberof Graph/helper | ||
* @throws can throw the following error msg: | ||
* INSUFFICIENT_DATA - msg if no nodes are provided | ||
* INVALID_LINKS - if links point to nonexistent nodes | ||
*/ | ||
function _validateGraphData(data) { | ||
if (!data.nodes || !data.nodes.length) { | ||
_utils2.default.throwErr('Graph', _err2.default.INSUFFICIENT_DATA); | ||
} | ||
var n = data.links.length; | ||
var _loop = function _loop(i) { | ||
var l = data.links[i]; | ||
if (!data.nodes.find(function (n) { | ||
return n.id === l.source; | ||
})) { | ||
_utils2.default.throwErr('Graph', _err2.default.INVALID_LINKS + ' - "' + l.source + '" is not a valid source node id'); | ||
} | ||
if (!data.nodes.find(function (n) { | ||
return n.id === l.target; | ||
})) { | ||
_utils2.default.throwErr('Graph', _err2.default.INVALID_LINKS + ' - "' + l.target + '" is not a valid target node id'); | ||
} | ||
}; | ||
for (var i = 0; i < n; i++) { | ||
_loop(i); | ||
} | ||
} | ||
/** | ||
* Method that actually is exported an consumed by Graph component in order to build all Nodes and Link | ||
@@ -282,10 +392,13 @@ * components. | ||
* @param {Function[]} linkCallbacks - array of callbacks for used defined event handler for link interactions. | ||
* @param {Object} config - an object containg rd3g consumer defined configurations {@link #config config} for the graph. | ||
* @param {Object} config - an object containing rd3g consumer defined configurations {@link #config config} for the graph. | ||
* @param {string} highlightedNode - this value contains a string that represents the some currently highlighted node. | ||
* @param {Object} highlightedLink - this object contains a source and target property for a link that is highlighted at some point in time. | ||
* @param {string} highlightedLink.source - id of source node for highlighted link. | ||
* @param {string} highlightedLink.target - id of target node for highlighted link. | ||
* @param {number} transform - value that indicates the amount of zoom transformation. | ||
* @returns {Object} returns an object containg the generated nodes and links that form the graph. The result is | ||
* @returns {Object} returns an object containing the generated nodes and links that form the graph. The result is | ||
* returned in a way that can be consumed by es6 **destructuring assignment**. | ||
* @memberof Graph/helper | ||
*/ | ||
function buildGraph(nodes, nodeCallbacks, links, linkCallbacks, config, highlightedNode, transform) { | ||
function buildGraph(nodes, nodeCallbacks, links, linkCallbacks, config, highlightedNode, highlightedLink, transform) { | ||
var linksComponents = []; | ||
@@ -297,7 +410,7 @@ var nodesComponents = []; | ||
var props = _buildNodeProps(nodes[nodeId], config, nodeCallbacks, highlightedNode, transform); | ||
var props = _buildNodeProps(nodes[nodeId], config, nodeCallbacks, highlightedNode, highlightedLink, transform); | ||
nodesComponents.push(_react2.default.createElement(_node2.default, _extends({ key: nodeId }, props))); | ||
linksComponents = linksComponents.concat(_buildNodeLinks(nodeId, nodes, links, config, linkCallbacks, highlightedNode, transform)); | ||
linksComponents = linksComponents.concat(_buildNodeLinks(nodeId, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform)); | ||
} | ||
@@ -313,4 +426,5 @@ | ||
* Create d3 forceSimulation to be applied on the graph.<br/> | ||
* <a href="https://github.com/d3/d3-force#forceSimulation" target="_blank">https://github.com/d3/d3-force#forceSimulation</a><br/> | ||
* <a href="https://github.com/d3/d3-force#simulation_force" target="_blank">https://github.com/d3/d3-force#simulation_force</a><br/> | ||
* {@link https://github.com/d3/d3-force#forceSimulation|d3-force#forceSimulation}<br/> | ||
* {@link https://github.com/d3/d3-force#simulation_force|d3-force#simulation_force}<br/> | ||
* Wtf is a force? {@link https://github.com/d3/d3-force#forces| here} | ||
* @param {number} width - the width of the container area of the graph. | ||
@@ -329,3 +443,3 @@ * @param {number} height - the height of the container area of the graph. | ||
/** | ||
* Incapsulates common procedures to initialize graph. | ||
* Encapsulates common procedures to initialize graph. | ||
* @param {Object} props - Graph component props, object that holds data, id and config. | ||
@@ -336,3 +450,3 @@ * @param {Object} props.data - Data object holds links (array of **Link**) and nodes (array of **Node**). | ||
* @param {Object} state - Graph component current state (same format as returned object on this function). | ||
* @returns a fully (re)initialized graph state object. | ||
* @returns {Object} a fully (re)initialized graph state object. | ||
* @memberof Graph/helper | ||
@@ -347,6 +461,6 @@ */ | ||
validateGraphData(data); | ||
_validateGraphData(data); | ||
if (state && state.nodes && state.links && state.nodeIndexMapping) { | ||
// absorve existent positining | ||
if (state && state.nodes && state.links) { | ||
// absorb existent positioning | ||
graph = { | ||
@@ -372,8 +486,4 @@ nodes: data.nodes.map(function (n) { | ||
var newConfig = Object.assign({}, _utils2.default.merge(_config2.default, config || {})); | ||
var _initializeNodes = initializeNodes(graph.nodes), | ||
nodes = _initializeNodes.nodes, | ||
nodeIndexMapping = _initializeNodes.nodeIndexMapping; | ||
var links = initializeLinks(graph.links); // Matrix of graph connections | ||
var nodes = _initializeNodes(graph.nodes); | ||
var links = _initializeLinks(graph.links); // matrix of graph connections | ||
var _graph = graph, | ||
@@ -389,3 +499,2 @@ d3Nodes = _graph.nodes, | ||
config: newConfig, | ||
nodeIndexMapping: nodeIndexMapping, | ||
links: links, | ||
@@ -403,95 +512,6 @@ d3Links: d3Links, | ||
/** | ||
* Receives a matrix of the graph with the links source and target as concrete node instances and it transforms it | ||
* in a lightweight matrix containing only links with source and target being strings representative of some node id | ||
* and the respective link value (if non existant will default to 1). | ||
* @param {Object[]} graphLinks - an array of all graph links but all the links contain the source and target nodes | ||
* objects. | ||
* @returns {Object.<string, Object>} 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). | ||
* @memberof Graph/helper | ||
*/ | ||
function initializeLinks(graphLinks) { | ||
return graphLinks.reduce(function (links, l) { | ||
var source = l.source.id || l.source; | ||
var target = l.target.id || l.target; | ||
if (!links[source]) { | ||
links[source] = {}; | ||
} | ||
if (!links[target]) { | ||
links[target] = {}; | ||
} | ||
// @TODO: If the graph is directed this should be adapted | ||
links[source][target] = links[target][source] = l.value || 1; | ||
return links; | ||
}, {}); | ||
} | ||
/** | ||
* Method that initialize graph nodes provided by rd3g consumer and adds additional default mandatory properties | ||
* that are optional for the user. Also it generates an index mapping, this maps nodes ids the their index in the array | ||
* of nodes. This is needed because d3 callbacks such as node click and link click return the index of the node. | ||
* @param {Object[]} graphNodes - the array of nodes provided by the rd3g consumer. | ||
* @returns {Object} returns the nodes ready to be used within rd3g with additional properties such as x, y | ||
* and highlighted values. Returns also the index mapping object of type Object.<number, string>. | ||
* @memberof Graph/helper | ||
*/ | ||
function initializeNodes(graphNodes) { | ||
var nodes = {}; | ||
var nodeIndexMapping = {}; | ||
var n = graphNodes.length; | ||
for (var i = 0; i < n; i++) { | ||
var node = graphNodes[i]; | ||
node.highlighted = false; | ||
if (!node.hasOwnProperty('x')) { | ||
node['x'] = 0; | ||
} | ||
if (!node.hasOwnProperty('y')) { | ||
node['y'] = 0; | ||
} | ||
nodes[node.id.toString()] = node; | ||
nodeIndexMapping[i] = node.id; | ||
} | ||
return { nodes: nodes, nodeIndexMapping: nodeIndexMapping }; | ||
} | ||
/** | ||
* Some integraty validations on links and nodes structure. If some validation fails the function will | ||
* throw an error. | ||
* @param {Object} data - Same as {@link #initializeGraphState|data in initializeGraphState}. | ||
* @memberof Graph/helper | ||
*/ | ||
function validateGraphData(data) { | ||
var _this = this; | ||
data.links.forEach(function (l) { | ||
if (!data.nodes.find(function (n) { | ||
return n.id === l.source; | ||
})) { | ||
_utils2.default.throwErr(_this.constructor.name, _err2.default.INVALID_LINKS + ' - ' + l.source + ' is not a valid node id'); | ||
} | ||
if (!data.nodes.find(function (n) { | ||
return n.id === l.target; | ||
})) { | ||
_utils2.default.throwErr(_this.constructor.name, _err2.default.INVALID_LINKS + ' - ' + l.target + ' is not a valid node id'); | ||
} | ||
}); | ||
} | ||
exports.default = { | ||
buildGraph: buildGraph, | ||
createForceSimulation: createForceSimulation, | ||
initializeGraphState: initializeGraphState, | ||
initializeLinks: initializeLinks, | ||
initializeNodes: initializeNodes | ||
initializeGraphState: initializeGraphState | ||
}; |
@@ -66,3 +66,3 @@ 'use strict'; | ||
* | ||
* // Graph payload (with minimalist structure) | ||
* // graph payload (with minimalist structure) | ||
* const data = { | ||
@@ -80,5 +80,6 @@ * nodes: [ | ||
* | ||
* // The graph configuration | ||
* // the graph configuration, you only need to pass down properties | ||
* // that you want to override, otherwise default ones will be used | ||
* const myConfig = { | ||
* highlightBehavior: true, | ||
* nodeHighlightBehavior: true, | ||
* node: { | ||
@@ -94,13 +95,13 @@ * color: 'lightgreen', | ||
* | ||
* // Graph event callbacks | ||
* // graph event callbacks | ||
* const onClickNode = function(nodeId) { | ||
* window.alert('Clicked node', nodeId); | ||
* window.alert('Clicked node ${nodeId}'); | ||
* }; | ||
* | ||
* const onMouseOverNode = function(nodeId) { | ||
* window.alert('Mouse over node', nodeId); | ||
* window.alert(`Mouse over node ${nodeId}`); | ||
* }; | ||
* | ||
* const onMouseOutNode = function(nodeId) { | ||
* window.alert('Mouse out node', nodeId); | ||
* window.alert(`Mouse out node ${nodeId}`); | ||
* }; | ||
@@ -112,2 +113,10 @@ * | ||
* | ||
* const onMouseOverLink = function(source, target) { | ||
* window.alert(`Mouse over in link between ${source} and ${target}`); | ||
* }; | ||
* | ||
* const onMouseOutLink = function(source, target) { | ||
* window.alert(`Mouse out link between ${source} and ${target}`); | ||
* }; | ||
* | ||
* <Graph | ||
@@ -120,3 +129,5 @@ * id='graph-id' // id is mandatory, if no id is defined rd3g will throw an error | ||
* onMouseOverNode={onMouseOverNode} | ||
* onMouseOutNode={onMouseOutNode} /> | ||
* onMouseOutNode={onMouseOutNode} | ||
* onMouseOverLink={onMouseOverLink} | ||
* onMouseOutLink={onMouseOutLink}/> | ||
*/ | ||
@@ -130,12 +141,21 @@ | ||
/** | ||
* Sets d3 tick function and configures other d3 stuff such as forces and drag events. | ||
*/ | ||
value: function _graphForcesConfig() { | ||
this.state.simulation.nodes(this.state.d3Nodes).on('tick', this._tick); | ||
var forceLink = (0, _d3Force.forceLink)(this.state.d3Links).id(function (l) { | ||
return l.id; | ||
}).distance(D3_CONST.LINK_IDEAL_DISTANCE).strength(D3_CONST.FORCE_LINK_STRENGTH); | ||
this.state.simulation.force(_const2.default.LINK_CLASS_NAME, forceLink); | ||
var customNodeDrag = (0, _d3Drag.drag)().on('start', this._onDragStart).on('drag', this._onDragMove).on('end', this._onDragEnd); | ||
(0, _d3Selection.select)('#' + this.state.id + '-' + _const2.default.GRAPH_WRAPPER_ID).selectAll('.node').call(customNodeDrag); | ||
} | ||
/** | ||
* This method resets all nodes fixed positions by deleting the properties fx (fixed x) | ||
* and fy (fixed y). Following this, a simulation is triggered in order to force nodes to go back | ||
* to their original positions (or at least new positions according to the d3 force parameters). | ||
* Handles d3 drag 'end' event. | ||
*/ | ||
@@ -145,4 +165,9 @@ | ||
/** | ||
* Handles mouse over node event. | ||
* @param {number} index - index of the mouse hovered node. | ||
* Handles d3 'drag' event. | ||
* @param {Object} ev - if not undefined it will contain event data. | ||
* @param {number} index - index of the node that is being dragged. | ||
* @param {Array.<Object>} nodeList - array of d3 nodes. This list of nodes is provided by d3, each | ||
* node contains all information that was previously fed by rd3g. | ||
* | ||
* {@link https://github.com/d3/d3-drag/blob/master/README.md#drag_subject|more about d3 drag} | ||
*/ | ||
@@ -152,4 +177,3 @@ | ||
/** | ||
* Handler for 'zoom' event within zoom config. | ||
* @returns {Object} returns the transformed elements within the svg graph area. | ||
* Handles d3 drag 'start' event. | ||
*/ | ||
@@ -159,2 +183,9 @@ | ||
/** | ||
* Sets nodes and links highlighted value. | ||
* @param {string} id - the id of the node to highlight. | ||
* @param {boolean} [value=false] - the highlight value to be set (true or false). | ||
*/ | ||
/** | ||
* The tick function simply calls React set state in order to update component and render nodes | ||
@@ -166,25 +197,22 @@ * along time as d3 calculates new node positioning. | ||
/** | ||
* Handles d3 drag 'start' event. | ||
* Configures zoom upon graph with default or user provided values.<br/> | ||
* {@link https://github.com/d3/d3-zoom#zoom} | ||
*/ | ||
/** | ||
* Handles d3 drag 'end' event. | ||
* Handler for 'zoom' event within zoom config. | ||
* @returns {Object} returns the transformed elements within the svg graph area. | ||
*/ | ||
value: function _graphForcesConfig() { | ||
this.state.simulation.nodes(this.state.d3Nodes).on('tick', this._tick); | ||
var forceLink = (0, _d3Force.forceLink)(this.state.d3Links).id(function (l) { | ||
return l.id; | ||
}).distance(D3_CONST.LINK_IDEAL_DISTANCE).strength(D3_CONST.FORCE_LINK_STRENGTH); | ||
this.state.simulation.force(_const2.default.LINK_CLASS_NAME, forceLink); | ||
/** | ||
* Handles mouse over node event. | ||
* @param {string} id - id of the node that participates in the event. | ||
*/ | ||
var customNodeDrag = (0, _d3Drag.drag)().on('start', this._onDragStart).on('drag', this._onDragMove).on('end', this._onDragEnd); | ||
(0, _d3Selection.select)('#' + this.state.id + '-' + _const2.default.GRAPH_WRAPPER_ID).selectAll('.node').call(customNodeDrag); | ||
} | ||
/** | ||
* Calls d3 simulation.restart().<br/> | ||
* {@link https://github.com/d3/d3-force#simulation_restart} | ||
* Handles mouse out node event. | ||
* @param {string} id - id of the node that participates in the event. | ||
*/ | ||
@@ -194,10 +222,12 @@ | ||
/** | ||
* Calls d3 simulation.stop().<br/> | ||
* {@link https://github.com/d3/d3-force#simulation_stop} | ||
*/ | ||
* Handles mouse over link event. | ||
* @param {string} source - id of the source node that participates in the event. | ||
* @param {string} target - id of the target node that participates in the event. | ||
*/ | ||
/** | ||
* Handles mouse out node event. | ||
* @param {number} index - index of the mouse hovered node. | ||
* Handles mouse out link event. | ||
* @param {string} source - id of the source node that participates in the event. | ||
* @param {string} target - id of the target node that participates in the event. | ||
*/ | ||
@@ -207,11 +237,11 @@ | ||
/** | ||
* Configures zoom upon graph with default or user provided values.<br/> | ||
* {@link https://github.com/d3/d3-zoom#zoom} | ||
*/ | ||
* Calls d3 simulation.stop().<br/> | ||
* {@link https://github.com/d3/d3-force#simulation_stop} | ||
*/ | ||
/** | ||
* Sets nodes and links highlighted value. | ||
* @param {number} index - the index of the node to highlight (and its adjacent). | ||
* @param {boolean} value - the highlight value to be set (true or false). | ||
* This method resets all nodes fixed positions by deleting the properties fx (fixed x) | ||
* and fy (fixed y). Following this, a simulation is triggered in order to force nodes to go back | ||
* to their original positions (or at least new positions according to the d3 force parameters). | ||
*/ | ||
@@ -221,5 +251,4 @@ | ||
/** | ||
* Handles d3 'drag' event. | ||
* @param {Object} ev - event. | ||
* @param {number} index - index of the node that is being dragged. | ||
* Calls d3 simulation.restart().<br/> | ||
* {@link https://github.com/d3/d3-force#simulation_restart} | ||
*/ | ||
@@ -238,6 +267,8 @@ | ||
_this._onDragMove = function (ev, index) { | ||
_this._onDragMove = function (ev, index, nodeList) { | ||
var id = nodeList[index].id; | ||
if (!_this.state.config.staticGraph) { | ||
// This is where d3 and react bind | ||
var draggedNode = _this.state.nodes[_this.state.nodeIndexMapping[index]]; | ||
// this is where d3 and react bind | ||
var draggedNode = _this.state.nodes[id]; | ||
@@ -247,3 +278,3 @@ draggedNode.x += _d3Selection.event.dx; | ||
// Set nodes fixing coords fx and fy | ||
// set nodes fixing coords fx and fy | ||
draggedNode['fx'] = draggedNode.x; | ||
@@ -260,9 +291,11 @@ draggedNode['fy'] = draggedNode.y; | ||
_this._setHighlighted = function (index, value) { | ||
_this.state.highlightedNode = value ? index : ''; | ||
_this.state.nodes[index].highlighted = value; | ||
_this._setNodeHighlightedValue = function (id) { | ||
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
_this.state.highlightedNode = value ? id : ''; | ||
_this.state.nodes[id].highlighted = value; | ||
// when highlightDegree is 0 we want only to highlight selected node | ||
if (_this.state.links[index] && _this.state.config.highlightDegree !== 0) { | ||
Object.keys(_this.state.links[index]).forEach(function (k) { | ||
if (_this.state.links[id] && _this.state.config.highlightDegree !== 0) { | ||
Object.keys(_this.state.links[id]).forEach(function (k) { | ||
_this.state.nodes[k].highlighted = value; | ||
@@ -291,14 +324,34 @@ }); | ||
_this.onMouseOutNode = function (index) { | ||
_this.props.onMouseOutNode && _this.props.onMouseOutNode(index); | ||
_this.onMouseOverNode = function (id) { | ||
_this.props.onMouseOverNode && _this.props.onMouseOverNode(id); | ||
_this.state.config.highlightBehavior && _this._setHighlighted(index, false); | ||
_this.state.config.nodeHighlightBehavior && _this._setNodeHighlightedValue(id, true); | ||
}; | ||
_this.onMouseOverNode = function (index) { | ||
_this.props.onMouseOverNode && _this.props.onMouseOverNode(index); | ||
_this.onMouseOutNode = function (id) { | ||
_this.props.onMouseOutNode && _this.props.onMouseOutNode(id); | ||
_this.state.config.highlightBehavior && _this._setHighlighted(index, true); | ||
_this.state.config.nodeHighlightBehavior && _this._setNodeHighlightedValue(id, false); | ||
}; | ||
_this.onMouseOverLink = function (source, target) { | ||
_this.props.onMouseOverLink && _this.props.onMouseOverLink(source, target); | ||
if (_this.state.config.linkHighlightBehavior) { | ||
_this.state.highlightedLink = { source: source, target: target }; | ||
_this._tick(); | ||
} | ||
}; | ||
_this.onMouseOutLink = function (source, target) { | ||
_this.props.onMouseOutLink && _this.props.onMouseOutLink(source, target); | ||
if (_this.state.config.linkHighlightBehavior) { | ||
_this.state.highlightedLink = undefined; | ||
_this._tick(); | ||
} | ||
}; | ||
_this.pauseSimulation = function () { | ||
@@ -350,3 +403,3 @@ return !_this.state.config.staticGraph && _this.state.simulation.stop(); | ||
// In order to properly update graph data we need to pause eventual d3 ongoing animations | ||
// in order to properly update graph data we need to pause eventual d3 ongoing animations | ||
newGraphElements && this.pauseSimulation(); | ||
@@ -366,3 +419,3 @@ | ||
value: function componentDidUpdate() { | ||
// If the property staticGraph was activated we want to stop possible ongoing simulation | ||
// if the property staticGraph was activated we want to stop possible ongoing simulation | ||
this.state.config.staticGraph && this.state.simulation.stop(); | ||
@@ -388,3 +441,3 @@ | ||
// Graph zoom and drag&drop all network | ||
// graph zoom and drag&drop all network | ||
this._zoomConfig(); | ||
@@ -404,3 +457,7 @@ } | ||
onMouseOut: this.onMouseOutNode | ||
}, this.state.links, { onClickLink: this.props.onClickLink }, this.state.config, this.state.highlightedNode, this.state.transform), | ||
}, this.state.links, { | ||
onClickLink: this.props.onClickLink, | ||
onMouseOverLink: this.onMouseOverLink, | ||
onMouseOutLink: this.onMouseOutLink | ||
}, this.state.config, this.state.highlightedNode, this.state.highlightedLink, this.state.transform), | ||
nodes = _graphHelper$buildGra.nodes, | ||
@@ -407,0 +464,0 @@ links = _graphHelper$buildGra.links; |
@@ -28,2 +28,10 @@ 'use strict'; | ||
* | ||
* const onMouseOverLink = function(source, target) { | ||
* window.alert(`Mouse over in link between ${source} and ${target}`); | ||
* }; | ||
* | ||
* const onMouseOutLink = function(source, target) { | ||
* window.alert(`Mouse out link between ${source} and ${target}`); | ||
* }; | ||
* | ||
* <Link | ||
@@ -40,3 +48,5 @@ * source='idSourceNode' | ||
* opacity=1 | ||
* onClickLink={onClickLink} /> | ||
* onClickLink={onClickLink} | ||
* onMouseOverLink={onMouseOverLink} | ||
* onMouseOutLink={onMouseOutLink} /> | ||
*/ | ||
@@ -59,2 +69,6 @@ var Link = function (_React$Component) { | ||
return _this.props.onClickLink && _this.props.onClickLink(_this.props.source, _this.props.target); | ||
}, _this.handleOnMouseOverLink = function () { | ||
return _this.props.onMouseOverLink && _this.props.onMouseOverLink(_this.props.source, _this.props.target); | ||
}, _this.handleOnMouseOutLink = function () { | ||
return _this.props.onMouseOutLink && _this.props.onMouseOutLink(_this.props.source, _this.props.target); | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
@@ -67,2 +81,12 @@ } | ||
/** | ||
* Handle mouse over link event. | ||
*/ | ||
/** | ||
* Handle mouse out link event. | ||
*/ | ||
_createClass(Link, [{ | ||
@@ -80,2 +104,4 @@ key: 'render', | ||
onClick: this.handleOnClickLink, | ||
onMouseOut: this.handleOnMouseOutLink, | ||
onMouseOver: this.handleOnMouseOverLink, | ||
style: lineStyle, | ||
@@ -82,0 +108,0 @@ x1: this.props.x1, |
@@ -6,8 +6,8 @@ "use strict"; | ||
}); | ||
/*eslint max-len: ["error", 200]*/ | ||
exports.default = { | ||
GRAPH_NO_ID_PROP: "id prop not defined! id property is mandatory and it should be unique.", | ||
STATIC_GRAPH_DATA_UPDATE: "a static graph cannot receive new data (nodes or links).\ | ||
Make sure config.staticGraph is set to true if you want to update graph data", | ||
INVALID_LINKS: "you provided a invalid links data structure.\ | ||
Links source and target attributes must point to an existent node" | ||
STATIC_GRAPH_DATA_UPDATE: "a static graph cannot receive new data (nodes or links). Make sure config.staticGraph is set to true if you want to update graph data", | ||
INVALID_LINKS: "you provided a invalid links data structure. Links source and target attributes must point to an existent node", | ||
INSUFFICIENT_DATA: "you have not provided enough data for react-d3-graph to render something. You need to provide at least one node" | ||
}; |
@@ -121,2 +121,6 @@ 'use strict'; | ||
if (Object.keys(o1 || {}).length === 0) { | ||
return o2 && !isObjectEmpty(o2) ? o2 : {}; | ||
} | ||
var _iteratorNormalCompletion2 = true; | ||
@@ -123,0 +127,0 @@ var _didIteratorError2 = false; |
{ | ||
"name": "react-d3-graph", | ||
"version": "0.4.0", | ||
"version": "1.0.0", | ||
"description": "React component to build interactive and configurable graphs with d3 effortlessly", | ||
@@ -22,3 +22,3 @@ "author": "Daniel Caldas", | ||
"test:clean": "jest --no-cache --updateSnapshot --verbose --coverage", | ||
"test:watch": "jest --verbose --watchAll" | ||
"test:watch": "jest --verbose --coverage --watchAll" | ||
}, | ||
@@ -49,3 +49,3 @@ "dependencies": { | ||
"html-webpack-plugin": "2.30.1", | ||
"jest": "21.1.0", | ||
"jest": "21.3.0-beta.8", | ||
"npm-run-all": "4.1.1", | ||
@@ -52,0 +52,0 @@ "react-addons-test-utils": "15.6.0", |
@@ -1,7 +0,7 @@ | ||
# react-d3-graph · [](https://travis-ci.org/danielcaldas/react-d3-graph) [](https://www.npmjs.com/package/react-d3-graph) [](https://npm-stat.com/) [](https://probot.github.io/) | ||
[:book:](https://danielcaldas.github.io/react-d3-graph/docs/index.html) | ||
# react-d3-graph · [](https://travis-ci.org/danielcaldas/react-d3-graph) [](https://www.npmjs.com/package/react-d3-graph) [](https://npm-stat.com/charts.html?package=react-d3-graph&from=2017-04-25&to=2017-12-02) [](https://probot.github.io/) | ||
:book: [1.0.0](https://danielcaldas.github.io/react-d3-graph/docs/index.html) | [0.4.0](https://danielcaldas.github.io/react-d3-graph/docs/0.4.0.html) | [0.3.0](https://danielcaldas.github.io/react-d3-graph/docs/0.3.0.html) | ||
### *Interactive and configurable graphs with react and d3 effortlessly* | ||
[](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html) | ||
[](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html) | ||
@@ -30,3 +30,3 @@ ## Playground | ||
// Graph payload (with minimalist structure) | ||
// graph payload (with minimalist structure) | ||
const data = { | ||
@@ -44,5 +44,6 @@ nodes: [ | ||
// The graph configuration | ||
// the graph configuration, you only need to pass down properties | ||
// that you want to override, otherwise default ones will be used | ||
const myConfig = { | ||
highlightBehavior: true, | ||
nodeHighlightBehavior: true, | ||
node: { | ||
@@ -58,13 +59,13 @@ color: 'lightgreen', | ||
// Graph event callbacks | ||
// graph event callbacks | ||
const onClickNode = function(nodeId) { | ||
window.alert('Clicked node', nodeId); | ||
window.alert('Clicked node ${nodeId}'); | ||
}; | ||
const onMouseOverNode = function(nodeId) { | ||
window.alert('Mouse over node', nodeId); | ||
window.alert(`Mouse over node ${nodeId}`); | ||
}; | ||
const onMouseOutNode = function(nodeId) { | ||
window.alert('Mouse out node', nodeId); | ||
window.alert(`Mouse out node ${nodeId}`); | ||
}; | ||
@@ -76,2 +77,10 @@ | ||
const onMouseOverLink = function(source, target) { | ||
window.alert(`Mouse over in link between ${source} and ${target}`); | ||
}; | ||
const onMouseOutLink = function(source, target) { | ||
window.alert(`Mouse out link between ${source} and ${target}`); | ||
}; | ||
<Graph | ||
@@ -84,6 +93,12 @@ id='graph-id' // id is mandatory, if no id is defined rd3g will throw an error | ||
onMouseOverNode={onMouseOverNode} | ||
onMouseOutNode={onMouseOutNode} /> | ||
onMouseOutNode={onMouseOutNode} | ||
onMouseOverLink={onMouseOverLink} | ||
onMouseOutLink={onMouseOutLink}/> | ||
``` | ||
## Roadmap :railway_track: | ||
Want to know what's ahead for react-d3-graph? Or simply curious on what comes next and stuff that is under development? | ||
Check [this trello board](https://trello.com/b/KrnmFXha/react-d3-graph) where everything related to react-d3-graph is managed. | ||
## Contributions | ||
Contributions are welcome fell free to submit new ideas/features. | ||
Contributions are welcome fell free to submit new ideas/features, just open an issue or send me an email or something. If you are more a *hands on* person, just submit a pull request. |
@@ -8,6 +8,6 @@ ## Release Process | ||
2. npm run docs:lint (fix if errors) | ||
3. npm run docs | ||
4. Small tweaks on documentation page (quicklinks) | ||
5. Replace current docs folder with gen-docs | ||
6. Update versioning in package.json | ||
3. Update versioning in package.json | ||
4. npm run docs | ||
5. Small tweaks on documentation page (quicklinks) | ||
6. Replace current files in docs for the generated ones in gen-docs | ||
7. git commit -m "Release x.x.x" | ||
@@ -14,0 +14,0 @@ 8. Create release x.x.x in github |
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
816208
26
15475
1
100