From 9d44f26c68b3f7c909a8c05250d03860a2ecee7a Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 24 Jul 2019 23:25:14 +0200 Subject: [PATCH] feat(edges): allow different types --- example/SimpleGraph.js | 2 +- .../{Edge.js => EdgeTypes/DefaultEdge.js} | 2 - src/EdgeRenderer/EdgeTypes/wrapEdge.js | 33 ++++++++++++ src/EdgeRenderer/index.js | 52 ++++++++++--------- src/EdgeRenderer/utils.js | 22 ++++++++ src/GraphContext/index.js | 4 +- src/GraphView/index.js | 6 ++- src/NodeRenderer/NodeTypes/wrapNode.js | 4 +- src/NodeRenderer/index.js | 8 +-- src/index.js | 16 ++++-- 10 files changed, 109 insertions(+), 40 deletions(-) rename src/EdgeRenderer/{Edge.js => EdgeTypes/DefaultEdge.js} (84%) create mode 100644 src/EdgeRenderer/EdgeTypes/wrapEdge.js create mode 100644 src/EdgeRenderer/utils.js diff --git a/example/SimpleGraph.js b/example/SimpleGraph.js index d8273bec..aead6e0d 100644 --- a/example/SimpleGraph.js +++ b/example/SimpleGraph.js @@ -85,7 +85,7 @@ class App extends PureComponent { return ( console.log(node)} + onElementClick={node => console.log('clicked', node)} onNodeRemove={nodeIds => console.log('remove', nodeIds)} style={{ width: '100%', height: '100%' }} onLoad={graphInstance => this.onLoad(graphInstance)} diff --git a/src/EdgeRenderer/Edge.js b/src/EdgeRenderer/EdgeTypes/DefaultEdge.js similarity index 84% rename from src/EdgeRenderer/Edge.js rename to src/EdgeRenderer/EdgeTypes/DefaultEdge.js index 9802307b..61ca5e9d 100644 --- a/src/EdgeRenderer/Edge.js +++ b/src/EdgeRenderer/EdgeTypes/DefaultEdge.js @@ -11,9 +11,7 @@ export default (props) => { return ( console.log('on edge click')} /> ); }; diff --git a/src/EdgeRenderer/EdgeTypes/wrapEdge.js b/src/EdgeRenderer/EdgeTypes/wrapEdge.js new file mode 100644 index 00000000..fc87957f --- /dev/null +++ b/src/EdgeRenderer/EdgeTypes/wrapEdge.js @@ -0,0 +1,33 @@ +import React, { useContext } from 'react'; +import ReactDraggable from 'react-draggable'; +import cx from 'classnames'; + +import { GraphContext } from '../../GraphContext'; +import { updateNodePos, setSelectedNodesIds } from '../../state/actions'; + +const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName); + +export default EdgeComponent => (props) => { + const { state, dispatch } = useContext(GraphContext); + const { data, onClick } = props; + const { id } = data; + const [ x, y, k ] = state.transform; + const selected = state.selectedNodeIds.includes(id); + const edgeClasses = cx('react-graph__edge', { selected }); + + return ( + { + if (isInputTarget(e)) { + return false; + } + + // dispatch(setSelectedNodesIds(id)); + onClick({ data }); + }} + > + + + ); +}; diff --git a/src/EdgeRenderer/index.js b/src/EdgeRenderer/index.js index 86a70d66..f8a12510 100644 --- a/src/EdgeRenderer/index.js +++ b/src/EdgeRenderer/index.js @@ -1,30 +1,34 @@ import React, { PureComponent } from 'react'; -import Edge from './Edge'; import { Consumer } from '../GraphContext'; -function renderEdge(e, nodes) { - const sourceNode = nodes.find(n => n.data.id === e.data.source); - const targetNode = nodes.find(n => n.data.id === e.data.target); - - if (!sourceNode) { - throw new Error(`couldn't create edge for source id: ${e.data.source}`); - } - - if (!targetNode) { - throw new Error(`couldn't create edge for source id: ${e.data.target}`); - } - - return ( - - ); -} - 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); + + if (!sourceNode) { + throw new Error(`couldn't create edge for source id: ${e.data.source}`); + } + + if (!targetNode) { + throw new Error(`couldn't create edge for source id: ${e.data.target}`); + } + + const EdgeComponent = this.props.edgeTypes[edgeType] || this.props.edgeTypes.default; + + return ( + + ); + } + render() { const { width, height } = this.props; @@ -34,7 +38,7 @@ class EdgeRenderer extends PureComponent { return ( - {({ state }) => ( + {({ state, onElementClick }) => ( - {state.edges.map(e => renderEdge(e, state.nodes))} + {state.edges.map(e => this.renderEdge(e, state.nodes, onElementClick))} )} diff --git a/src/EdgeRenderer/utils.js b/src/EdgeRenderer/utils.js new file mode 100644 index 00000000..70d56807 --- /dev/null +++ b/src/EdgeRenderer/utils.js @@ -0,0 +1,22 @@ +import DefaultEdge from './EdgeTypes/DefaultEdge'; +import wrapEdge from './EdgeTypes/wrapEdge'; + +export function createEdgeTypes(edgeTypes) { + const standardTypes = { + default: wrapEdge(edgeTypes.default || DefaultEdge), + }; + + const specialTypes = Object + .keys(DefaultEdge) + .filter(k => !['default'].includes(k)) + .reduce((res, key) => { + res[key] = wrapEdge(nodeTypes[key] || DefaultEdge); + + return res; + }, {}); + + return { + ...standardTypes, + ...specialTypes + }; +} diff --git a/src/GraphContext/index.js b/src/GraphContext/index.js index fec6e866..d164c655 100644 --- a/src/GraphContext/index.js +++ b/src/GraphContext/index.js @@ -19,7 +19,7 @@ function usePrevious(value) { export const Provider = (props) => { const { - onNodeClick, + onElementClick, children } = props; @@ -52,7 +52,7 @@ export const Provider = (props) => { }); const graphContext = { - onNodeClick, + onElementClick, state, dispatch }; diff --git a/src/GraphView/index.js b/src/GraphView/index.js index f81f757b..20d6ab5c 100644 --- a/src/GraphView/index.js +++ b/src/GraphView/index.js @@ -74,7 +74,11 @@ const GraphView = (props) => { return (
- + {shiftPressed && } {state.nodesSelectionActive && }
(props) => { const { state, dispatch } = useContext(GraphContext); const [offset, setOffset] = useState({ x: 0, y: 0 }); - const { data, onNodeClick, __rg } = props; + const { data, onClick, __rg } = props; const { position } = __rg; const { id } = data; const [ x, y, k ] = state.transform; @@ -70,7 +70,7 @@ export default NodeComponent => (props) => { } dispatch(setSelectedNodesIds(id)); - onNodeClick({ data, position }); + onClick({ data, position }); }} > diff --git a/src/NodeRenderer/index.js b/src/NodeRenderer/index.js index cbd501f2..12abfbae 100644 --- a/src/NodeRenderer/index.js +++ b/src/NodeRenderer/index.js @@ -4,7 +4,7 @@ import { Consumer } from '../GraphContext'; class NodeRenderer extends PureComponent { - renderNode(d, onNodeClick) { + renderNode(d, onElementClick) { const nodeType = d.data.type || 'default'; if (!this.props.nodeTypes[nodeType]) { console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`); @@ -15,7 +15,7 @@ class NodeRenderer extends PureComponent { return ( ); @@ -24,14 +24,14 @@ class NodeRenderer extends PureComponent { render() { return ( - {({ onNodeClick, state }) => ( + {({ onElementClick, state }) => (
- {state.nodes.map(d => this.renderNode(d, onNodeClick))} + {state.nodes.map(d => this.renderNode(d, onElementClick))}
)}
diff --git a/src/index.js b/src/index.js index b3207a8f..5558b9d0 100644 --- a/src/index.js +++ b/src/index.js @@ -4,11 +4,14 @@ import { parseElements, separateElements } from './graph-utils'; import GraphView from './GraphView'; import GlobalKeyHandler from './GlobalKeyHandler'; import { Provider } from './GraphContext'; -import { createNodeTypes } from './NodeRenderer/utils'; import DefaultNode from './NodeRenderer/NodeTypes/DefaultNode'; import InputNode from './NodeRenderer/NodeTypes/InputNode'; import OutputNode from './NodeRenderer/NodeTypes/OutputNode'; +import { createNodeTypes } from './NodeRenderer/utils'; + +import DefaultEdge from './EdgeRenderer/EdgeTypes/DefaultEdge'; +import { createEdgeTypes } from './EdgeRenderer/utils'; import './style.css'; @@ -17,11 +20,12 @@ class ReactGraph extends PureComponent { super(props); this.nodeTypes = createNodeTypes(props.nodeTypes); + this.edgeTypes = createEdgeTypes(props.edgeTypes); } render() { const { - style, onNodeClick, children, onLoad, onMove, onChange, elements, onNodeRemove + style, onElementClick, children, onLoad, onMove, onChange, elements, onNodeRemove } = this.props; const { nodes, edges } = elements @@ -30,12 +34,13 @@ class ReactGraph extends PureComponent { return (
- + {}, + onElementClick: () => {}, onNodeRemove: () => {}, onLoad: () => {}, onMove: () => {}, @@ -57,6 +62,9 @@ ReactGraph.defaultProps = { input: InputNode, default: DefaultNode, output: OutputNode + }, + edgeTypes: { + default: DefaultEdge } };