@og-mcp/reactflow-mcp 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +74 -74
- package/dist/data/api-types.js +69 -69
- package/dist/data/components.js +217 -217
- package/dist/data/hooks.js +106 -106
- package/dist/data/migration.js +44 -44
- package/dist/data/patterns.js +582 -582
- package/dist/data/templates.js +126 -126
- package/dist/data/utilities.js +29 -29
- package/dist/index.js +0 -0
- package/dist/tools/cheatsheet.js +84 -84
- package/dist/tools/generate-flow.js +89 -89
- package/package.json +54 -54
package/dist/data/templates.js
CHANGED
|
@@ -2,133 +2,133 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TEMPLATES = void 0;
|
|
4
4
|
exports.TEMPLATES = {
|
|
5
|
-
"custom-node": `import { memo } from 'react';
|
|
6
|
-
import { Handle, Position, NodeToolbar, type NodeProps, type Node } from '@xyflow/react';
|
|
7
|
-
|
|
8
|
-
type CustomNodeData = {
|
|
9
|
-
label: string;
|
|
10
|
-
description?: string;
|
|
11
|
-
icon?: string;
|
|
12
|
-
status?: 'active' | 'inactive' | 'error';
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type CustomNodeType = Node<CustomNodeData, 'custom'>;
|
|
16
|
-
|
|
17
|
-
const CustomNode = memo(({ data, selected }: NodeProps<CustomNodeType>) => {
|
|
18
|
-
const statusColor = {
|
|
19
|
-
active: 'bg-green-500',
|
|
20
|
-
inactive: 'bg-gray-400',
|
|
21
|
-
error: 'bg-red-500',
|
|
22
|
-
}[data.status ?? 'inactive'];
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<>
|
|
26
|
-
<NodeToolbar isVisible={selected} position={Position.Top}>
|
|
27
|
-
<button className="px-2 py-1 text-xs bg-white border rounded shadow">Edit</button>
|
|
28
|
-
<button className="px-2 py-1 text-xs bg-red-50 border border-red-200 rounded shadow ml-1">Delete</button>
|
|
29
|
-
</NodeToolbar>
|
|
30
|
-
|
|
31
|
-
<Handle type="target" position={Position.Left} />
|
|
32
|
-
|
|
33
|
-
<div className={\`px-4 py-3 rounded-lg border shadow-sm bg-white \${selected ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-200'}\`}>
|
|
34
|
-
<div className="flex items-center gap-2">
|
|
35
|
-
<div className={\`w-2 h-2 rounded-full \${statusColor}\`} />
|
|
36
|
-
<span className="font-medium text-sm">{data.label}</span>
|
|
37
|
-
</div>
|
|
38
|
-
{data.description && (
|
|
39
|
-
<p className="text-xs text-gray-500 mt-1">{data.description}</p>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<Handle type="source" position={Position.Right} />
|
|
44
|
-
</>
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
CustomNode.displayName = 'CustomNode';
|
|
5
|
+
"custom-node": `import { memo } from 'react';
|
|
6
|
+
import { Handle, Position, NodeToolbar, type NodeProps, type Node } from '@xyflow/react';
|
|
7
|
+
|
|
8
|
+
type CustomNodeData = {
|
|
9
|
+
label: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
status?: 'active' | 'inactive' | 'error';
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type CustomNodeType = Node<CustomNodeData, 'custom'>;
|
|
16
|
+
|
|
17
|
+
const CustomNode = memo(({ data, selected }: NodeProps<CustomNodeType>) => {
|
|
18
|
+
const statusColor = {
|
|
19
|
+
active: 'bg-green-500',
|
|
20
|
+
inactive: 'bg-gray-400',
|
|
21
|
+
error: 'bg-red-500',
|
|
22
|
+
}[data.status ?? 'inactive'];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<NodeToolbar isVisible={selected} position={Position.Top}>
|
|
27
|
+
<button className="px-2 py-1 text-xs bg-white border rounded shadow">Edit</button>
|
|
28
|
+
<button className="px-2 py-1 text-xs bg-red-50 border border-red-200 rounded shadow ml-1">Delete</button>
|
|
29
|
+
</NodeToolbar>
|
|
30
|
+
|
|
31
|
+
<Handle type="target" position={Position.Left} />
|
|
32
|
+
|
|
33
|
+
<div className={\`px-4 py-3 rounded-lg border shadow-sm bg-white \${selected ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-200'}\`}>
|
|
34
|
+
<div className="flex items-center gap-2">
|
|
35
|
+
<div className={\`w-2 h-2 rounded-full \${statusColor}\`} />
|
|
36
|
+
<span className="font-medium text-sm">{data.label}</span>
|
|
37
|
+
</div>
|
|
38
|
+
{data.description && (
|
|
39
|
+
<p className="text-xs text-gray-500 mt-1">{data.description}</p>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<Handle type="source" position={Position.Right} />
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
CustomNode.displayName = 'CustomNode';
|
|
49
49
|
export default CustomNode;`,
|
|
50
|
-
"custom-edge": `import { BaseEdge, EdgeLabelRenderer, getBezierPath, useReactFlow, type EdgeProps } from '@xyflow/react';
|
|
51
|
-
|
|
52
|
-
export default function CustomEdge({
|
|
53
|
-
id, sourceX, sourceY, targetX, targetY,
|
|
54
|
-
sourcePosition, targetPosition, style, markerEnd, selected,
|
|
55
|
-
}: EdgeProps) {
|
|
56
|
-
const [edgePath, labelX, labelY] = getBezierPath({
|
|
57
|
-
sourceX, sourceY, targetX, targetY,
|
|
58
|
-
sourcePosition, targetPosition,
|
|
59
|
-
});
|
|
60
|
-
const { setEdges } = useReactFlow();
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<>
|
|
64
|
-
<BaseEdge path={edgePath} markerEnd={markerEnd} style={style} />
|
|
65
|
-
{selected && (
|
|
66
|
-
<EdgeLabelRenderer>
|
|
67
|
-
<div
|
|
68
|
-
style={{
|
|
69
|
-
position: 'absolute',
|
|
70
|
-
transform: \`translate(-50%, -50%) translate(\${labelX}px,\${labelY}px)\`,
|
|
71
|
-
pointerEvents: 'all',
|
|
72
|
-
}}
|
|
73
|
-
className="nodrag nopan"
|
|
74
|
-
>
|
|
75
|
-
<button
|
|
76
|
-
className="w-5 h-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center hover:bg-red-600"
|
|
77
|
-
onClick={() => setEdges((es) => es.filter((e) => e.id !== id))}
|
|
78
|
-
>
|
|
79
|
-
x
|
|
80
|
-
</button>
|
|
81
|
-
</div>
|
|
82
|
-
</EdgeLabelRenderer>
|
|
83
|
-
)}
|
|
84
|
-
</>
|
|
85
|
-
);
|
|
50
|
+
"custom-edge": `import { BaseEdge, EdgeLabelRenderer, getBezierPath, useReactFlow, type EdgeProps } from '@xyflow/react';
|
|
51
|
+
|
|
52
|
+
export default function CustomEdge({
|
|
53
|
+
id, sourceX, sourceY, targetX, targetY,
|
|
54
|
+
sourcePosition, targetPosition, style, markerEnd, selected,
|
|
55
|
+
}: EdgeProps) {
|
|
56
|
+
const [edgePath, labelX, labelY] = getBezierPath({
|
|
57
|
+
sourceX, sourceY, targetX, targetY,
|
|
58
|
+
sourcePosition, targetPosition,
|
|
59
|
+
});
|
|
60
|
+
const { setEdges } = useReactFlow();
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<BaseEdge path={edgePath} markerEnd={markerEnd} style={style} />
|
|
65
|
+
{selected && (
|
|
66
|
+
<EdgeLabelRenderer>
|
|
67
|
+
<div
|
|
68
|
+
style={{
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
transform: \`translate(-50%, -50%) translate(\${labelX}px,\${labelY}px)\`,
|
|
71
|
+
pointerEvents: 'all',
|
|
72
|
+
}}
|
|
73
|
+
className="nodrag nopan"
|
|
74
|
+
>
|
|
75
|
+
<button
|
|
76
|
+
className="w-5 h-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center hover:bg-red-600"
|
|
77
|
+
onClick={() => setEdges((es) => es.filter((e) => e.id !== id))}
|
|
78
|
+
>
|
|
79
|
+
x
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</EdgeLabelRenderer>
|
|
83
|
+
)}
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
86
|
}`,
|
|
87
|
-
"zustand-store": `import { create } from 'zustand';
|
|
88
|
-
import {
|
|
89
|
-
type Node, type Edge, type OnNodesChange, type OnEdgesChange, type OnConnect,
|
|
90
|
-
applyNodeChanges, applyEdgeChanges, addEdge,
|
|
91
|
-
} from '@xyflow/react';
|
|
92
|
-
|
|
93
|
-
export type FlowState = {
|
|
94
|
-
nodes: Node[];
|
|
95
|
-
edges: Edge[];
|
|
96
|
-
onNodesChange: OnNodesChange;
|
|
97
|
-
onEdgesChange: OnEdgesChange;
|
|
98
|
-
onConnect: OnConnect;
|
|
99
|
-
setNodes: (nodes: Node[]) => void;
|
|
100
|
-
setEdges: (edges: Edge[]) => void;
|
|
101
|
-
addNode: (node: Node) => void;
|
|
102
|
-
removeNode: (id: string) => void;
|
|
103
|
-
updateNodeData: (id: string, data: Partial<Record<string, unknown>>) => void;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const useFlowStore = create<FlowState>((set, get) => ({
|
|
107
|
-
nodes: [],
|
|
108
|
-
edges: [],
|
|
109
|
-
onNodesChange: (changes) => set({ nodes: applyNodeChanges(changes, get().nodes) }),
|
|
110
|
-
onEdgesChange: (changes) => set({ edges: applyEdgeChanges(changes, get().edges) }),
|
|
111
|
-
onConnect: (connection) => set({ edges: addEdge(connection, get().edges) }),
|
|
112
|
-
setNodes: (nodes) => set({ nodes }),
|
|
113
|
-
setEdges: (edges) => set({ edges }),
|
|
114
|
-
addNode: (node) => set({ nodes: [...get().nodes, node] }),
|
|
115
|
-
removeNode: (id) => set({
|
|
116
|
-
nodes: get().nodes.filter((n) => n.id !== id),
|
|
117
|
-
edges: get().edges.filter((e) => e.source !== id && e.target !== id),
|
|
118
|
-
}),
|
|
119
|
-
updateNodeData: (id, data) => set({
|
|
120
|
-
nodes: get().nodes.map((n) => n.id === id ? { ...n, data: { ...n.data, ...data } } : n),
|
|
121
|
-
}),
|
|
122
|
-
}));
|
|
123
|
-
|
|
124
|
-
// Stable selector — use to prevent re-renders
|
|
125
|
-
export const flowSelector = (s: FlowState) => ({
|
|
126
|
-
nodes: s.nodes,
|
|
127
|
-
edges: s.edges,
|
|
128
|
-
onNodesChange: s.onNodesChange,
|
|
129
|
-
onEdgesChange: s.onEdgesChange,
|
|
130
|
-
onConnect: s.onConnect,
|
|
131
|
-
});
|
|
132
|
-
|
|
87
|
+
"zustand-store": `import { create } from 'zustand';
|
|
88
|
+
import {
|
|
89
|
+
type Node, type Edge, type OnNodesChange, type OnEdgesChange, type OnConnect,
|
|
90
|
+
applyNodeChanges, applyEdgeChanges, addEdge,
|
|
91
|
+
} from '@xyflow/react';
|
|
92
|
+
|
|
93
|
+
export type FlowState = {
|
|
94
|
+
nodes: Node[];
|
|
95
|
+
edges: Edge[];
|
|
96
|
+
onNodesChange: OnNodesChange;
|
|
97
|
+
onEdgesChange: OnEdgesChange;
|
|
98
|
+
onConnect: OnConnect;
|
|
99
|
+
setNodes: (nodes: Node[]) => void;
|
|
100
|
+
setEdges: (edges: Edge[]) => void;
|
|
101
|
+
addNode: (node: Node) => void;
|
|
102
|
+
removeNode: (id: string) => void;
|
|
103
|
+
updateNodeData: (id: string, data: Partial<Record<string, unknown>>) => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const useFlowStore = create<FlowState>((set, get) => ({
|
|
107
|
+
nodes: [],
|
|
108
|
+
edges: [],
|
|
109
|
+
onNodesChange: (changes) => set({ nodes: applyNodeChanges(changes, get().nodes) }),
|
|
110
|
+
onEdgesChange: (changes) => set({ edges: applyEdgeChanges(changes, get().edges) }),
|
|
111
|
+
onConnect: (connection) => set({ edges: addEdge(connection, get().edges) }),
|
|
112
|
+
setNodes: (nodes) => set({ nodes }),
|
|
113
|
+
setEdges: (edges) => set({ edges }),
|
|
114
|
+
addNode: (node) => set({ nodes: [...get().nodes, node] }),
|
|
115
|
+
removeNode: (id) => set({
|
|
116
|
+
nodes: get().nodes.filter((n) => n.id !== id),
|
|
117
|
+
edges: get().edges.filter((e) => e.source !== id && e.target !== id),
|
|
118
|
+
}),
|
|
119
|
+
updateNodeData: (id, data) => set({
|
|
120
|
+
nodes: get().nodes.map((n) => n.id === id ? { ...n, data: { ...n.data, ...data } } : n),
|
|
121
|
+
}),
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
// Stable selector — use to prevent re-renders
|
|
125
|
+
export const flowSelector = (s: FlowState) => ({
|
|
126
|
+
nodes: s.nodes,
|
|
127
|
+
edges: s.edges,
|
|
128
|
+
onNodesChange: s.onNodesChange,
|
|
129
|
+
onEdgesChange: s.onEdgesChange,
|
|
130
|
+
onConnect: s.onConnect,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
133
|
export default useFlowStore;`,
|
|
134
134
|
};
|
package/dist/data/utilities.js
CHANGED
|
@@ -7,9 +7,9 @@ const addEdgeUtil = {
|
|
|
7
7
|
description: "Convenience function to add a new edge to an array. Validates and prevents duplicates.",
|
|
8
8
|
importPath: "import { addEdge } from '@xyflow/react'",
|
|
9
9
|
returns: "Edge[]",
|
|
10
|
-
usage: `const onConnect = useCallback(
|
|
11
|
-
(connection) => setEdges((eds) => addEdge(connection, eds)),
|
|
12
|
-
[setEdges],
|
|
10
|
+
usage: `const onConnect = useCallback(
|
|
11
|
+
(connection) => setEdges((eds) => addEdge(connection, eds)),
|
|
12
|
+
[setEdges],
|
|
13
13
|
);`,
|
|
14
14
|
examples: [],
|
|
15
15
|
relatedApis: ["ReactFlow", "useEdgesState"],
|
|
@@ -20,8 +20,8 @@ const applyNodeChangesUtil = {
|
|
|
20
20
|
description: "Apply an array of NodeChange objects to your nodes array. Used in Zustand stores for controlled flows.",
|
|
21
21
|
importPath: "import { applyNodeChanges } from '@xyflow/react'",
|
|
22
22
|
returns: "Node[]",
|
|
23
|
-
usage: `onNodesChange: (changes) => {
|
|
24
|
-
set({ nodes: applyNodeChanges(changes, get().nodes) });
|
|
23
|
+
usage: `onNodesChange: (changes) => {
|
|
24
|
+
set({ nodes: applyNodeChanges(changes, get().nodes) });
|
|
25
25
|
},`,
|
|
26
26
|
examples: [],
|
|
27
27
|
relatedApis: ["applyEdgeChanges", "useNodesState"],
|
|
@@ -32,8 +32,8 @@ const applyEdgeChangesUtil = {
|
|
|
32
32
|
description: "Apply an array of EdgeChange objects to your edges array. Used in Zustand stores for controlled flows.",
|
|
33
33
|
importPath: "import { applyEdgeChanges } from '@xyflow/react'",
|
|
34
34
|
returns: "Edge[]",
|
|
35
|
-
usage: `onEdgesChange: (changes) => {
|
|
36
|
-
set({ edges: applyEdgeChanges(changes, get().edges) });
|
|
35
|
+
usage: `onEdgesChange: (changes) => {
|
|
36
|
+
set({ edges: applyEdgeChanges(changes, get().edges) });
|
|
37
37
|
},`,
|
|
38
38
|
examples: [],
|
|
39
39
|
relatedApis: ["applyNodeChanges", "useEdgesState"],
|
|
@@ -44,10 +44,10 @@ const getBezierPathUtil = {
|
|
|
44
44
|
description: "Returns SVG path string and label position for a bezier edge between two points.",
|
|
45
45
|
importPath: "import { getBezierPath } from '@xyflow/react'",
|
|
46
46
|
returns: "[path: string, labelX: number, labelY: number, offsetX: number, offsetY: number]",
|
|
47
|
-
usage: `const [edgePath, labelX, labelY] = getBezierPath({
|
|
48
|
-
sourceX, sourceY, targetX, targetY,
|
|
49
|
-
sourcePosition, targetPosition,
|
|
50
|
-
curvature: 0.25, // optional
|
|
47
|
+
usage: `const [edgePath, labelX, labelY] = getBezierPath({
|
|
48
|
+
sourceX, sourceY, targetX, targetY,
|
|
49
|
+
sourcePosition, targetPosition,
|
|
50
|
+
curvature: 0.25, // optional
|
|
51
51
|
});`,
|
|
52
52
|
examples: [],
|
|
53
53
|
relatedApis: ["getSmoothStepPath", "getStraightPath", "getSimpleBezierPath", "BaseEdge"],
|
|
@@ -58,11 +58,11 @@ const getSmoothStepPathUtil = {
|
|
|
58
58
|
description: "Returns SVG path string for a stepped/rounded edge with configurable border radius.",
|
|
59
59
|
importPath: "import { getSmoothStepPath } from '@xyflow/react'",
|
|
60
60
|
returns: "[path, labelX, labelY, offsetX, offsetY]",
|
|
61
|
-
usage: `const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
62
|
-
sourceX, sourceY, targetX, targetY,
|
|
63
|
-
sourcePosition, targetPosition,
|
|
64
|
-
borderRadius: 8, // rounded corners
|
|
65
|
-
offset: 25, // step offset
|
|
61
|
+
usage: `const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
62
|
+
sourceX, sourceY, targetX, targetY,
|
|
63
|
+
sourcePosition, targetPosition,
|
|
64
|
+
borderRadius: 8, // rounded corners
|
|
65
|
+
offset: 25, // step offset
|
|
66
66
|
});`,
|
|
67
67
|
examples: [],
|
|
68
68
|
relatedApis: ["getBezierPath", "getStraightPath"],
|
|
@@ -73,8 +73,8 @@ const getStraightPathUtil = {
|
|
|
73
73
|
description: "Calculates a straight line path between two points.",
|
|
74
74
|
importPath: "import { getStraightPath } from '@xyflow/react'",
|
|
75
75
|
returns: "[path, labelX, labelY]",
|
|
76
|
-
usage: `const [edgePath, labelX, labelY] = getStraightPath({
|
|
77
|
-
sourceX, sourceY, targetX, targetY,
|
|
76
|
+
usage: `const [edgePath, labelX, labelY] = getStraightPath({
|
|
77
|
+
sourceX, sourceY, targetX, targetY,
|
|
78
78
|
});`,
|
|
79
79
|
examples: [],
|
|
80
80
|
relatedApis: ["getBezierPath", "getSmoothStepPath"],
|
|
@@ -85,8 +85,8 @@ const getSimpleBezierPathUtil = {
|
|
|
85
85
|
description: "Returns SVG path for a simple bezier curve (less pronounced curve than getBezierPath).",
|
|
86
86
|
importPath: "import { getSimpleBezierPath } from '@xyflow/react'",
|
|
87
87
|
returns: "[path, labelX, labelY, offsetX, offsetY]",
|
|
88
|
-
usage: `const [edgePath] = getSimpleBezierPath({
|
|
89
|
-
sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition,
|
|
88
|
+
usage: `const [edgePath] = getSimpleBezierPath({
|
|
89
|
+
sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition,
|
|
90
90
|
});`,
|
|
91
91
|
examples: [],
|
|
92
92
|
relatedApis: ["getBezierPath"],
|
|
@@ -147,20 +147,20 @@ const reconnectEdgeUtil = {
|
|
|
147
147
|
description: "Reconnect an existing edge with new source/target. Used in onReconnect handlers.",
|
|
148
148
|
importPath: "import { reconnectEdge } from '@xyflow/react'",
|
|
149
149
|
returns: "Edge[]",
|
|
150
|
-
usage: `const onReconnect = useCallback((oldEdge, newConnection) => {
|
|
151
|
-
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
|
150
|
+
usage: `const onReconnect = useCallback((oldEdge, newConnection) => {
|
|
151
|
+
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
|
152
152
|
}, []);`,
|
|
153
153
|
examples: [
|
|
154
154
|
{
|
|
155
155
|
title: "Edge reconnection",
|
|
156
156
|
category: "connections",
|
|
157
|
-
code: `<ReactFlow
|
|
158
|
-
edgesReconnectable
|
|
159
|
-
onReconnect={(oldEdge, newConnection) =>
|
|
160
|
-
setEdges((els) => reconnectEdge(oldEdge, newConnection, els))
|
|
161
|
-
}
|
|
162
|
-
onReconnectStart={() => setIsReconnecting(true)}
|
|
163
|
-
onReconnectEnd={() => setIsReconnecting(false)}
|
|
157
|
+
code: `<ReactFlow
|
|
158
|
+
edgesReconnectable
|
|
159
|
+
onReconnect={(oldEdge, newConnection) =>
|
|
160
|
+
setEdges((els) => reconnectEdge(oldEdge, newConnection, els))
|
|
161
|
+
}
|
|
162
|
+
onReconnectStart={() => setIsReconnecting(true)}
|
|
163
|
+
onReconnectEnd={() => setIsReconnecting(false)}
|
|
164
164
|
/>`,
|
|
165
165
|
},
|
|
166
166
|
],
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/tools/cheatsheet.js
CHANGED
|
@@ -6,90 +6,90 @@ function register(server) {
|
|
|
6
6
|
description: "React Flow v12 quick reference cheatsheet",
|
|
7
7
|
mimeType: "text/markdown",
|
|
8
8
|
}, async () => {
|
|
9
|
-
const text = `# React Flow v12 — Cheatsheet
|
|
10
|
-
|
|
11
|
-
## Install & Import
|
|
12
|
-
\`\`\`bash
|
|
13
|
-
npm install @xyflow/react zustand
|
|
14
|
-
\`\`\`
|
|
15
|
-
\`\`\`tsx
|
|
16
|
-
import { ReactFlow, Background, Controls, MiniMap, Handle, Position,
|
|
17
|
-
useReactFlow, useNodesState, useEdgesState, addEdge,
|
|
18
|
-
applyNodeChanges, applyEdgeChanges } from '@xyflow/react';
|
|
19
|
-
import '@xyflow/react/dist/style.css';
|
|
20
|
-
\`\`\`
|
|
21
|
-
|
|
22
|
-
## Minimal Flow
|
|
23
|
-
\`\`\`tsx
|
|
24
|
-
const nodes = [{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Hello' } }];
|
|
25
|
-
const edges = [{ id: 'e1-2', source: '1', target: '2' }];
|
|
26
|
-
|
|
27
|
-
<div style={{ width: '100%', height: '100vh' }}>
|
|
28
|
-
<ReactFlow nodes={nodes} edges={edges} fitView>
|
|
29
|
-
<Background /> <Controls />
|
|
30
|
-
</ReactFlow>
|
|
31
|
-
</div>
|
|
32
|
-
\`\`\`
|
|
33
|
-
|
|
34
|
-
## Custom Node
|
|
35
|
-
\`\`\`tsx
|
|
36
|
-
type MyNode = Node<{ label: string }, 'myNode'>;
|
|
37
|
-
|
|
38
|
-
function MyNode({ data }: NodeProps<MyNode>) {
|
|
39
|
-
return (
|
|
40
|
-
<>
|
|
41
|
-
<Handle type="target" position={Position.Left} />
|
|
42
|
-
<div>{data.label}</div>
|
|
43
|
-
<Handle type="source" position={Position.Right} />
|
|
44
|
-
</>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
const nodeTypes = { myNode: MyNode }; // define OUTSIDE component
|
|
48
|
-
\`\`\`
|
|
49
|
-
|
|
50
|
-
## Controlled Flow (Zustand)
|
|
51
|
-
\`\`\`tsx
|
|
52
|
-
const useStore = create((set, get) => ({
|
|
53
|
-
nodes: [], edges: [],
|
|
54
|
-
onNodesChange: (c) => set({ nodes: applyNodeChanges(c, get().nodes) }),
|
|
55
|
-
onEdgesChange: (c) => set({ edges: applyEdgeChanges(c, get().edges) }),
|
|
56
|
-
onConnect: (conn) => set({ edges: addEdge(conn, get().edges) }),
|
|
57
|
-
}));
|
|
58
|
-
\`\`\`
|
|
59
|
-
|
|
60
|
-
## Key Hooks
|
|
61
|
-
| Hook | Use |
|
|
62
|
-
|------|-----|
|
|
63
|
-
| \`useReactFlow()\` | Imperative API — getNodes, setNodes, fitView, screenToFlowPosition |
|
|
64
|
-
| \`useNodesState()\` | Quick prototyping — [nodes, setNodes, onNodesChange] |
|
|
65
|
-
| \`useNodesData(ids)\` | Subscribe to specific node data changes |
|
|
66
|
-
| \`useConnection()\` | Active connection state during drag |
|
|
67
|
-
| \`useNodesInitialized()\` | Wait for all nodes to be measured |
|
|
68
|
-
|
|
69
|
-
## Node Types
|
|
70
|
-
- \`"default"\` — both handles
|
|
71
|
-
- \`"input"\` — source handle only
|
|
72
|
-
- \`"output"\` — target handle only
|
|
73
|
-
- \`"group"\` — container for sub-flows
|
|
74
|
-
|
|
75
|
-
## Edge Types
|
|
76
|
-
- \`"default"\` — bezier curve
|
|
77
|
-
- \`"straight"\` — straight line
|
|
78
|
-
- \`"step"\` — right-angle steps
|
|
79
|
-
- \`"smoothstep"\` — rounded steps
|
|
80
|
-
- \`"simplebezier"\` — simple curve
|
|
81
|
-
|
|
82
|
-
## CSS Classes
|
|
83
|
-
- \`nodrag\` — prevent drag on elements inside nodes
|
|
84
|
-
- \`nopan\` — prevent pan when clicking element
|
|
85
|
-
- \`nowheel\` — prevent zoom on scroll
|
|
86
|
-
|
|
87
|
-
## v12 Key Changes (from v11)
|
|
88
|
-
- Package: \`reactflow\` → \`@xyflow/react\`
|
|
89
|
-
- Import: default → named exports
|
|
90
|
-
- \`node.width\` → \`node.measured.width\`
|
|
91
|
-
- \`project()\` → \`screenToFlowPosition()\`
|
|
92
|
-
- \`onEdgeUpdate\` → \`onReconnect\`
|
|
9
|
+
const text = `# React Flow v12 — Cheatsheet
|
|
10
|
+
|
|
11
|
+
## Install & Import
|
|
12
|
+
\`\`\`bash
|
|
13
|
+
npm install @xyflow/react zustand
|
|
14
|
+
\`\`\`
|
|
15
|
+
\`\`\`tsx
|
|
16
|
+
import { ReactFlow, Background, Controls, MiniMap, Handle, Position,
|
|
17
|
+
useReactFlow, useNodesState, useEdgesState, addEdge,
|
|
18
|
+
applyNodeChanges, applyEdgeChanges } from '@xyflow/react';
|
|
19
|
+
import '@xyflow/react/dist/style.css';
|
|
20
|
+
\`\`\`
|
|
21
|
+
|
|
22
|
+
## Minimal Flow
|
|
23
|
+
\`\`\`tsx
|
|
24
|
+
const nodes = [{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Hello' } }];
|
|
25
|
+
const edges = [{ id: 'e1-2', source: '1', target: '2' }];
|
|
26
|
+
|
|
27
|
+
<div style={{ width: '100%', height: '100vh' }}>
|
|
28
|
+
<ReactFlow nodes={nodes} edges={edges} fitView>
|
|
29
|
+
<Background /> <Controls />
|
|
30
|
+
</ReactFlow>
|
|
31
|
+
</div>
|
|
32
|
+
\`\`\`
|
|
33
|
+
|
|
34
|
+
## Custom Node
|
|
35
|
+
\`\`\`tsx
|
|
36
|
+
type MyNode = Node<{ label: string }, 'myNode'>;
|
|
37
|
+
|
|
38
|
+
function MyNode({ data }: NodeProps<MyNode>) {
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<Handle type="target" position={Position.Left} />
|
|
42
|
+
<div>{data.label}</div>
|
|
43
|
+
<Handle type="source" position={Position.Right} />
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const nodeTypes = { myNode: MyNode }; // define OUTSIDE component
|
|
48
|
+
\`\`\`
|
|
49
|
+
|
|
50
|
+
## Controlled Flow (Zustand)
|
|
51
|
+
\`\`\`tsx
|
|
52
|
+
const useStore = create((set, get) => ({
|
|
53
|
+
nodes: [], edges: [],
|
|
54
|
+
onNodesChange: (c) => set({ nodes: applyNodeChanges(c, get().nodes) }),
|
|
55
|
+
onEdgesChange: (c) => set({ edges: applyEdgeChanges(c, get().edges) }),
|
|
56
|
+
onConnect: (conn) => set({ edges: addEdge(conn, get().edges) }),
|
|
57
|
+
}));
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
## Key Hooks
|
|
61
|
+
| Hook | Use |
|
|
62
|
+
|------|-----|
|
|
63
|
+
| \`useReactFlow()\` | Imperative API — getNodes, setNodes, fitView, screenToFlowPosition |
|
|
64
|
+
| \`useNodesState()\` | Quick prototyping — [nodes, setNodes, onNodesChange] |
|
|
65
|
+
| \`useNodesData(ids)\` | Subscribe to specific node data changes |
|
|
66
|
+
| \`useConnection()\` | Active connection state during drag |
|
|
67
|
+
| \`useNodesInitialized()\` | Wait for all nodes to be measured |
|
|
68
|
+
|
|
69
|
+
## Node Types
|
|
70
|
+
- \`"default"\` — both handles
|
|
71
|
+
- \`"input"\` — source handle only
|
|
72
|
+
- \`"output"\` — target handle only
|
|
73
|
+
- \`"group"\` — container for sub-flows
|
|
74
|
+
|
|
75
|
+
## Edge Types
|
|
76
|
+
- \`"default"\` — bezier curve
|
|
77
|
+
- \`"straight"\` — straight line
|
|
78
|
+
- \`"step"\` — right-angle steps
|
|
79
|
+
- \`"smoothstep"\` — rounded steps
|
|
80
|
+
- \`"simplebezier"\` — simple curve
|
|
81
|
+
|
|
82
|
+
## CSS Classes
|
|
83
|
+
- \`nodrag\` — prevent drag on elements inside nodes
|
|
84
|
+
- \`nopan\` — prevent pan when clicking element
|
|
85
|
+
- \`nowheel\` — prevent zoom on scroll
|
|
86
|
+
|
|
87
|
+
## v12 Key Changes (from v11)
|
|
88
|
+
- Package: \`reactflow\` → \`@xyflow/react\`
|
|
89
|
+
- Import: default → named exports
|
|
90
|
+
- \`node.width\` → \`node.measured.width\`
|
|
91
|
+
- \`project()\` → \`screenToFlowPosition()\`
|
|
92
|
+
- \`onEdgeUpdate\` → \`onReconnect\`
|
|
93
93
|
`;
|
|
94
94
|
return {
|
|
95
95
|
contents: [
|