@infinit-canvas/react 0.1.19 → 0.1.21
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/README.md +126 -6
- package/dist/react-infinite-canvas.cjs +564 -555
- package/dist/react-infinite-canvas.js +2581 -2528
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,13 +61,123 @@ function App() {
|
|
|
61
61
|
| Rendering | DOM (React divs + SVG) | OffscreenCanvas + Web Worker |
|
|
62
62
|
| Max nodes (60fps) | ~500 | 5,000+ |
|
|
63
63
|
| Main thread | Blocked during render | Always free |
|
|
64
|
-
| Custom nodes | React components | Hybrid: canvas + DOM
|
|
64
|
+
| Custom nodes | React components (all DOM) | Hybrid: canvas + spatial DOM |
|
|
65
65
|
| API | React Flow API | Same API (drop-in compatible) |
|
|
66
66
|
|
|
67
|
+
## Architecture
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
┌─────────────────────────────────┐
|
|
71
|
+
│ Your React App │
|
|
72
|
+
└──────────┬──────────────────────┘
|
|
73
|
+
│
|
|
74
|
+
┌──────────▼──────────────────────┐
|
|
75
|
+
│ <InfiniteCanvas> │
|
|
76
|
+
│ │
|
|
77
|
+
│ ┌────────────┐ ┌────────────┐ │
|
|
78
|
+
│ │ Canvas │ │ DOM │ │
|
|
79
|
+
│ │ Worker │ │ Overlay │ │
|
|
80
|
+
│ │ │ │ │ │
|
|
81
|
+
│ │ All nodes │ │ Nearest 50 │ │
|
|
82
|
+
│ │ as styled │ │ nodes as │ │
|
|
83
|
+
│ │ canvas │ │ full React │ │
|
|
84
|
+
│ │ primitives │ │ components │ │
|
|
85
|
+
│ └────────────┘ └────────────┘ │
|
|
86
|
+
└──────────────────────────────────┘
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**The key insight:** Most nodes don't need to be DOM elements. Only the ones near your viewport get promoted to real React components. The rest render as styled canvas primitives via a Web Worker — zero DOM cost.
|
|
90
|
+
|
|
91
|
+
## Spatial DOM Promotion
|
|
92
|
+
|
|
93
|
+
By default, up to **50 custom nodes** nearest to your viewport center are rendered as full React components. Everything else renders on canvas. This gives you rich interactivity where you need it and canvas performance everywhere else.
|
|
94
|
+
|
|
95
|
+
```jsx
|
|
96
|
+
<InfiniteCanvas
|
|
97
|
+
nodes={nodes}
|
|
98
|
+
edges={edges}
|
|
99
|
+
nodeTypes={{ workflow: WorkflowNode }}
|
|
100
|
+
domNodeLimit={50} // Max DOM nodes (default: 50, 0 = pure canvas)
|
|
101
|
+
canvasNodeTypes={{ // How non-DOM nodes look on canvas
|
|
102
|
+
workflow: {
|
|
103
|
+
fill: '#f0f9ff',
|
|
104
|
+
stroke: '#c7d8f0',
|
|
105
|
+
radius: 10,
|
|
106
|
+
accent: { side: 'left', width: 5, color: '#3b82f6' },
|
|
107
|
+
icon: { dataField: 'icon', x: 20, size: 18 },
|
|
108
|
+
title: { dataField: 'label', x: 40, y: 16 },
|
|
109
|
+
subtitle: { dataField: 'description', x: 40, y: 36 },
|
|
110
|
+
badge: { dataField: 'badge', bg: '#3b82f6' },
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Promotion rules (priority order):
|
|
117
|
+
1. **Pinned nodes** — user clicked the pin button, stays DOM forever
|
|
118
|
+
2. **Selected/dragging nodes** — always promoted while interacting
|
|
119
|
+
3. **Nearest visible nodes** — fills remaining `domNodeLimit` slots by distance to viewport center
|
|
120
|
+
|
|
121
|
+
### Pin nodes to DOM
|
|
122
|
+
|
|
123
|
+
Every DOM-promoted node shows a pin button on hover. Click it to keep that node as a full React component even when you scroll away. Useful for nodes with live data, inputs, or interactive content.
|
|
124
|
+
|
|
125
|
+
## Canvas Node Types
|
|
126
|
+
|
|
127
|
+
Three ways to define how nodes look when rendered on canvas (non-DOM):
|
|
128
|
+
|
|
129
|
+
### 1. Declarative Config (recommended)
|
|
130
|
+
|
|
131
|
+
Define node appearance with a config object. The worker draws shapes, text, icons, and badges per node — reading from `node.data` for dynamic content.
|
|
132
|
+
|
|
133
|
+
```jsx
|
|
134
|
+
canvasNodeTypes={{
|
|
135
|
+
workflow: {
|
|
136
|
+
fill: '#f0f9ff',
|
|
137
|
+
stroke: '#c7d8f0',
|
|
138
|
+
strokeWidth: 1,
|
|
139
|
+
radius: 10,
|
|
140
|
+
accent: { side: 'left', width: 5, color: '#3b82f6' },
|
|
141
|
+
icon: { dataField: 'icon', x: 20, size: 18 },
|
|
142
|
+
title: { dataField: 'label', x: 40, y: 16, font: '600 13px system-ui', color: '#1e293b' },
|
|
143
|
+
subtitle: { dataField: 'description', x: 40, y: 36, font: '11px system-ui', color: '#64748b' },
|
|
144
|
+
badge: { dataField: 'badge', bg: '#3b82f6', color: '#fff' },
|
|
145
|
+
}
|
|
146
|
+
}}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 2. SVG String
|
|
150
|
+
|
|
151
|
+
Provide an SVG string — converted to `ImageBitmap` and drawn via `drawImage`. Good for static visual shells (the worker still draws `node.data.label` on top).
|
|
152
|
+
|
|
153
|
+
```jsx
|
|
154
|
+
canvasNodeTypes={{
|
|
155
|
+
workflow: `<svg xmlns="http://www.w3.org/2000/svg" width="160" height="70">
|
|
156
|
+
<rect x="1" y="1" width="158" height="68" rx="10" fill="#f0f9ff" stroke="#c7d8f0"/>
|
|
157
|
+
<rect x="1" y="1" width="6" height="68" rx="3" fill="#3b82f6"/>
|
|
158
|
+
</svg>`
|
|
159
|
+
}}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 3. Function (per-node variation)
|
|
163
|
+
|
|
164
|
+
Provide a function `(data) => svgString` for per-node bitmaps. Results are deduplicated and cached — if 5000 nodes share 8 color variants, only 8 bitmaps are created.
|
|
165
|
+
|
|
166
|
+
```jsx
|
|
167
|
+
canvasNodeTypes={{
|
|
168
|
+
workflow: (data) => `<svg xmlns="http://www.w3.org/2000/svg" width="160" height="70">
|
|
169
|
+
<rect x="1" y="1" width="158" height="68" rx="10" fill="${data.color}" stroke="#ccc"/>
|
|
170
|
+
</svg>`
|
|
171
|
+
}}
|
|
172
|
+
```
|
|
173
|
+
|
|
67
174
|
## Features
|
|
68
175
|
|
|
69
176
|
- **Full React Flow API** — nodes, edges, handles, connections, selection, drag, zoom, pan
|
|
70
177
|
- **Custom node & edge types** — use any React component
|
|
178
|
+
- **Spatial DOM virtualization** — only nearest N nodes are real DOM elements
|
|
179
|
+
- **Canvas node rendering** — declarative config, SVG bitmap, or per-node functions
|
|
180
|
+
- **Node pinning** — keep specific nodes as DOM permanently
|
|
71
181
|
- **Built-in components** — Controls, MiniMap, Background, Panel, Handle, NodeResizer, NodeToolbar, EdgeLabelRenderer, ViewportPortal
|
|
72
182
|
- **All hooks** — useReactFlow, useNodes, useEdges, useViewport, useConnection, useNodesData, useOnViewportChange, useOnSelectionChange, useKeyPress, useStore, useStoreApi, + more
|
|
73
183
|
- **Sub-flows** — parentId, extent: 'parent'
|
|
@@ -78,11 +188,10 @@ function App() {
|
|
|
78
188
|
- **Snap to grid** — configurable grid snapping
|
|
79
189
|
- **Undo/redo** — built-in history management
|
|
80
190
|
- **Delete validation** — onBeforeDelete async callback
|
|
81
|
-
- **
|
|
191
|
+
- **Frustum culling** — spatial grid index for O(visible) rendering
|
|
192
|
+
- **LOD rendering** — handles and text hidden at low zoom for performance
|
|
82
193
|
- **noDragClassName / noPanClassName** — prevent drag/pan on specific elements
|
|
83
194
|
- **Elevate on select** — bring nodes/edges to front on selection
|
|
84
|
-
- **Node virtualization** — only visible custom nodes are mounted in DOM
|
|
85
|
-
- **Frustum culling** — spatial grid index for O(visible) rendering
|
|
86
195
|
|
|
87
196
|
## Props
|
|
88
197
|
|
|
@@ -96,8 +205,10 @@ function App() {
|
|
|
96
205
|
onConnect={onConnect}
|
|
97
206
|
|
|
98
207
|
// Types
|
|
99
|
-
nodeTypes={nodeTypes}
|
|
100
|
-
edgeTypes={edgeTypes}
|
|
208
|
+
nodeTypes={nodeTypes} // React components for custom nodes
|
|
209
|
+
edgeTypes={edgeTypes} // React components for custom edges
|
|
210
|
+
canvasNodeTypes={configs} // Canvas rendering: config | SVG | function
|
|
211
|
+
domNodeLimit={50} // Max DOM nodes (0 = pure canvas)
|
|
101
212
|
|
|
102
213
|
// Events
|
|
103
214
|
onNodeClick onNodeDoubleClick onNodeContextMenu
|
|
@@ -118,6 +229,7 @@ function App() {
|
|
|
118
229
|
noDragClassName="nodrag" noPanClassName="nopan"
|
|
119
230
|
deleteKeyCode="Delete"
|
|
120
231
|
isValidConnection={fn}
|
|
232
|
+
edgeRouting={true}
|
|
121
233
|
|
|
122
234
|
// Viewport
|
|
123
235
|
fitView initialCamera={{ x: 0, y: 0, zoom: 1 }}
|
|
@@ -150,6 +262,14 @@ function App() {
|
|
|
150
262
|
|
|
151
263
|
`applyNodeChanges` · `applyEdgeChanges` · `addEdge` · `isNode` · `isEdge` · `getConnectedEdges` · `getIncomers` · `getOutgoers` · `getNodesBounds` · `getBezierPath` · `getSmoothStepPath` · `getStraightPath` · `getSimpleBezierPath` · `snapPosition` · `reconnectEdge`
|
|
152
264
|
|
|
265
|
+
## Migration from React Flow
|
|
266
|
+
|
|
267
|
+
1. Replace `reactflow` with `@infinit-canvas/react` in your imports
|
|
268
|
+
2. Replace `reactflow/dist/style.css` with `@infinit-canvas/react/styles.css`
|
|
269
|
+
3. That's it — same API, same hooks, same components
|
|
270
|
+
|
|
271
|
+
For large node counts, add `canvasNodeTypes` to define how nodes look on canvas and set `domNodeLimit` to control the DOM budget.
|
|
272
|
+
|
|
153
273
|
## Sponsor
|
|
154
274
|
|
|
155
275
|
If this package saves you time, consider supporting its development:
|