Skip to content

Instantly share code, notes, and snippets.

@misiek08
Created November 7, 2024 18:13
Show Gist options
  • Save misiek08/6a63eeec587cbce7021166492c7cc230 to your computer and use it in GitHub Desktop.
Save misiek08/6a63eeec587cbce7021166492c7cc230 to your computer and use it in GitHub Desktop.
ReactFlow add node when edge dropped nowhere, like UE
import { useState, useCallback, useRef } from 'react';
import {
ReactFlow,
addEdge,
MiniMap,
Controls,
Background,
useNodesState,
useEdgesState,
useReactFlow,
ReactFlowProvider,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{ id: '1', position: { x: 250, y: 0 }, data: { label: 'Node 1' } },
];
const compatibleNodes = [
{ id: 'comp1', data: { label: 'Compatible Node 1' }, type: 'default' },
{ id: 'comp2', data: { label: 'Compatible Node 2' }, type: 'default' },
];
const FlowWithSuggestionsInner = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [suggestedNodes, setSuggestedNodes] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [newEdgePosition, setNewEdgePosition] = useState(null);
const popupRef = useRef(null);
const { screenToFlowPosition } = useReactFlow(); // Jest w kontekście ReactFlowProvider
const onConnect = useCallback(
(params) => {
setEdges((eds) => addEdge(params, eds));
},
[setEdges]
);
const onMouseDown = (event) => {
if (!popupRef.current.contains(event.target)) {
setShowSuggestions(false);
}
};
const onConnectEnd = (event) => {
if (!event.target.closest('.react-flow__node')) {
const canvasPosition = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
setSuggestedNodes(compatibleNodes);
setShowSuggestions(true);
setNewEdgePosition({
node: canvasPosition,
mouse: { x: event.clientX, y: event.clientY },
});
}
};
const addCompatibleNode = (nodeTemplate) => {
if (!newEdgePosition) return;
const newNodeId = (nodes.length + 1).toString();
const newNode = {
...nodeTemplate,
id: newNodeId,
position: { x: newEdgePosition.node.x, y: newEdgePosition.node.y },
};
setNodes((nds) => [...nds, newNode]);
setEdges((eds) =>
addEdge({ id: `e${newNodeId}`, source: '1', target: newNodeId }, eds)
);
setShowSuggestions(false);
setNewEdgePosition(null);
};
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onMouseDownCapture={onMouseDown}
onConnect={onConnect}
onConnectEnd={onConnectEnd}
onMouse
fitView
>
<MiniMap />
<Controls />
<Background />
{showSuggestions && (
<div
ref={popupRef}
className="suggestion-popup"
style={{
position: 'absolute',
left: newEdgePosition?.mouse.x || 0,
top: newEdgePosition?.mouse.y || 0,
background: 'white',
padding: '10px',
border: '1px solid black',
borderRadius: '5px',
zIndex: 10,
}}
>
<h4>Select compatible node:</h4>
{suggestedNodes.map((node) => (
<button key={node.id} onClick={() => addCompatibleNode(node)}>
{node.data.label}
</button>
))}
</div>
)}
</ReactFlow>
);
};
// Główny komponent otaczający ReactFlowProvider
const FlowWithSuggestions = () => (
<div style={{ width: '100%', height: '100%' }}>
<ReactFlowProvider>
{' '}
{/* ReactFlowProvider jako główny komponent */}
<FlowWithSuggestionsInner />
</ReactFlowProvider>
</div>
);
export default FlowWithSuggestions;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment