fix(connection): always scale handle dimensions
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
export default (props) => {
|
||||
const [sourceNode, setSourceNode] = useState(null);
|
||||
useEffect(() => {
|
||||
setSourceNode(props.nodes.find(n => n.id === props.connectionSourceId));
|
||||
}, []);
|
||||
|
||||
if (!sourceNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const style = props.style || {};
|
||||
const className = cx('react-graph__edge', 'connector', props.className);
|
||||
|
||||
const sourceHandle = sourceNode.__rg.handleBounds.source;
|
||||
const sourceHandleX = sourceHandle ? sourceHandle.x + (sourceHandle.width / 2) : sourceNode.__rg.width / 2;
|
||||
const sourceHandleY = sourceHandle ? sourceHandle.y + (sourceHandle.height / 2) : sourceNode.__rg.height;
|
||||
const sourceX = sourceNode.__rg.position.x + sourceHandleX;
|
||||
const sourceY = sourceNode.__rg.position.y + sourceHandleY;
|
||||
|
||||
const targetX = props.connectionPosition.x;
|
||||
const targetY = props.connectionPosition.y;
|
||||
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
const dAttr = `M${sourceX},${sourceY} C${sourceX},${centerY} ${targetX},${centerY} ${targetX},${targetY}`;
|
||||
|
||||
return (
|
||||
<path
|
||||
className={className}
|
||||
d={dAttr}
|
||||
{...style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Consumer } from '../GraphContext';
|
||||
import ConnectorEdge from '../ConnectorEdge';
|
||||
|
||||
class EdgeRenderer extends PureComponent {
|
||||
renderEdge(e, nodes, onElementClick) {
|
||||
@@ -30,7 +31,7 @@ class EdgeRenderer extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { width, height } = this.props;
|
||||
const { width, height, onElementClick } = this.props;
|
||||
|
||||
if (!width) {
|
||||
return null;
|
||||
@@ -38,7 +39,7 @@ class EdgeRenderer extends PureComponent {
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{({ state, onElementClick }) => (
|
||||
{({ state }) => (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
@@ -49,6 +50,13 @@ class EdgeRenderer extends PureComponent {
|
||||
>
|
||||
{state.edges.map(e => this.renderEdge(e, state.nodes, onElementClick))}
|
||||
</g>
|
||||
{state.isConnecting && (
|
||||
<ConnectorEdge
|
||||
nodes={state.nodes}
|
||||
connectionSourceId={state.connectionSourceId}
|
||||
connectionPosition={state.connectionPosition}
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
)}
|
||||
</Consumer>
|
||||
|
||||
@@ -88,6 +88,7 @@ const GraphView = memo((props) => {
|
||||
width={state.width}
|
||||
height={state.height}
|
||||
edgeTypes={props.edgeTypes}
|
||||
onElementClick={props.onElementClick}
|
||||
/>
|
||||
{shiftPressed && <UserSelection />}
|
||||
{state.nodesSelectionActive && <NodesSelection />}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { Consumer } from '../NodeIdContext'
|
||||
import { GraphContext } from '../../GraphContext';
|
||||
import { setConnecting, setConnectionPos } from '../../state/actions';
|
||||
|
||||
function onDragStart(evt, nodeId) {
|
||||
function onDragStart(evt, nodeId, dispatch) {
|
||||
evt.dataTransfer.setData("text/plain", nodeId);
|
||||
|
||||
// dispatch(setConnecting({ isConnecting: true, connectionSourceId: nodeId }));
|
||||
}
|
||||
|
||||
function onDragStop(evt, dispatch) {
|
||||
// dispatch(setConnecting({ isConnecting: false }));
|
||||
}
|
||||
|
||||
function onDrag(evt, dispatch) {
|
||||
// dispatch(setConnectionPos({ x: evt.clientX, y: evt.clientY }));
|
||||
}
|
||||
|
||||
export default memo(({ source, target, ...rest }) => {
|
||||
const { dispatch } = useContext(GraphContext)
|
||||
const handleClasses = cx(
|
||||
'react-graph__handle',
|
||||
rest.className,
|
||||
@@ -30,7 +43,9 @@ export default memo(({ source, target, ...rest }) => {
|
||||
className={handleClasses}
|
||||
{...rest}
|
||||
draggable
|
||||
onDragStart={evt => onDragStart(evt, nodeId)}
|
||||
onDragStart={evt => onDragStart(evt, nodeId, dispatch)}
|
||||
onDrag={evt => onDrag(evt, dispatch)}
|
||||
onDragEnd={evt => onDragStop(evt, dispatch)}
|
||||
/>
|
||||
)}
|
||||
</Consumer>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Provider } from '../NodeIdContext';
|
||||
const isInput = e => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||
const isHandle = e => e.target.className && e.target.className.includes('source');
|
||||
|
||||
const getHandleBounds = (sel, nodeElement, parentBounds) => {
|
||||
const getHandleBounds = (sel, nodeElement, parentBounds, k) => {
|
||||
const handle = nodeElement.querySelector(sel);
|
||||
|
||||
if (!handle) {
|
||||
@@ -18,11 +18,14 @@ const getHandleBounds = (sel, nodeElement, parentBounds) => {
|
||||
}
|
||||
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const unscaledWith = Math.round(bounds.width * (1 / k));
|
||||
const unscaledHeight = Math.round(bounds.height * (1 / k));
|
||||
|
||||
return {
|
||||
x: bounds.x - parentBounds.x,
|
||||
y: bounds.y - parentBounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
x: (bounds.x - parentBounds.x) * (1 / k),
|
||||
y: (bounds.y - parentBounds.y) * (1 / k),
|
||||
width: unscaledWith,
|
||||
height: unscaledHeight
|
||||
};
|
||||
};
|
||||
|
||||
@@ -49,8 +52,8 @@ export default NodeComponent => memo((props) => {
|
||||
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)
|
||||
source: getHandleBounds('.source', nodeElement.current, bounds, k),
|
||||
target: getHandleBounds('.target', nodeElement.current, bounds, k)
|
||||
};
|
||||
|
||||
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight, handleBounds }));
|
||||
|
||||
@@ -37,7 +37,7 @@ class ReactGraph extends PureComponent {
|
||||
|
||||
return (
|
||||
<div style={style} className="react-graph">
|
||||
<Provider nodes={nodes} edges={edges} onElementClick={onElementClick}>
|
||||
<Provider nodes={nodes} edges={edges}>
|
||||
<GraphView
|
||||
onLoad={onLoad}
|
||||
onMove={onMove}
|
||||
|
||||
+11
-2
@@ -2,7 +2,8 @@ import {
|
||||
UPDATE_TRANSFORM, UPDATE_SIZE, SET_NODES, SET_EDGES,
|
||||
UPDATE_NODE_DATA, UPDATE_NODE_POS, INIT_D3, FIT_VIEW,
|
||||
UPDATE_SELECTION, SET_SELECTION, SET_NODES_SELECTION,
|
||||
SET_SELECTED_ELEMENTS, REMOVE_NODES, ZOOM_IN, ZOOM_OUT
|
||||
SET_SELECTED_ELEMENTS, REMOVE_NODES, ZOOM_IN, ZOOM_OUT,
|
||||
SET_CONNECTING, SET_CONNECTION_POS
|
||||
} from './index';
|
||||
|
||||
export const updateTransform = (transform) => {
|
||||
@@ -90,7 +91,7 @@ export const removeNodes = (ids) => {
|
||||
const idArray = Array.isArray(ids) ? ids : [ids];
|
||||
|
||||
return { type: REMOVE_NODES, payload: { ids: idArray } };
|
||||
}
|
||||
};
|
||||
|
||||
export const updateSelection = (selection) => {
|
||||
return {
|
||||
@@ -100,3 +101,11 @@ export const updateSelection = (selection) => {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const setConnecting = ({ isConnecting, connectionSourceId }) => {
|
||||
return { type: SET_CONNECTING, payload: { isConnecting, connectionSourceId }};
|
||||
};
|
||||
|
||||
export const setConnectionPos = ({ x, y }) => {
|
||||
return { type: SET_CONNECTION_POS, payload: { connectionPosition: { x, y } } };
|
||||
}
|
||||
@@ -17,6 +17,8 @@ export const SET_SELECTION = 'SET_SELECTION';
|
||||
export const SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
||||
export const SET_SELECTED_ELEMENTS = 'SET_SELECTED_ELEMENTS';
|
||||
export const REMOVE_NODES = 'REMOVE_NODES';
|
||||
export const SET_CONNECTING = 'SET_CONNECTING';
|
||||
export const SET_CONNECTION_POS = 'SET_CONNECTION_POS';
|
||||
|
||||
export const initialState = {
|
||||
width: 0,
|
||||
@@ -34,6 +36,10 @@ export const initialState = {
|
||||
nodesSelectionActive: false,
|
||||
selectionActive: false,
|
||||
selection: {},
|
||||
|
||||
isConnecting: false,
|
||||
connectionSourceId: null,
|
||||
connectionPosition: { x: 0, y: 0 }
|
||||
};
|
||||
|
||||
export const reducer = (state, action) => {
|
||||
@@ -126,6 +132,8 @@ export const reducer = (state, action) => {
|
||||
case UPDATE_SIZE:
|
||||
case SET_SELECTION:
|
||||
case SET_SELECTED_ELEMENTS:
|
||||
case SET_CONNECTING:
|
||||
case SET_CONNECTION_POS:
|
||||
return { ...state, ...action.payload };
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
stroke-dasharray: 5;
|
||||
animation: dashdraw 0.5s linear infinite;
|
||||
}
|
||||
|
||||
&.connector {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dashdraw {
|
||||
|
||||
Reference in New Issue
Block a user