fix(connectionline): draw line from handle where user starts closes #270

This commit is contained in:
moklick
2020-06-01 23:14:14 +02:00
parent 08948df140
commit ce32d34c6b
6 changed files with 41 additions and 30 deletions
+9 -11
View File
@@ -1,10 +1,11 @@
import React, { useEffect, useState, CSSProperties } from 'react';
import cx from 'classnames';
import { ElementId, Node, Transform, HandleElement, Position, ConnectionLineType } from '../../types';
import { ElementId, Node, Transform, HandleElement, Position, ConnectionLineType, HandleType } from '../../types';
interface ConnectionLineProps {
connectionSourceId: ElementId;
connectionNodeId: ElementId;
connectionHandleType: HandleType;
connectionPositionX: number;
connectionPositionY: number;
connectionLineType: ConnectionLineType;
@@ -16,7 +17,8 @@ interface ConnectionLineProps {
}
export default ({
connectionSourceId,
connectionNodeId,
connectionHandleType,
connectionLineStyle,
connectionPositionX,
connectionPositionY,
@@ -27,8 +29,8 @@ export default ({
isInteractive,
}: ConnectionLineProps) => {
const [sourceNode, setSourceNode] = useState<Node | null>(null);
const hasHandleId = connectionSourceId.includes('__');
const sourceIdSplitted = connectionSourceId.split('__');
const hasHandleId = connectionNodeId.includes('__');
const sourceIdSplitted = connectionNodeId.split('__');
const nodeId = sourceIdSplitted[0];
const handleId = hasHandleId ? sourceIdSplitted[1] : null;
@@ -42,14 +44,10 @@ export default ({
}
const connectionLineClasses: string = cx('react-flow__connection', className);
const hasSource = sourceNode.__rg.handleBounds.source !== null;
// output nodes don't have source handles so we need to use the target one
const handleKey = hasSource ? 'source' : 'target';
const sourceHandle = handleId
? sourceNode.__rg.handleBounds[handleKey].find((d: HandleElement) => d.id === handleId)
: sourceNode.__rg.handleBounds[handleKey][0];
? sourceNode.__rg.handleBounds[connectionHandleType].find((d: HandleElement) => d.id === handleId)
: sourceNode.__rg.handleBounds[connectionHandleType][0];
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;
+9 -9
View File
@@ -1,17 +1,17 @@
import React, { memo, MouseEvent as ReactMouseEvent, CSSProperties } from 'react';
import cx from 'classnames';
import { HandleType, ElementId, Position, XYPosition, OnConnectFunc, Connection } from '../../types';
import { HandleType, ElementId, Position, XYPosition, OnConnectFunc, Connection, SetConnectionId } from '../../types';
type ValidConnectionFunc = (connection: Connection) => boolean;
type SetSourceIdFunc = (nodeId: ElementId | null) => void;
type SetSourceIdFunc = (params: SetConnectionId) => void;
interface BaseHandleProps {
type: HandleType;
nodeId: ElementId;
onConnect: OnConnectFunc;
position: Position;
setSourceId: SetSourceIdFunc;
setConnectionNodeId: SetSourceIdFunc;
setPosition: (pos: XYPosition) => void;
isValidConnection: ValidConnectionFunc;
id?: ElementId | boolean;
@@ -29,8 +29,8 @@ type Result = {
function onMouseDown(
evt: ReactMouseEvent,
nodeId: ElementId,
setSourceId: SetSourceIdFunc,
setPosition: (pos: XYPosition) => any,
setConnectionNodeId: SetSourceIdFunc,
setPosition: (pos: XYPosition) => void,
onConnect: OnConnectFunc,
isTarget: boolean,
isValidConnection: ValidConnectionFunc
@@ -48,7 +48,7 @@ function onMouseDown(
x: evt.clientX - containerBounds.left,
y: evt.clientY - containerBounds.top,
});
setSourceId(nodeId);
setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleType: isTarget ? 'target' : 'source' });
function resetRecentHandle(): void {
if (!recentHoveredHandle) {
@@ -120,7 +120,7 @@ function onMouseDown(
}
resetRecentHandle();
setSourceId(null);
setConnectionNodeId({ connectionNodeId: null, connectionHandleType: null });
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
@@ -136,7 +136,7 @@ const BaseHandle = memo(
nodeId,
onConnect,
position,
setSourceId,
setConnectionNodeId,
setPosition,
className,
id = false,
@@ -157,7 +157,7 @@ const BaseHandle = memo(
data-handlepos={position}
className={handleClasses}
onMouseDown={(evt) =>
onMouseDown(evt, nodeIdWithHandleId, setSourceId, setPosition, onConnect, isTarget, isValidConnection)
onMouseDown(evt, nodeIdWithHandleId, setConnectionNodeId, setPosition, onConnect, isTarget, isValidConnection)
}
{...rest}
/>
+2 -2
View File
@@ -18,7 +18,7 @@ const Handle = memo(
}: HandleProps) => {
const nodeId = useContext(NodeIdContext) as ElementId;
const setPosition = useStoreActions((a) => a.setConnectionPosition);
const setSourceId = useStoreActions((a) => a.setConnectionSourceId);
const setConnectionNodeId = useStoreActions((a) => a.setConnectionNodeId);
const onConnectAction = useStoreState((s) => s.onConnect);
const onConnectExtended = (params: Connection) => {
onConnectAction(params);
@@ -29,7 +29,7 @@ const Handle = memo(
<BaseHandle
nodeId={nodeId}
setPosition={setPosition}
setSourceId={setSourceId}
setConnectionNodeId={setConnectionNodeId}
onConnect={onConnectExtended}
type={type}
position={position}
+6 -3
View File
@@ -190,7 +190,8 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
const [tX, tY, tScale] = useStoreState((s) => s.transform);
const edges = useStoreState((s) => s.edges);
const nodes = useStoreState((s) => s.nodes);
const connectionSourceId = useStoreState((s) => s.connectionSourceId);
const connectionNodeId = useStoreState((s) => s.connectionNodeId);
const connectionHandleType = useStoreState((s) => s.connectionHandleType);
const connectionPosition = useStoreState((s) => s.connectionPosition);
const selectedElements = useStoreState((s) => s.selectedElements);
const isInteractive = useStoreState((s) => s.isInteractive);
@@ -202,15 +203,17 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
}
const transformStyle = `translate(${tX},${tY}) scale(${tScale})`;
const renderConnectionLine = connectionNodeId && connectionHandleType;
return (
<svg width={width} height={height} className="react-flow__edges">
<g transform={transformStyle}>
{edges.map((e: Edge) => renderEdge(e, props, nodes, selectedElements, isInteractive))}
{connectionSourceId && (
{renderConnectionLine && (
<ConnectionLine
nodes={nodes}
connectionSourceId={connectionSourceId}
connectionNodeId={connectionNodeId!}
connectionHandleType={connectionHandleType!}
connectionPositionX={connectionPosition.x}
connectionPositionY={connectionPosition.y}
transform={[tX, tY, tScale]}
+10 -5
View File
@@ -17,6 +17,8 @@ import {
XYPosition,
OnConnectFunc,
SelectionRect,
HandleType,
SetConnectionId,
} from '../types';
type TransformXYK = {
@@ -69,7 +71,8 @@ export interface StoreModel {
userSelectionRect: SelectionRect;
connectionSourceId: ElementId | null;
connectionNodeId: ElementId | null;
connectionHandleType: HandleType | null;
connectionPosition: XYPosition;
snapToGrid: boolean;
@@ -105,7 +108,7 @@ export interface StoreModel {
setConnectionPosition: Action<StoreModel, XYPosition>;
setConnectionSourceId: Action<StoreModel, ElementId | null>;
setConnectionNodeId: Action<StoreModel, SetConnectionId>;
setInteractive: Action<StoreModel, boolean>;
@@ -140,7 +143,8 @@ const storeModel: StoreModel = {
height: 0,
draw: false,
},
connectionSourceId: null,
connectionNodeId: null,
connectionHandleType: 'source',
connectionPosition: { x: 0, y: 0 },
snapGrid: [16, 16],
@@ -330,8 +334,9 @@ const storeModel: StoreModel = {
state.connectionPosition = position;
}),
setConnectionSourceId: action((state, sourceId) => {
state.connectionSourceId = sourceId;
setConnectionNodeId: action((state, { connectionNodeId, connectionHandleType }) => {
state.connectionNodeId = connectionNodeId;
state.connectionHandleType = connectionHandleType;
}),
setSnapGrid: action((state, { snapToGrid, snapGrid }) => {
+5
View File
@@ -160,6 +160,11 @@ export enum ConnectionLineType {
export type OnConnectFunc = (connection: Connection) => void;
export type SetConnectionId = {
connectionNodeId: ElementId | null;
connectionHandleType: HandleType | null;
};
export interface HandleElement extends XYPosition, Dimensions {
id?: ElementId | null;
position: Position;