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 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
- You can use `ngx-workflow` in two ways: **Standalone Component** (recommended for Angular 14+) or **NgModule**.
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
- (connect)="onConnect($event)"
50
- ></ngx-workflow-diagram>
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', type: 'default' },
57
- { id: '2', position: { x: 300, y: 100 }, label: 'End', type: 'default' }
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', sourceHandle: 'right', targetHandle: 'left' }
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
- onConnect(connection: any) {
69
- console.log('Connected:', connection);
84
+ onBeforeDelete(event: any) {
85
+ if (!confirm('Delete selected items?')) {
86
+ event.cancel();
87
+ }
70
88
  }
71
89
  }
72
90
  ```
73
91
 
74
- ### Option 2: NgModule (Modular Approach)
92
+ ---
75
93
 
76
- If you are using NgModules, import `NgxWorkflowModule` in your module.
94
+ ## 📖 Complete API Reference
77
95
 
78
- ```typescript
79
- import { NgModule } from '@angular/core';
80
- import { BrowserModule } from '@angular/platform-browser';
81
- import { NgxWorkflowModule } from 'ngx-workflow';
82
- import { AppComponent } from './app.component';
83
-
84
- @NgModule({
85
- declarations: [AppComponent],
86
- imports: [
87
- BrowserModule,
88
- NgxWorkflowModule // Import the module here
89
- ],
90
- bootstrap: [AppComponent]
91
- })
92
- export class AppModule { }
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
- Then use it in your component template just like in the standalone example.
189
+ #### Mouse Interaction Events
96
190
 
97
- ## 📖 API Reference
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
- ### `<ngx-workflow-diagram>`
199
+ #### Canvas Events
100
200
 
101
- The main component for rendering the workflow.
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
- #### Inputs
207
+ #### Connection Events
104
208
 
105
- | Name | Type | Default | Description |
106
- |------|------|---------|-------------|
107
- | `initialNodes` | `Node[]` | `[]` | Initial array of nodes to display. |
108
- | `initialEdges` | `Edge[]` | `[]` | Initial array of edges to display. |
109
- | `initialViewport` | `Viewport` | `undefined` | Initial viewport state `{ x, y, zoom }`. |
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
- #### Outputs
215
+ #### Control Events
122
216
 
123
- | Name | Type | Description |
124
- |------|------|-------------|
125
- | `nodeClick` | `EventEmitter<Node>` | Emitted when a node is clicked. |
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
- ### Interfaces
221
+ ---
133
222
 
134
- #### `Node`
223
+ ## 🎯 Interfaces
224
+
225
+ ### `Node`
135
226
 
136
227
  ```typescript
137
228
  interface Node {
138
229
  id: string; // Unique identifier
139
- position: { x: number; y: number }; // Position on canvas
140
- label?: string; // Default label
141
- data?: any; // Custom data passed to your custom node component
142
- type?: string; // 'default', 'group', or your custom type
143
- width?: number; // Width in pixels (default: 170)
144
- height?: number; // Height in pixels (default: 60)
145
- draggable?: boolean; // Is the node draggable? (default: true)
146
- selectable?: boolean; // Is the node selectable? (default: true)
147
- connectable?: boolean; // Can edges be connected? (default: true)
148
- resizable?: boolean; // Is this specific node resizable? (default: true)
149
- class?: string; // Custom CSS class
150
- style?: object; // Custom inline styles
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
- #### `Edge`
248
+ ### `Edge`
155
249
 
156
250
  ```typescript
157
251
  interface Edge {
158
- id: string;
159
- source: string; // ID of source node
160
- target: string; // ID of target node
161
- sourceHandle?: string; // ID of source handle (optional)
162
- targetHandle?: string; // ID of target handle (optional)
163
- label?: string; // Label text displayed on the edge
164
- type?: 'bezier' | 'straight' | 'step'; // Path type (default: 'bezier')
165
- animated?: boolean; // Show animation (dashed moving line)?
166
- markerEnd?: 'arrow' | 'arrowclosed'; // Arrowhead type
167
- style?: object; // SVG styles (stroke, stroke-width, etc.)
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 Nodes
174
- You can create your own node types by creating an Angular component and registering it.
175
-
176
- 1. **Create the Component**:
177
- It should accept an input `node`.
178
-
179
- ```typescript
180
- @Component({
181
- selector: 'app-custom-node',
182
- template: `
183
- <div class="custom-node">
184
- <div class="header">{{ node.data.title }}</div>
185
- <div class="content">{{ node.data.content }}</div>
186
- <!-- Add handles -->
187
- <ngx-workflow-handle type="source" position="right"></ngx-workflow-handle>
188
- <ngx-workflow-handle type="target" position="left"></ngx-workflow-handle>
189
- </div>
190
- `,
191
- styles: [`
192
- .custom-node { border: 1px solid #333; background: white; border-radius: 4px; }
193
- .header { background: #eee; padding: 4px; border-bottom: 1px solid #333; }
194
- .content { padding: 8px; }
195
- `]
196
- })
197
- export class CustomNodeComponent {
198
- @Input() node!: Node;
199
- }
200
- ```
201
-
202
- 2. **Register the Component**:
203
- Provide it in your module configuration using `NGX_WORKFLOW_NODE_TYPES`.
204
-
205
- ```typescript
206
- import { NGX_WORKFLOW_NODE_TYPES } from 'ngx-workflow';
207
-
208
- providers: [
209
- {
210
- provide: NGX_WORKFLOW_NODE_TYPES,
211
- useValue: {
212
- 'my-custom-type': CustomNodeComponent
213
- }
214
- }
215
- ]
216
- ```
217
-
218
- 3. **Use it**:
219
- ```typescript
220
- { id: '3', type: 'my-custom-type', position: { x: 0, y: 0 }, data: { title: 'My Node', content: '...' } }
221
- ```
222
-
223
- ### Styling
224
- `ngx-workflow` uses CSS variables for easy theming. Override these in your global styles:
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
- ## ⌨️ Keyboard Shortcuts
429
+ ---
240
430
 
241
- | Shortcut | Action |
242
- |----------|--------|
243
- | `Delete` / `Backspace` | Delete selected nodes/edges |
244
- | `Ctrl` + `Z` | Undo |
245
- | `Ctrl` + `Shift` + `Z` | Redo |
246
- | `Shift` + `Drag` | Lasso Selection |
247
- | `Ctrl` + `Click` | Multi-select |
248
- | `Ctrl` + `C` | Copy |
249
- | `Ctrl` + `V` | Paste |
250
- | `Ctrl` + `X` | Cut |
251
- | `Ctrl` + `D` | Duplicate |
252
- | `Ctrl` + `G` | Group Selected Nodes |
253
- | `Ctrl` + `Shift` + `G` | Ungroup Selected Group |
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 are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
518
+ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
258
519
 
259
520
  ## 📄 License
260
521
 
261
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
522
+ MIT License - see [LICENSE](LICENSE).