@redvars/peacock 3.8.6 → 3.9.1
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/dist/code-highlighter.js +1 -4
- package/dist/code-highlighter.js.map +1 -1
- package/dist/custom-elements-jsdocs.json +93 -213
- package/dist/custom-elements.json +488 -2149
- package/dist/elevation.js +55 -2
- package/dist/elevation.js.map +1 -1
- package/dist/flow.js +762 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/loader.js +10 -9
- package/dist/loader.js.map +1 -1
- package/dist/menu-item.js +144 -63
- package/dist/menu-item.js.map +1 -1
- package/dist/menu.js +161 -73
- package/dist/menu.js.map +1 -1
- package/dist/src/flow/flow.d.ts +102 -0
- package/dist/src/flow/index.d.ts +2 -0
- package/dist/src/index.d.ts +3 -4
- package/dist/src/menu/menu/menu.d.ts +10 -9
- package/dist/src/menu/menu-item/menu-item.d.ts +1 -3
- package/dist/src/timeline/index.d.ts +2 -0
- package/dist/src/timeline/timeline-item.d.ts +23 -0
- package/dist/src/timeline/timeline.d.ts +26 -0
- package/dist/sub-menu.js +28 -1
- package/dist/sub-menu.js.map +1 -1
- package/dist/timeline-item.js +163 -0
- package/dist/timeline-item.js.map +1 -0
- package/dist/timeline.js +52 -0
- package/dist/timeline.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/readme.md +2 -2
- package/src/code-highlighter/code-highlighter.scss +1 -7
- package/src/elevation/demo/index.html +52 -0
- package/src/elevation/elevation.scss +52 -5
- package/src/elevation/elevation.ts +8 -1
- package/src/flow/flow.scss +195 -0
- package/src/flow/flow.ts +683 -0
- package/src/flow/index.ts +2 -0
- package/src/index.ts +3 -16
- package/src/loader.ts +10 -9
- package/src/menu/menu/copy-component-files.mjs +47 -0
- package/src/menu/menu/menu.scss +55 -17
- package/src/menu/menu/menu.ts +155 -84
- package/src/menu/menu-item/menu-item-colors.scss +3 -3
- package/src/menu/menu-item/menu-item.scss +118 -24
- package/src/menu/menu-item/menu-item.ts +61 -55
- package/src/menu/sub-menu/sub-menu.scss +30 -1
- package/src/timeline/index.ts +2 -0
- package/src/timeline/timeline-item.scss +98 -0
- package/src/timeline/timeline-item.ts +63 -0
- package/src/timeline/timeline.scss +11 -0
- package/src/timeline/timeline.ts +41 -0
- package/dist/canvas.js +0 -385
- package/dist/canvas.js.map +0 -1
- package/dist/flow-designer-node-CGSm6cUH.js +0 -553
- package/dist/flow-designer-node-CGSm6cUH.js.map +0 -1
- package/dist/flow-designer-node.js +0 -4
- package/dist/flow-designer-node.js.map +0 -1
- package/dist/flow-designer.js +0 -1417
- package/dist/flow-designer.js.map +0 -1
- package/dist/src/canvas/canvas.d.ts +0 -92
- package/dist/src/canvas/index.d.ts +0 -2
- package/dist/src/flow-designer/commands.d.ts +0 -66
- package/dist/src/flow-designer/flow-designer-node.d.ts +0 -46
- package/dist/src/flow-designer/flow-designer.d.ts +0 -133
- package/dist/src/flow-designer/index.d.ts +0 -7
- package/dist/src/flow-designer/layout.d.ts +0 -30
- package/dist/src/flow-designer/types.d.ts +0 -142
- package/dist/src/flow-designer/validation.d.ts +0 -43
- package/dist/src/flow-designer/workflow-utils.d.ts +0 -40
- package/src/canvas/canvas.scss +0 -72
- package/src/canvas/canvas.ts +0 -413
- package/src/canvas/index.ts +0 -2
- package/src/flow-designer/DEMO.md +0 -239
- package/src/flow-designer/commands.ts +0 -278
- package/src/flow-designer/flow-designer-node.ts +0 -172
- package/src/flow-designer/flow-designer.scss +0 -457
- package/src/flow-designer/flow-designer.ts +0 -611
- package/src/flow-designer/index.ts +0 -41
- package/src/flow-designer/layout.ts +0 -357
- package/src/flow-designer/types.ts +0 -166
- package/src/flow-designer/validation.ts +0 -284
- package/src/flow-designer/workflow-utils.ts +0 -282
- /package/dist/test/{flow-designer.test.d.ts → elevation.test.d.ts} +0 -0
|
@@ -1,611 +0,0 @@
|
|
|
1
|
-
import { html, LitElement, nothing } from 'lit';
|
|
2
|
-
import { property, query, state } from 'lit/decorators.js';
|
|
3
|
-
import IndividualComponent from '@/IndividualComponent.js';
|
|
4
|
-
|
|
5
|
-
import '../toolbar/toolbar.js';
|
|
6
|
-
import '../button/icon-button/icon-button.js';
|
|
7
|
-
import '../icon/icon.js';
|
|
8
|
-
import './flow-designer-node.js';
|
|
9
|
-
|
|
10
|
-
import styles from './flow-designer.scss';
|
|
11
|
-
import type {
|
|
12
|
-
Workflow,
|
|
13
|
-
WorkflowNode,
|
|
14
|
-
PositionedNode,
|
|
15
|
-
WorkflowChangeEvent,
|
|
16
|
-
HistoryEntry,
|
|
17
|
-
EditorState,
|
|
18
|
-
WorkflowCommand,
|
|
19
|
-
} from './types.js';
|
|
20
|
-
import {
|
|
21
|
-
AddNodeCommand,
|
|
22
|
-
DeleteNodeCommand,
|
|
23
|
-
EditNodeCommand,
|
|
24
|
-
MoveNodeCommand,
|
|
25
|
-
} from './commands.js';
|
|
26
|
-
import { SwimlaneLayout } from './layout.js';
|
|
27
|
-
import { WorkflowValidator } from './validation.js';
|
|
28
|
-
import { cloneWorkflow } from './workflow-utils.js';
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @label Flow Designer
|
|
32
|
-
* @tag wc-flow-designer
|
|
33
|
-
* @rawTag flow-designer
|
|
34
|
-
* @summary Low-code business process flow designer with swimlane layout, undo/redo, and interactive editing.
|
|
35
|
-
*
|
|
36
|
-
* @cssprop --flow-designer-height - Height of the flow designer container. Defaults to 400px.
|
|
37
|
-
* @cssprop --flow-designer-border-color - Border color of the flow designer. Defaults to outline-variant.
|
|
38
|
-
* @cssprop --flow-designer-background - Background color of the designer. Defaults to surface.
|
|
39
|
-
* @cssprop --flow-designer-border-radius - Corner radius. Defaults to medium shape.
|
|
40
|
-
* @cssprop --flow-designer-action-bar-bg - Background color of the action bar. Defaults to surface-container.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```html
|
|
44
|
-
* <wc-flow-designer id="editor"></wc-flow-designer>
|
|
45
|
-
* <script>
|
|
46
|
-
* const workflow = {
|
|
47
|
-
* workflow_id: "demo",
|
|
48
|
-
* nodes: {
|
|
49
|
-
* id: "node_1",
|
|
50
|
-
* type: "trigger",
|
|
51
|
-
* label: "Start"
|
|
52
|
-
* }
|
|
53
|
-
* };
|
|
54
|
-
* document.querySelector('#editor').workflow = workflow;
|
|
55
|
-
* </script>
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
@IndividualComponent
|
|
59
|
-
export class FlowDesigner extends LitElement {
|
|
60
|
-
static styles = [styles];
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The workflow definition to display and edit
|
|
64
|
-
*/
|
|
65
|
-
@property({ type: Object })
|
|
66
|
-
workflow: Workflow = { workflow_id: '', nodes: { id: 'root', type: 'trigger', label: 'Start' } };
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Whether the flow designer is in read-only mode
|
|
70
|
-
*/
|
|
71
|
-
@property({ type: Boolean, reflect: true, attribute: 'readonly' })
|
|
72
|
-
readonly: boolean = false;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Whether the flow designer is disabled
|
|
76
|
-
*/
|
|
77
|
-
@property({ type: Boolean, reflect: true })
|
|
78
|
-
disabled: boolean = false;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Show validation errors/warnings
|
|
82
|
-
*/
|
|
83
|
-
@property({ type: Boolean, attribute: 'show-validation' })
|
|
84
|
-
showValidation: boolean = false;
|
|
85
|
-
|
|
86
|
-
@state()
|
|
87
|
-
private _editor: EditorState = {
|
|
88
|
-
selectedNodeId: null,
|
|
89
|
-
isEditing: false,
|
|
90
|
-
editingNode: null,
|
|
91
|
-
hoveredNodeId: null,
|
|
92
|
-
isDragging: false,
|
|
93
|
-
draggedNodeId: null,
|
|
94
|
-
zoom: 1,
|
|
95
|
-
panX: 0,
|
|
96
|
-
panY: 0,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
@state()
|
|
100
|
-
private _positionedNodes: PositionedNode[] = [];
|
|
101
|
-
|
|
102
|
-
@state()
|
|
103
|
-
private _history: HistoryEntry[] = [];
|
|
104
|
-
|
|
105
|
-
@state()
|
|
106
|
-
private _historyIndex: number = -1;
|
|
107
|
-
|
|
108
|
-
@query('.flow-designer')
|
|
109
|
-
private scrollElm?: HTMLElement;
|
|
110
|
-
|
|
111
|
-
private _isDragScrolling: boolean = false;
|
|
112
|
-
private _dragStartX: number = 0;
|
|
113
|
-
private _dragStartY: number = 0;
|
|
114
|
-
private _scrollStartX: number = 0;
|
|
115
|
-
private _scrollStartY: number = 0;
|
|
116
|
-
|
|
117
|
-
connectedCallback() {
|
|
118
|
-
super.connectedCallback();
|
|
119
|
-
window.addEventListener('mouseup', this._handleMouseUp);
|
|
120
|
-
window.addEventListener('keydown', this._handleKeyDown);
|
|
121
|
-
this._recalculateLayout();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
disconnectedCallback() {
|
|
125
|
-
window.removeEventListener('mouseup', this._handleMouseUp);
|
|
126
|
-
window.removeEventListener('keydown', this._handleKeyDown);
|
|
127
|
-
super.disconnectedCallback();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
protected willUpdate() {
|
|
131
|
-
this._recalculateLayout();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Recalculate layout when workflow changes
|
|
136
|
-
*/
|
|
137
|
-
private _recalculateLayout() {
|
|
138
|
-
if (!this.workflow?.nodes) return;
|
|
139
|
-
this._positionedNodes = SwimlaneLayout.calculateLayout(this.workflow.nodes);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Add a new node
|
|
144
|
-
*/
|
|
145
|
-
addNode(
|
|
146
|
-
newNode: WorkflowNode,
|
|
147
|
-
parentNodeId: string,
|
|
148
|
-
connectionType: 'child' | 'branch' | 'task' = 'child',
|
|
149
|
-
branchKey?: string
|
|
150
|
-
): void {
|
|
151
|
-
const command = new AddNodeCommand(
|
|
152
|
-
newNode,
|
|
153
|
-
parentNodeId,
|
|
154
|
-
connectionType,
|
|
155
|
-
branchKey
|
|
156
|
-
);
|
|
157
|
-
this._executeCommand(command);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Delete a node by ID
|
|
162
|
-
*/
|
|
163
|
-
deleteNode(nodeId: string): void {
|
|
164
|
-
const command = new DeleteNodeCommand(nodeId, this.workflow);
|
|
165
|
-
this._executeCommand(command);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Edit a node
|
|
170
|
-
*/
|
|
171
|
-
editNode(nodeId: string, updates: Partial<WorkflowNode>): void {
|
|
172
|
-
const command = new EditNodeCommand(nodeId, updates, this.workflow);
|
|
173
|
-
this._executeCommand(command);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Move a node to a different parent/position
|
|
178
|
-
*/
|
|
179
|
-
moveNode(
|
|
180
|
-
nodeId: string,
|
|
181
|
-
newParentId: string,
|
|
182
|
-
newIndex: number,
|
|
183
|
-
connectionType: 'child' | 'branch' | 'task' = 'child',
|
|
184
|
-
branchKey?: string
|
|
185
|
-
): void {
|
|
186
|
-
const command = new MoveNodeCommand(
|
|
187
|
-
nodeId,
|
|
188
|
-
newParentId,
|
|
189
|
-
newIndex,
|
|
190
|
-
connectionType,
|
|
191
|
-
branchKey,
|
|
192
|
-
this.workflow
|
|
193
|
-
);
|
|
194
|
-
this._executeCommand(command);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Execute a command and add to history
|
|
199
|
-
*/
|
|
200
|
-
private _executeCommand(command: WorkflowCommand): void {
|
|
201
|
-
const newWorkflow = command.execute(this.workflow);
|
|
202
|
-
|
|
203
|
-
// Validate workflow after change
|
|
204
|
-
const errors = WorkflowValidator.validate(newWorkflow);
|
|
205
|
-
const hasErrors = errors.some((e) => e.severity === 'error');
|
|
206
|
-
|
|
207
|
-
if (hasErrors && !confirm('Workflow has errors. Continue anyway?')) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Add to history
|
|
212
|
-
this._history = this._history.slice(0, this._historyIndex + 1);
|
|
213
|
-
this._history.push({
|
|
214
|
-
command,
|
|
215
|
-
workflow: newWorkflow,
|
|
216
|
-
timestamp: Date.now(),
|
|
217
|
-
});
|
|
218
|
-
this._historyIndex++;
|
|
219
|
-
|
|
220
|
-
// Update workflow
|
|
221
|
-
this.workflow = newWorkflow;
|
|
222
|
-
|
|
223
|
-
// Emit change event
|
|
224
|
-
this._emitWorkflowChange('node-edited', undefined);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Undo last operation
|
|
229
|
-
*/
|
|
230
|
-
undo(): void {
|
|
231
|
-
if (this._historyIndex <= 0) return;
|
|
232
|
-
|
|
233
|
-
this._historyIndex--;
|
|
234
|
-
const entry = this._history[this._historyIndex];
|
|
235
|
-
this.workflow = cloneWorkflow(entry.workflow);
|
|
236
|
-
this._emitWorkflowChange('undo', undefined);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Redo last undone operation
|
|
241
|
-
*/
|
|
242
|
-
redo(): void {
|
|
243
|
-
if (this._historyIndex >= this._history.length - 1) return;
|
|
244
|
-
|
|
245
|
-
this._historyIndex++;
|
|
246
|
-
const entry = this._history[this._historyIndex];
|
|
247
|
-
this.workflow = cloneWorkflow(entry.workflow);
|
|
248
|
-
this._emitWorkflowChange('redo', undefined);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Check if undo is available
|
|
253
|
-
*/
|
|
254
|
-
canUndo(): boolean {
|
|
255
|
-
return this._historyIndex > 0;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Check if redo is available
|
|
260
|
-
*/
|
|
261
|
-
canRedo(): boolean {
|
|
262
|
-
return this._historyIndex < this._history.length - 1;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Export current workflow as JSON
|
|
267
|
-
*/
|
|
268
|
-
exportWorkflow(): string {
|
|
269
|
-
return JSON.stringify(this.workflow, null, 2);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Validate workflow
|
|
274
|
-
*/
|
|
275
|
-
validate(): void {
|
|
276
|
-
const errors = WorkflowValidator.validate(this.workflow);
|
|
277
|
-
this.dispatchEvent(
|
|
278
|
-
new CustomEvent('validation-result', {
|
|
279
|
-
detail: { errors },
|
|
280
|
-
bubbles: true,
|
|
281
|
-
composed: true,
|
|
282
|
-
})
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
private _emitWorkflowChange(
|
|
287
|
-
type: WorkflowChangeEvent['type'],
|
|
288
|
-
nodeId?: string
|
|
289
|
-
): void {
|
|
290
|
-
this.dispatchEvent(
|
|
291
|
-
new CustomEvent('workflow-changed', {
|
|
292
|
-
detail: {
|
|
293
|
-
type,
|
|
294
|
-
nodeId,
|
|
295
|
-
workflow: this.workflow,
|
|
296
|
-
} as WorkflowChangeEvent,
|
|
297
|
-
bubbles: true,
|
|
298
|
-
composed: true,
|
|
299
|
-
})
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
private _handleKeyDown = (event: KeyboardEvent) => {
|
|
304
|
-
if (this.disabled || this.readonly) return;
|
|
305
|
-
|
|
306
|
-
if (event.ctrlKey || event.metaKey) {
|
|
307
|
-
if (event.key === 'z') {
|
|
308
|
-
event.preventDefault();
|
|
309
|
-
this.undo();
|
|
310
|
-
} else if (event.key === 'y') {
|
|
311
|
-
event.preventDefault();
|
|
312
|
-
this.redo();
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (event.key === 'Delete' && this._editor.selectedNodeId) {
|
|
317
|
-
event.preventDefault();
|
|
318
|
-
this.deleteNode(this._editor.selectedNodeId);
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
private _handleMouseUp = () => {
|
|
323
|
-
this._isDragScrolling = false;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
private _handleCanvasMouseDown = (e: MouseEvent) => {
|
|
327
|
-
if (this.disabled) return;
|
|
328
|
-
|
|
329
|
-
if (e.target === this.scrollElm || (e.target as HTMLElement).classList.contains('canvas-container')) {
|
|
330
|
-
this._isDragScrolling = true;
|
|
331
|
-
this._dragStartX = e.clientX;
|
|
332
|
-
this._dragStartY = e.clientY;
|
|
333
|
-
if (this.scrollElm) {
|
|
334
|
-
this._scrollStartX = this.scrollElm.scrollLeft;
|
|
335
|
-
this._scrollStartY = this.scrollElm.scrollTop;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
private _handleCanvasMouseMove = (e: MouseEvent) => {
|
|
341
|
-
if (!this._isDragScrolling || !this.scrollElm) return;
|
|
342
|
-
|
|
343
|
-
const deltaX = e.clientX - this._dragStartX;
|
|
344
|
-
const deltaY = e.clientY - this._dragStartY;
|
|
345
|
-
|
|
346
|
-
this.scrollElm.scrollLeft = this._scrollStartX - deltaX;
|
|
347
|
-
this.scrollElm.scrollTop = this._scrollStartY - deltaY;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
private _handleNodeClick = (e: CustomEvent) => {
|
|
351
|
-
const nodeId = e.detail.nodeId;
|
|
352
|
-
this._editor.selectedNodeId = nodeId;
|
|
353
|
-
this.requestUpdate();
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
private _handleNodeDelete = (e: CustomEvent) => {
|
|
357
|
-
const nodeId = e.detail.nodeId;
|
|
358
|
-
this.deleteNode(nodeId);
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
private _handleNodeEdit = (e: CustomEvent) => {
|
|
362
|
-
const nodeId = e.detail.nodeId;
|
|
363
|
-
this._editor.selectedNodeId = nodeId;
|
|
364
|
-
this._editor.isEditing = true;
|
|
365
|
-
this.requestUpdate();
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
private _handleZoomIn = () => {
|
|
369
|
-
this._editor.zoom = Math.min(2, this._editor.zoom + 0.1);
|
|
370
|
-
this.requestUpdate();
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
private _handleZoomOut = () => {
|
|
374
|
-
this._editor.zoom = Math.max(0.5, this._editor.zoom - 0.1);
|
|
375
|
-
this.requestUpdate();
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
protected render() {
|
|
379
|
-
if (!this.workflow?.nodes) {
|
|
380
|
-
return html`<div class="flow-designer-container">
|
|
381
|
-
<p class="empty-state">No workflow loaded</p>
|
|
382
|
-
</div>`;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const validationErrors = this.showValidation
|
|
386
|
-
? WorkflowValidator.validate(this.workflow)
|
|
387
|
-
: [];
|
|
388
|
-
const canvasBounds = SwimlaneLayout.getCanvasBounds(this._positionedNodes);
|
|
389
|
-
|
|
390
|
-
return html`
|
|
391
|
-
<div class="flow-designer-container">
|
|
392
|
-
<wc-toolbar
|
|
393
|
-
class="editor-toolbar"
|
|
394
|
-
variant="floating"
|
|
395
|
-
orientation="horizontal"
|
|
396
|
-
elevated
|
|
397
|
-
>
|
|
398
|
-
<wc-icon-button
|
|
399
|
-
variant="text"
|
|
400
|
-
?disabled=${this._editor.zoom <= 0.5}
|
|
401
|
-
@click=${this._handleZoomOut}
|
|
402
|
-
title="Zoom Out (Ctrl+-)"
|
|
403
|
-
>
|
|
404
|
-
<wc-icon name="remove"></wc-icon>
|
|
405
|
-
</wc-icon-button>
|
|
406
|
-
<span class="zoom-display">${Math.round(this._editor.zoom * 100)}%</span>
|
|
407
|
-
<wc-icon-button
|
|
408
|
-
variant="text"
|
|
409
|
-
?disabled=${this._editor.zoom >= 2}
|
|
410
|
-
@click=${this._handleZoomIn}
|
|
411
|
-
title="Zoom In (Ctrl++)"
|
|
412
|
-
>
|
|
413
|
-
<wc-icon name="add"></wc-icon>
|
|
414
|
-
</wc-icon-button>
|
|
415
|
-
<wc-icon-button
|
|
416
|
-
variant="text"
|
|
417
|
-
?disabled=${!this.canUndo()}
|
|
418
|
-
@click=${() => this.undo()}
|
|
419
|
-
title="Undo (Ctrl+Z)"
|
|
420
|
-
>
|
|
421
|
-
<wc-icon name="undo"></wc-icon>
|
|
422
|
-
</wc-icon-button>
|
|
423
|
-
<wc-icon-button
|
|
424
|
-
variant="text"
|
|
425
|
-
?disabled=${!this.canRedo()}
|
|
426
|
-
@click=${() => this.redo()}
|
|
427
|
-
title="Redo (Ctrl+Y)"
|
|
428
|
-
>
|
|
429
|
-
<wc-icon name="redo"></wc-icon>
|
|
430
|
-
</wc-icon-button>
|
|
431
|
-
${!this.readonly
|
|
432
|
-
? html`
|
|
433
|
-
<wc-icon-button
|
|
434
|
-
variant="text"
|
|
435
|
-
@click=${() => this.validate()}
|
|
436
|
-
title="Validate Workflow"
|
|
437
|
-
>
|
|
438
|
-
<wc-icon name="check_circle"></wc-icon>
|
|
439
|
-
</wc-icon-button>
|
|
440
|
-
`
|
|
441
|
-
: nothing}
|
|
442
|
-
</wc-toolbar>
|
|
443
|
-
|
|
444
|
-
<!-- Validation messages -->
|
|
445
|
-
${validationErrors.length > 0
|
|
446
|
-
? html`
|
|
447
|
-
<div class="validation-panel">
|
|
448
|
-
${validationErrors.map(
|
|
449
|
-
(error) =>
|
|
450
|
-
html`
|
|
451
|
-
<div class="validation-item ${error.severity}">
|
|
452
|
-
<wc-icon
|
|
453
|
-
name=${error.severity === 'error' ? 'error' : 'warning'}
|
|
454
|
-
></wc-icon>
|
|
455
|
-
<span>${error.message}</span>
|
|
456
|
-
</div>
|
|
457
|
-
`
|
|
458
|
-
)}
|
|
459
|
-
</div>
|
|
460
|
-
`
|
|
461
|
-
: nothing}
|
|
462
|
-
|
|
463
|
-
<!-- Flow canvas -->
|
|
464
|
-
<div
|
|
465
|
-
class="flow-designer"
|
|
466
|
-
@mousedown=${this._handleCanvasMouseDown}
|
|
467
|
-
@mousemove=${this._handleCanvasMouseMove}
|
|
468
|
-
>
|
|
469
|
-
<div
|
|
470
|
-
class="canvas-container"
|
|
471
|
-
style="
|
|
472
|
-
transform: scale(${this._editor.zoom});
|
|
473
|
-
width: ${canvasBounds.width}px;
|
|
474
|
-
height: ${canvasBounds.height}px;
|
|
475
|
-
"
|
|
476
|
-
>
|
|
477
|
-
<!-- SVG Connectors -->
|
|
478
|
-
<svg
|
|
479
|
-
class="connectors-layer"
|
|
480
|
-
width="${canvasBounds.width}"
|
|
481
|
-
height="${canvasBounds.height}"
|
|
482
|
-
viewBox="0 0 ${canvasBounds.width} ${canvasBounds.height}"
|
|
483
|
-
>
|
|
484
|
-
<defs>
|
|
485
|
-
<marker
|
|
486
|
-
id="arrowhead"
|
|
487
|
-
markerWidth="10"
|
|
488
|
-
markerHeight="10"
|
|
489
|
-
refX="9"
|
|
490
|
-
refY="3"
|
|
491
|
-
orient="auto"
|
|
492
|
-
>
|
|
493
|
-
<polygon points="0 0, 10 3, 0 6" fill="currentColor"></polygon>
|
|
494
|
-
</marker>
|
|
495
|
-
</defs>
|
|
496
|
-
${this._renderConnectors()}
|
|
497
|
-
</svg>
|
|
498
|
-
|
|
499
|
-
<!-- Swimlane backgrounds -->
|
|
500
|
-
<div class="swimlanes-container">
|
|
501
|
-
${this._renderSwimlanes()}
|
|
502
|
-
</div>
|
|
503
|
-
|
|
504
|
-
<!-- Positioned nodes -->
|
|
505
|
-
<div class="nodes-layer">
|
|
506
|
-
${this._renderNodes()}
|
|
507
|
-
</div>
|
|
508
|
-
</div>
|
|
509
|
-
</div>
|
|
510
|
-
</div>
|
|
511
|
-
`;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
private _renderConnectors() {
|
|
515
|
-
return this._positionedNodes.flatMap((node) => {
|
|
516
|
-
if (!node.connectorPoints) return [];
|
|
517
|
-
|
|
518
|
-
return node.connectorPoints.map((connector, idx) => {
|
|
519
|
-
const { from, to, type } = connector;
|
|
520
|
-
const isLoopback = type === 'curved';
|
|
521
|
-
|
|
522
|
-
if (isLoopback) {
|
|
523
|
-
// Render curved path for loop back
|
|
524
|
-
const midY = (from.y + to.y) / 2;
|
|
525
|
-
const d =
|
|
526
|
-
`M ${from.x} ${from.y} ` +
|
|
527
|
-
`L ${from.x + 30} ${from.y} ` +
|
|
528
|
-
`Q ${from.x + 60} ${midY} ${to.x - 30} ${to.y} ` +
|
|
529
|
-
`L ${to.x} ${to.y}`;
|
|
530
|
-
|
|
531
|
-
return html`
|
|
532
|
-
<path
|
|
533
|
-
key=${`${node.node.id}-connector-${idx}`}
|
|
534
|
-
d=${d}
|
|
535
|
-
class="connector ${type}"
|
|
536
|
-
marker-end="url(#arrowhead)"
|
|
537
|
-
vector-effect="non-scaling-stroke"
|
|
538
|
-
></path>
|
|
539
|
-
`;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Render straight connector
|
|
543
|
-
const d = `M ${from.x} ${from.y} L ${to.x} ${to.y}`;
|
|
544
|
-
return html`
|
|
545
|
-
<path
|
|
546
|
-
key=${`${node.node.id}-connector-${idx}`}
|
|
547
|
-
d=${d}
|
|
548
|
-
class="connector ${type}"
|
|
549
|
-
marker-end="url(#arrowhead)"
|
|
550
|
-
vector-effect="non-scaling-stroke"
|
|
551
|
-
></path>
|
|
552
|
-
`;
|
|
553
|
-
});
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
private _renderSwimlanes() {
|
|
558
|
-
const swimlanes = SwimlaneLayout.getSwimlanes(this._positionedNodes);
|
|
559
|
-
|
|
560
|
-
return swimlanes.map(
|
|
561
|
-
(lane) => {
|
|
562
|
-
const laneTop = Math.min(...lane.nodes.map((n) => n.y)) - 14;
|
|
563
|
-
const laneBottom = Math.max(...lane.nodes.map((n) => n.y + n.height)) + 14;
|
|
564
|
-
const laneHeight = Math.max(120, laneBottom - laneTop);
|
|
565
|
-
|
|
566
|
-
return html`
|
|
567
|
-
<div
|
|
568
|
-
class="swimlane ${lane.isParallel ? 'parallel' : ''}"
|
|
569
|
-
style="top: ${laneTop}px; height: ${laneHeight}px;"
|
|
570
|
-
>
|
|
571
|
-
<div class="swimlane-header">${lane.name}</div>
|
|
572
|
-
</div>
|
|
573
|
-
`;
|
|
574
|
-
}
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
private _renderNodes() {
|
|
579
|
-
return this._positionedNodes.map(
|
|
580
|
-
(posNode) =>
|
|
581
|
-
html`
|
|
582
|
-
<div
|
|
583
|
-
class="positioned-node"
|
|
584
|
-
style="
|
|
585
|
-
left: ${posNode.x}px;
|
|
586
|
-
top: ${posNode.y}px;
|
|
587
|
-
width: ${posNode.width}px;
|
|
588
|
-
height: ${posNode.height}px;
|
|
589
|
-
"
|
|
590
|
-
>
|
|
591
|
-
<wc-flow-designer-node
|
|
592
|
-
.node=${posNode.node}
|
|
593
|
-
?selected=${posNode.node.id === this._editor.selectedNodeId}
|
|
594
|
-
?editing=${this._editor.isEditing &&
|
|
595
|
-
posNode.node.id === this._editor.selectedNodeId}
|
|
596
|
-
?disabled=${this.disabled}
|
|
597
|
-
@node-click=${this._handleNodeClick}
|
|
598
|
-
@node-delete=${this._handleNodeDelete}
|
|
599
|
-
@node-edit-start=${this._handleNodeEdit}
|
|
600
|
-
></wc-flow-designer-node>
|
|
601
|
-
</div>
|
|
602
|
-
`
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
declare global {
|
|
608
|
-
interface HTMLElementTagNameMap {
|
|
609
|
-
'wc-flow-designer': FlowDesigner;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export { FlowDesigner } from './flow-designer.js';
|
|
2
|
-
export { FlowDesignerNode } from './flow-designer-node.js';
|
|
3
|
-
|
|
4
|
-
// Types
|
|
5
|
-
export type {
|
|
6
|
-
Workflow,
|
|
7
|
-
WorkflowNode,
|
|
8
|
-
WorkflowCommand,
|
|
9
|
-
PositionedNode,
|
|
10
|
-
ValidationError,
|
|
11
|
-
HistoryEntry,
|
|
12
|
-
EditorState,
|
|
13
|
-
NodeType,
|
|
14
|
-
WorkflowChangeEvent,
|
|
15
|
-
SwimlaneConfig,
|
|
16
|
-
NodeTemplate,
|
|
17
|
-
} from './types.js';
|
|
18
|
-
|
|
19
|
-
// Utilities
|
|
20
|
-
export { SwimlaneLayout } from './layout.js';
|
|
21
|
-
export { WorkflowValidator } from './validation.js';
|
|
22
|
-
export {
|
|
23
|
-
cloneWorkflow,
|
|
24
|
-
cloneNode,
|
|
25
|
-
findNodeById,
|
|
26
|
-
removeNodeById,
|
|
27
|
-
insertNodeIntoWorkflow,
|
|
28
|
-
getAllNodes,
|
|
29
|
-
getNodePath,
|
|
30
|
-
isDescendant,
|
|
31
|
-
replaceNode,
|
|
32
|
-
} from './workflow-utils.js';
|
|
33
|
-
|
|
34
|
-
// Commands
|
|
35
|
-
export {
|
|
36
|
-
AddNodeCommand,
|
|
37
|
-
DeleteNodeCommand,
|
|
38
|
-
EditNodeCommand,
|
|
39
|
-
MoveNodeCommand,
|
|
40
|
-
BatchCommand,
|
|
41
|
-
} from './commands.js';
|