react-cosmos-diagram 0.10.0 → 0.11.0

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.
Files changed (88) hide show
  1. package/README.md +527 -99
  2. package/dist/esm/components/Edges/EdgeWrapper/index.d.ts.map +1 -1
  3. package/dist/esm/components/Edges/EdgeWrapper/type.d.ts +2 -2
  4. package/dist/esm/components/Edges/EdgeWrapper/type.d.ts.map +1 -1
  5. package/dist/esm/components/Node/NodeWrapper/index.d.ts +1 -1
  6. package/dist/esm/components/Node/NodeWrapper/index.d.ts.map +1 -1
  7. package/dist/esm/components/Node/NodeWrapper/type.d.ts +2 -4
  8. package/dist/esm/components/Node/NodeWrapper/type.d.ts.map +1 -1
  9. package/dist/esm/components/ReactDiagramProvider/type.d.ts +16 -3
  10. package/dist/esm/components/ReactDiagramProvider/type.d.ts.map +1 -1
  11. package/dist/esm/components/StoreUpdater/index.d.ts +2 -2
  12. package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
  13. package/dist/esm/container/ConnectionLineRenderer/ConnectionPath.d.ts.map +1 -1
  14. package/dist/esm/container/DiagramRenderer/index.d.ts +3 -6
  15. package/dist/esm/container/DiagramRenderer/index.d.ts.map +1 -1
  16. package/dist/esm/container/EdgeRenderer/index.d.ts.map +1 -1
  17. package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -1
  18. package/dist/esm/container/ReactDiagram/DiagramView.d.ts +1 -1
  19. package/dist/esm/container/ReactDiagram/DiagramView.d.ts.map +1 -1
  20. package/dist/esm/container/ReactDiagram/index.d.ts +3 -6
  21. package/dist/esm/container/ReactDiagram/index.d.ts.map +1 -1
  22. package/dist/esm/hooks/useGlobalKeyHandler.d.ts +6 -2
  23. package/dist/esm/hooks/useGlobalKeyHandler.d.ts.map +1 -1
  24. package/dist/esm/hooks/useKeyPress.d.ts +7 -0
  25. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -0
  26. package/dist/esm/hooks/useNodesEdgesState/index.d.ts +1 -1
  27. package/dist/esm/hooks/useNodesEdgesState/index.d.ts.map +1 -1
  28. package/dist/esm/index.js +169 -71
  29. package/dist/esm/index.mjs +169 -71
  30. package/dist/esm/store/index.d.ts.map +1 -1
  31. package/dist/esm/types/core.d.ts +6 -6
  32. package/dist/esm/types/core.d.ts.map +1 -1
  33. package/dist/esm/types/general.d.ts +59 -1
  34. package/dist/esm/types/general.d.ts.map +1 -1
  35. package/dist/esm/types/index.d.ts +1 -2
  36. package/dist/esm/types/index.d.ts.map +1 -1
  37. package/dist/esm/utils/changes.d.ts +5 -5
  38. package/dist/esm/utils/changes.d.ts.map +1 -1
  39. package/dist/style.css +21 -3
  40. package/dist/umd/components/Edges/EdgeWrapper/index.d.ts.map +1 -1
  41. package/dist/umd/components/Edges/EdgeWrapper/type.d.ts +2 -2
  42. package/dist/umd/components/Edges/EdgeWrapper/type.d.ts.map +1 -1
  43. package/dist/umd/components/Node/NodeWrapper/index.d.ts +1 -1
  44. package/dist/umd/components/Node/NodeWrapper/index.d.ts.map +1 -1
  45. package/dist/umd/components/Node/NodeWrapper/type.d.ts +2 -4
  46. package/dist/umd/components/Node/NodeWrapper/type.d.ts.map +1 -1
  47. package/dist/umd/components/ReactDiagramProvider/type.d.ts +16 -3
  48. package/dist/umd/components/ReactDiagramProvider/type.d.ts.map +1 -1
  49. package/dist/umd/components/StoreUpdater/index.d.ts +2 -2
  50. package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
  51. package/dist/umd/container/ConnectionLineRenderer/ConnectionPath.d.ts.map +1 -1
  52. package/dist/umd/container/DiagramRenderer/index.d.ts +3 -6
  53. package/dist/umd/container/DiagramRenderer/index.d.ts.map +1 -1
  54. package/dist/umd/container/EdgeRenderer/index.d.ts.map +1 -1
  55. package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -1
  56. package/dist/umd/container/ReactDiagram/DiagramView.d.ts +1 -1
  57. package/dist/umd/container/ReactDiagram/DiagramView.d.ts.map +1 -1
  58. package/dist/umd/container/ReactDiagram/index.d.ts +3 -6
  59. package/dist/umd/container/ReactDiagram/index.d.ts.map +1 -1
  60. package/dist/umd/hooks/useGlobalKeyHandler.d.ts +6 -2
  61. package/dist/umd/hooks/useGlobalKeyHandler.d.ts.map +1 -1
  62. package/dist/umd/hooks/useKeyPress.d.ts +7 -0
  63. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -0
  64. package/dist/umd/hooks/useNodesEdgesState/index.d.ts +1 -1
  65. package/dist/umd/hooks/useNodesEdgesState/index.d.ts.map +1 -1
  66. package/dist/umd/index.js +2 -2
  67. package/dist/umd/store/index.d.ts.map +1 -1
  68. package/dist/umd/types/core.d.ts +6 -6
  69. package/dist/umd/types/core.d.ts.map +1 -1
  70. package/dist/umd/types/general.d.ts +59 -1
  71. package/dist/umd/types/general.d.ts.map +1 -1
  72. package/dist/umd/types/index.d.ts +1 -2
  73. package/dist/umd/types/index.d.ts.map +1 -1
  74. package/dist/umd/utils/changes.d.ts +5 -5
  75. package/dist/umd/utils/changes.d.ts.map +1 -1
  76. package/package.json +4 -4
  77. package/dist/esm/hooks/useDragSelectionKeyPress.d.ts +0 -4
  78. package/dist/esm/hooks/useDragSelectionKeyPress.d.ts.map +0 -1
  79. package/dist/esm/hooks/useNodesEdgesState/type.d.ts +0 -57
  80. package/dist/esm/hooks/useNodesEdgesState/type.d.ts.map +0 -1
  81. package/dist/esm/utils/deepEqual.d.ts +0 -2
  82. package/dist/esm/utils/deepEqual.d.ts.map +0 -1
  83. package/dist/umd/hooks/useDragSelectionKeyPress.d.ts +0 -4
  84. package/dist/umd/hooks/useDragSelectionKeyPress.d.ts.map +0 -1
  85. package/dist/umd/hooks/useNodesEdgesState/type.d.ts +0 -57
  86. package/dist/umd/hooks/useNodesEdgesState/type.d.ts.map +0 -1
  87. package/dist/umd/utils/deepEqual.d.ts +0 -2
  88. package/dist/umd/utils/deepEqual.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,147 +1,575 @@
