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.
- package/README.md +527 -99
- package/dist/esm/components/Edges/EdgeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/Edges/EdgeWrapper/type.d.ts +2 -2
- package/dist/esm/components/Edges/EdgeWrapper/type.d.ts.map +1 -1
- package/dist/esm/components/Node/NodeWrapper/index.d.ts +1 -1
- package/dist/esm/components/Node/NodeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/Node/NodeWrapper/type.d.ts +2 -4
- package/dist/esm/components/Node/NodeWrapper/type.d.ts.map +1 -1
- package/dist/esm/components/ReactDiagramProvider/type.d.ts +16 -3
- package/dist/esm/components/ReactDiagramProvider/type.d.ts.map +1 -1
- package/dist/esm/components/StoreUpdater/index.d.ts +2 -2
- package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
- package/dist/esm/container/ConnectionLineRenderer/ConnectionPath.d.ts.map +1 -1
- package/dist/esm/container/DiagramRenderer/index.d.ts +3 -6
- package/dist/esm/container/DiagramRenderer/index.d.ts.map +1 -1
- package/dist/esm/container/EdgeRenderer/index.d.ts.map +1 -1
- package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -1
- package/dist/esm/container/ReactDiagram/DiagramView.d.ts +1 -1
- package/dist/esm/container/ReactDiagram/DiagramView.d.ts.map +1 -1
- package/dist/esm/container/ReactDiagram/index.d.ts +3 -6
- package/dist/esm/container/ReactDiagram/index.d.ts.map +1 -1
- package/dist/esm/hooks/useGlobalKeyHandler.d.ts +6 -2
- package/dist/esm/hooks/useGlobalKeyHandler.d.ts.map +1 -1
- package/dist/esm/hooks/useKeyPress.d.ts +7 -0
- package/dist/esm/hooks/useKeyPress.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesEdgesState/index.d.ts +1 -1
- package/dist/esm/hooks/useNodesEdgesState/index.d.ts.map +1 -1
- package/dist/esm/index.js +169 -71
- package/dist/esm/index.mjs +169 -71
- package/dist/esm/store/index.d.ts.map +1 -1
- package/dist/esm/types/core.d.ts +6 -6
- package/dist/esm/types/core.d.ts.map +1 -1
- package/dist/esm/types/general.d.ts +59 -1
- package/dist/esm/types/general.d.ts.map +1 -1
- package/dist/esm/types/index.d.ts +1 -2
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/esm/utils/changes.d.ts +5 -5
- package/dist/esm/utils/changes.d.ts.map +1 -1
- package/dist/style.css +21 -3
- package/dist/umd/components/Edges/EdgeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/Edges/EdgeWrapper/type.d.ts +2 -2
- package/dist/umd/components/Edges/EdgeWrapper/type.d.ts.map +1 -1
- package/dist/umd/components/Node/NodeWrapper/index.d.ts +1 -1
- package/dist/umd/components/Node/NodeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/Node/NodeWrapper/type.d.ts +2 -4
- package/dist/umd/components/Node/NodeWrapper/type.d.ts.map +1 -1
- package/dist/umd/components/ReactDiagramProvider/type.d.ts +16 -3
- package/dist/umd/components/ReactDiagramProvider/type.d.ts.map +1 -1
- package/dist/umd/components/StoreUpdater/index.d.ts +2 -2
- package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
- package/dist/umd/container/ConnectionLineRenderer/ConnectionPath.d.ts.map +1 -1
- package/dist/umd/container/DiagramRenderer/index.d.ts +3 -6
- package/dist/umd/container/DiagramRenderer/index.d.ts.map +1 -1
- package/dist/umd/container/EdgeRenderer/index.d.ts.map +1 -1
- package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -1
- package/dist/umd/container/ReactDiagram/DiagramView.d.ts +1 -1
- package/dist/umd/container/ReactDiagram/DiagramView.d.ts.map +1 -1
- package/dist/umd/container/ReactDiagram/index.d.ts +3 -6
- package/dist/umd/container/ReactDiagram/index.d.ts.map +1 -1
- package/dist/umd/hooks/useGlobalKeyHandler.d.ts +6 -2
- package/dist/umd/hooks/useGlobalKeyHandler.d.ts.map +1 -1
- package/dist/umd/hooks/useKeyPress.d.ts +7 -0
- package/dist/umd/hooks/useKeyPress.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesEdgesState/index.d.ts +1 -1
- package/dist/umd/hooks/useNodesEdgesState/index.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/store/index.d.ts.map +1 -1
- package/dist/umd/types/core.d.ts +6 -6
- package/dist/umd/types/core.d.ts.map +1 -1
- package/dist/umd/types/general.d.ts +59 -1
- package/dist/umd/types/general.d.ts.map +1 -1
- package/dist/umd/types/index.d.ts +1 -2
- package/dist/umd/types/index.d.ts.map +1 -1
- package/dist/umd/utils/changes.d.ts +5 -5
- package/dist/umd/utils/changes.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/esm/hooks/useDragSelectionKeyPress.d.ts +0 -4
- package/dist/esm/hooks/useDragSelectionKeyPress.d.ts.map +0 -1
- package/dist/esm/hooks/useNodesEdgesState/type.d.ts +0 -57
- package/dist/esm/hooks/useNodesEdgesState/type.d.ts.map +0 -1
- package/dist/esm/utils/deepEqual.d.ts +0 -2
- package/dist/esm/utils/deepEqual.d.ts.map +0 -1
- package/dist/umd/hooks/useDragSelectionKeyPress.d.ts +0 -4
- package/dist/umd/hooks/useDragSelectionKeyPress.d.ts.map +0 -1
- package/dist/umd/hooks/useNodesEdgesState/type.d.ts +0 -57
- package/dist/umd/hooks/useNodesEdgesState/type.d.ts.map +0 -1
- package/dist/umd/utils/deepEqual.d.ts +0 -2
- package/dist/umd/utils/deepEqual.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,147 +1,575 @@
|
|
|
1
|
+
# react-cosmos-diagram
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/react-cosmos-diagram)
|
|
4
|
+
[](https://github.com/taehunlim/react-diagram/blob/main/LICENSE)
|
|
5
|
+
[](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
|
-
|
|
36
|
+
Import the stylesheet once at your app entry point:
|
|
8
37
|
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
38
|
+
```tsx
|
|
39
|
+
import 'react-cosmos-diagram/styles/style.css';
|
|
40
|
+
```
|
|
12
41
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
171
|
+
const edgeConnected = useRef(true);
|
|
91
172
|
|
|
92
|
-
|
|
93
|
-
|
|
173
|
+
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
|
174
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
94
175
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
176
|
+
const onConnect = useCallback(
|
|
177
|
+
(params: Connection) => setEdges((edges) => addEdge({ ...params }, edges)),
|
|
178
|
+
[],
|
|
179
|
+
);
|
|
99
180
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
181
|
+
const onEdgeUpdateStart = useCallback(() => {
|
|
182
|
+
edgeConnected.current = false;
|
|
183
|
+
}, []);
|
|
103
184
|
|
|
104
|
-
|
|
185
|
+
const onEdgeUpdateEnd = useCallback(
|
|
186
|
+
(_e: MouseEvent, currentEdge: Edge, _portType: PortType) => {
|
|
105
187
|
if (!edgeConnected.current) {
|
|
106
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
203
|
+
return (
|
|
204
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
121
205
|
<ReactDiagram
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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 &
|
|
9
|
-
|
|
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>;
|