refactor(nodes,edges): data structure
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
export default (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 sourceY = sourceNode.__rg.position.y + sourceNode.__rg.height;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
export default (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 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) => {
|
||||
const { state, dispatch } = useContext(GraphContext);
|
||||
const { data = {}, onClick } = props;
|
||||
const { source, target, animated, type, onClick } = props;
|
||||
const selected = state.selectedElements
|
||||
.filter(e => isEdge(e))
|
||||
.find(e => e.data.source === data.source && e.data.target === data.target);
|
||||
const edgeClasses = cx('react-graph__edge', { selected, animated: data.animated });
|
||||
.find(e => e.source === source && e.target === target);
|
||||
const edgeClasses = cx('react-graph__edge', { selected, animated: animated });
|
||||
|
||||
return (
|
||||
<g
|
||||
@@ -23,8 +23,8 @@ export default EdgeComponent => memo((props) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
dispatch(setSelectedElements({ data }));
|
||||
onClick({ data });
|
||||
dispatch(setSelectedElements({ source, target }));
|
||||
onClick({ source, target, type });
|
||||
}}
|
||||
>
|
||||
<EdgeComponent {...props} />
|
||||
|
||||
@@ -4,23 +4,23 @@ import { Consumer } from '../GraphContext';
|
||||
|
||||
class EdgeRenderer extends PureComponent {
|
||||
renderEdge(e, nodes, onElementClick) {
|
||||
const edgeType = e.data.type || 'default';
|
||||
const sourceNode = nodes.find(n => n.data.id === e.data.source);
|
||||
const targetNode = nodes.find(n => n.data.id === e.data.target);
|
||||
const edgeType = e.type || 'default';
|
||||
const sourceNode = nodes.find(n => n.id === e.source);
|
||||
const targetNode = nodes.find(n => n.id === e.target);
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
return (
|
||||
<EdgeComponent
|
||||
key={`${e.data.source}-${e.data.target}`}
|
||||
key={`${e.source}-${e.target}`}
|
||||
sourceNode={sourceNode}
|
||||
targetNode={targetNode}
|
||||
onClick={onElementClick}
|
||||
|
||||
@@ -27,7 +27,7 @@ export const Provider = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
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) {
|
||||
return {
|
||||
|
||||
@@ -4,7 +4,7 @@ import cx from 'classnames';
|
||||
|
||||
import { GraphContext } from '../../GraphContext';
|
||||
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);
|
||||
|
||||
@@ -12,12 +12,10 @@ export default NodeComponent => memo((props) => {
|
||||
const nodeElement = useRef(null);
|
||||
const { state, dispatch } = useContext(GraphContext);
|
||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||
|
||||
const { data, onClick, __rg } = props;
|
||||
const { data, onClick, type, id, __rg } = props;
|
||||
const { position } = __rg;
|
||||
const { id } = data;
|
||||
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 });
|
||||
|
||||
useEffect(() => {
|
||||
@@ -60,8 +58,8 @@ export default NodeComponent => memo((props) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
dispatch(setSelectedElements({ data }));
|
||||
onClick({ data, position });
|
||||
dispatch(setSelectedElements({ data, id }));
|
||||
onClick({ id, type, data, position });
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Consumer } from '../GraphContext';
|
||||
class NodeRenderer extends PureComponent {
|
||||
|
||||
renderNode(d, onElementClick) {
|
||||
const nodeType = d.data.type || 'default';
|
||||
const nodeType = d.type || 'default';
|
||||
if (!this.props.nodeTypes[nodeType]) {
|
||||
console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class NodeRenderer extends PureComponent {
|
||||
|
||||
return (
|
||||
<NodeComponent
|
||||
key={d.data.id}
|
||||
key={d.id}
|
||||
onClick={onElementClick}
|
||||
{...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 { GraphContext } from '../GraphContext';
|
||||
@@ -14,7 +14,7 @@ function getStartPositions(elements) {
|
||||
y: node.__rg.position.y || node.position.x
|
||||
};
|
||||
|
||||
res[node.data.id] = startPosition;
|
||||
res[node.id] = startPosition;
|
||||
|
||||
return res;
|
||||
}, {});
|
||||
@@ -48,9 +48,9 @@ export default memo(() => {
|
||||
};
|
||||
|
||||
state.selectedElements.filter(isNode).forEach(node => {
|
||||
dispatch(updateNodePos(node.data.id, {
|
||||
x: startPositions[node.data.id].x + scaledClient.x - position.x - offset.x - x ,
|
||||
y: startPositions[node.data.id].y + scaledClient.y - position.y - offset.y - y
|
||||
dispatch(updateNodePos(node.id, {
|
||||
x: startPositions[node.id].x + scaledClient.x - position.x - offset.x - x ,
|
||||
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 isNode = element => element.data && !element.data.source && !element.data.target;
|
||||
export const isEdge = element => element.source && element.target;
|
||||
export const isNode = element => !element.source && !element.target;
|
||||
|
||||
export const parseElements = e => {
|
||||
e.type = e.type || 'default';
|
||||
|
||||
if (isEdge(e)) {
|
||||
return e;
|
||||
}
|
||||
@@ -93,8 +95,8 @@ export const getNodesInside = (nodes, bbox, transform = [0, 0, 1]) => {
|
||||
};
|
||||
|
||||
export const getConnectedEdges = (nodes, edges) => {
|
||||
const nodeIds = nodes.map(n => n.data.id);
|
||||
return edges.filter(e => nodeIds.includes(e.data.source) || nodeIds.includes(e.data.target))
|
||||
const nodeIds = nodes.map(n => n.id);
|
||||
return edges.filter(e => nodeIds.includes(e.source) || nodeIds.includes(e.target))
|
||||
}
|
||||
|
||||
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';
|
||||
import GraphView from './GraphView';
|
||||
import GlobalKeyHandler from './GlobalKeyHandler';
|
||||
import { Provider } from './GraphContext';
|
||||
export const isNode = isNodeRG;
|
||||
export const isEdge = isEdgeRG;
|
||||
|
||||
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;
|
||||
export default ReactGraph;
|
||||
+4
-4
@@ -40,7 +40,7 @@ export const reducer = (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
nodes: state.nodes.map((n) => {
|
||||
if (n.data.id === action.payload.id) {
|
||||
if (n.id === action.payload.id) {
|
||||
n.__rg = {
|
||||
...n.__rg,
|
||||
...action.payload.data
|
||||
@@ -54,7 +54,7 @@ export const reducer = (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
nodes: state.nodes.map((n) => {
|
||||
if (n.data.id === action.payload.id) {
|
||||
if (n.id === action.payload.id) {
|
||||
n.__rg = {
|
||||
...n.__rg,
|
||||
position: action.payload.pos
|
||||
@@ -99,8 +99,8 @@ export const reducer = (state, action) => {
|
||||
// unused
|
||||
case REMOVE_NODES: {
|
||||
const { ids } = action.payload;
|
||||
const nextEdges = state.edges.filter(e => !ids.includes(e.data.target) && !ids.includes(e.data.source));
|
||||
const nextNodes = state.nodes.filter(n => !ids.includes(n.data.id));
|
||||
const nextEdges = state.edges.filter(e => !ids.includes(e.target) && !ids.includes(e.source));
|
||||
const nextNodes = state.nodes.filter(n => !ids.includes(n.id));
|
||||
|
||||
return { ...state, nodes: nextNodes, edges: nextEdges };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user