1
+ # react-cosmos-diagram
2
+
3
+ [![npm version](https://img.shields.io/npm/v/react-cosmos-diagram)](https://www.npmjs.com/package/react-cosmos-diagram)
4
+ [![license](https://img.shields.io/npm/l/react-cosmos-diagram)](https://github.com/taehunlim/react-diagram/blob/main/LICENSE)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-supported-blue)](https://www.typescriptlang.org/)
6
+
7
+ A highly customizable React library for building node-based diagrams and graph UIs.
8
+ [→ Live demo](https://codesandbox.io/p/devbox/headless-darkness-nqdfz2)
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [Installation](#installation)
15
+ - [Features](#features)
16
+ - [Quickstart](#quickstart)
17
+ - [Custom Nodes](#custom-nodes)
18
+ - [Custom Edges](#custom-edges)
19
+ - [Custom Connection Line](#custom-connection-line)
20
+ - [Components](#components)
21
+ - [Hooks](#hooks)
22
+ - [Utilities](#utilities)
23
+ - [Props Reference](#props-reference)
24
+ - [TypeScript Generics](#typescript-generics)
25
+ - [Credits](#credits)
26
+ - [License](#license)
27
+
28
+ ---
29
+
1
30
  ## Installation
2
31
 
3
32
  ```bash
4
33
  npm install react-cosmos-diagram
5
34
  ```
6
35
 
7
- ## Core characteristics
36
+ Import the stylesheet once at your app entry point:
8
37
 
9
- - Easy zooming and panning, single and multiple selection of graph elements and keyboard shortcuts are supported natively
10
- - Customizable support for nodes, ports, and edges
11
- - Written in [Typescript](https://www.typescriptlang.org/)
38
+ ```tsx
39
+ import 'react-cosmos-diagram/styles/style.css';
40
+ ```
12
41
 
13
- ## [demo](https://codesandbox.io/p/sandbox/blue-framework-hv666c?file=%2Fsrc%2FApp.tsx%3A47%2C66)
42
+ **Peer requirements:** React >= 17, React DOM >= 17
43
+
44
+ ---
45
+
46
+ ## Features
47
+
48
+ - Zoom, pan, drag, multi-select, and keyboard shortcuts supported natively
49
+ - Fully customizable nodes, edges, and ports with TypeScript generics
50
+ - Nested nodes via `parentNode`
51
+ - Drag-selection box (default key: `Shift`)
52
+ - Auto-panning during node drag and edge connection
53
+ - `onlyRenderVisibleElements` for large-diagram performance
54
+ - `Background` component for grid overlays
55
+ - Grid snapping via `gridStep`
56
+
57
+ ---
14
58
 
15
59
  ## Quickstart
16
60
 
17
- ```jsx
61
+ ### Minimal example
62
+
63
+ ```tsx
64
+ import ReactDiagram, { useNodesState, useEdgesState, addEdge } from 'react-cosmos-diagram';
65
+ import 'react-cosmos-diagram/styles/style.css';
66
+ import { useCallback } from 'react';
67
+ import type { Connection } from 'react-cosmos-diagram';
68
+
69
+ const initialNodes = [
70
+ { id: '1', position: { x: 100, y: 100 }, data: { label: 'Node 1' } },
71
+ { id: '2', position: { x: 300, y: 100 }, data: { label: 'Node 2' } },
72
+ ];
73
+
74
+ const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
75
+
76
+ export default function App() {
77
+ const [nodes, , onNodesChange] = useNodesState(initialNodes);
78
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
79
+
80
+ const onConnect = useCallback(
81
+ (params: Connection) => setEdges((eds) => addEdge(params, eds)),
82
+ [],
83
+ );
84
+
85
+ return (
86
+ <div style={{ width: '100vw', height: '100vh' }}>
87
+ <ReactDiagram
88
+ nodes={nodes}
89
+ edges={edges}
90
+ onNodesChange={onNodesChange}
91
+ onEdgesChange={onEdgesChange}
92
+ onConnect={onConnect}
93
+ />
94
+ </div>
95
+ );
96
+ }
97
+ ```
98
+
99
+ > `ReactDiagram` needs a container with explicit `width` and `height`.
100
+
101
+ ### Full example with edge reconnection
102
+
103
+ ```tsx
18
104
  import { useCallback, useRef } from 'react';
19
105
 
20
106
  import ReactDiagram, {
21
- useNodesState,
22
- useEdgesState,
23
- addEdge,
24
- updateEdge,
25
- Connection,
26
- Edge,
27
- MarkerType,
107
+ useNodesState,
108
+ useEdgesState,
109
+ addEdge,
110
+ updateEdge,
111
+ Connection,
112
+ Edge,
113
+ MarkerType,
114
+ PortType,
28
115
  } from 'react-cosmos-diagram';
29
116
 
30
117
  import 'react-cosmos-diagram/styles/style.css';
31
118
 
32
119
  const initialNodes = [
33
- {
34
- id: '1',
35
- width: 200,
36
- height: 100,
37
- data: { label: 'Node1' },
38
- position: { x: 100, y: 100 },
39
- },
40
- {
41
- id: '2',
42
- data: { label: 'Node2' },
43
- position: { x: 300, y: 50 },
44
- },
45
- {
46
- id: '3',
47
- data: { label: 'Node3' },
48
- position: { x: 10, y: 10 },
49
- parentNode: '1',
50
- },
51
- {
52
- id: '4',
53
- data: { label: 'Node4' },
54
- position: { x: 650, y: 100 },
55
- },
120
+ {
121
+ id: '1',
122
+ width: 200,
123
+ height: 100,
124
+ data: { label: 'Node1' },
125
+ position: { x: 100, y: 100 },
126
+ },
127
+ {
128
+ id: '2',
129
+ data: { label: 'Node2' },
130
+ position: { x: 300, y: 50 },
131
+ },
132
+ {
133
+ id: '3',
134
+ data: { label: 'Node3' },
135
+ position: { x: 10, y: 10 },
136
+ parentNode: '1',
137
+ },
138
+ {
139
+ id: '4',
140
+ data: { label: 'Node4' },
141
+ position: { x: 650, y: 100 },
142
+ },
56
143
  ];
57
144
 
58
145
  const initialEdges = [
59
- {
60
- id: 'e-1-2',
61
- type: 'bezier',
62
- source: '1',
63
- target: '2',
64
- markerStart: {
65
- type: MarkerType.Arrow,
66
- },
67
- },
68
- {
69
- id: 'e-2-3',
70
- source: '2',
71
- target: '3',
72
- markerEnd: {
73
- type: MarkerType.Arrow,
74
- },
75
- type: 'step',
76
- label: 'label',
77
- },
78
- {
79
- id: 'e-3-4',
80
- type: 'c',
81
- source: '3',
82
- target: '4',
83
- markerEnd: {
84
- type: MarkerType.Arrow,
85
- },
86
- },
146
+ {
147
+ id: 'e-1-2',
148
+ type: 'bezier',
149
+ source: '1',
150
+ target: '2',
151
+ markerStart: { type: MarkerType.Arrow },
152
+ },
153
+ {
154
+ id: 'e-2-3',
155
+ source: '2',
156
+ target: '3',
157
+ markerEnd: { type: MarkerType.Arrow },
158
+ type: 'step',
159
+ label: 'label',
160
+ },
161
+ {
162
+ id: 'e-3-4',
163
+ type: 'step',
164
+ source: '3',
165
+ target: '4',
166
+ markerEnd: { type: MarkerType.Arrow },
167
+ },
87
168
  ];
88
169
 
89
170
  function Diagram() {
90
- const edgeConnected = useRef(true);
171
+ const edgeConnected = useRef(true);
91
172
 
92
- const [nodes, _setNodes, onNodesChange] = useNodesState(initialNodes);
93
- const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
173
+ const [nodes, , onNodesChange] = useNodesState(initialNodes);
174
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
94
175
 
95
- const onConnect = useCallback(
96
- (params: Connection) => setEdges((edges) => addEdge({ ...params }, edges)),
97
- [],
98
- );
176
+ const onConnect = useCallback(
177
+ (params: Connection) => setEdges((edges) => addEdge({ ...params }, edges)),
178
+ [],
179
+ );
99
180
 
100
- const onEdgeUpdateStart = useCallback(() => {
101
- edgeConnected.current = false;
102
- }, []);
181
+ const onEdgeUpdateStart = useCallback(() => {
182
+ edgeConnected.current = false;
183
+ }, []);
103
184
 
104
- const onEdgeUpdateEnd = useCallback((_e: any, currentEdge: Edge) => {
185
+ const onEdgeUpdateEnd = useCallback(
186
+ (_e: MouseEvent, currentEdge: Edge, _portType: PortType) => {
105
187
  if (!edgeConnected.current) {
106
- setEdges((edges) => edges.filter((edge) => edge.id !== currentEdge.id));
188
+ setEdges((edges) => edges.filter((edge) => edge.id !== currentEdge.id));
107
189
  }
108
-
109
190
  edgeConnected.current = true;
110
- }, []);
191
+ },
192
+ [],
193
+ );
111
194
 
112
- const onEdgeUpdate = useCallback(
113
- (originEdge: Edge, newConnection: Connection) => {
114
- edgeConnected.current = true;
115
- setEdges((edges) => updateEdge(originEdge, newConnection, edges));
116
- },
117
- [],
118
- );
195
+ const onEdgeUpdate = useCallback(
196
+ (originEdge: Edge, newConnection: Connection) => {
197
+ edgeConnected.current = true;
198
+ setEdges((edges) => updateEdge(originEdge, newConnection, edges));
199
+ },
200
+ [],
201
+ );
119
202
 
120
- return (
203
+ return (
204
+ <div style={{ width: '100vw', height: '100vh' }}>
121
205
  <ReactDiagram
122
- nodes={nodes}
123
- edges={edges}
124
- onNodesChange={onNodesChange}
125
- onEdgesChange={onEdgesChange}
126
- onConnect={onConnect}
127
- onEdgeUpdateStart={onEdgeUpdateStart}
128
- onEdgeUpdateEnd={onEdgeUpdateEnd}
129
- onEdgeUpdate={onEdgeUpdate}
206
+ nodes={nodes}
207
+ edges={edges}
208
+ onNodesChange={onNodesChange}
209
+ onEdgesChange={onEdgesChange}
210
+ onConnect={onConnect}
211
+ onEdgeUpdateStart={onEdgeUpdateStart}
212
+ onEdgeUpdateEnd={onEdgeUpdateEnd}
213
+ onEdgeUpdate={onEdgeUpdate}
130
214
  />
131
- );
215
+ </div>
216
+ );
132
217
  }
133
218
 
134
219
  export default Diagram;
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Custom Nodes
225
+
226
+ Use `NodeProps<YourNodeType>` to get typed props. Place `Port` components inside to define connection points.
227
+
228
+ ```tsx
229
+ import { memo } from 'react';
230
+ import { Port, NodeProps, Node } from 'react-cosmos-diagram';
231
+ import { Position } from 'react-cosmos-diagram';
232
+
233
+ type MyNodeData = { label: string; color?: string };
234
+ type MyNode = Node<MyNodeData, 'myNode'>;
235
+
236
+ function MyNode({ data }: NodeProps<MyNode>) {
237
+ return (
238
+ <div style={{ padding: 10, background: data.color ?? '#fff', border: '1px solid #ccc' }}>
239
+ <Port type="target" position={Position.Top} />
240
+ <div>{data.label}</div>
241
+ <Port type="source" position={Position.Bottom} />
242
+ </div>
243
+ );
244
+ }
245
+
246
+ export default memo(MyNode);
247
+ ```
248
+
249
+ Register custom nodes in `nodeTypes` (define outside the component to avoid recreation):
250
+
251
+ ```tsx
252
+ import { nodeTypes } from './nodeTypes';
253
+
254
+ const nodeTypes = { myNode: MyNode };
255
+
256
+ <ReactDiagram nodes={nodes} nodeTypes={nodeTypes} ... />
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Custom Edges
262
+
263
+ Use `EdgeProps<YourEdgeType>` along with `BaseEdge` and `getBezierPath`:
264
+
265
+ ```tsx
266
+ import { memo } from 'react';
267
+ import { BaseEdge, EdgeProps, Edge, getBezierPath } from 'react-cosmos-diagram';
268
+ import { Position } from 'react-cosmos-diagram';
269
+
270
+ type MyEdgeData = { animated?: boolean };
271
+ type MyEdge = Edge<MyEdgeData, 'myEdge'>;
272
+
273
+ function MyEdge({
274
+ id,
275
+ sourceX,
276
+ sourceY,
277
+ targetX,
278
+ targetY,
279
+ sourcePosition = Position.Bottom,
280
+ targetPosition = Position.Top,
281
+ style,
282
+ markerEnd,
283
+ }: EdgeProps<MyEdge>) {
284
+ const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
285
+ sourceX,
286
+ sourceY,
287
+ sourcePosition,
288
+ targetX,
289
+ targetY,
290
+ targetPosition,
291
+ });
292
+
293
+ return (
294
+ <BaseEdge
295
+ path={path}
296
+ labelX={labelX}
297
+ labelY={labelY}
298
+ style={style}
299
+ markerEnd={markerEnd}
300
+ />
301
+ );
302
+ }
303
+
304
+ export default memo(MyEdge);
305
+ ```
306
+
307
+ Register in `edgeTypes`:
308
+
309
+ ```tsx
310
+ const edgeTypes = { myEdge: MyEdge };
311
+
312
+ <ReactDiagram edges={edges} edgeTypes={edgeTypes} ... />
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Custom Connection Line
318
+
319
+ Provide a `ConnectionLineComponent` prop to render a custom line while the user is dragging a new connection:
320
+
321
+ ```tsx
322
+ import type { ConnectionLineComponentProps } from 'react-cosmos-diagram';
323
+
324
+ function CustomConnectionLine({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) {
325
+ return (
326
+ <g>
327
+ <path
328
+ fill="none"
329
+ stroke="#222"
330
+ strokeWidth={2}
331
+ d={`M${fromX},${fromY} C${fromX},${toY} ${toX},${fromY} ${toX},${toY}`}
332
+ />
333
+ <circle cx={toX} cy={toY} r={4} fill="#222" />
334
+ </g>
335
+ );
336
+ }
135
337
 
338
+ <ReactDiagram ConnectionLineComponent={CustomConnectionLine} ... />
136
339
  ```
137
340
 
341
+ ---
342
+
343
+ ## Components
344
+
345
+ | Component | Description |
346
+ |---|---|
347
+ | `ReactDiagram` | Main diagram canvas. Default export. |
348
+ | `ReactDiagramProvider` | Context provider for accessing the store outside the canvas. |
349
+ | `Port` | Connection port placed inside a custom node. |
350
+ | `BaseEdge` | SVG edge primitive used inside custom edges. |
351
+ | `BezierEdge` | Built-in bezier curve edge. |
352
+ | `StepEdge` | Built-in right-angle step edge. |
353
+ | `Background` | Background grid component. |
354
+
355
+ ---
356
+
357
+ ## Hooks
358
+
359
+ ### `useNodesState<NodeType>(initialNodes)`
360
+
361
+ ```ts
362
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
363
+ ```
364
+
365
+ Returns `[NodeType[], Dispatch<SetStateAction<NodeType[]>>, OnNodesChange<NodeType>]`.
366
+
367
+ ### `useEdgesState<EdgeType>(initialEdges)`
368
+
369
+ ```ts
370
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
371
+ ```
372
+
373
+ Returns `[EdgeType[], Dispatch<SetStateAction<EdgeType[]>>, OnEdgesChange<EdgeType>]`.
374
+
375
+ ### `useStore(selector)`
376
+
377
+ Zustand selector hook for reading internal diagram state from within the canvas.
378
+
379
+ ### `useStoreApi()`
380
+
381
+ Returns `{ getState, setState, subscribe }` — use when you need imperative access to the store, e.g. from event handlers outside the React tree.
382
+
383
+ ---
384
+
385
+ ## Utilities
386
+
387
+ ### Edge paths
388
+
389
+ | Function | Returns | Description |
390
+ |---|---|---|
391
+ | `getBezierPath(params)` | `[path, labelX, labelY, offsetX, offsetY]` | Bezier SVG path + label position |
392
+ | `getBezierEdgeCenter(params)` | `[centerX, centerY, offsetX, offsetY]` | Center point of a bezier edge |
393
+ | `getStepPath(params)` | `[path, labelX, labelY, offsetX, offsetY]` | Right-angle step path |
394
+ | `getStraightPath(params)` | `[path, labelX, labelY, offsetX, offsetY]` | Straight-line path |
395
+
396
+ ### Edge state
397
+
398
+ | Function | Description |
399
+ |---|---|
400
+ | `addEdge(connection, edges)` | Appends a new edge from a `Connection` object |
401
+ | `updateEdge(oldEdge, newConnection, edges)` | Replaces an edge's source/target with a new connection |
402
+
403
+ ### Type guards
404
+
405
+ | Function | Description |
406
+ |---|---|
407
+ | `isCoreNode(element)` | Returns `true` if the value is a `CoreNode` |
408
+ | `isCoreEdge(element)` | Returns `true` if the value is a `CoreEdge` |
409
+
410
+ ### Geometry
411
+
412
+ | Function | Description |
413
+ |---|---|
414
+ | `clamp(value, min, max)` | Clamps a number between min and max |
415
+ | `rectToBox(rect)` | Converts `Rect` to `Box` |
416
+ | `boxToRect(box)` | Converts `Box` to `Rect` |
417
+
418
+ ---
419
+
420
+ ## Props Reference
421
+
422
+ ### Data
423
+
424
+ | Prop | Type | Description |
425
+ |---|---|---|
426
+ | `nodes` | `NodeType[]` | Array of node objects |
427
+ | `edges` | `EdgeType[]` | Array of edge objects |
428
+ | `nodeTypes` | `NodeTypes<NodeType>` | Map of type name → custom node component |
429
+ | `edgeTypes` | `EdgeTypes<EdgeType>` | Map of type name → custom edge component |
430
+
431
+ ### Change Handlers
432
+
433
+ | Prop | Type | Description |
434
+ |---|---|---|
435
+ | `onNodesChange` | `OnNodesChange<NodeType>` | Called on node position, selection, dimension, or removal changes |
436
+ | `onEdgesChange` | `OnEdgesChange<EdgeType>` | Called on edge selection or removal changes |
437
+
438
+ ### Connection
439
+
440
+ | Prop | Type | Default | Description |
441
+ |---|---|---|---|
442
+ | `onConnect` | `(connection: Connection) => void` | — | Called when a new connection is completed |
443
+ | `onConnectStart` | `OnConnectStart` | — | Called when the user starts dragging a connection |
444
+ | `onConnectEnd` | `OnConnectEnd` | — | Called when the connection drag ends |
445
+ | `connectionRadius` | `number` | — | Snap radius around a port for completing a connection |
446
+ | `ConnectionLineComponent` | `ConnectionLineComponent` | — | Custom component for the in-progress connection line |
447
+
448
+ ### Node Events
449
+
450
+ | Prop | Type |
451
+ |---|---|
452
+ | `onNodeClick` | `(event: MouseEvent, node: NodeType) => void` |
453
+ | `onNodeDoubleClick` | `(event: MouseEvent, node: NodeType) => void` |
454
+ | `onNodeContextMenu` | `(event: MouseEvent, node: NodeType) => void` |
455
+ | `onNodeMouseEnter` | `(event: MouseEvent, node: NodeType) => void` |
456
+ | `onNodeMouseMove` | `(event: MouseEvent, node: NodeType) => void` |
457
+ | `onNodeMouseLeave` | `(event: MouseEvent, node: NodeType) => void` |
458
+ | `onNodeDragStart` | `NodeDragHandler<NodeType>` |
459
+ | `onNodeDrag` | `NodeDragHandler<NodeType>` |
460
+ | `onNodeDragEnd` | `NodeDragHandler<NodeType>` |
461
+
462
+ ### Edge Events
463
+
464
+ | Prop | Type |
465
+ |---|---|
466
+ | `onEdgeClick` | `(event: MouseEvent, edge: EdgeType) => void` |
467
+ | `onEdgeDoubleClick` | `EdgeMouseHandler<EdgeType>` |
468
+ | `onEdgeContextMenu` | `EdgeMouseHandler<EdgeType>` |
469
+ | `onEdgeMouseEnter` | `EdgeMouseHandler<EdgeType>` |
470
+ | `onEdgeMouseMove` | `EdgeMouseHandler<EdgeType>` |
471
+ | `onEdgeMouseLeave` | `EdgeMouseHandler<EdgeType>` |
472
+ | `onEdgeUpdate` | `(oldEdge: EdgeType, newConnection: Connection) => void` |
473
+ | `onEdgeUpdateStart` | `(event: MouseEvent, edge: EdgeType, portType: PortType) => void` |
474
+ | `onEdgeUpdateEnd` | `(event: MouseEvent, edge: EdgeType, portType: PortType) => void` |
475
+
476
+ ### Viewport
477
+
478
+ | Prop | Type | Default | Description |
479
+ |---|---|---|---|
480
+ | `defaultViewport` | `Viewport` | `{ x:0, y:0, zoom:1 }` | Initial viewport position and zoom |
481
+ | `minZoom` | `number` | `0.5` | Minimum zoom level |
482
+ | `maxZoom` | `number` | `2` | Maximum zoom level |
483
+ | `translateExtent` | `CoordinateExtent` | — | Restricts how far the viewport can be panned |
484
+ | `nodeExtent` | `CoordinateExtent` | — | Restricts how far nodes can be dragged |
485
+ | `panning` | `boolean` | `true` | Enable/disable viewport panning |
486
+
487
+ ### Behavior
488
+
489
+ | Prop | Type | Default | Description |
490
+ |---|---|---|---|
491
+ | `nodesDraggable` | `boolean` | `true` | Allow nodes to be dragged |
492
+ | `elevateNodesOnSelect` | `boolean` | — | Raise selected nodes above others |
493
+ | `autoPanOnNodeDrag` | `boolean` | `true` | Auto-pan when dragging a node near the edge |
494
+ | `autoPanOnConnect` | `boolean` | `true` | Auto-pan when dragging a connection near the edge |
495
+ | `onlyRenderVisibleElements` | `boolean` | `false` | Skip rendering off-screen nodes and edges |
496
+ | `multiSelectionKeyCode` | `KeyCode` | `'Meta'` | Key to hold for multi-select |
497
+ | `dragSelectionKeyCode` | `KeyCode` | `'Shift'` | Key to hold for drag-selection box |
498
+ | `noDragClassName` | `string` | `'nodrag'` | Elements with this class won't trigger node drag |
499
+ | `noPanClassName` | `string` | `'nopan'` | Elements with this class won't trigger panning |
500
+ | `smoothStep` | `boolean` | — | Use smooth corners on step edges |
501
+ | `centerStep` | `boolean` | — | Center the step path between source and target |
502
+ | `gridStep` | `GridStep` | — | Snap nodes to a grid while dragging |
503
+
504
+ ---
505
+
506
+ ## TypeScript Generics
507
+
508
+ v0.10.0 improved generic inference so that custom `Node` and `Edge` types flow through hooks and the `ReactDiagram` component without manual casting.
509
+
510
+ ```tsx
511
+ import ReactDiagram, {
512
+ useNodesState,
513
+ useEdgesState,
514
+ addEdge,
515
+ Node,
516
+ Edge,
517
+ } from 'react-cosmos-diagram';
518
+ import { useCallback } from 'react';
519
+ import type { Connection } from 'react-cosmos-diagram';
520
+
521
+ // 1. Define typed Node and Edge
522
+ type AppNode = Node<{ label: string; color: string }, 'colored'>;
523
+ type AppEdge = Edge<{ weight: number }, 'weighted'>;
524
+
525
+ const initialNodes: AppNode[] = [
526
+ { id: '1', type: 'colored', position: { x: 0, y: 0 }, data: { label: 'A', color: 'red' } },
527
+ { id: '2', type: 'colored', position: { x: 200, y: 0 }, data: { label: 'B', color: 'blue' } },
528
+ ];
529
+
530
+ const initialEdges: AppEdge[] = [
531
+ { id: 'e1-2', source: '1', target: '2', data: { weight: 5 } },
532
+ ];
533
+
534
+ export default function TypedDiagram() {
535
+ // 2. Hooks infer AppNode / AppEdge from initial values
536
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
537
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
538
+
539
+ const onConnect = useCallback(
540
+ (params: Connection) =>
541
+ setEdges((eds) => addEdge({ ...params, data: { weight: 1 } }, eds)),
542
+ [],
543
+ );
544
+
545
+ // 3. ReactDiagram<AppNode, AppEdge> is inferred — nodeTypes and event handlers
546
+ // are fully typed without explicit generics.
547
+ return (
548
+ <div style={{ width: '100vw', height: '100vh' }}>
549
+ <ReactDiagram
550
+ nodes={nodes}
551
+ edges={edges}
552
+ onNodesChange={onNodesChange}
553
+ onEdgesChange={onEdgesChange}
554
+ onConnect={onConnect}
555
+ onNodeClick={(_e, node) => console.log(node.data.color)} // typed!
556
+ />
557
+ </div>
558
+ );
559
+ }
560
+ ```
561
+
562
+ ---
563
+
138
564
  ## Credits
139
565
 
140
- Under the hood, React Cosmos Diagram depends on these great libraries:
566
+ Under the hood, react-cosmos-diagram depends on these great libraries:
567
+
568
+ - [d3-zoom](https://github.com/d3/d3-zoom) — zoom, pan and drag interactions
569
+ - [d3-drag](https://github.com/d3/d3-drag) — node dragging
570
+ - [zustand](https://github.com/pmndrs/zustand) — internal state management
141
571
 
142
- - [d3-zoom](https://github.com/d3/d3-zoom) - used for zoom, pan and drag interactions with the graph canvas
143
- - [d3-drag](https://github.com/d3/d3-drag) - used for making the nodes draggable
144
- - [zustand](https://github.com/pmndrs/zustand) - internal state management
572
+ ---
145
573
 
146
574
  ## License
147
575
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../react-diagram/packages/react/src/components/Edges/EdgeWrapper/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAiB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,wBAAgB,eAAe,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EACzD,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,EACjE,OAAO,CAAC,EAAE,CACP,KAAK,EAAE,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EAC/C,IAAI,EAAE,QAAQ,KACZ,IAAI,YAII,gBAAgB,WAAW,EAAE,UAAU,CAAC,uBAOvD;AAED,QAAA,MAAM,QAAQ,2MA0Sb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../react-diagram/packages/react/src/components/Edges/EdgeWrapper/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAiB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,wBAAgB,eAAe,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EACzD,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,EACjE,OAAO,CAAC,EAAE,CACP,KAAK,EAAE,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EAC/C,IAAI,EAAE,QAAQ,KACZ,IAAI,YAII,gBAAgB,WAAW,EAAE,UAAU,CAAC,uBAOvD;AAED,QAAA,MAAM,QAAQ,2MAmUb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -5,8 +5,8 @@ import { Node, ReactDiagramProps } from '../../../types';
5
5
  import { ReactDiagramStore } from '../../ReactDiagramProvider/type';
6
6
  export type EdgeMouseHandler<EdgeType extends Edge = Edge> = (event: ReactMouseEvent, edge: EdgeType) => void;
7
7
  export type OnEdgeUpdateFunc<EdgeType extends Edge = Edge> = (originEdge: EdgeType, newConnection: Connection) => void;
8
- export type WrapEdgeProps<EdgeType extends Edge = Edge> = Edge & EdgePosition & Partial<Pick<ReactDiagramStore<Node, EdgeType>, 'rfId' | 'elementsSelectable'>> & Pick<ReactDiagramProps<Node, EdgeType>, 'edgeUpdaterRadius' | 'onEdgeUpdate' | 'onEdgeUpdateStart' | 'onEdgeUpdateEnd'> & {
9
- isFocusable: boolean;
8
+ export type WrapEdgeProps<EdgeType extends Edge = Edge> = Edge & EdgePosition & Pick<ReactDiagramStore<Node, EdgeType>, 'elementsSelectable'> & Pick<ReactDiagramProps<Node, EdgeType>, 'edgeUpdaterRadius' | 'onEdgeUpdate' | 'onEdgeUpdateStart' | 'onEdgeUpdateEnd'> & {
9
+ rfId?: string;
10
10
  onClick?: EdgeMouseHandler<EdgeType>;
11
11
  onDoubleClick?: EdgeMouseHandler<EdgeType>;
12
12
  onContextMenu?: EdgeMouseHandler<EdgeType>;