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.
Files changed (2) hide show
  1. package/README.md +181 -211
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,291 +1,261 @@
1
1
  # ngx-workflow
2
2
 
3
- An Angular library for building interactive workflow diagrams with drag-and-drop nodes and connectable edges.
3
+ [![npm version](https://img.shields.io/npm/v/ngx-workflow.svg)](https://www.npmjs.com/package/ngx-workflow)
4
+ [![License](https://img.shields.io/npm/l/ngx-workflow.svg)](https://github.com/abdulkyume/ngx-workflow/blob/main/LICENSE)
4
5
 
5
- ## Features
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
- - đŸŽ¯ **Drag & Drop Nodes** - Reposition nodes by dragging
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
+ ![Demo Screenshot](https://raw.githubusercontent.com/abdulkyume/ngx-workflow/main/assets/demo.png)
14
9
 
15
- ## Installation
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
- ### Declarative Approach (Recommended)
31
+ ### Option 1: Standalone Component (Recommended)
24
32
 
25
- Use `@Input()` and `@Output()` properties for a more Angular-friendly, template-driven approach:
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-workflow',
40
+ selector: 'app-root',
41
+ standalone: true,
42
+ imports: [NgxWorkflowModule],
33
43
  template: `
34
- <div style="width: 100%; height: 600px;">
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 WorkflowComponent {
54
+ export class AppComponent {
45
55
  nodes: Node[] = [
46
- {
47
- id: '1',
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('Node clicked:', node);
65
+ console.log('Clicked:', node);
73
66
  }
74
67
 
75
- onConnect(connection: { source: string; target: string }) {
76
- console.log('New connection:', connection);
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
- ### Imperative Approach (Service Injection)
74
+ ### Option 2: NgModule (Modular Approach)
88
75
 
89
- Use `DiagramStateService` for programmatic control:
76
+ If you are using NgModules, import `NgxWorkflowModule` in your module.
90
77
 
91
78
  ```typescript
92
- import { NgxFlowModule } from 'ngx-workflow';
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
- imports: [NgxFlowModule],
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
- ### 2. Use the Component
95
+ Then use it in your component template just like in the standalone example.
101
96
 
102
- ```typescript
103
- import { Component } from '@angular/core';
104
- import { DiagramStateService } from 'ngx-workflow';
97
+ ## 📖 API Reference
105
98
 
106
- @Component({
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
- ## API Reference
159
-
160
- ### DiagramComponent
101
+ The main component for rendering the workflow.
161
102
 
162
103
  #### Inputs
163
104
 
164
- - `[initialNodes]` - Initial array of nodes to display
165
- - `[initialEdges]` - Initial array of edges to display
166
- - `[initialViewport]` - Initial viewport state `{ x: number, y: number, zoom: number }`
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
- - `(nodeClick)` - Emitted when a node is clicked. Payload: `Node`
171
- - `(edgeClick)` - Emitted when an edge is clicked. Payload: `Edge`
172
- - `(connect)` - Emitted when a new edge is created. Payload: `{ source: string, sourceHandle?: string, target: string, targetHandle?: string }`
173
- - `(nodesChange)` - Emitted when nodes change. Payload: `Node[]`
174
- - `(edgesChange)` - Emitted when edges change. Payload: `Edge[]`
175
-
176
- ### DiagramStateService
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
- The main service for managing diagram state.
132
+ ### Interfaces
179
133
 
180
- #### Methods
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
- data?: any;
212
- width?: number; // Default: 170
213
- height?: number; // Default: 60
214
- selected?: boolean;
215
- dragging?: boolean;
216
- draggable?: boolean; // Default: true
217
- type?: string;
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
- ### Edge Interface
154
+ #### `Edge`
222
155
 
223
156
  ```typescript
224
157
  interface Edge {
225
158
  id: string;
226
- source: string;
227
- sourceHandle?: string; // 'top' | 'right' | 'bottom' | 'left'
228
- target: string;
229
- targetHandle?: string; // 'top' | 'right' | 'bottom' | 'left'
230
- type?: 'bezier' | 'straight' | 'step'; // Default: 'bezier'
231
- animated?: boolean;
232
- style?: { [key: string]: any };
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
- ## Interactions
237
-
238
- ### Node Dragging
239
- - Click and drag on a node's body to move it
240
- - Nodes must have `draggable: true` to be draggable
241
-
242
- ### Creating Edges
243
- 1. Click on a handle (small blue circle at node edges)
244
- 2. Drag to another node's handle
245
- 3. Release to create the connection
246
-
247
- ### Panning
248
- - Click and drag on empty canvas to pan
249
- - Or use middle mouse button
250
-
251
- ### Zooming
252
- - Use mouse wheel to zoom in/out
253
- - Zoom is centered on mouse position
254
-
255
- ### Selection
256
- - Click a node to select it
257
- - Ctrl/Cmd + click to add to selection
258
- - Shift + drag on canvas for lasso selection
259
- - Press Delete to remove selected nodes/edges
260
-
261
- ### Undo/Redo
262
- - Ctrl/Cmd + Z to undo
263
- - Ctrl/Cmd + Shift + Z to redo
264
-
265
- ## Customization
266
-
267
- ### CSS Variables
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
- :host {
227
+ :root {
271
228
  --ngx-workflow-primary: #3b82f6;
272
- --ngx-workflow-primary-hover: #2563eb;
273
229
  --ngx-workflow-bg: #f8fafc;
274
- --ngx-workflow-surface: #ffffff;
275
- --ngx-workflow-border: #e2e8f0;
276
- --ngx-workflow-text-primary: #1e293b;
277
- --ngx-workflow-text-secondary: #64748b;
278
- --ngx-workflow-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
279
- --ngx-workflow-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
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
- ## License
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
- MIT
257
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
288
258
 
289
- ## Contributing
259
+ ## 📄 License
290
260
 
291
- Contributions are welcome! Please open an issue or submit a pull request.
261
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-workflow",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=17.0.0",
6
6
  "@angular/core": ">=17.0.0"