From 5f91498d19b0bf7379e81824e8773b268e8df203 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 1 Aug 2020 12:54:14 +0200 Subject: [PATCH] feat(connection): add start and stop props closes #380 --- README.md | 2 ++ example/src/Validation/index.js | 6 ++++ src/components/Handle/BaseHandle.tsx | 45 ++++++++++++++++++++++++---- src/components/Handle/index.tsx | 15 ++++++++-- src/container/GraphView/index.tsx | 19 ++++++++++++ src/container/ReactFlow/index.tsx | 7 +++++ src/store/index.ts | 16 ++++++++-- src/types/index.ts | 6 ++++ 8 files changed, 105 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e2ca976a..bfdc0ce8 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,8 @@ const BasicFlow = () => ; - `onNodeMouseLeave(evt: MouseEvent, node: Node)`: node mouse leave - `onNodeContextMenu(evt: MouseEvent, node: Node)`: node context menu - `onConnect({ source, target })`: called when user connects two nodes +- `onConnectStart({ nodeId, handleType })`: called when user starts to drag connection line +- `onConnectStop()`: called when user stops to drag connection line - `onLoad(reactFlowInstance)`: called after flow is initialized - `onMove()`: called when user is panning or zooming - `onMoveStart()`: called when user starts panning or zooming diff --git a/example/src/Validation/index.js b/example/src/Validation/index.js index fb82a320..48cbeafa 100644 --- a/example/src/Validation/index.js +++ b/example/src/Validation/index.js @@ -15,6 +15,10 @@ const isValidConnection = (connection) => connection.target === 'B'; const onLoad = (reactFlowInstance) => reactFlowInstance.fitView(); +const onConnectStart = ({ nodeId, handleType }) => console.log('on connect start', { nodeId, handleType }); + +const onConnectStop = () => console.log('on connect stop'); + const CustomInput = () => ( <>
Only connectable with B
@@ -47,6 +51,8 @@ const HorizontalFlow = () => { onLoad={onLoad} className="validationflow" nodeTypes={nodeTypes} + onConnectStart={onConnectStart} + onConnectStop={onConnectStop} /> ); }; diff --git a/src/components/Handle/BaseHandle.tsx b/src/components/Handle/BaseHandle.tsx index 706aca81..3eebae64 100644 --- a/src/components/Handle/BaseHandle.tsx +++ b/src/components/Handle/BaseHandle.tsx @@ -1,7 +1,17 @@ import React, { memo, MouseEvent as ReactMouseEvent, CSSProperties } from 'react'; import cc from 'classcat'; -import { HandleType, ElementId, Position, XYPosition, OnConnectFunc, Connection, SetConnectionId } from '../../types'; +import { + HandleType, + ElementId, + Position, + XYPosition, + OnConnectFunc, + OnConnectStartFunc, + OnConnectStopFunc, + Connection, + SetConnectionId, +} from '../../types'; type ValidConnectionFunc = (connection: Connection) => boolean; type SetSourceIdFunc = (params: SetConnectionId) => void; @@ -10,6 +20,8 @@ interface BaseHandleProps { type: HandleType; nodeId: ElementId; onConnect: OnConnectFunc; + onConnectStart?: OnConnectStartFunc; + onConnectStop?: OnConnectStopFunc; position: Position; setConnectionNodeId: SetSourceIdFunc; setPosition: (pos: XYPosition) => void; @@ -33,7 +45,9 @@ function onMouseDown( setPosition: (pos: XYPosition) => void, onConnect: OnConnectFunc, isTarget: boolean, - isValidConnection: ValidConnectionFunc + isValidConnection: ValidConnectionFunc, + onConnectStart?: OnConnectStartFunc, + onConnectStop?: OnConnectStopFunc ): void { const reactFlowNode = document.querySelector('.react-flow'); @@ -41,6 +55,7 @@ function onMouseDown( return; } + const handleType = isTarget ? 'target' : 'source'; const containerBounds = reactFlowNode.getBoundingClientRect(); let recentHoveredHandle: Element; @@ -48,7 +63,11 @@ function onMouseDown( x: evt.clientX - containerBounds.left, y: evt.clientY - containerBounds.top, }); - setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleType: isTarget ? 'target' : 'source' }); + setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleType: handleType }); + + if (onConnectStart) { + onConnectStart({ nodeId, handleType }); + } function resetRecentHandle(): void { if (!recentHoveredHandle) { @@ -115,13 +134,17 @@ function onMouseDown( function onMouseUp(evt: MouseEvent) { const { connection, isValid } = checkElementBelowIsValid(evt); - if (isValid) { + if (isValid && onConnect) { onConnect(connection); } resetRecentHandle(); setConnectionNodeId({ connectionNodeId: null, connectionHandleType: null }); + if (onConnectStop) { + onConnectStop(); + } + document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); } @@ -134,6 +157,8 @@ const BaseHandle = ({ type, nodeId, onConnect, + onConnectStart, + onConnectStop, position, setConnectionNodeId, setPosition, @@ -162,7 +187,17 @@ const BaseHandle = ({ data-handlepos={position} className={handleClasses} onMouseDown={(evt) => - onMouseDown(evt, nodeIdWithHandleId, setConnectionNodeId, setPosition, onConnect, isTarget, isValidConnection) + onMouseDown( + evt, + nodeIdWithHandleId, + setConnectionNodeId, + setPosition, + onConnect, + isTarget, + isValidConnection, + onConnectStart, + onConnectStop + ) } {...rest} /> diff --git a/src/components/Handle/index.tsx b/src/components/Handle/index.tsx index f181183e..cba3d67b 100644 --- a/src/components/Handle/index.tsx +++ b/src/components/Handle/index.tsx @@ -10,20 +10,27 @@ import { HandleProps, ElementId, Position, Connection } from '../../types'; const Handle = ({ type = 'source', position = Position.Top, - onConnect = () => {}, isValidConnection = () => true, isConnectable = true, style, className, id, + onConnect, }: HandleProps) => { const nodeId = useContext(NodeIdContext) as ElementId; const setPosition = useStoreActions((a) => a.setConnectionPosition); const setConnectionNodeId = useStoreActions((a) => a.setConnectionNodeId); const onConnectAction = useStoreState((s) => s.onConnect); + const onConnectStart = useStoreState((s) => s.onConnectStart); + const onConnectStop = useStoreState((s) => s.onConnectStop); const onConnectExtended = (params: Connection) => { - onConnectAction(params); - onConnect(params); + if (onConnectAction) { + onConnectAction(params); + } + + if (onConnect) { + onConnect(params); + } }; const handleClasses = cc([className, { connectable: isConnectable }]); @@ -35,6 +42,8 @@ const Handle = ({ setPosition={setPosition} setConnectionNodeId={setConnectionNodeId} onConnect={onConnectExtended} + onConnectStart={onConnectStart} + onConnectStop={onConnectStop} type={type} position={position} isValidConnection={isValidConnection} diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 1d598c71..9ebd9767 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -22,6 +22,7 @@ import { Connection, ConnectionLineType, FlowTransform, + OnConnectStartFunc, } from '../../types'; export interface GraphViewProps { @@ -35,6 +36,8 @@ export interface GraphViewProps { onNodeDragStart?: (node: Node) => void; onNodeDragStop?: (node: Node) => void; onConnect?: (connection: Connection | Edge) => void; + onConnectStart?: OnConnectStartFunc; + onConnectStop?: () => void; onLoad?: OnLoadFunc; onMove?: (flowTransform?: FlowTransform) => void; onMoveStart?: (flowTransform?: FlowTransform) => void; @@ -85,6 +88,8 @@ const GraphView = ({ deleteKeyCode, elements, onConnect, + onConnectStart, + onConnectStop, snapToGrid, snapGrid, onlyRenderVisibleNodes, @@ -112,6 +117,8 @@ const GraphView = ({ const updateSize = useStoreActions((actions) => actions.updateSize); const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection); const setOnConnect = useStoreActions((a) => a.setOnConnect); + const setOnConnectStart = useStoreActions((a) => a.setOnConnectStart); + const setOnConnectStop = useStoreActions((a) => a.setOnConnectStop); const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid); const setNodesDraggable = useStoreActions((actions) => actions.setNodesDraggable); const setNodesConnectable = useStoreActions((actions) => actions.setNodesConnectable); @@ -210,6 +217,18 @@ const GraphView = ({ } }, [onConnect]); + useEffect(() => { + if (onConnectStart) { + setOnConnectStart(onConnectStart); + } + }, [onConnectStart]); + + useEffect(() => { + if (onConnectStop) { + setOnConnectStop(onConnectStop); + } + }, [onConnectStop]); + useEffect(() => { setSnapGrid({ snapToGrid, snapGrid }); }, [snapToGrid]); diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index 44882aa7..8e383073 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -27,6 +27,7 @@ import { Connection, ConnectionLineType, FlowTransform, + OnConnectStartFunc, } from '../../types'; import '../../style.css'; @@ -42,6 +43,8 @@ export interface ReactFlowProps extends Omit, 'on onNodeDragStart?: (node: Node) => void; onNodeDragStop?: (node: Node) => void; onConnect?: (connection: Edge | Connection) => void; + onConnectStart?: OnConnectStartFunc; + onConnectStop?: () => void; onLoad?: OnLoadFunc; onMove?: (flowTransform?: FlowTransform) => void; onMoveStart?: (flowTransform?: FlowTransform) => void; @@ -86,6 +89,8 @@ const ReactFlow = ({ onMoveEnd, onElementsRemove, onConnect, + onConnectStart, + onConnectStop, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, @@ -143,6 +148,8 @@ const ReactFlow = ({ deleteKeyCode={deleteKeyCode} elements={elements} onConnect={onConnect} + onConnectStart={onConnectStart} + onConnectStop={onConnectStop} snapToGrid={snapToGrid} snapGrid={snapGrid} onlyRenderVisibleNodes={onlyRenderVisibleNodes} diff --git a/src/store/index.ts b/src/store/index.ts index cf710ab2..be995188 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -18,6 +18,8 @@ import { Dimensions, XYPosition, OnConnectFunc, + OnConnectStartFunc, + OnConnectStopFunc, SelectionRect, HandleType, SetConnectionId, @@ -87,9 +89,13 @@ export interface StoreModel { reactFlowVersion: string; - onConnect: OnConnectFunc; + onConnect?: OnConnectFunc; + onConnectStart?: OnConnectStartFunc; + onConnectStop?: OnConnectStopFunc; setOnConnect: Action; + setOnConnectStart: Action; + setOnConnectStop: Action; setElements: Action; @@ -177,11 +183,15 @@ export const storeModel: StoreModel = { reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-', - onConnect: () => {}, - setOnConnect: action((state, onConnect) => { state.onConnect = onConnect; }), + setOnConnectStart: action((state, onConnectStart) => { + state.onConnectStart = onConnectStart; + }), + setOnConnectStop: action((state, onConnectStop) => { + state.onConnectStop = onConnectStop; + }), setElements: action((state, elements) => { state.elements = elements; diff --git a/src/types/index.ts b/src/types/index.ts index fc1124a0..c1ac6057 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -198,6 +198,12 @@ export enum ConnectionLineType { } export type OnConnectFunc = (connection: Connection) => void; +export type OnConnectStartParams = { + nodeId: ElementId | null; + handleType: HandleType | null; +}; +export type OnConnectStartFunc = (params: OnConnectStartParams) => void; +export type OnConnectStopFunc = () => void; export type SetConnectionId = { connectionNodeId: ElementId | null;