diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts index 444586ca..7cd3eb13 100644 --- a/examples/react/src/App/routes.ts +++ b/examples/react/src/App/routes.ts @@ -49,6 +49,7 @@ import UseConnection from '../examples/UseConnection'; import UseNodesInitialized from '../examples/UseNodesInit'; import UseNodesData from '../examples/UseNodesData'; import UseHandleConnections from '../examples/UseHandleConnections'; +import AddNodeOnEdgeDrop from '../examples/AddNodeOnEdgeDrop'; export interface IRoute { name: string; @@ -57,6 +58,11 @@ export interface IRoute { } const routes: IRoute[] = [ + { + name: 'Add Node on edge Drop', + path: 'add-node-edge-drop', + component: AddNodeOnEdgeDrop, + }, { name: 'Basic', path: 'basic', diff --git a/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx b/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx new file mode 100644 index 00000000..e1204169 --- /dev/null +++ b/examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx @@ -0,0 +1,99 @@ +import { useCallback, useRef } from 'react'; +import { + ReactFlow, + useNodesState, + useEdgesState, + addEdge, + useReactFlow, + ReactFlowProvider, + OnConnect, + OnConnectStart, + OnConnectEnd, + Node, + Edge, +} from '@xyflow/react'; +import '@xyflow/react/dist/style.css'; + +const initialNodes: Node[] = [ + { + id: '0', + type: 'input', + data: { label: 'Node' }, + position: { x: 0, y: 50 }, + }, +]; + +const initialEdges: Edge[] = []; + +let id = 1; +const getId = () => `${id++}`; + +const AddNodeOnEdgeDrop = () => { + const reactFlowWrapper = useRef(null); + const connectingNodeId = useRef(null); + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + const { screenToFlowPosition } = useReactFlow(); + const onConnect: OnConnect = useCallback((params) => { + // reset the start node on connections + connectingNodeId.current = null; + setEdges((eds) => addEdge(params, eds)); + }, []); + + const onConnectStart: OnConnectStart = useCallback((_, { nodeId }) => { + connectingNodeId.current = nodeId; + }, []); + + const onConnectEnd: OnConnectEnd = useCallback( + (event) => { + if (!connectingNodeId.current) return; + + const targetIsPane = (event.target as HTMLDivElement)?.classList.contains('react-flow__pane'); + + if (targetIsPane && 'clientX' in event && 'clientY' in event) { + // we need to remove the wrapper bounds, in order to get the correct position + const id = getId(); + const newNode: Node = { + id, + position: screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }), + data: { label: `Node ${id}` }, + origin: [0.5, 0.0], + }; + + const newEdge: Edge = { + id, + source: connectingNodeId.current, + target: id, + }; + + setNodes((nds) => nds.concat(newNode)); + setEdges((eds) => eds.concat(newEdge)); + } + }, + [screenToFlowPosition] + ); + + return ( +
+ +
+ ); +}; + +export default () => ( + + + +);