feat(rg): add onConnect callback
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
import React, { memo } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
export default memo(({ input, output, ...rest }) => {
|
||||
const handleClasses = cx(
|
||||
'react-graph__handle', { input, output }
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={handleClasses}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { memo } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { Consumer } from '../NodeIdContext'
|
||||
|
||||
function onDragStart(evt, nodeId) {
|
||||
evt.dataTransfer.setData("text/plain", nodeId);
|
||||
}
|
||||
|
||||
export default memo(({ source, target, ...rest }) => {
|
||||
const handleClasses = cx(
|
||||
'react-graph__handle',
|
||||
{ source, target }
|
||||
);
|
||||
|
||||
if (target) {
|
||||
return (
|
||||
<div
|
||||
className={handleClasses}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{nodeId => (
|
||||
<div
|
||||
className={handleClasses}
|
||||
{...rest}
|
||||
draggable
|
||||
onDragStart={evt => onDragStart(evt, nodeId)}
|
||||
/>
|
||||
)}
|
||||
</Consumer>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import BaseHandle from './BaseHandle';
|
||||
|
||||
export default props => <BaseHandle source {...props} />;
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import BaseHandle from './BaseHandle';
|
||||
|
||||
export default props => <BaseHandle target {...props} />;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
const NodeIdContext = createContext(null);
|
||||
|
||||
export const Provider = NodeIdContext.Provider;
|
||||
export const Consumer = NodeIdContext.Consumer;
|
||||
|
||||
export default NodeIdContext;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import Handle from '../Handle';
|
||||
import TargetHandle from '../HandleTypes/TargetHandle';
|
||||
import SourceHandle from '../HandleTypes/SourceHandle';
|
||||
|
||||
const nodeStyles = {
|
||||
background: '#ff6060',
|
||||
@@ -11,8 +12,8 @@ const nodeStyles = {
|
||||
|
||||
export default ({ data, style }) => (
|
||||
<div style={{ ...nodeStyles, ...style }}>
|
||||
<Handle input />
|
||||
<TargetHandle />
|
||||
{data.label}
|
||||
<Handle output />
|
||||
<SourceHandle />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import Handle from '../Handle';
|
||||
import SourceHandle from '../HandleTypes/SourceHandle';
|
||||
|
||||
const nodeStyles = {
|
||||
background: '#9999ff',
|
||||
@@ -15,6 +15,6 @@ export default ({ data, style }) => (
|
||||
className="react-graph__node-inner"
|
||||
>
|
||||
{data.label}
|
||||
<Handle output />
|
||||
<SourceHandle />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import Handle from '../Handle';
|
||||
import TargetHandle from '../HandleTypes/TargetHandle';
|
||||
|
||||
const nodeStyles = {
|
||||
background: '#55dd99',
|
||||
@@ -11,7 +11,7 @@ const nodeStyles = {
|
||||
|
||||
export default ({ data, style }) => (
|
||||
<div style={{ ...nodeStyles, ...style }}>
|
||||
<Handle input />
|
||||
<TargetHandle />
|
||||
{data.label}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,14 +5,38 @@ import cx from 'classnames';
|
||||
import { GraphContext } from '../../GraphContext';
|
||||
import { updateNodeData, updateNodePos, setSelectedElements } from '../../state/actions';
|
||||
import { isNode } from '../../graph-utils';
|
||||
import { Provider } from '../NodeIdContext';
|
||||
|
||||
const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||
const isInput = e => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||
const isHandle = e => e.target.className.includes('source');
|
||||
|
||||
const getHandleBounds = (sel, nodeElement, parentBounds) => {
|
||||
const handle = nodeElement.querySelector(sel);
|
||||
|
||||
if (!handle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
return {
|
||||
x: bounds.x - parentBounds.x,
|
||||
y: bounds.y - parentBounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
};
|
||||
};
|
||||
|
||||
const onDragOver = evt => {
|
||||
evt.preventDefault();
|
||||
|
||||
evt.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
|
||||
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, type, id, __rg } = props;
|
||||
const { data, onClick, type, id, __rg, onConnect } = props;
|
||||
const { position } = __rg;
|
||||
const [ x, y, k ] = state.transform;
|
||||
const selected = state.selectedElements.filter(isNode).map(e => e.id).includes(id);
|
||||
@@ -22,12 +46,16 @@ export default NodeComponent => memo((props) => {
|
||||
const bounds = nodeElement.current.getBoundingClientRect();
|
||||
const unscaledWith = Math.round(bounds.width * (1 / k));
|
||||
const unscaledHeight = Math.round(bounds.height * (1 / k));
|
||||
const handleBounds = {
|
||||
source: getHandleBounds('.source', nodeElement.current, bounds),
|
||||
target: getHandleBounds('.target', nodeElement.current, bounds)
|
||||
};
|
||||
|
||||
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight }));
|
||||
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight, handleBounds }));
|
||||
}, []);
|
||||
|
||||
const onStart = (evt) => {
|
||||
if (isInputTarget(evt)) {
|
||||
if (isInput(evt) || isHandle(evt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,7 +82,7 @@ export default NodeComponent => memo((props) => {
|
||||
};
|
||||
|
||||
const onNodeClick = (evt) => {
|
||||
if (isInputTarget(evt)) {
|
||||
if (isInput(evt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -62,6 +90,18 @@ export default NodeComponent => memo((props) => {
|
||||
onClick({ id, type, data, position });
|
||||
};
|
||||
|
||||
const onDrop = (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
const sourceId = evt.dataTransfer.getData('text/plain');
|
||||
|
||||
if (sourceId === id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onConnect({ sourceId, targetId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactDraggable.DraggableCore
|
||||
grid={[1, 1]}
|
||||
@@ -70,12 +110,16 @@ export default NodeComponent => memo((props) => {
|
||||
scale={k}
|
||||
>
|
||||
<div
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragOver}
|
||||
className={nodeClasses}
|
||||
ref={nodeElement}
|
||||
style={{ zIndex: selected ? 10 : 3, transform: `translate(${position.x}px,${position.y}px)` }}
|
||||
onClick={onNodeClick}
|
||||
>
|
||||
<NodeComponent {...props} selected={selected} />
|
||||
<Provider value={id}>
|
||||
<NodeComponent {...props} selected={selected} />
|
||||
</Provider>
|
||||
</div>
|
||||
</ReactDraggable.DraggableCore>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Consumer } from '../GraphContext';
|
||||
|
||||
class NodeRenderer extends PureComponent {
|
||||
|
||||
renderNode(d, onElementClick) {
|
||||
renderNode(d) {
|
||||
const nodeType = d.type || 'default';
|
||||
if (!this.props.nodeTypes[nodeType]) {
|
||||
console.warn(`No node type found for type "${nodeType}". Using fallback type "default".`);
|
||||
@@ -15,7 +15,8 @@ class NodeRenderer extends PureComponent {
|
||||
return (
|
||||
<NodeComponent
|
||||
key={d.id}
|
||||
onClick={onElementClick}
|
||||
onClick={this.props.onElementClick}
|
||||
onConnect={this.props.onConnect}
|
||||
{...d}
|
||||
/>
|
||||
);
|
||||
@@ -24,14 +25,14 @@ class NodeRenderer extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<Consumer>
|
||||
{({ onElementClick, state }) => (
|
||||
{({ state }) => (
|
||||
<div
|
||||
className="react-graph__nodes"
|
||||
style={{
|
||||
transform: `translate(${state.transform[0]}px,${state.transform[1]}px) scale(${state.transform[2]})`
|
||||
}}
|
||||
>
|
||||
{state.nodes.map(d => this.renderNode(d, onElementClick))}
|
||||
{state.nodes.map(d => this.renderNode(d))}
|
||||
</div>
|
||||
)}
|
||||
</Consumer>
|
||||
|
||||
Reference in New Issue
Block a user