refactor(nodes,edges): data structure
This commit is contained in:
Vendored
+56
-40
@@ -1,8 +1,8 @@
|
|||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react'), require('prop-types')) :
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types')) :
|
||||||
typeof define === 'function' && define.amd ? define(['react', 'prop-types'], factory) :
|
typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types'], factory) :
|
||||||
(global = global || self, global.ReactGraph = factory(global.React, global.PropTypes));
|
(global = global || self, factory(global.ReactGraph = {}, global.React, global.PropTypes));
|
||||||
}(this, function (React, PropTypes$1) { 'use strict';
|
}(this, function (exports, React, PropTypes$1) { 'use strict';
|
||||||
|
|
||||||
var React__default = 'default' in React ? React['default'] : React;
|
var React__default = 'default' in React ? React['default'] : React;
|
||||||
PropTypes$1 = PropTypes$1 && PropTypes$1.hasOwnProperty('default') ? PropTypes$1['default'] : PropTypes$1;
|
PropTypes$1 = PropTypes$1 && PropTypes$1.hasOwnProperty('default') ? PropTypes$1['default'] : PropTypes$1;
|
||||||
@@ -201,12 +201,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isEdge = function isEdge(element) {
|
var isEdge = function isEdge(element) {
|
||||||
return element.data && element.data.source && element.data.target;
|
return element.source && element.target;
|
||||||
};
|
};
|
||||||
var isNode = function isNode(element) {
|
var isNode = function isNode(element) {
|
||||||
return element.data && !element.data.source && !element.data.target;
|
return !element.source && !element.target;
|
||||||
};
|
};
|
||||||
var parseElements = function parseElements(e) {
|
var parseElements = function parseElements(e) {
|
||||||
|
e.type = e.type || 'default';
|
||||||
|
|
||||||
if (isEdge(e)) {
|
if (isEdge(e)) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -285,10 +287,10 @@
|
|||||||
};
|
};
|
||||||
var getConnectedEdges = function getConnectedEdges(nodes, edges) {
|
var getConnectedEdges = function getConnectedEdges(nodes, edges) {
|
||||||
var nodeIds = nodes.map(function (n) {
|
var nodeIds = nodes.map(function (n) {
|
||||||
return n.data.id;
|
return n.id;
|
||||||
});
|
});
|
||||||
return edges.filter(function (e) {
|
return edges.filter(function (e) {
|
||||||
return nodeIds.includes(e.data.source) || nodeIds.includes(e.data.target);
|
return nodeIds.includes(e.source) || nodeIds.includes(e.target);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29861,7 +29863,7 @@
|
|||||||
{
|
{
|
||||||
return _objectSpread2({}, state, {
|
return _objectSpread2({}, state, {
|
||||||
nodes: state.nodes.map(function (n) {
|
nodes: state.nodes.map(function (n) {
|
||||||
if (n.data.id === action.payload.id) {
|
if (n.id === action.payload.id) {
|
||||||
n.__rg = _objectSpread2({}, n.__rg, {}, action.payload.data);
|
n.__rg = _objectSpread2({}, n.__rg, {}, action.payload.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29874,7 +29876,7 @@
|
|||||||
{
|
{
|
||||||
return _objectSpread2({}, state, {
|
return _objectSpread2({}, state, {
|
||||||
nodes: state.nodes.map(function (n) {
|
nodes: state.nodes.map(function (n) {
|
||||||
if (n.data.id === action.payload.id) {
|
if (n.id === action.payload.id) {
|
||||||
n.__rg = _objectSpread2({}, n.__rg, {
|
n.__rg = _objectSpread2({}, n.__rg, {
|
||||||
position: action.payload.pos
|
position: action.payload.pos
|
||||||
});
|
});
|
||||||
@@ -29928,10 +29930,10 @@
|
|||||||
{
|
{
|
||||||
var ids = action.payload.ids;
|
var ids = action.payload.ids;
|
||||||
var nextEdges = state.edges.filter(function (e) {
|
var nextEdges = state.edges.filter(function (e) {
|
||||||
return !ids.includes(e.data.target) && !ids.includes(e.data.source);
|
return !ids.includes(e.target) && !ids.includes(e.source);
|
||||||
});
|
});
|
||||||
var nextNodes = state.nodes.filter(function (n) {
|
var nextNodes = state.nodes.filter(function (n) {
|
||||||
return !ids.includes(n.data.id);
|
return !ids.includes(n.id);
|
||||||
});
|
});
|
||||||
return _objectSpread2({}, state, {
|
return _objectSpread2({}, state, {
|
||||||
nodes: nextNodes,
|
nodes: nextNodes,
|
||||||
@@ -30070,7 +30072,7 @@
|
|||||||
React.useEffect(function () {
|
React.useEffect(function () {
|
||||||
var nextNodes = props.nodes.map(function (propNode) {
|
var nextNodes = props.nodes.map(function (propNode) {
|
||||||
var existingNode = state.nodes.find(function (n) {
|
var existingNode = state.nodes.find(function (n) {
|
||||||
return n.data.id === propNode.data.id;
|
return n.id === propNode.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingNode) {
|
if (existingNode) {
|
||||||
@@ -30125,7 +30127,7 @@
|
|||||||
_createClass(NodeRenderer, [{
|
_createClass(NodeRenderer, [{
|
||||||
key: "renderNode",
|
key: "renderNode",
|
||||||
value: function renderNode(d, onElementClick) {
|
value: function renderNode(d, onElementClick) {
|
||||||
var nodeType = d.data.type || 'default';
|
var nodeType = d.type || 'default';
|
||||||
|
|
||||||
if (!this.props.nodeTypes[nodeType]) {
|
if (!this.props.nodeTypes[nodeType]) {
|
||||||
console.warn("No node type found for type \"".concat(nodeType, "\". Using fallback type \"default\"."));
|
console.warn("No node type found for type \"".concat(nodeType, "\". Using fallback type \"default\"."));
|
||||||
@@ -30133,7 +30135,7 @@
|
|||||||
|
|
||||||
var NodeComponent = this.props.nodeTypes[nodeType] || this.props.nodeTypes["default"];
|
var NodeComponent = this.props.nodeTypes[nodeType] || this.props.nodeTypes["default"];
|
||||||
return React__default.createElement(NodeComponent, _extends({
|
return React__default.createElement(NodeComponent, _extends({
|
||||||
key: d.data.id,
|
key: d.id,
|
||||||
onClick: onElementClick
|
onClick: onElementClick
|
||||||
}, d));
|
}, d));
|
||||||
}
|
}
|
||||||
@@ -30174,25 +30176,25 @@
|
|||||||
_createClass(EdgeRenderer, [{
|
_createClass(EdgeRenderer, [{
|
||||||
key: "renderEdge",
|
key: "renderEdge",
|
||||||
value: function renderEdge(e, nodes, onElementClick) {
|
value: function renderEdge(e, nodes, onElementClick) {
|
||||||
var edgeType = e.data.type || 'default';
|
var edgeType = e.type || 'default';
|
||||||
var sourceNode = nodes.find(function (n) {
|
var sourceNode = nodes.find(function (n) {
|
||||||
return n.data.id === e.data.source;
|
return n.id === e.source;
|
||||||
});
|
});
|
||||||
var targetNode = nodes.find(function (n) {
|
var targetNode = nodes.find(function (n) {
|
||||||
return n.data.id === e.data.target;
|
return n.id === e.target;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
throw new Error("couldn't create edge for source id: ".concat(e.data.source));
|
throw new Error("couldn't create edge for source id: ".concat(e.source));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetNode) {
|
if (!targetNode) {
|
||||||
throw new Error("couldn't create edge for source id: ".concat(e.data.target));
|
throw new Error("couldn't create edge for source id: ".concat(e.target));
|
||||||
}
|
}
|
||||||
|
|
||||||
var EdgeComponent = this.props.edgeTypes[edgeType] || this.props.edgeTypes["default"];
|
var EdgeComponent = this.props.edgeTypes[edgeType] || this.props.edgeTypes["default"];
|
||||||
return React__default.createElement(EdgeComponent, _extends({
|
return React__default.createElement(EdgeComponent, _extends({
|
||||||
key: "".concat(e.data.source, "-").concat(e.data.target),
|
key: "".concat(e.source, "-").concat(e.target),
|
||||||
sourceNode: sourceNode,
|
sourceNode: sourceNode,
|
||||||
targetNode: targetNode,
|
targetNode: targetNode,
|
||||||
onClick: onElementClick
|
onClick: onElementClick
|
||||||
@@ -32549,7 +32551,7 @@
|
|||||||
x: node.__rg.position.x || node.position.x,
|
x: node.__rg.position.x || node.position.x,
|
||||||
y: node.__rg.position.y || node.position.x
|
y: node.__rg.position.y || node.position.x
|
||||||
};
|
};
|
||||||
res[node.data.id] = startPosition;
|
res[node.id] = startPosition;
|
||||||
return res;
|
return res;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
@@ -32601,9 +32603,9 @@
|
|||||||
y: evt.clientY * (1 / k)
|
y: evt.clientY * (1 / k)
|
||||||
};
|
};
|
||||||
state.selectedElements.filter(isNode).forEach(function (node) {
|
state.selectedElements.filter(isNode).forEach(function (node) {
|
||||||
dispatch(updateNodePos(node.data.id, {
|
dispatch(updateNodePos(node.id, {
|
||||||
x: startPositions[node.data.id].x + scaledClient.x - position.x - offset.x - x,
|
x: startPositions[node.id].x + scaledClient.x - position.x - offset.x - x,
|
||||||
y: startPositions[node.data.id].y + scaledClient.y - position.y - offset.y - y
|
y: startPositions[node.id].y + scaledClient.y - position.y - offset.y - y
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -32903,19 +32905,18 @@
|
|||||||
|
|
||||||
var data = props.data,
|
var data = props.data,
|
||||||
onClick = props.onClick,
|
onClick = props.onClick,
|
||||||
|
type = props.type,
|
||||||
|
id = props.id,
|
||||||
__rg = props.__rg;
|
__rg = props.__rg;
|
||||||
var position = __rg.position;
|
var position = __rg.position;
|
||||||
var id = data.id;
|
|
||||||
|
|
||||||
var _state$transform = _slicedToArray(state.transform, 3),
|
var _state$transform = _slicedToArray(state.transform, 3),
|
||||||
x = _state$transform[0],
|
x = _state$transform[0],
|
||||||
y = _state$transform[1],
|
y = _state$transform[1],
|
||||||
k = _state$transform[2];
|
k = _state$transform[2];
|
||||||
|
|
||||||
var selected = state.selectedElements.filter(function (e) {
|
var selected = state.selectedElements.filter(isNode).map(function (e) {
|
||||||
return !isEdge(e);
|
return e.id;
|
||||||
}).map(function (e) {
|
|
||||||
return e.data.id;
|
|
||||||
}).includes(id);
|
}).includes(id);
|
||||||
var nodeClasses = classnames('react-graph__node', {
|
var nodeClasses = classnames('react-graph__node', {
|
||||||
selected: selected
|
selected: selected
|
||||||
@@ -32964,9 +32965,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(setSelectedElements({
|
dispatch(setSelectedElements({
|
||||||
data: data
|
data: data,
|
||||||
|
id: id
|
||||||
}));
|
}));
|
||||||
onClick({
|
onClick({
|
||||||
|
id: id,
|
||||||
|
type: type,
|
||||||
data: data,
|
data: data,
|
||||||
position: position
|
position: position
|
||||||
});
|
});
|
||||||
@@ -33007,7 +33011,7 @@
|
|||||||
var BezierEdge = (function (props) {
|
var BezierEdge = (function (props) {
|
||||||
var targetNode = props.targetNode,
|
var targetNode = props.targetNode,
|
||||||
sourceNode = props.sourceNode;
|
sourceNode = props.sourceNode;
|
||||||
var style = props.data ? props.data.style : {};
|
var style = props.style || {};
|
||||||
var sourceX = sourceNode.__rg.position.x + sourceNode.__rg.width / 2;
|
var sourceX = sourceNode.__rg.position.x + sourceNode.__rg.width / 2;
|
||||||
var sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
var sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
||||||
var targetX = targetNode.__rg.position.x + targetNode.__rg.width / 2;
|
var targetX = targetNode.__rg.position.x + targetNode.__rg.width / 2;
|
||||||
@@ -33023,7 +33027,7 @@
|
|||||||
var StraightEdge = (function (props) {
|
var StraightEdge = (function (props) {
|
||||||
var targetNode = props.targetNode,
|
var targetNode = props.targetNode,
|
||||||
sourceNode = props.sourceNode;
|
sourceNode = props.sourceNode;
|
||||||
var style = props.data ? props.data.style : {};
|
var style = props.style || {};
|
||||||
var sourceX = sourceNode.__rg.position.x + sourceNode.__rg.width / 2;
|
var sourceX = sourceNode.__rg.position.x + sourceNode.__rg.width / 2;
|
||||||
var sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
var sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
||||||
var targetX = targetNode.__rg.position.x + targetNode.__rg.width / 2;
|
var targetX = targetNode.__rg.position.x + targetNode.__rg.width / 2;
|
||||||
@@ -33043,17 +33047,19 @@
|
|||||||
state = _useContext.state,
|
state = _useContext.state,
|
||||||
dispatch = _useContext.dispatch;
|
dispatch = _useContext.dispatch;
|
||||||
|
|
||||||
var _props$data = props.data,
|
var source = props.source,
|
||||||
data = _props$data === void 0 ? {} : _props$data,
|
target = props.target,
|
||||||
|
animated = props.animated,
|
||||||
|
type = props.type,
|
||||||
_onClick = props.onClick;
|
_onClick = props.onClick;
|
||||||
var selected = state.selectedElements.filter(function (e) {
|
var selected = state.selectedElements.filter(function (e) {
|
||||||
return isEdge(e);
|
return isEdge(e);
|
||||||
}).find(function (e) {
|
}).find(function (e) {
|
||||||
return e.data.source === data.source && e.data.target === data.target;
|
return e.source === source && e.target === target;
|
||||||
});
|
});
|
||||||
var edgeClasses = classnames('react-graph__edge', {
|
var edgeClasses = classnames('react-graph__edge', {
|
||||||
selected: selected,
|
selected: selected,
|
||||||
animated: data.animated
|
animated: animated
|
||||||
});
|
});
|
||||||
return React__default.createElement("g", {
|
return React__default.createElement("g", {
|
||||||
className: edgeClasses,
|
className: edgeClasses,
|
||||||
@@ -33063,11 +33069,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(setSelectedElements({
|
dispatch(setSelectedElements({
|
||||||
data: data
|
source: source,
|
||||||
|
target: target
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_onClick({
|
_onClick({
|
||||||
data: data
|
source: source,
|
||||||
|
target: target,
|
||||||
|
type: type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, React__default.createElement(EdgeComponent, props));
|
}, React__default.createElement(EdgeComponent, props));
|
||||||
@@ -33190,6 +33199,13 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return ReactGraph;
|
var isNode$1 = isNode;
|
||||||
|
var isEdge$1 = isEdge;
|
||||||
|
|
||||||
|
exports.default = ReactGraph;
|
||||||
|
exports.isEdge = isEdge$1;
|
||||||
|
exports.isNode = isNode$1;
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|||||||
+26
-21
@@ -1,14 +1,14 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import Graph from '../src';
|
import Graph, { isEdge } from '../src';
|
||||||
// import Graph from '../dist/ReactGraph';
|
// import Graph from '../dist/ReactGraph';
|
||||||
|
|
||||||
const SpecialNode = ({ data, styles }) => (
|
const SpecialNode = ({ data, onChange, styles }) => (
|
||||||
<div
|
<div
|
||||||
style={{ background: '#FFCC00', padding: 10, borderRadius: 30, ...styles }}
|
style={{ background: '#FFCC00', padding: 10, borderRadius: 30, ...styles }}
|
||||||
>
|
>
|
||||||
<div>I am <strong>special</strong>!<br />{data.label}</div>
|
<div>I am <strong>special</strong>!<br />{data.label}</div>
|
||||||
<select onChange={(e) => data.onChange(e.target.value, data)}>
|
<select onChange={(e) => onChange(e.target.value, data)}>
|
||||||
<option value="1">1</option>
|
<option value="1">1</option>
|
||||||
<option value="2">2</option>
|
<option value="2">2</option>
|
||||||
<option value="3">3</option>
|
<option value="3">3</option>
|
||||||
@@ -22,30 +22,35 @@ class App extends PureComponent {
|
|||||||
|
|
||||||
const onChange = (id, d) => {
|
const onChange = (id, d) => {
|
||||||
this.setState(prevState => (
|
this.setState(prevState => (
|
||||||
{ elements: prevState.elements.map(e => ({
|
{elements: prevState.elements.map(e => {
|
||||||
...e,
|
if (isEdge(e)) {
|
||||||
data: {
|
return e;
|
||||||
...e.data,
|
|
||||||
label: '6' === e.data.id ? `option ${id} selected` : e.data.label
|
|
||||||
}
|
}
|
||||||
}))
|
|
||||||
}
|
return {
|
||||||
|
...e,
|
||||||
|
data: {
|
||||||
|
...e.data,
|
||||||
|
label: '6' === e.id ? `option ${id} selected` : e.data.label
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
elements: [
|
elements: [
|
||||||
{ data: { id: '1', label: 'Tests', type: 'input' }, position: { x: 50, y: 50 } },
|
{ id: '1', type: 'input', data: { label: 'Tests' }, position: { x: 50, y: 50 } },
|
||||||
{ data: { id: '2', label: 'This is a node This is a node This is a node This is a node' }, position: { x: 100, y: 100 } },
|
{ id: '2', data: { label: 'This is a node This is a node This is a node This is a node' }, position: { x: 100, y: 100 } },
|
||||||
{ data: { id: '3', label: 'This is a node' }, position: { x: 100, y: 200 }, style: { background: '#222', color: '#fff' } },
|
{ id: '3', data: { label: 'This is a node' }, position: { x: 100, y: 200 }, style: { background: '#222', color: '#fff' } },
|
||||||
{ data: { id: '4', label: 'nody nodes', type: 'output' }, position: { x: 50, y: 300 } },
|
{ id: '4', type: 'output', data: { label: 'nody nodes' }, position: { x: 50, y: 300 } },
|
||||||
{ data: { id: '5', label: 'Another node', type: 'default' }, position: { x: 400, y: 300 } },
|
{ id: '5', type: 'default', data: { label: 'Another node'}, position: { x: 400, y: 300 } },
|
||||||
{ data: { id: '6', label: 'no option selected', type: 'special', onChange }, position: { x: 400, y: 400 } },
|
{ id: '6', type: 'special', onChange, data: { label: 'no option selected' }, position: { x: 400, y: 400 } },
|
||||||
{ data: { source: '1', target: '2', type: 'bezier', animated: true, style: { stroke: 'orange' } } },
|
{ source: '1', target: '2', type: 'bezier', animated: true, style: { stroke: 'orange' } },
|
||||||
{ data: { source: '2', target: '3' } },
|
{ source: '2', target: '3' },
|
||||||
{ data: { source: '3', target: '4' } },
|
{ source: '3', target: '4' },
|
||||||
{ data: { source: '3', target: '5' } },
|
{ source: '3', target: '5' },
|
||||||
{ data: { source: '5', target: '6' } }
|
{ source: '5', target: '6' }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const { targetNode, sourceNode } = props;
|
const { targetNode, sourceNode } = props;
|
||||||
const style = props.data ? props.data.style : {};
|
const style = props.style || {};
|
||||||
|
|
||||||
const sourceX = sourceNode.__rg.position.x + (sourceNode.__rg.width / 2);
|
const sourceX = sourceNode.__rg.position.x + (sourceNode.__rg.width / 2);
|
||||||
const sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
const sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const { targetNode, sourceNode } = props;
|
const { targetNode, sourceNode } = props;
|
||||||
const style = props.data ? props.data.style : {};
|
const style = props.style || {};
|
||||||
|
|
||||||
const sourceX = sourceNode.__rg.position.x + (sourceNode.__rg.width / 2);
|
const sourceX = sourceNode.__rg.position.x + (sourceNode.__rg.width / 2);
|
||||||
const sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
const sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.n
|
|||||||
|
|
||||||
export default EdgeComponent => memo((props) => {
|
export default EdgeComponent => memo((props) => {
|
||||||
const { state, dispatch } = useContext(GraphContext);
|
const { state, dispatch } = useContext(GraphContext);
|
||||||
const { data = {}, onClick } = props;
|
const { source, target, animated, type, onClick } = props;
|
||||||
const selected = state.selectedElements
|
const selected = state.selectedElements
|
||||||
.filter(e => isEdge(e))
|
.filter(e => isEdge(e))
|
||||||
.find(e => e.data.source === data.source && e.data.target === data.target);
|
.find(e => e.source === source && e.target === target);
|
||||||
const edgeClasses = cx('react-graph__edge', { selected, animated: data.animated });
|
const edgeClasses = cx('react-graph__edge', { selected, animated: animated });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<g
|
||||||
@@ -23,8 +23,8 @@ export default EdgeComponent => memo((props) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(setSelectedElements({ data }));
|
dispatch(setSelectedElements({ source, target }));
|
||||||
onClick({ data });
|
onClick({ source, target, type });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EdgeComponent {...props} />
|
<EdgeComponent {...props} />
|
||||||
|
|||||||
@@ -4,23 +4,23 @@ import { Consumer } from '../GraphContext';
|
|||||||
|
|
||||||
class EdgeRenderer extends PureComponent {
|
class EdgeRenderer extends PureComponent {
|
||||||
renderEdge(e, nodes, onElementClick) {
|
renderEdge(e, nodes, onElementClick) {
|
||||||
const edgeType = e.data.type || 'default';
|
const edgeType = e.type || 'default';
|
||||||
const sourceNode = nodes.find(n => n.data.id === e.data.source);
|
const sourceNode = nodes.find(n => n.id === e.source);
|
||||||
const targetNode = nodes.find(n => n.data.id === e.data.target);
|
const targetNode = nodes.find(n => n.id === e.target);
|
||||||
|
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
throw new Error(`couldn't create edge for source id: ${e.data.source}`);
|
throw new Error(`couldn't create edge for source id: ${e.source}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetNode) {
|
if (!targetNode) {
|
||||||
throw new Error(`couldn't create edge for source id: ${e.data.target}`);
|
throw new Error(`couldn't create edge for source id: ${e.target}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EdgeComponent = this.props.edgeTypes[edgeType] || this.props.edgeTypes.default;
|
const EdgeComponent = this.props.edgeTypes[edgeType] || this.props.edgeTypes.default;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EdgeComponent
|
<EdgeComponent
|
||||||
key={`${e.data.source}-${e.data.target}`}
|
key={`${e.source}-${e.target}`}
|
||||||
sourceNode={sourceNode}
|
sourceNode={sourceNode}
|
||||||
targetNode={targetNode}
|
targetNode={targetNode}
|
||||||
onClick={onElementClick}
|
onClick={onElementClick}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const Provider = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nextNodes = props.nodes.map(propNode => {
|
const nextNodes = props.nodes.map(propNode => {
|
||||||
const existingNode = state.nodes.find(n => n.data.id === propNode.data.id);
|
const existingNode = state.nodes.find(n => n.id === propNode.id);
|
||||||
|
|
||||||
if (existingNode) {
|
if (existingNode) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import cx from 'classnames';
|
|||||||
|
|
||||||
import { GraphContext } from '../../GraphContext';
|
import { GraphContext } from '../../GraphContext';
|
||||||
import { updateNodeData, updateNodePos, setSelectedElements } from '../../state/actions';
|
import { updateNodeData, updateNodePos, setSelectedElements } from '../../state/actions';
|
||||||
import { isEdge } from '../../graph-utils';
|
import { isNode } from '../../graph-utils';
|
||||||
|
|
||||||
const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||||
|
|
||||||
@@ -12,12 +12,10 @@ export default NodeComponent => memo((props) => {
|
|||||||
const nodeElement = useRef(null);
|
const nodeElement = useRef(null);
|
||||||
const { state, dispatch } = useContext(GraphContext);
|
const { state, dispatch } = useContext(GraphContext);
|
||||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||||
|
const { data, onClick, type, id, __rg } = props;
|
||||||
const { data, onClick, __rg } = props;
|
|
||||||
const { position } = __rg;
|
const { position } = __rg;
|
||||||
const { id } = data;
|
|
||||||
const [ x, y, k ] = state.transform;
|
const [ x, y, k ] = state.transform;
|
||||||
const selected = state.selectedElements.filter(e => !isEdge(e)).map(e => e.data.id).includes(id);
|
const selected = state.selectedElements.filter(isNode).map(e => e.id).includes(id);
|
||||||
const nodeClasses = cx('react-graph__node', { selected });
|
const nodeClasses = cx('react-graph__node', { selected });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -60,8 +58,8 @@ export default NodeComponent => memo((props) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(setSelectedElements({ data }));
|
dispatch(setSelectedElements({ data, id }));
|
||||||
onClick({ data, position });
|
onClick({ id, type, data, position });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Consumer } from '../GraphContext';
|
|||||||
class NodeRenderer extends PureComponent {
|
class NodeRenderer extends PureComponent {
|
||||||
|
|
||||||
renderNode(d, onElementClick) {
|
renderNode(d, onElementClick) {
|
||||||
const nodeType = d.data.type || 'default';
|
const nodeType = d.type || 'default';
|
||||||
if (!this.props.nodeTypes[nodeType]) {
|
if (!this.props.nodeTypes[nodeType]) {
|
||||||
console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`);
|
console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`);
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ class NodeRenderer extends PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeComponent
|
<NodeComponent
|
||||||
key={d.data.id}
|
key={d.id}
|
||||||
onClick={onElementClick}
|
onClick={onElementClick}
|
||||||
{...d}
|
{...d}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useState, useCallback, memo } from 'react';
|
import React, { useContext, useState, memo } from 'react';
|
||||||
import ReactDraggable from 'react-draggable';
|
import ReactDraggable from 'react-draggable';
|
||||||
|
|
||||||
import { GraphContext } from '../GraphContext';
|
import { GraphContext } from '../GraphContext';
|
||||||
@@ -14,7 +14,7 @@ function getStartPositions(elements) {
|
|||||||
y: node.__rg.position.y || node.position.x
|
y: node.__rg.position.y || node.position.x
|
||||||
};
|
};
|
||||||
|
|
||||||
res[node.data.id] = startPosition;
|
res[node.id] = startPosition;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}, {});
|
}, {});
|
||||||
@@ -48,9 +48,9 @@ export default memo(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state.selectedElements.filter(isNode).forEach(node => {
|
state.selectedElements.filter(isNode).forEach(node => {
|
||||||
dispatch(updateNodePos(node.data.id, {
|
dispatch(updateNodePos(node.id, {
|
||||||
x: startPositions[node.data.id].x + scaledClient.x - position.x - offset.x - x ,
|
x: startPositions[node.id].x + scaledClient.x - position.x - offset.x - x ,
|
||||||
y: startPositions[node.data.id].y + scaledClient.y - position.y - offset.y - y
|
y: startPositions[node.id].y + scaledClient.y - position.y - offset.y - y
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { parseElements, separateElements } from '../graph-utils';
|
||||||
|
import GraphView from '../GraphView';
|
||||||
|
import GlobalKeyHandler from '../GlobalKeyHandler';
|
||||||
|
import { Provider } from '../GraphContext';
|
||||||
|
|
||||||
|
import DefaultNode from '../NodeRenderer/NodeTypes/DefaultNode';
|
||||||
|
import InputNode from '../NodeRenderer/NodeTypes/InputNode';
|
||||||
|
import OutputNode from '../NodeRenderer/NodeTypes/OutputNode';
|
||||||
|
import { createNodeTypes } from '../NodeRenderer/utils';
|
||||||
|
|
||||||
|
import BezierEdge from '../EdgeRenderer/EdgeTypes/BezierEdge';
|
||||||
|
import StraightEdge from '../EdgeRenderer/EdgeTypes/StraightEdge';
|
||||||
|
import { createEdgeTypes } from '../EdgeRenderer/utils';
|
||||||
|
|
||||||
|
import '../style.css';
|
||||||
|
|
||||||
|
class ReactGraph extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.nodeTypes = createNodeTypes(props.nodeTypes);
|
||||||
|
this.edgeTypes = createEdgeTypes(props.edgeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
style, onElementClick, children, onLoad,
|
||||||
|
onMove, onChange, elements, onElementsRemove
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { nodes, edges } = elements
|
||||||
|
.map(parseElements)
|
||||||
|
.reduce(separateElements, {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={style} className="react-graph">
|
||||||
|
<Provider nodes={nodes} edges={edges} onElementClick={onElementClick}>
|
||||||
|
<GraphView
|
||||||
|
onLoad={onLoad}
|
||||||
|
onMove={onMove}
|
||||||
|
onChange={onChange}
|
||||||
|
nodeTypes={this.nodeTypes}
|
||||||
|
edgeTypes={this.edgeTypes}
|
||||||
|
/>
|
||||||
|
<GlobalKeyHandler
|
||||||
|
onElementsRemove={onElementsRemove}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactGraph.defaultProps = {
|
||||||
|
onElementClick: () => {},
|
||||||
|
onElementsRemove: () => {},
|
||||||
|
onLoad: () => {},
|
||||||
|
onMove: () => {},
|
||||||
|
onChange: () => {},
|
||||||
|
nodeTypes: {
|
||||||
|
input: InputNode,
|
||||||
|
default: DefaultNode,
|
||||||
|
output: OutputNode
|
||||||
|
},
|
||||||
|
edgeTypes: {
|
||||||
|
default: BezierEdge,
|
||||||
|
straight: StraightEdge
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReactGraph;
|
||||||
+6
-4
@@ -1,7 +1,9 @@
|
|||||||
export const isEdge = element => element.data && element.data.source && element.data.target;
|
export const isEdge = element => element.source && element.target;
|
||||||
export const isNode = element => element.data && !element.data.source && !element.data.target;
|
export const isNode = element => !element.source && !element.target;
|
||||||
|
|
||||||
export const parseElements = e => {
|
export const parseElements = e => {
|
||||||
|
e.type = e.type || 'default';
|
||||||
|
|
||||||
if (isEdge(e)) {
|
if (isEdge(e)) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -93,8 +95,8 @@ export const getNodesInside = (nodes, bbox, transform = [0, 0, 1]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getConnectedEdges = (nodes, edges) => {
|
export const getConnectedEdges = (nodes, edges) => {
|
||||||
const nodeIds = nodes.map(n => n.data.id);
|
const nodeIds = nodes.map(n => n.id);
|
||||||
return edges.filter(e => nodeIds.includes(e.data.source) || nodeIds.includes(e.data.target))
|
return edges.filter(e => nodeIds.includes(e.source) || nodeIds.includes(e.target))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
+5
-72
@@ -1,74 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import ReactGraph from './ReactGraph';
|
||||||
|
import { isNode as isNodeRG , isEdge as isEdgeRG } from './graph-utils';
|
||||||
|
|
||||||
import { parseElements, separateElements } from './graph-utils';
|
export const isNode = isNodeRG;
|
||||||
import GraphView from './GraphView';
|
export const isEdge = isEdgeRG;
|
||||||
import GlobalKeyHandler from './GlobalKeyHandler';
|
|
||||||
import { Provider } from './GraphContext';
|
|
||||||
|
|
||||||
import DefaultNode from './NodeRenderer/NodeTypes/DefaultNode';
|
export default ReactGraph;
|
||||||
import InputNode from './NodeRenderer/NodeTypes/InputNode';
|
|
||||||
import OutputNode from './NodeRenderer/NodeTypes/OutputNode';
|
|
||||||
import { createNodeTypes } from './NodeRenderer/utils';
|
|
||||||
|
|
||||||
import BezierEdge from './EdgeRenderer/EdgeTypes/BezierEdge';
|
|
||||||
import StraightEdge from './EdgeRenderer/EdgeTypes/StraightEdge';
|
|
||||||
import { createEdgeTypes } from './EdgeRenderer/utils';
|
|
||||||
|
|
||||||
import './style.css';
|
|
||||||
|
|
||||||
class ReactGraph extends PureComponent {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.nodeTypes = createNodeTypes(props.nodeTypes);
|
|
||||||
this.edgeTypes = createEdgeTypes(props.edgeTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
style, onElementClick, children, onLoad,
|
|
||||||
onMove, onChange, elements, onElementsRemove
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { nodes, edges } = elements
|
|
||||||
.map(parseElements)
|
|
||||||
.reduce(separateElements, {});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={style} className="react-graph">
|
|
||||||
<Provider nodes={nodes} edges={edges} onElementClick={onElementClick}>
|
|
||||||
<GraphView
|
|
||||||
onLoad={onLoad}
|
|
||||||
onMove={onMove}
|
|
||||||
onChange={onChange}
|
|
||||||
nodeTypes={this.nodeTypes}
|
|
||||||
edgeTypes={this.edgeTypes}
|
|
||||||
/>
|
|
||||||
<GlobalKeyHandler
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</Provider>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactGraph.defaultProps = {
|
|
||||||
onElementClick: () => {},
|
|
||||||
onElementsRemove: () => {},
|
|
||||||
onLoad: () => {},
|
|
||||||
onMove: () => {},
|
|
||||||
onChange: () => {},
|
|
||||||
nodeTypes: {
|
|
||||||
input: InputNode,
|
|
||||||
default: DefaultNode,
|
|
||||||
output: OutputNode
|
|
||||||
},
|
|
||||||
edgeTypes: {
|
|
||||||
default: BezierEdge,
|
|
||||||
straight: StraightEdge
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReactGraph;
|
|
||||||
+4
-4
@@ -40,7 +40,7 @@ export const reducer = (state, action) => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
nodes: state.nodes.map((n) => {
|
nodes: state.nodes.map((n) => {
|
||||||
if (n.data.id === action.payload.id) {
|
if (n.id === action.payload.id) {
|
||||||
n.__rg = {
|
n.__rg = {
|
||||||
...n.__rg,
|
...n.__rg,
|
||||||
...action.payload.data
|
...action.payload.data
|
||||||
@@ -54,7 +54,7 @@ export const reducer = (state, action) => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
nodes: state.nodes.map((n) => {
|
nodes: state.nodes.map((n) => {
|
||||||
if (n.data.id === action.payload.id) {
|
if (n.id === action.payload.id) {
|
||||||
n.__rg = {
|
n.__rg = {
|
||||||
...n.__rg,
|
...n.__rg,
|
||||||
position: action.payload.pos
|
position: action.payload.pos
|
||||||
@@ -99,8 +99,8 @@ export const reducer = (state, action) => {
|
|||||||
// unused
|
// unused
|
||||||
case REMOVE_NODES: {
|
case REMOVE_NODES: {
|
||||||
const { ids } = action.payload;
|
const { ids } = action.payload;
|
||||||
const nextEdges = state.edges.filter(e => !ids.includes(e.data.target) && !ids.includes(e.data.source));
|
const nextEdges = state.edges.filter(e => !ids.includes(e.target) && !ids.includes(e.source));
|
||||||
const nextNodes = state.nodes.filter(n => !ids.includes(n.data.id));
|
const nextNodes = state.nodes.filter(n => !ids.includes(n.id));
|
||||||
|
|
||||||
return { ...state, nodes: nextNodes, edges: nextEdges };
|
return { ...state, nodes: nextNodes, edges: nextEdges };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user