ngx-workflow 0.0.1 â 0.0.2
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 +181 -211
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,291 +1,261 @@
|
|
|
1
1
|
# ngx-workflow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/ngx-workflow)
|
|
4
|
+
[](https://github.com/abdulkyume/ngx-workflow/blob/main/LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A powerful, highly customizable Angular library for building interactive node-based editors, flow charts, and diagrams. Built with Angular Signals for high performance and reactivity.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- đ **Connect Edges** - Connect nodes via handles with bezier/straight/step paths
|
|
9
|
-
- đ **Pan & Zoom** - Navigate large diagrams with mouse wheel zoom and canvas panning
|
|
10
|
-
- đ¨ **Customizable** - Style nodes and edges via CSS variables or custom components
|
|
11
|
-
- âŠī¸ **Undo/Redo** - Full undo/redo support for all diagram changes
|
|
12
|
-
- â¨ī¸ **Keyboard Shortcuts** - Delete, undo (Ctrl+Z), redo (Ctrl+Shift+Z)
|
|
13
|
-
- đĻ **Lasso Selection** - Select multiple nodes (Shift + drag)
|
|
8
|
+

|
|
14
9
|
|
|
15
|
-
##
|
|
10
|
+
## đ Features
|
|
11
|
+
|
|
12
|
+
- **Native Angular**: Built from the ground up for Angular, using Signals and OnPush change detection.
|
|
13
|
+
- **Interactive**: Drag & drop nodes, zoom & pan canvas, connect edges.
|
|
14
|
+
- **Customizable**: Fully custom node and edge templates.
|
|
15
|
+
- **Rich UI**: Built-in minimap, background patterns, controls, and alignment tools.
|
|
16
|
+
- **Layouts**: Automatic layout support via Dagre and ELK.
|
|
17
|
+
- **History**: Robust Undo/Redo history stack.
|
|
18
|
+
- **Export**: Export to JSON, PNG, or SVG.
|
|
19
|
+
- **Theming**: Extensive CSS variables for easy styling.
|
|
20
|
+
|
|
21
|
+
## đĻ Installation
|
|
16
22
|
|
|
17
23
|
```bash
|
|
18
24
|
npm install ngx-workflow
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
## Quick Start
|
|
27
|
+
## đ Quick Start
|
|
28
|
+
|
|
29
|
+
You can use `ngx-workflow` in two ways: **Standalone Component** (recommended for Angular 14+) or **NgModule**.
|
|
22
30
|
|
|
23
|
-
###
|
|
31
|
+
### Option 1: Standalone Component (Recommended)
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
Import `NgxWorkflowModule` directly into your standalone component's `imports` array.
|
|
26
34
|
|
|
27
35
|
```typescript
|
|
28
36
|
import { Component } from '@angular/core';
|
|
29
|
-
import { Node, Edge } from 'ngx-workflow';
|
|
37
|
+
import { NgxWorkflowModule, Node, Edge } from 'ngx-workflow';
|
|
30
38
|
|
|
31
39
|
@Component({
|
|
32
|
-
selector: 'app-
|
|
40
|
+
selector: 'app-root',
|
|
41
|
+
standalone: true,
|
|
42
|
+
imports: [NgxWorkflowModule],
|
|
33
43
|
template: `
|
|
34
|
-
<div style="width: 100%;
|
|
35
|
-
<ngx-diagram
|
|
44
|
+
<div style="height: 100vh; width: 100%;">
|
|
45
|
+
<ngx-workflow-diagram
|
|
36
46
|
[initialNodes]="nodes"
|
|
37
47
|
[initialEdges]="edges"
|
|
38
48
|
(nodeClick)="onNodeClick($event)"
|
|
39
49
|
(connect)="onConnect($event)"
|
|
40
|
-
></ngx-diagram>
|
|
50
|
+
></ngx-workflow-diagram>
|
|
41
51
|
</div>
|
|
42
52
|
`
|
|
43
53
|
})
|
|
44
|
-
export class
|
|
54
|
+
export class AppComponent {
|
|
45
55
|
nodes: Node[] = [
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
position: { x: 50, y: 50 },
|
|
49
|
-
data: { label: 'Start' },
|
|
50
|
-
draggable: true
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: '2',
|
|
54
|
-
position: { x: 300, y: 50 },
|
|
55
|
-
data: { label: 'Process' },
|
|
56
|
-
draggable: true
|
|
57
|
-
}
|
|
56
|
+
{ id: '1', position: { x: 100, y: 100 }, label: 'Start', type: 'default' },
|
|
57
|
+
{ id: '2', position: { x: 300, y: 100 }, label: 'End', type: 'default' }
|
|
58
58
|
];
|
|
59
59
|
|
|
60
60
|
edges: Edge[] = [
|
|
61
|
-
{
|
|
62
|
-
id: 'e1',
|
|
63
|
-
source: '1',
|
|
64
|
-
sourceHandle: 'right',
|
|
65
|
-
target: '2',
|
|
66
|
-
targetHandle: 'left',
|
|
67
|
-
type: 'bezier'
|
|
68
|
-
}
|
|
61
|
+
{ id: 'e1-2', source: '1', target: '2', sourceHandle: 'right', targetHandle: 'left' }
|
|
69
62
|
];
|
|
70
63
|
|
|
71
64
|
onNodeClick(node: Node) {
|
|
72
|
-
console.log('
|
|
65
|
+
console.log('Clicked:', node);
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
onConnect(connection:
|
|
76
|
-
console.log('
|
|
77
|
-
// Add new edge to your data
|
|
78
|
-
this.edges = [...this.edges, {
|
|
79
|
-
id: Date.now().toString(),
|
|
80
|
-
...connection,
|
|
81
|
-
type: 'bezier'
|
|
82
|
-
}];
|
|
68
|
+
onConnect(connection: any) {
|
|
69
|
+
console.log('Connected:', connection);
|
|
83
70
|
}
|
|
84
71
|
}
|
|
85
72
|
```
|
|
86
73
|
|
|
87
|
-
###
|
|
74
|
+
### Option 2: NgModule (Modular Approach)
|
|
88
75
|
|
|
89
|
-
|
|
76
|
+
If you are using NgModules, import `NgxWorkflowModule` in your module.
|
|
90
77
|
|
|
91
78
|
```typescript
|
|
92
|
-
import {
|
|
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';
|
|
93
83
|
|
|
94
84
|
@NgModule({
|
|
95
|
-
|
|
85
|
+
declarations: [AppComponent],
|
|
86
|
+
imports: [
|
|
87
|
+
BrowserModule,
|
|
88
|
+
NgxWorkflowModule // Import the module here
|
|
89
|
+
],
|
|
90
|
+
bootstrap: [AppComponent]
|
|
96
91
|
})
|
|
97
|
-
export class AppModule {}
|
|
92
|
+
export class AppModule { }
|
|
98
93
|
```
|
|
99
94
|
|
|
100
|
-
|
|
95
|
+
Then use it in your component template just like in the standalone example.
|
|
101
96
|
|
|
102
|
-
|
|
103
|
-
import { Component } from '@angular/core';
|
|
104
|
-
import { DiagramStateService } from 'ngx-workflow';
|
|
97
|
+
## đ API Reference
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
selector: 'app-workflow',
|
|
108
|
-
template: `
|
|
109
|
-
<div style="width: 100%; height: 600px;">
|
|
110
|
-
<ngx-diagram></ngx-diagram>
|
|
111
|
-
</div>
|
|
112
|
-
<button (click)="addNode()">Add Node</button>
|
|
113
|
-
`
|
|
114
|
-
})
|
|
115
|
-
export class WorkflowComponent {
|
|
116
|
-
constructor(private diagramState: DiagramStateService) {
|
|
117
|
-
// Add initial nodes
|
|
118
|
-
this.diagramState.addNode({
|
|
119
|
-
id: '1',
|
|
120
|
-
position: { x: 50, y: 50 },
|
|
121
|
-
data: { label: 'Start' },
|
|
122
|
-
draggable: true,
|
|
123
|
-
width: 170,
|
|
124
|
-
height: 60
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
this.diagramState.addNode({
|
|
128
|
-
id: '2',
|
|
129
|
-
position: { x: 300, y: 50 },
|
|
130
|
-
data: { label: 'Process' },
|
|
131
|
-
draggable: true,
|
|
132
|
-
width: 170,
|
|
133
|
-
height: 60
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Add an edge
|
|
137
|
-
this.diagramState.addEdge({
|
|
138
|
-
id: 'e1',
|
|
139
|
-
source: '1',
|
|
140
|
-
sourceHandle: 'right',
|
|
141
|
-
target: '2',
|
|
142
|
-
targetHandle: 'left',
|
|
143
|
-
type: 'bezier'
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
addNode() {
|
|
148
|
-
this.diagramState.addNode({
|
|
149
|
-
id: Date.now().toString(),
|
|
150
|
-
position: { x: 200, y: 200 },
|
|
151
|
-
data: { label: 'New Node' },
|
|
152
|
-
draggable: true
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
```
|
|
99
|
+
### `<ngx-workflow-diagram>`
|
|
157
100
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### DiagramComponent
|
|
101
|
+
The main component for rendering the workflow.
|
|
161
102
|
|
|
162
103
|
#### Inputs
|
|
163
104
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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. |
|
|
167
120
|
|
|
168
121
|
#### Outputs
|
|
169
122
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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. |
|
|
177
131
|
|
|
178
|
-
|
|
132
|
+
### Interfaces
|
|
179
133
|
|
|
180
|
-
####
|
|
181
|
-
|
|
182
|
-
- `addNode(node: Node): void` - Add a new node
|
|
183
|
-
- `removeNode(nodeId: string): void` - Remove a node
|
|
184
|
-
- `moveNode(nodeId: string, position: XYPosition): void` - Move a node
|
|
185
|
-
- `addEdge(edge: Edge): void` - Add a new edge
|
|
186
|
-
- `removeEdge(edgeId: string): void` - Remove an edge
|
|
187
|
-
- `selectNodes(nodeIds: string[], append?: boolean): void` - Select nodes
|
|
188
|
-
- `undo(): void` - Undo last change
|
|
189
|
-
- `redo(): void` - Redo last undone change
|
|
190
|
-
|
|
191
|
-
#### Signals
|
|
192
|
-
|
|
193
|
-
- `nodes()` - Current nodes array
|
|
194
|
-
- `edges()` - Current edges array
|
|
195
|
-
- `viewport()` - Current viewport state (x, y, zoom)
|
|
196
|
-
|
|
197
|
-
#### Events
|
|
198
|
-
|
|
199
|
-
- `nodeClick` - Emitted when a node is clicked
|
|
200
|
-
- `edgeClick` - Emitted when an edge is clicked
|
|
201
|
-
- `connect` - Emitted when a new edge is created
|
|
202
|
-
- `nodesChange` - Emitted when nodes change
|
|
203
|
-
- `edgesChange` - Emitted when edges change
|
|
204
|
-
|
|
205
|
-
### Node Interface
|
|
134
|
+
#### `Node`
|
|
206
135
|
|
|
207
136
|
```typescript
|
|
208
137
|
interface Node {
|
|
209
|
-
id: string;
|
|
210
|
-
position: { x: number; y: number };
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
draggable?: boolean;
|
|
217
|
-
|
|
138
|
+
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
|
|
218
151
|
}
|
|
219
152
|
```
|
|
220
153
|
|
|
221
|
-
|
|
154
|
+
#### `Edge`
|
|
222
155
|
|
|
223
156
|
```typescript
|
|
224
157
|
interface Edge {
|
|
225
158
|
id: string;
|
|
226
|
-
source: string;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
targetHandle?: string;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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.)
|
|
233
168
|
}
|
|
234
169
|
```
|
|
235
170
|
|
|
236
|
-
##
|
|
237
|
-
|
|
238
|
-
###
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
-
|
|
253
|
-
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
171
|
+
## đ¨ Customization
|
|
172
|
+
|
|
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:
|
|
268
225
|
|
|
269
226
|
```css
|
|
270
|
-
:
|
|
227
|
+
:root {
|
|
271
228
|
--ngx-workflow-primary: #3b82f6;
|
|
272
|
-
--ngx-workflow-primary-hover: #2563eb;
|
|
273
229
|
--ngx-workflow-bg: #f8fafc;
|
|
274
|
-
--ngx-workflow-
|
|
275
|
-
--ngx-workflow-
|
|
276
|
-
--ngx-workflow-
|
|
277
|
-
--ngx-workflow-
|
|
278
|
-
--ngx-workflow-
|
|
279
|
-
--ngx-workflow-
|
|
280
|
-
--ngx-workflow-glass-bg: rgba(255, 255, 255, 0.8);
|
|
281
|
-
--ngx-workflow-glass-blur: blur(12px);
|
|
230
|
+
--ngx-workflow-grid-color: #e2e8f0;
|
|
231
|
+
--ngx-workflow-node-bg: #ffffff;
|
|
232
|
+
--ngx-workflow-node-border: #cbd5e1;
|
|
233
|
+
--ngx-workflow-handle-color: #3b82f6;
|
|
234
|
+
--ngx-workflow-edge-stroke: #64748b;
|
|
235
|
+
--ngx-workflow-selection-stroke: #3b82f6;
|
|
282
236
|
}
|
|
283
237
|
```
|
|
284
238
|
|
|
285
|
-
##
|
|
239
|
+
## â¨ī¸ Keyboard Shortcuts
|
|
240
|
+
|
|
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 |
|
|
254
|
+
|
|
255
|
+
## đ¤ Contributing
|
|
286
256
|
|
|
287
|
-
|
|
257
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
288
258
|
|
|
289
|
-
##
|
|
259
|
+
## đ License
|
|
290
260
|
|
|
291
|
-
|
|
261
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|