ngx-workflow 0.0.2 → 0.1.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 +413 -152
- package/fesm2022/ngx-workflow.mjs +3434 -473
- package/fesm2022/ngx-workflow.mjs.map +1 -1
- package/package.json +2 -1
- package/types/ngx-workflow.d.ts +642 -34
- package/types/ngx-workflow.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,21 @@ A powerful, highly customizable Angular library for building interactive node-ba
|
|
|
18
18
|
- **Export**: Export to JSON, PNG, or SVG.
|
|
19
19
|
- **Theming**: Extensive CSS variables for easy styling.
|
|
20
20
|
|
|
21
|
+
## ✨ New Features (Latest Release)
|
|
22
|
+
|
|
23
|
+
We've added **8 powerful new features** to enhance your workflow experience:
|
|
24
|
+
|
|
25
|
+
1. **Before Delete Hook** - Control deletion with cancellable events
|
|
26
|
+
2. **Z-Index Layer Management** - Keyboard shortcuts + context menu for node stacking
|
|
27
|
+
3. **Connection Limits** - Restrict connections per handle (global or per-handle)
|
|
28
|
+
4. **Edge Label Components** - Use custom Angular components for rich edge labels
|
|
29
|
+
5. **Batch Operations** - `selectAll()`, `alignNodes()`, `distributeNodes()` methods
|
|
30
|
+
6. **Mini-Map Enhancements** - Node colors, selection highlighting, pulse animations
|
|
31
|
+
7. **Node Collision Detection** - Visual feedback when nodes overlap during drag
|
|
32
|
+
8. **Bug Fixes** - Edge interaction & injection context improvements
|
|
33
|
+
|
|
34
|
+
📖 **[View complete feature documentation →](./FEATURES.md)**
|
|
35
|
+
|
|
21
36
|
## 📦 Installation
|
|
22
37
|
|
|
23
38
|
```bash
|
|
@@ -26,11 +41,7 @@ npm install ngx-workflow
|
|
|
26
41
|
|
|
27
42
|
## 🏁 Quick Start
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
### Option 1: Standalone Component (Recommended)
|
|
32
|
-
|
|
33
|
-
Import `NgxWorkflowModule` directly into your standalone component's `imports` array.
|
|
44
|
+
### Standalone Component (Recommended)
|
|
34
45
|
|
|
35
46
|
```typescript
|
|
36
47
|
import { Component } from '@angular/core';
|
|
@@ -45,183 +56,362 @@ import { NgxWorkflowModule, Node, Edge } from 'ngx-workflow';
|
|
|
45
56
|
<ngx-workflow-diagram
|
|
46
57
|
[initialNodes]="nodes"
|
|
47
58
|
[initialEdges]="edges"
|
|
59
|
+
[zIndexMode]="'layered'"
|
|
60
|
+
[preventNodeOverlap]="true"
|
|
48
61
|
(nodeClick)="onNodeClick($event)"
|
|
49
|
-
(
|
|
50
|
-
|
|
62
|
+
(beforeDelete)="onBeforeDelete($event)">
|
|
63
|
+
|
|
64
|
+
<ngx-workflow-minimap [showNodeColors]="true">
|
|
65
|
+
</ngx-workflow-minimap>
|
|
66
|
+
</ngx-workflow-diagram>
|
|
51
67
|
</div>
|
|
52
68
|
`
|
|
53
69
|
})
|
|
54
70
|
export class AppComponent {
|
|
55
71
|
nodes: Node[] = [
|
|
56
|
-
{ id: '1', position: { x: 100, y: 100 }, label: 'Start'
|
|
57
|
-
{ id: '2', position: { x: 300, y: 100 }, label: 'End'
|
|
72
|
+
{ id: '1', position: { x: 100, y: 100 }, label: 'Start' },
|
|
73
|
+
{ id: '2', position: { x: 300, y: 100 }, label: 'End' }
|
|
58
74
|
];
|
|
59
75
|
|
|
60
76
|
edges: Edge[] = [
|
|
61
|
-
{ id: 'e1-2', source: '1', target: '2'
|
|
77
|
+
{ id: 'e1-2', source: '1', target: '2' }
|
|
62
78
|
];
|
|
63
79
|
|
|
64
80
|
onNodeClick(node: Node) {
|
|
65
81
|
console.log('Clicked:', node);
|
|
66
82
|
}
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
onBeforeDelete(event: any) {
|
|
85
|
+
if (!confirm('Delete selected items?')) {
|
|
86
|
+
event.cancel();
|
|
87
|
+
}
|
|
70
88
|
}
|
|
71
89
|
}
|
|
72
90
|
```
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
---
|
|
75
93
|
|
|
76
|
-
|
|
94
|
+
## 📖 Complete API Reference
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
### `<ngx-workflow-diagram>` Inputs
|
|
97
|
+
|
|
98
|
+
#### Core Configuration
|
|
99
|
+
|
|
100
|
+
| Input | Type | Default | Description |
|
|
101
|
+
|-------|------|---------|-------------|
|
|
102
|
+
| `initialNodes` | `Node[]` | `[]` | Initial nodes array |
|
|
103
|
+
| `initialEdges` | `Edge[]` | `[]` | Initial edges array |
|
|
104
|
+
| `initialViewport` | `Viewport` | `undefined` | Initial viewport `{ x, y, zoom }` |
|
|
105
|
+
|
|
106
|
+
#### Display Options
|
|
107
|
+
|
|
108
|
+
| Input | Type | Default | Description |
|
|
109
|
+
|-------|------|---------|-------------|
|
|
110
|
+
| `showZoomControls` | `boolean` | `true` | Show zoom controls (bottom-left) |
|
|
111
|
+
| `showMinimap` | `boolean` | `true` | Show minimap (bottom-right) |
|
|
112
|
+
| `showBackground` | `boolean` | `true` | Show background pattern |
|
|
113
|
+
| `showGrid` | `boolean` | `false` | Show grid overlay |
|
|
114
|
+
| `showExportControls` | `boolean` | `false` | Show export controls |
|
|
115
|
+
| `showLayoutControls` | `boolean` | `false` | Show layout controls |
|
|
116
|
+
|
|
117
|
+
#### Background Configuration
|
|
118
|
+
|
|
119
|
+
| Input | Type | Default | Description |
|
|
120
|
+
|-------|------|---------|-------------|
|
|
121
|
+
| `backgroundVariant` | `'dots' \| 'lines' \| 'cross'` | `'dots'` | Background pattern style |
|
|
122
|
+
| `backgroundGap` | `number` | `20` | Gap between pattern elements |
|
|
123
|
+
| `backgroundSize` | `number` | `1` | Size of pattern elements |
|
|
124
|
+
| `backgroundColor` | `string` | `'#81818a'` | Pattern color |
|
|
125
|
+
| `backgroundBgColor` | `string` | `'#f0f0f0'` | Canvas background color |
|
|
126
|
+
|
|
127
|
+
#### Grid Configuration
|
|
128
|
+
|
|
129
|
+
| Input | Type | Default | Description |
|
|
130
|
+
|-------|------|---------|-------------|
|
|
131
|
+
| `gridSize` | `number` | `20` | Grid cell size in pixels |
|
|
132
|
+
| `snapToGrid` | `boolean` | `false` | Snap nodes to grid |
|
|
133
|
+
|
|
134
|
+
#### Node & Edge Behavior
|
|
135
|
+
|
|
136
|
+
| Input | Type | Default | Description |
|
|
137
|
+
|-------|------|---------|-------------|
|
|
138
|
+
| `nodesResizable` | `boolean` | `true` | Global toggle for node resizing |
|
|
139
|
+
| `nodeDraggable` | `boolean` | `true` | Can nodes be dragged |
|
|
140
|
+
| `edgeReconnectable` | `boolean` | `false` | Can edges be reconnected |
|
|
141
|
+
| `validateConnection` | `Function` | `undefined` | Custom connection validation |
|
|
142
|
+
| `maxConnectionsPerHandle` | `number` | `undefined` | Global connection limit per handle |
|
|
143
|
+
|
|
144
|
+
#### Z-Index & Layer Management
|
|
145
|
+
|
|
146
|
+
| Input | Type | Default | Description |
|
|
147
|
+
|-------|------|---------|-------------|
|
|
148
|
+
| `zIndexMode` | `'default' \| 'layered'` | `'default'` | Enable z-index layer management |
|
|
149
|
+
|
|
150
|
+
#### Collision Detection
|
|
151
|
+
|
|
152
|
+
| Input | Type | Default | Description |
|
|
153
|
+
|-------|------|---------|-------------|
|
|
154
|
+
| `preventNodeOverlap` | `boolean` | `false` | Enable collision detection |
|
|
155
|
+
| `nodeSpacing` | `number` | `10` | Minimum spacing between nodes (px) |
|
|
156
|
+
|
|
157
|
+
#### Auto-Panning
|
|
158
|
+
|
|
159
|
+
| Input | Type | Default | Description |
|
|
160
|
+
|-------|------|---------|-------------|
|
|
161
|
+
| `autoPanOnNodeDrag` | `boolean` | `true` | Auto-pan when dragging near edge |
|
|
162
|
+
| `autoPanOnConnect` | `boolean` | `true` | Auto-pan when connecting near edge |
|
|
163
|
+
| `autoPanSpeed` | `number` | `15` | Pan speed (pixels per frame) |
|
|
164
|
+
| `autoPanEdgeThreshold` | `number` | `50` | Distance from edge to trigger (px) |
|
|
165
|
+
|
|
166
|
+
#### Auto-Save
|
|
167
|
+
|
|
168
|
+
| Input | Type | Default | Description |
|
|
169
|
+
|-------|------|---------|-------------|
|
|
170
|
+
| `autoSave` | `boolean` | `false` | Enable auto-save |
|
|
171
|
+
| `autoSaveInterval` | `number` | `1000` | Auto-save interval (milliseconds) |
|
|
172
|
+
| `maxVersions` | `number` | `10` | Maximum saved versions |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### `<ngx-workflow-diagram>` Outputs
|
|
177
|
+
|
|
178
|
+
#### Basic Events
|
|
179
|
+
|
|
180
|
+
| Output | Type | Description |
|
|
181
|
+
|--------|------|-------------|
|
|
182
|
+
| `nodeClick` | `EventEmitter<Node>` | Node clicked |
|
|
183
|
+
| `nodeDoubleClick` | `EventEmitter<Node>` | Node double-clicked |
|
|
184
|
+
| `edgeClick` | `EventEmitter<Edge>` | Edge clicked |
|
|
185
|
+
| `connect` | `EventEmitter<Connection>` | New connection created |
|
|
186
|
+
| `nodesChange` | `EventEmitter<Node[]>` | Nodes array changed |
|
|
187
|
+
| `edgesChange` | `EventEmitter<Edge[]>` | Edges array changed |
|
|
94
188
|
|
|
95
|
-
|
|
189
|
+
#### Mouse Interaction Events
|
|
96
190
|
|
|
97
|
-
|
|
191
|
+
| Output | Type | Description |
|
|
192
|
+
|--------|------|-------------|
|
|
193
|
+
| `nodeMouseEnter` | `EventEmitter<Node>` | Mouse entered node |
|
|
194
|
+
| `nodeMouseLeave` | `EventEmitter<Node>` | Mouse left node |
|
|
195
|
+
| `nodeMouseMove` | `EventEmitter<{node, event}>` | Mouse moved over node |
|
|
196
|
+
| `edgeMouseEnter` | `EventEmitter<Edge>` | Mouse entered edge |
|
|
197
|
+
| `edgeMouseLeave` | `EventEmitter<Edge>` | Mouse left edge |
|
|
98
198
|
|
|
99
|
-
|
|
199
|
+
#### Canvas Events
|
|
100
200
|
|
|
101
|
-
|
|
201
|
+
| Output | Type | Description |
|
|
202
|
+
|--------|------|-------------|
|
|
203
|
+
| `paneClick` | `EventEmitter<{event, position}>` | Empty canvas clicked |
|
|
204
|
+
| `paneScroll` | `EventEmitter<WheelEvent>` | Canvas scrolled/zoomed |
|
|
205
|
+
| `contextMenu` | `EventEmitter<{type, item, event}>` | Right-click context menu |
|
|
102
206
|
|
|
103
|
-
####
|
|
207
|
+
#### Connection Events
|
|
104
208
|
|
|
105
|
-
|
|
|
106
|
-
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `showZoomControls` | `boolean` | `true` | Whether to show the zoom control buttons (bottom-left). |
|
|
111
|
-
| `showMinimap` | `boolean` | `true` | Whether to show the minimap (bottom-right). |
|
|
112
|
-
| `showBackground` | `boolean` | `true` | Whether to show the background pattern. |
|
|
113
|
-
| `backgroundVariant` | `'dots' \| 'lines' \| 'cross'` | `'dots'` | The pattern style of the background. |
|
|
114
|
-
| `backgroundGap` | `number` | `20` | Gap between background pattern elements. |
|
|
115
|
-
| `backgroundSize` | `number` | `1` | Size of background pattern elements. |
|
|
116
|
-
| `backgroundColor` | `string` | `'#81818a'` | Color of the background pattern dots/lines. |
|
|
117
|
-
| `backgroundBgColor` | `string` | `'#f0f0f0'` | Background color of the canvas itself. |
|
|
118
|
-
| `connectionValidator` | `(source: string, target: string) => boolean` | `undefined` | Custom function to validate connections. Return `false` to prevent connection. |
|
|
119
|
-
| `nodesResizable` | `boolean` | `true` | Global toggle to enable/disable node resizing. |
|
|
209
|
+
| Output | Type | Description |
|
|
210
|
+
|--------|------|-------------|
|
|
211
|
+
| `connectStart` | `EventEmitter<{nodeId, handleId}>` | Connection drag started |
|
|
212
|
+
| `connectEnd` | `EventEmitter<{nodeId, handleId}>` | Connection drag ended |
|
|
213
|
+
| `connectionDrop` | `EventEmitter<{position, event, sourceNodeId, sourceHandleId}>` | Connection dropped |
|
|
120
214
|
|
|
121
|
-
####
|
|
215
|
+
#### Control Events
|
|
122
216
|
|
|
123
|
-
|
|
|
124
|
-
|
|
125
|
-
| `
|
|
126
|
-
| `edgeClick` | `EventEmitter<Edge>` | Emitted when an edge is clicked. |
|
|
127
|
-
| `connect` | `EventEmitter<Connection>` | Emitted when a new connection is created. |
|
|
128
|
-
| `nodesChange` | `EventEmitter<Node[]>` | Emitted when the nodes array changes (move, add, delete). |
|
|
129
|
-
| `edgesChange` | `EventEmitter<Edge[]>` | Emitted when the edges array changes. |
|
|
130
|
-
| `nodeDoubleClick` | `EventEmitter<Node>` | Emitted when a node is double-clicked. |
|
|
217
|
+
| Output | Type | Description |
|
|
218
|
+
|--------|------|-------------|
|
|
219
|
+
| `beforeDelete` | `EventEmitter<{nodes, edges, cancel}>` | Before deletion (cancellable) |
|
|
131
220
|
|
|
132
|
-
|
|
221
|
+
---
|
|
133
222
|
|
|
134
|
-
|
|
223
|
+
## 🎯 Interfaces
|
|
224
|
+
|
|
225
|
+
### `Node`
|
|
135
226
|
|
|
136
227
|
```typescript
|
|
137
228
|
interface Node {
|
|
138
229
|
id: string; // Unique identifier
|
|
139
|
-
position: { x: number
|
|
140
|
-
label?: string; //
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
width?: number; // Width in pixels (default:
|
|
144
|
-
height?: number; // Height in pixels (default:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
230
|
+
position: XYPosition; // { x: number, y: number }
|
|
231
|
+
label?: string; // Display label
|
|
232
|
+
type?: string; // 'default', 'group', or custom
|
|
233
|
+
data?: any; // Custom data for your components
|
|
234
|
+
width?: number; // Width in pixels (default: 150)
|
|
235
|
+
height?: number; // Height in pixels (default: 40)
|
|
236
|
+
zIndex?: number; // Stacking order (when zIndexMode='layered')
|
|
237
|
+
draggable?: boolean; // Can be dragged (default: true)
|
|
238
|
+
selectable?: boolean; // Can be selected (default: true)
|
|
239
|
+
connectable?: boolean; // Can connect edges (default: true)
|
|
240
|
+
resizable?: boolean; // Can be resized (default: true)
|
|
241
|
+
selected?: boolean; // Currently selected
|
|
242
|
+
className?: string; // Custom CSS class
|
|
243
|
+
style?: CSSStyleDeclaration; // Inline styles
|
|
244
|
+
parentId?: string; // Parent node ID (for grouping)
|
|
151
245
|
}
|
|
152
246
|
```
|
|
153
247
|
|
|
154
|
-
|
|
248
|
+
### `Edge`
|
|
155
249
|
|
|
156
250
|
```typescript
|
|
157
251
|
interface Edge {
|
|
158
|
-
id: string;
|
|
159
|
-
source: string; //
|
|
160
|
-
target: string; //
|
|
161
|
-
sourceHandle?: string; //
|
|
162
|
-
targetHandle?: string; //
|
|
163
|
-
label?: string; // Label text
|
|
164
|
-
type?: 'bezier' | 'straight' | 'step'; // Path type
|
|
165
|
-
animated?: boolean; //
|
|
166
|
-
markerEnd?: 'arrow' | 'arrowclosed';
|
|
167
|
-
|
|
252
|
+
id: string; // Unique identifier
|
|
253
|
+
source: string; // Source node ID
|
|
254
|
+
target: string; // Target node ID
|
|
255
|
+
sourceHandle?: string; // Source handle ID
|
|
256
|
+
targetHandle?: string; // Target handle ID
|
|
257
|
+
label?: string; // Label text
|
|
258
|
+
type?: 'bezier' | 'straight' | 'step'; // Path type
|
|
259
|
+
animated?: boolean; // Animated dashed line
|
|
260
|
+
markerEnd?: 'arrow' | 'arrowclosed'; // Arrow type
|
|
261
|
+
selected?: boolean; // Currently selected
|
|
262
|
+
style?: CSSStyleDeclaration; // SVG styles
|
|
263
|
+
data?: any; // Custom data
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### `Viewport`
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
interface Viewport {
|
|
271
|
+
x: number; // Pan X offset
|
|
272
|
+
y: number; // Pan Y offset
|
|
273
|
+
zoom: number; // Zoom level (1 = 100%)
|
|
168
274
|
}
|
|
169
275
|
```
|
|
170
276
|
|
|
277
|
+
### `Connection`
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
interface Connection {
|
|
281
|
+
source: string; // Source node ID
|
|
282
|
+
sourceHandle?: string; // Source handle ID
|
|
283
|
+
target: string; // Target node ID
|
|
284
|
+
targetHandle?: string; // Target handle ID
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## ⌨️ Keyboard Shortcuts
|
|
291
|
+
|
|
292
|
+
### Selection & Navigation
|
|
293
|
+
|
|
294
|
+
| Shortcut | Action |
|
|
295
|
+
|----------|--------|
|
|
296
|
+
| `Ctrl/Cmd + A` | Select all nodes |
|
|
297
|
+
| `Ctrl/Cmd + Click` | Multi-select nodes |
|
|
298
|
+
| `Shift + Drag` | Lasso selection |
|
|
299
|
+
| `Escape` | Clear selection |
|
|
300
|
+
|
|
301
|
+
### Editing
|
|
302
|
+
|
|
303
|
+
| Shortcut | Action |
|
|
304
|
+
|----------|--------|
|
|
305
|
+
| `Delete / Backspace` | Delete selected |
|
|
306
|
+
| `Ctrl/Cmd + C` | Copy |
|
|
307
|
+
| `Ctrl/Cmd + V` | Paste |
|
|
308
|
+
| `Ctrl/Cmd + X` | Cut |
|
|
309
|
+
| `Ctrl/Cmd + D` | Duplicate |
|
|
310
|
+
|
|
311
|
+
### History
|
|
312
|
+
|
|
313
|
+
| Shortcut | Action |
|
|
314
|
+
|----------|--------|
|
|
315
|
+
| `Ctrl/Cmd + Z` | Undo |
|
|
316
|
+
| `Ctrl/Cmd + Shift + Z` | Redo |
|
|
317
|
+
|
|
318
|
+
### Z-Index (Layer Management)
|
|
319
|
+
|
|
320
|
+
| Shortcut | Action |
|
|
321
|
+
|----------|--------|
|
|
322
|
+
| `Ctrl/Cmd + ]` | Bring to front |
|
|
323
|
+
| `Ctrl/Cmd + [` | Send to back |
|
|
324
|
+
| `Ctrl/Cmd + Shift + ]` | Raise layer |
|
|
325
|
+
| `Ctrl/Cmd + Shift + [` | Lower layer |
|
|
326
|
+
|
|
327
|
+
### Grouping
|
|
328
|
+
|
|
329
|
+
| Shortcut | Action |
|
|
330
|
+
|----------|--------|
|
|
331
|
+
| `Ctrl/Cmd + G` | Group selected nodes |
|
|
332
|
+
| `Ctrl/Cmd + Shift + G` | Ungroup |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
171
336
|
## 🎨 Customization
|
|
172
337
|
|
|
173
|
-
### Custom
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
338
|
+
### Custom Node Components
|
|
339
|
+
|
|
340
|
+
Create custom node types:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
@Component({
|
|
344
|
+
selector: 'app-custom-node',
|
|
345
|
+
template: `
|
|
346
|
+
<div class="custom-node">
|
|
347
|
+
<h3>{{ node.data.title }}</h3>
|
|
348
|
+
<p>{{ node.data.description }}</p>
|
|
349
|
+
<ngx-workflow-handle type="source" position="right">
|
|
350
|
+
</ngx-workflow-handle>
|
|
351
|
+
<ngx-workflow-handle type="target" position="left">
|
|
352
|
+
</ngx-workflow-handle>
|
|
353
|
+
</div>
|
|
354
|
+
`,
|
|
355
|
+
styles: [`
|
|
356
|
+
.custom-node {
|
|
357
|
+
background: white;
|
|
358
|
+
border: 2px solid #3b82f6;
|
|
359
|
+
border-radius: 8px;
|
|
360
|
+
padding: 12px;
|
|
361
|
+
min-width: 200px;
|
|
362
|
+
}
|
|
363
|
+
`]
|
|
364
|
+
})
|
|
365
|
+
export class CustomNodeComponent {
|
|
366
|
+
@Input() node!: Node;
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Register it:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
import { NGX_WORKFLOW_NODE_TYPES } from 'ngx-workflow';
|
|
374
|
+
|
|
375
|
+
providers: [
|
|
376
|
+
{
|
|
377
|
+
provide: NGX_WORKFLOW_NODE_TYPES,
|
|
378
|
+
useValue: {
|
|
379
|
+
'custom': CustomNodeComponent
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Use it:
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
{
|
|
389
|
+
id: '1',
|
|
390
|
+
type: 'custom',
|
|
391
|
+
position: { x: 0, y: 0 },
|
|
392
|
+
data: { title: 'My Node', description: 'Details...' }
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Custom Edge Labels
|
|
397
|
+
|
|
398
|
+
Use Angular components for edge labels:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
<ngx-workflow-diagram [nodes]="nodes" [edges]="edges">
|
|
402
|
+
<ng-template #edgeLabelTemplate let-edge>
|
|
403
|
+
<div class="custom-label">
|
|
404
|
+
<button (click)="editEdge(edge)">✏️</button>
|
|
405
|
+
<span>{{ edge.label }}</span>
|
|
406
|
+
<span class="badge">{{ edge.data?.priority }}</span>
|
|
407
|
+
</div>
|
|
408
|
+
</ng-template>
|
|
409
|
+
</ngx-workflow-diagram>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Theming
|
|
413
|
+
|
|
414
|
+
Override CSS variables:
|
|
225
415
|
|
|
226
416
|
```css
|
|
227
417
|
:root {
|
|
@@ -236,26 +426,97 @@ You can create your own node types by creating an Angular component and register
|
|
|
236
426
|
}
|
|
237
427
|
```
|
|
238
428
|
|
|
239
|
-
|
|
429
|
+
---
|
|
240
430
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
431
|
+
## 🔧 Programmatic API
|
|
432
|
+
|
|
433
|
+
Access via `DiagramStateService`:
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
constructor(private diagramState: DiagramStateService) {}
|
|
437
|
+
|
|
438
|
+
// Selection
|
|
439
|
+
this.diagramState.selectAll();
|
|
440
|
+
this.diagramState.deselectAll();
|
|
441
|
+
this.diagramState.selectNodes(['node-1', 'node-2']);
|
|
442
|
+
|
|
443
|
+
// Alignment
|
|
444
|
+
this.diagramState.alignNodes('left');
|
|
445
|
+
// Options: 'left', 'right', 'center', 'top', 'bottom', 'middle'
|
|
446
|
+
|
|
447
|
+
// Distribution
|
|
448
|
+
this.diagramState.distributeNodes('horizontal');
|
|
449
|
+
// Options: 'horizontal', 'vertical'
|
|
450
|
+
|
|
451
|
+
// Deletion
|
|
452
|
+
this.diagramState.deleteAll();
|
|
453
|
+
|
|
454
|
+
// Z-Index
|
|
455
|
+
this.diagramState.bringToFront('node-1');
|
|
456
|
+
this.diagramState.sendToBack('node-1');
|
|
457
|
+
this.diagramState.raiseLayer('node-1');
|
|
458
|
+
this.diagramState.lowerLayer('node-1');
|
|
459
|
+
|
|
460
|
+
// Viewport
|
|
461
|
+
this.diagramState.setViewport({ x: 0, y: 0, zoom: 1 });
|
|
462
|
+
this.diagramState.fitView();
|
|
463
|
+
|
|
464
|
+
// Nodes & Edges
|
|
465
|
+
this.diagramState.addNode(node);
|
|
466
|
+
this.diagramState.updateNode('node-1', { label: 'Updated' });
|
|
467
|
+
this.diagramState.deleteNode('node-1');
|
|
468
|
+
this.diagramState.addEdge(edge);
|
|
469
|
+
this.diagramState.deleteEdge('edge-1');
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## 📋 Examples
|
|
475
|
+
|
|
476
|
+
### With Connection Limits
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
<ngx-workflow-diagram
|
|
480
|
+
[maxConnectionsPerHandle]="1">
|
|
481
|
+
</ngx-workflow-diagram>
|
|
482
|
+
|
|
483
|
+
// Or per-handle:
|
|
484
|
+
node.data = {
|
|
485
|
+
handleConfig: {
|
|
486
|
+
'output': { maxConnections: 1 }
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### With Collision Detection
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
<ngx-workflow-diagram
|
|
495
|
+
[preventNodeOverlap]="true"
|
|
496
|
+
[nodeSpacing]="10">
|
|
497
|
+
</ngx-workflow-diagram>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### With Before Delete Hook
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
<ngx-workflow-diagram
|
|
504
|
+
(beforeDelete)="onBeforeDelete($event)">
|
|
505
|
+
</ngx-workflow-diagram>
|
|
506
|
+
|
|
507
|
+
onBeforeDelete(event: any) {
|
|
508
|
+
if (!confirm('Delete?')) {
|
|
509
|
+
event.cancel();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
254
515
|
|
|
255
516
|
## 🤝 Contributing
|
|
256
517
|
|
|
257
|
-
Contributions
|
|
518
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
258
519
|
|
|
259
520
|
## 📄 License
|
|
260
521
|
|
|
261
|
-
|
|
522
|
+
MIT License - see [LICENSE](LICENSE).
|