@redvars/peacock 3.5.1 → 3.6.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.
Files changed (198) hide show
  1. package/dist/{BaseButton-DuASuVth.js → BaseButton-BNFAYn-S.js} +2 -2
  2. package/dist/{BaseButton-DuASuVth.js.map → BaseButton-BNFAYn-S.js.map} +1 -1
  3. package/dist/BaseInput-14YmcfK7.js +27 -0
  4. package/dist/BaseInput-14YmcfK7.js.map +1 -0
  5. package/dist/banner.js +2 -3
  6. package/dist/banner.js.map +1 -1
  7. package/dist/{button-DouvOfEU.js → button-colors-Ccys3hvS.js} +5 -294
  8. package/dist/button-colors-Ccys3hvS.js.map +1 -0
  9. package/dist/button-group.js +226 -6
  10. package/dist/button-group.js.map +1 -1
  11. package/dist/button.js +294 -8
  12. package/dist/button.js.map +1 -1
  13. package/dist/calendar-column-view.js +634 -0
  14. package/dist/calendar-column-view.js.map +1 -0
  15. package/dist/calendar-event-BrQ_SEKD.js +199 -0
  16. package/dist/calendar-event-BrQ_SEKD.js.map +1 -0
  17. package/dist/calendar-month-view.js +376 -0
  18. package/dist/calendar-month-view.js.map +1 -0
  19. package/dist/calendar.js +339 -0
  20. package/dist/calendar.js.map +1 -0
  21. package/dist/canvas.js +361 -0
  22. package/dist/canvas.js.map +1 -0
  23. package/dist/cb-compound-expression.js +125 -0
  24. package/dist/cb-compound-expression.js.map +1 -0
  25. package/dist/cb-divider.js +150 -0
  26. package/dist/cb-divider.js.map +1 -0
  27. package/dist/cb-expression.js +75 -0
  28. package/dist/cb-expression.js.map +1 -0
  29. package/dist/cb-predicate.js +137 -0
  30. package/dist/cb-predicate.js.map +1 -0
  31. package/dist/code-editor.js +2 -1
  32. package/dist/code-editor.js.map +1 -1
  33. package/dist/condition-builder.js +58 -0
  34. package/dist/condition-builder.js.map +1 -0
  35. package/dist/custom-elements-jsdocs.json +7976 -4294
  36. package/dist/custom-elements.json +14358 -7589
  37. package/dist/dropdown-button.js +216 -0
  38. package/dist/dropdown-button.js.map +1 -0
  39. package/dist/event-manager-D-QCmUgR.js +113 -0
  40. package/dist/event-manager-D-QCmUgR.js.map +1 -0
  41. package/dist/fab.js +1 -1
  42. package/dist/flow-designer-dZnLJOQT.js +1656 -0
  43. package/dist/flow-designer-dZnLJOQT.js.map +1 -0
  44. package/dist/flow-designer-node-XMe-jlKg.js +548 -0
  45. package/dist/flow-designer-node-XMe-jlKg.js.map +1 -0
  46. package/dist/flow-designer-node.js +4 -0
  47. package/dist/flow-designer-node.js.map +1 -0
  48. package/dist/flow-designer.js +16 -0
  49. package/dist/flow-designer.js.map +1 -0
  50. package/dist/html-editor.js +358 -0
  51. package/dist/html-editor.js.map +1 -0
  52. package/dist/icon-button-CK1ZuE-2.js +247 -0
  53. package/dist/icon-button-CK1ZuE-2.js.map +1 -0
  54. package/dist/index.js +29 -6
  55. package/dist/index.js.map +1 -1
  56. package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
  57. package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
  58. package/dist/modal.js +418 -0
  59. package/dist/modal.js.map +1 -0
  60. package/dist/{navigation-rail-Lxetd5-Z.js → navigation-rail-DyO0oAZU.js} +306 -2197
  61. package/dist/navigation-rail-DyO0oAZU.js.map +1 -0
  62. package/dist/notification-manager.js +268 -0
  63. package/dist/notification-manager.js.map +1 -0
  64. package/dist/peacock-loader.js +84 -8
  65. package/dist/peacock-loader.js.map +1 -1
  66. package/dist/popover-NC7b1lTq.js +1971 -0
  67. package/dist/popover-NC7b1lTq.js.map +1 -0
  68. package/dist/popover-content.js +125 -0
  69. package/dist/popover-content.js.map +1 -0
  70. package/dist/popover.js +4 -0
  71. package/dist/popover.js.map +1 -0
  72. package/dist/split-button.js +388 -0
  73. package/dist/split-button.js.map +1 -0
  74. package/dist/src/__controllers/floating-controller.d.ts +35 -0
  75. package/dist/src/calendar/base-event.d.ts +10 -0
  76. package/dist/src/calendar/calendar-column-view.d.ts +41 -0
  77. package/dist/src/calendar/calendar-event.d.ts +7 -0
  78. package/dist/src/calendar/calendar-month-view.d.ts +31 -0
  79. package/dist/src/calendar/calendar.d.ts +65 -0
  80. package/dist/src/calendar/event-manager.d.ts +17 -0
  81. package/dist/src/calendar/index.d.ts +4 -0
  82. package/dist/src/calendar/types.d.ts +13 -0
  83. package/dist/src/calendar/utils.d.ts +31 -0
  84. package/dist/src/canvas/canvas.d.ts +92 -0
  85. package/dist/src/canvas/index.d.ts +2 -0
  86. package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
  87. package/dist/src/condition-builder/cb-divider.d.ts +26 -0
  88. package/dist/src/condition-builder/cb-expression.d.ts +31 -0
  89. package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
  90. package/dist/src/condition-builder/condition-builder.d.ts +27 -0
  91. package/dist/src/condition-builder/index.d.ts +5 -0
  92. package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
  93. package/dist/src/dropdown-button/index.d.ts +1 -0
  94. package/dist/src/flow-designer/commands.d.ts +66 -0
  95. package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
  96. package/dist/src/flow-designer/flow-designer.d.ts +133 -0
  97. package/dist/src/flow-designer/index.d.ts +7 -0
  98. package/dist/src/flow-designer/layout.d.ts +30 -0
  99. package/dist/src/flow-designer/types.d.ts +142 -0
  100. package/dist/src/flow-designer/validation.d.ts +43 -0
  101. package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
  102. package/dist/src/html-editor/html-editor.d.ts +56 -0
  103. package/dist/src/html-editor/index.d.ts +2 -0
  104. package/dist/src/index.d.ts +13 -0
  105. package/dist/src/menu/menu/menu.d.ts +5 -7
  106. package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
  107. package/dist/src/modal/index.d.ts +1 -0
  108. package/dist/src/modal/modal.d.ts +63 -0
  109. package/dist/src/notification-manager/index.d.ts +1 -0
  110. package/dist/src/notification-manager/notification-manager.d.ts +44 -0
  111. package/dist/src/popover/index.d.ts +2 -0
  112. package/dist/src/popover/popover-content.d.ts +29 -0
  113. package/dist/src/popover/popover.d.ts +62 -0
  114. package/dist/src/split-button/index.d.ts +1 -0
  115. package/dist/src/split-button/split-button.d.ts +72 -0
  116. package/dist/src/tooltip/tooltip.d.ts +2 -15
  117. package/dist/test/flow-designer.test.d.ts +1 -0
  118. package/dist/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +4 -2
  120. package/readme.md +2 -2
  121. package/src/__controllers/floating-controller.ts +237 -0
  122. package/src/banner/banner.scss +2 -3
  123. package/src/button/button/button.ts +1 -0
  124. package/src/calendar/base-event.ts +49 -0
  125. package/src/calendar/calendar-column-view.scss +326 -0
  126. package/src/calendar/calendar-column-view.ts +392 -0
  127. package/src/calendar/calendar-event.ts +20 -0
  128. package/src/calendar/calendar-month-view.scss +192 -0
  129. package/src/calendar/calendar-month-view.ts +244 -0
  130. package/src/calendar/calendar.scss +71 -0
  131. package/src/calendar/calendar.ts +298 -0
  132. package/src/calendar/event-manager.ts +117 -0
  133. package/src/calendar/index.ts +4 -0
  134. package/src/calendar/types.ts +14 -0
  135. package/src/calendar/utils.ts +180 -0
  136. package/src/canvas/canvas.scss +60 -0
  137. package/src/canvas/canvas.ts +391 -0
  138. package/src/canvas/index.ts +2 -0
  139. package/src/condition-builder/cb-compound-expression.scss +37 -0
  140. package/src/condition-builder/cb-compound-expression.ts +80 -0
  141. package/src/condition-builder/cb-divider.scss +93 -0
  142. package/src/condition-builder/cb-divider.ts +56 -0
  143. package/src/condition-builder/cb-expression.scss +14 -0
  144. package/src/condition-builder/cb-expression.ts +49 -0
  145. package/src/condition-builder/cb-predicate.scss +35 -0
  146. package/src/condition-builder/cb-predicate.ts +102 -0
  147. package/src/condition-builder/condition-builder.scss +13 -0
  148. package/src/condition-builder/condition-builder.ts +38 -0
  149. package/src/condition-builder/index.ts +5 -0
  150. package/src/dropdown-button/demo/index.html +110 -0
  151. package/src/dropdown-button/dropdown-button.scss +22 -0
  152. package/src/dropdown-button/dropdown-button.ts +206 -0
  153. package/src/dropdown-button/index.ts +1 -0
  154. package/src/flow-designer/DEMO.md +239 -0
  155. package/src/flow-designer/commands.ts +278 -0
  156. package/src/flow-designer/flow-designer-node.ts +172 -0
  157. package/src/flow-designer/flow-designer.scss +457 -0
  158. package/src/flow-designer/flow-designer.ts +611 -0
  159. package/src/flow-designer/index.ts +41 -0
  160. package/src/flow-designer/layout.ts +357 -0
  161. package/src/flow-designer/types.ts +166 -0
  162. package/src/flow-designer/validation.ts +284 -0
  163. package/src/flow-designer/workflow-utils.ts +282 -0
  164. package/src/html-editor/html-editor.scss +146 -0
  165. package/src/html-editor/html-editor.ts +276 -0
  166. package/src/html-editor/index.ts +3 -0
  167. package/src/index.ts +25 -0
  168. package/src/menu/menu/menu.scss +2 -2
  169. package/src/menu/menu/menu.ts +91 -101
  170. package/src/menu/menu-item/menu-item.scss +4 -0
  171. package/src/menu/menu-item/menu-item.ts +82 -78
  172. package/src/modal/index.ts +1 -0
  173. package/src/modal/modal.scss +206 -0
  174. package/src/modal/modal.ts +201 -0
  175. package/src/notification-manager/index.ts +1 -0
  176. package/src/notification-manager/notification-manager.scss +113 -0
  177. package/src/notification-manager/notification-manager.ts +199 -0
  178. package/src/peacock-loader.ts +71 -0
  179. package/src/popover/index.ts +2 -0
  180. package/src/popover/popover-content.scss +69 -0
  181. package/src/popover/popover-content.ts +51 -0
  182. package/src/popover/popover.scss +7 -0
  183. package/src/popover/popover.ts +170 -0
  184. package/src/split-button/index.ts +1 -0
  185. package/src/split-button/split-button-colors.scss +56 -0
  186. package/src/split-button/split-button-sizes.scss +28 -0
  187. package/src/split-button/split-button.scss +79 -0
  188. package/src/split-button/split-button.ts +236 -0
  189. package/src/table/table.ts +2 -2
  190. package/src/tooltip/tooltip.scss +4 -3
  191. package/src/tooltip/tooltip.ts +46 -104
  192. package/dist/button-DouvOfEU.js.map +0 -1
  193. package/dist/button-group-CEdMwvJJ.js +0 -464
  194. package/dist/button-group-CEdMwvJJ.js.map +0 -1
  195. package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
  196. package/dist/navigation-rail-Lxetd5-Z.js.map +0 -1
  197. package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
  198. package/src/menu/menu/MenuSurfaceController.ts +0 -61
@@ -0,0 +1,611 @@
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
+ }
@@ -0,0 +1,41 @@
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';