@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.
- package/dist/{BaseButton-DuASuVth.js → BaseButton-BNFAYn-S.js} +2 -2
- package/dist/{BaseButton-DuASuVth.js.map → BaseButton-BNFAYn-S.js.map} +1 -1
- package/dist/BaseInput-14YmcfK7.js +27 -0
- package/dist/BaseInput-14YmcfK7.js.map +1 -0
- package/dist/banner.js +2 -3
- package/dist/banner.js.map +1 -1
- package/dist/{button-DouvOfEU.js → button-colors-Ccys3hvS.js} +5 -294
- package/dist/button-colors-Ccys3hvS.js.map +1 -0
- package/dist/button-group.js +226 -6
- package/dist/button-group.js.map +1 -1
- package/dist/button.js +294 -8
- package/dist/button.js.map +1 -1
- package/dist/calendar-column-view.js +634 -0
- package/dist/calendar-column-view.js.map +1 -0
- package/dist/calendar-event-BrQ_SEKD.js +199 -0
- package/dist/calendar-event-BrQ_SEKD.js.map +1 -0
- package/dist/calendar-month-view.js +376 -0
- package/dist/calendar-month-view.js.map +1 -0
- package/dist/calendar.js +339 -0
- package/dist/calendar.js.map +1 -0
- package/dist/canvas.js +361 -0
- package/dist/canvas.js.map +1 -0
- package/dist/cb-compound-expression.js +125 -0
- package/dist/cb-compound-expression.js.map +1 -0
- package/dist/cb-divider.js +150 -0
- package/dist/cb-divider.js.map +1 -0
- package/dist/cb-expression.js +75 -0
- package/dist/cb-expression.js.map +1 -0
- package/dist/cb-predicate.js +137 -0
- package/dist/cb-predicate.js.map +1 -0
- package/dist/code-editor.js +2 -1
- package/dist/code-editor.js.map +1 -1
- package/dist/condition-builder.js +58 -0
- package/dist/condition-builder.js.map +1 -0
- package/dist/custom-elements-jsdocs.json +7976 -4294
- package/dist/custom-elements.json +14358 -7589
- package/dist/dropdown-button.js +216 -0
- package/dist/dropdown-button.js.map +1 -0
- package/dist/event-manager-D-QCmUgR.js +113 -0
- package/dist/event-manager-D-QCmUgR.js.map +1 -0
- package/dist/fab.js +1 -1
- package/dist/flow-designer-dZnLJOQT.js +1656 -0
- package/dist/flow-designer-dZnLJOQT.js.map +1 -0
- package/dist/flow-designer-node-XMe-jlKg.js +548 -0
- package/dist/flow-designer-node-XMe-jlKg.js.map +1 -0
- package/dist/flow-designer-node.js +4 -0
- package/dist/flow-designer-node.js.map +1 -0
- package/dist/flow-designer.js +16 -0
- package/dist/flow-designer.js.map +1 -0
- package/dist/html-editor.js +358 -0
- package/dist/html-editor.js.map +1 -0
- package/dist/icon-button-CK1ZuE-2.js +247 -0
- package/dist/icon-button-CK1ZuE-2.js.map +1 -0
- package/dist/index.js +29 -6
- package/dist/index.js.map +1 -1
- package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
- package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
- package/dist/modal.js +418 -0
- package/dist/modal.js.map +1 -0
- package/dist/{navigation-rail-Lxetd5-Z.js → navigation-rail-DyO0oAZU.js} +306 -2197
- package/dist/navigation-rail-DyO0oAZU.js.map +1 -0
- package/dist/notification-manager.js +268 -0
- package/dist/notification-manager.js.map +1 -0
- package/dist/peacock-loader.js +84 -8
- package/dist/peacock-loader.js.map +1 -1
- package/dist/popover-NC7b1lTq.js +1971 -0
- package/dist/popover-NC7b1lTq.js.map +1 -0
- package/dist/popover-content.js +125 -0
- package/dist/popover-content.js.map +1 -0
- package/dist/popover.js +4 -0
- package/dist/popover.js.map +1 -0
- package/dist/split-button.js +388 -0
- package/dist/split-button.js.map +1 -0
- package/dist/src/__controllers/floating-controller.d.ts +35 -0
- package/dist/src/calendar/base-event.d.ts +10 -0
- package/dist/src/calendar/calendar-column-view.d.ts +41 -0
- package/dist/src/calendar/calendar-event.d.ts +7 -0
- package/dist/src/calendar/calendar-month-view.d.ts +31 -0
- package/dist/src/calendar/calendar.d.ts +65 -0
- package/dist/src/calendar/event-manager.d.ts +17 -0
- package/dist/src/calendar/index.d.ts +4 -0
- package/dist/src/calendar/types.d.ts +13 -0
- package/dist/src/calendar/utils.d.ts +31 -0
- package/dist/src/canvas/canvas.d.ts +92 -0
- package/dist/src/canvas/index.d.ts +2 -0
- package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
- package/dist/src/condition-builder/cb-divider.d.ts +26 -0
- package/dist/src/condition-builder/cb-expression.d.ts +31 -0
- package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
- package/dist/src/condition-builder/condition-builder.d.ts +27 -0
- package/dist/src/condition-builder/index.d.ts +5 -0
- package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
- package/dist/src/dropdown-button/index.d.ts +1 -0
- package/dist/src/flow-designer/commands.d.ts +66 -0
- package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
- package/dist/src/flow-designer/flow-designer.d.ts +133 -0
- package/dist/src/flow-designer/index.d.ts +7 -0
- package/dist/src/flow-designer/layout.d.ts +30 -0
- package/dist/src/flow-designer/types.d.ts +142 -0
- package/dist/src/flow-designer/validation.d.ts +43 -0
- package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
- package/dist/src/html-editor/html-editor.d.ts +56 -0
- package/dist/src/html-editor/index.d.ts +2 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/menu/menu/menu.d.ts +5 -7
- package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
- package/dist/src/modal/index.d.ts +1 -0
- package/dist/src/modal/modal.d.ts +63 -0
- package/dist/src/notification-manager/index.d.ts +1 -0
- package/dist/src/notification-manager/notification-manager.d.ts +44 -0
- package/dist/src/popover/index.d.ts +2 -0
- package/dist/src/popover/popover-content.d.ts +29 -0
- package/dist/src/popover/popover.d.ts +62 -0
- package/dist/src/split-button/index.d.ts +1 -0
- package/dist/src/split-button/split-button.d.ts +72 -0
- package/dist/src/tooltip/tooltip.d.ts +2 -15
- package/dist/test/flow-designer.test.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -2
- package/readme.md +2 -2
- package/src/__controllers/floating-controller.ts +237 -0
- package/src/banner/banner.scss +2 -3
- package/src/button/button/button.ts +1 -0
- package/src/calendar/base-event.ts +49 -0
- package/src/calendar/calendar-column-view.scss +326 -0
- package/src/calendar/calendar-column-view.ts +392 -0
- package/src/calendar/calendar-event.ts +20 -0
- package/src/calendar/calendar-month-view.scss +192 -0
- package/src/calendar/calendar-month-view.ts +244 -0
- package/src/calendar/calendar.scss +71 -0
- package/src/calendar/calendar.ts +298 -0
- package/src/calendar/event-manager.ts +117 -0
- package/src/calendar/index.ts +4 -0
- package/src/calendar/types.ts +14 -0
- package/src/calendar/utils.ts +180 -0
- package/src/canvas/canvas.scss +60 -0
- package/src/canvas/canvas.ts +391 -0
- package/src/canvas/index.ts +2 -0
- package/src/condition-builder/cb-compound-expression.scss +37 -0
- package/src/condition-builder/cb-compound-expression.ts +80 -0
- package/src/condition-builder/cb-divider.scss +93 -0
- package/src/condition-builder/cb-divider.ts +56 -0
- package/src/condition-builder/cb-expression.scss +14 -0
- package/src/condition-builder/cb-expression.ts +49 -0
- package/src/condition-builder/cb-predicate.scss +35 -0
- package/src/condition-builder/cb-predicate.ts +102 -0
- package/src/condition-builder/condition-builder.scss +13 -0
- package/src/condition-builder/condition-builder.ts +38 -0
- package/src/condition-builder/index.ts +5 -0
- package/src/dropdown-button/demo/index.html +110 -0
- package/src/dropdown-button/dropdown-button.scss +22 -0
- package/src/dropdown-button/dropdown-button.ts +206 -0
- package/src/dropdown-button/index.ts +1 -0
- package/src/flow-designer/DEMO.md +239 -0
- package/src/flow-designer/commands.ts +278 -0
- package/src/flow-designer/flow-designer-node.ts +172 -0
- package/src/flow-designer/flow-designer.scss +457 -0
- package/src/flow-designer/flow-designer.ts +611 -0
- package/src/flow-designer/index.ts +41 -0
- package/src/flow-designer/layout.ts +357 -0
- package/src/flow-designer/types.ts +166 -0
- package/src/flow-designer/validation.ts +284 -0
- package/src/flow-designer/workflow-utils.ts +282 -0
- package/src/html-editor/html-editor.scss +146 -0
- package/src/html-editor/html-editor.ts +276 -0
- package/src/html-editor/index.ts +3 -0
- package/src/index.ts +25 -0
- package/src/menu/menu/menu.scss +2 -2
- package/src/menu/menu/menu.ts +91 -101
- package/src/menu/menu-item/menu-item.scss +4 -0
- package/src/menu/menu-item/menu-item.ts +82 -78
- package/src/modal/index.ts +1 -0
- package/src/modal/modal.scss +206 -0
- package/src/modal/modal.ts +201 -0
- package/src/notification-manager/index.ts +1 -0
- package/src/notification-manager/notification-manager.scss +113 -0
- package/src/notification-manager/notification-manager.ts +199 -0
- package/src/peacock-loader.ts +71 -0
- package/src/popover/index.ts +2 -0
- package/src/popover/popover-content.scss +69 -0
- package/src/popover/popover-content.ts +51 -0
- package/src/popover/popover.scss +7 -0
- package/src/popover/popover.ts +170 -0
- package/src/split-button/index.ts +1 -0
- package/src/split-button/split-button-colors.scss +56 -0
- package/src/split-button/split-button-sizes.scss +28 -0
- package/src/split-button/split-button.scss +79 -0
- package/src/split-button/split-button.ts +236 -0
- package/src/table/table.ts +2 -2
- package/src/tooltip/tooltip.scss +4 -3
- package/src/tooltip/tooltip.ts +46 -104
- package/dist/button-DouvOfEU.js.map +0 -1
- package/dist/button-group-CEdMwvJJ.js +0 -464
- package/dist/button-group-CEdMwvJJ.js.map +0 -1
- package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
- package/dist/navigation-rail-Lxetd5-Z.js.map +0 -1
- package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
- package/src/menu/menu/MenuSurfaceController.ts +0 -61
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import type { WorkflowNode, PositionedNode, SwimlaneConfig } from './types.js';
|
|
2
|
+
import { getAllNodes } from './workflow-utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Swimlane layout algorithm for flow designer
|
|
6
|
+
* Positions nodes in left-to-right swimlanes for parallel/branching flows
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface LayoutNode {
|
|
10
|
+
node: WorkflowNode;
|
|
11
|
+
parent: WorkflowNode | null;
|
|
12
|
+
lane: string;
|
|
13
|
+
depth: number;
|
|
14
|
+
branchPath?: string;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const NODE_WIDTH = 200;
|
|
20
|
+
const NODE_HEIGHT = 100;
|
|
21
|
+
const HORIZONTAL_GAP = 60; // Gap between depth levels (columns)
|
|
22
|
+
const VERTICAL_GAP = 40; // Gap between lanes
|
|
23
|
+
const LANE_HEIGHT = 140; // Height of each swimlane row
|
|
24
|
+
const LANE_HEADER_HEIGHT = 84; // Top offset so first row is not clipped by floating UI
|
|
25
|
+
|
|
26
|
+
export class SwimlaneLayout {
|
|
27
|
+
/**
|
|
28
|
+
* Calculate layout positions for all nodes in a workflow
|
|
29
|
+
*/
|
|
30
|
+
static calculateLayout(rootNode: WorkflowNode): PositionedNode[] {
|
|
31
|
+
const layoutNodes: LayoutNode[] = [];
|
|
32
|
+
const lanes = new Map<string, LayoutNode[]>(); // lane -> nodes in that lane
|
|
33
|
+
const depthMap = new Map<string, number>(); // Track max depth per lane
|
|
34
|
+
|
|
35
|
+
// First pass: assign lanes and depths
|
|
36
|
+
this._traverseAndAssignLanes(rootNode, null, 'main', 0, layoutNodes, lanes);
|
|
37
|
+
|
|
38
|
+
// Second pass: calculate positions
|
|
39
|
+
const positionedNodes: PositionedNode[] = [];
|
|
40
|
+
const nodePositions = new Map<string, { x: number; y: number }>();
|
|
41
|
+
|
|
42
|
+
for (const layoutNode of layoutNodes) {
|
|
43
|
+
const x =
|
|
44
|
+
layoutNode.depth * (NODE_WIDTH + HORIZONTAL_GAP) + HORIZONTAL_GAP;
|
|
45
|
+
const laneIndex = Array.from(lanes.keys()).indexOf(layoutNode.lane);
|
|
46
|
+
const y = laneIndex * (LANE_HEIGHT + VERTICAL_GAP) + LANE_HEADER_HEIGHT;
|
|
47
|
+
|
|
48
|
+
nodePositions.set(layoutNode.node.id, { x, y });
|
|
49
|
+
|
|
50
|
+
const positioned: PositionedNode = {
|
|
51
|
+
node: layoutNode.node,
|
|
52
|
+
x,
|
|
53
|
+
y,
|
|
54
|
+
width: NODE_WIDTH,
|
|
55
|
+
height: NODE_HEIGHT,
|
|
56
|
+
lane: layoutNode.lane,
|
|
57
|
+
depth: layoutNode.depth,
|
|
58
|
+
branchPath: layoutNode.branchPath,
|
|
59
|
+
parentId: layoutNode.parent?.id,
|
|
60
|
+
connectorPoints: [],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
positionedNodes.push(positioned);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Third pass: calculate connector points
|
|
67
|
+
this._calculateConnectors(positionedNodes, nodePositions);
|
|
68
|
+
|
|
69
|
+
return positionedNodes;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Traverse workflow tree and assign lane/depth to each node
|
|
74
|
+
*/
|
|
75
|
+
private static _traverseAndAssignLanes(
|
|
76
|
+
node: WorkflowNode,
|
|
77
|
+
parent: WorkflowNode | null,
|
|
78
|
+
baseLane: string,
|
|
79
|
+
depth: number,
|
|
80
|
+
layoutNodes: LayoutNode[],
|
|
81
|
+
lanes: Map<string, LayoutNode[]>
|
|
82
|
+
): void {
|
|
83
|
+
const layoutNode: LayoutNode = {
|
|
84
|
+
node,
|
|
85
|
+
parent,
|
|
86
|
+
lane: baseLane,
|
|
87
|
+
depth,
|
|
88
|
+
width: NODE_WIDTH,
|
|
89
|
+
height: NODE_HEIGHT,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Add to layout nodes
|
|
93
|
+
layoutNodes.push(layoutNode);
|
|
94
|
+
|
|
95
|
+
// Register in lanes map
|
|
96
|
+
if (!lanes.has(baseLane)) {
|
|
97
|
+
lanes.set(baseLane, []);
|
|
98
|
+
}
|
|
99
|
+
lanes.get(baseLane)!.push(layoutNode);
|
|
100
|
+
|
|
101
|
+
// Process children
|
|
102
|
+
if (node.children && node.children.length > 0) {
|
|
103
|
+
for (const child of node.children) {
|
|
104
|
+
this._traverseAndAssignLanes(
|
|
105
|
+
child,
|
|
106
|
+
node,
|
|
107
|
+
baseLane,
|
|
108
|
+
depth + 1,
|
|
109
|
+
layoutNodes,
|
|
110
|
+
lanes
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Process decision branches into separate swimlanes
|
|
116
|
+
if (node.branches) {
|
|
117
|
+
let branchIndex = 0;
|
|
118
|
+
for (const [branchKey, branchNodes] of Object.entries(node.branches)) {
|
|
119
|
+
const branchLane = `${baseLane}_${branchKey}_${branchIndex}`;
|
|
120
|
+
|
|
121
|
+
for (const branchNode of branchNodes) {
|
|
122
|
+
this._traverseAndAssignLanes(
|
|
123
|
+
branchNode,
|
|
124
|
+
node,
|
|
125
|
+
branchLane,
|
|
126
|
+
depth + 1,
|
|
127
|
+
layoutNodes,
|
|
128
|
+
lanes
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
branchIndex++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Process fork into parallel lanes
|
|
136
|
+
if (node.type === 'fork' && node.tasks) {
|
|
137
|
+
let taskIndex = 0;
|
|
138
|
+
for (const task of node.tasks) {
|
|
139
|
+
const parallelLane = `${baseLane}_parallel_${taskIndex}`;
|
|
140
|
+
this._traverseAndAssignLanes(
|
|
141
|
+
task,
|
|
142
|
+
node,
|
|
143
|
+
parallelLane,
|
|
144
|
+
depth + 1,
|
|
145
|
+
layoutNodes,
|
|
146
|
+
lanes
|
|
147
|
+
);
|
|
148
|
+
taskIndex++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Process fork join node
|
|
153
|
+
if (node.type === 'fork' && node.join) {
|
|
154
|
+
// Join node goes back to main lane at next depth
|
|
155
|
+
this._traverseAndAssignLanes(
|
|
156
|
+
node.join,
|
|
157
|
+
node,
|
|
158
|
+
baseLane,
|
|
159
|
+
depth + 2,
|
|
160
|
+
layoutNodes,
|
|
161
|
+
lanes
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Process task nodes (used in forks)
|
|
166
|
+
if (node.tasks && node.type !== 'fork') {
|
|
167
|
+
for (const task of node.tasks) {
|
|
168
|
+
this._traverseAndAssignLanes(
|
|
169
|
+
task,
|
|
170
|
+
node,
|
|
171
|
+
baseLane,
|
|
172
|
+
depth + 1,
|
|
173
|
+
layoutNodes,
|
|
174
|
+
lanes
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Calculate SVG connector points between nodes
|
|
182
|
+
*/
|
|
183
|
+
private static _calculateConnectors(
|
|
184
|
+
positionedNodes: PositionedNode[],
|
|
185
|
+
nodePositions: Map<string, { x: number; y: number }>
|
|
186
|
+
): void {
|
|
187
|
+
const nodeMap = new Map<string, PositionedNode>(
|
|
188
|
+
positionedNodes.map((n) => [n.node.id, n])
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
for (const positioned of positionedNodes) {
|
|
192
|
+
const connectors: PositionedNode['connectorPoints'] = [];
|
|
193
|
+
const nodeMiddleRight = {
|
|
194
|
+
x: positioned.x + positioned.width,
|
|
195
|
+
y: positioned.y + positioned.height / 2,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Connect to children (sequential)
|
|
199
|
+
if (positioned.node.children && positioned.node.children.length > 0) {
|
|
200
|
+
for (const child of positioned.node.children) {
|
|
201
|
+
const childPos = nodeMap.get(child.id);
|
|
202
|
+
if (childPos) {
|
|
203
|
+
connectors.push({
|
|
204
|
+
from: nodeMiddleRight,
|
|
205
|
+
to: {
|
|
206
|
+
x: childPos.x,
|
|
207
|
+
y: childPos.y + childPos.height / 2,
|
|
208
|
+
},
|
|
209
|
+
type: 'straight',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Connect to branches (decision)
|
|
216
|
+
if (positioned.node.branches) {
|
|
217
|
+
for (const branchNodes of Object.values(positioned.node.branches)) {
|
|
218
|
+
|
|
219
|
+
for (const branchNode of branchNodes) {
|
|
220
|
+
const childPos = nodeMap.get(branchNode.id);
|
|
221
|
+
if (childPos) {
|
|
222
|
+
// Curved path to branch
|
|
223
|
+
connectors.push({
|
|
224
|
+
from: nodeMiddleRight,
|
|
225
|
+
to: {
|
|
226
|
+
x: childPos.x,
|
|
227
|
+
y: childPos.y + childPos.height / 2,
|
|
228
|
+
},
|
|
229
|
+
type: 'branch',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Connect fork to parallel tasks
|
|
237
|
+
if (positioned.node.type === 'fork' && positioned.node.tasks) {
|
|
238
|
+
for (const task of positioned.node.tasks) {
|
|
239
|
+
const taskPos = nodeMap.get(task.id);
|
|
240
|
+
if (taskPos) {
|
|
241
|
+
connectors.push({
|
|
242
|
+
from: nodeMiddleRight,
|
|
243
|
+
to: {
|
|
244
|
+
x: taskPos.x,
|
|
245
|
+
y: taskPos.y + taskPos.height / 2,
|
|
246
|
+
},
|
|
247
|
+
type: 'fork',
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Connect to join
|
|
254
|
+
if (positioned.node.type === 'fork' && positioned.node.join) {
|
|
255
|
+
const joinPos = nodeMap.get(positioned.node.join.id);
|
|
256
|
+
if (joinPos) {
|
|
257
|
+
connectors.push({
|
|
258
|
+
from: nodeMiddleRight,
|
|
259
|
+
to: {
|
|
260
|
+
x: joinPos.x,
|
|
261
|
+
y: joinPos.y + joinPos.height / 2,
|
|
262
|
+
},
|
|
263
|
+
type: 'join',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Connect loop back
|
|
269
|
+
if (positioned.node.type === 'loop_end' && positioned.node.target_id) {
|
|
270
|
+
const targetPos = nodeMap.get(positioned.node.target_id);
|
|
271
|
+
if (targetPos) {
|
|
272
|
+
connectors.push({
|
|
273
|
+
from: {
|
|
274
|
+
x: positioned.x + positioned.width,
|
|
275
|
+
y: positioned.y + positioned.height / 2,
|
|
276
|
+
},
|
|
277
|
+
to: {
|
|
278
|
+
x: targetPos.x,
|
|
279
|
+
y: targetPos.y + targetPos.height / 2,
|
|
280
|
+
},
|
|
281
|
+
type: 'curved',
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
positioned.connectorPoints = connectors;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get swimlane configurations for rendering
|
|
292
|
+
*/
|
|
293
|
+
static getSwimlanes(positionedNodes: PositionedNode[]): SwimlaneConfig[] {
|
|
294
|
+
const swimlanesMap = new Map<string, PositionedNode[]>();
|
|
295
|
+
|
|
296
|
+
for (const node of positionedNodes) {
|
|
297
|
+
if (!swimlanesMap.has(node.lane)) {
|
|
298
|
+
swimlanesMap.set(node.lane, []);
|
|
299
|
+
}
|
|
300
|
+
swimlanesMap.get(node.lane)!.push(node);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const swimlanes: SwimlaneConfig[] = [];
|
|
304
|
+
for (const [laneId, nodes] of swimlanesMap.entries()) {
|
|
305
|
+
const isParallel = laneId.includes('parallel');
|
|
306
|
+
const name = this._getSwimlaneName(laneId);
|
|
307
|
+
|
|
308
|
+
swimlanes.push({
|
|
309
|
+
id: laneId,
|
|
310
|
+
name,
|
|
311
|
+
nodes,
|
|
312
|
+
isParallel,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return swimlanes;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generate human-readable swimlane name
|
|
321
|
+
*/
|
|
322
|
+
private static _getSwimlaneName(laneId: string): string {
|
|
323
|
+
if (laneId === 'main') return 'Main Flow';
|
|
324
|
+
if (laneId.includes('yes')) return 'Yes Path';
|
|
325
|
+
if (laneId.includes('no')) return 'No Path';
|
|
326
|
+
if (laneId.includes('parallel')) {
|
|
327
|
+
const match = laneId.match(/parallel_(\d+)/);
|
|
328
|
+
if (match) return `Parallel Task ${parseInt(match[1]) + 1}`;
|
|
329
|
+
}
|
|
330
|
+
return laneId;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Calculate canvas bounds for sizing
|
|
335
|
+
*/
|
|
336
|
+
static getCanvasBounds(positionedNodes: PositionedNode[]): {
|
|
337
|
+
width: number;
|
|
338
|
+
height: number;
|
|
339
|
+
} {
|
|
340
|
+
if (positionedNodes.length === 0) {
|
|
341
|
+
return { width: 600, height: 400 };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let maxX = 0;
|
|
345
|
+
let maxY = 0;
|
|
346
|
+
|
|
347
|
+
for (const node of positionedNodes) {
|
|
348
|
+
maxX = Math.max(maxX, node.x + node.width + HORIZONTAL_GAP);
|
|
349
|
+
maxY = Math.max(maxY, node.y + node.height + VERTICAL_GAP);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
width: Math.max(600, maxX),
|
|
354
|
+
height: Math.max(400, maxY),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Data Types for Flow Designer
|
|
3
|
+
* Defines the structure for low-code business process workflows
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Supported node types in a workflow
|
|
8
|
+
*/
|
|
9
|
+
export type NodeType =
|
|
10
|
+
| 'trigger'
|
|
11
|
+
| 'action'
|
|
12
|
+
| 'decision'
|
|
13
|
+
| 'loop_start'
|
|
14
|
+
| 'loop_end'
|
|
15
|
+
| 'fork'
|
|
16
|
+
| 'join';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Payload for workflow-changed event
|
|
20
|
+
*/
|
|
21
|
+
export interface WorkflowChangeEvent {
|
|
22
|
+
type:
|
|
23
|
+
| 'node-added'
|
|
24
|
+
| 'node-deleted'
|
|
25
|
+
| 'node-edited'
|
|
26
|
+
| 'node-moved'
|
|
27
|
+
| 'undo'
|
|
28
|
+
| 'redo';
|
|
29
|
+
nodeId?: string;
|
|
30
|
+
workflow: Workflow;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Core workflow node structure
|
|
35
|
+
*/
|
|
36
|
+
export interface WorkflowNode {
|
|
37
|
+
id: string;
|
|
38
|
+
type: NodeType;
|
|
39
|
+
label: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
icon?: string;
|
|
42
|
+
/**
|
|
43
|
+
* For sequential nodes in parallel execution (fork tasks)
|
|
44
|
+
*/
|
|
45
|
+
tasks?: WorkflowNode[];
|
|
46
|
+
/**
|
|
47
|
+
* For conditional branching (decision nodes)
|
|
48
|
+
* Keys are branch conditions like "yes", "no"
|
|
49
|
+
*/
|
|
50
|
+
branches?: Record<string, WorkflowNode[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Child nodes for sequential flow
|
|
53
|
+
*/
|
|
54
|
+
children?: WorkflowNode[];
|
|
55
|
+
/**
|
|
56
|
+
* For loop_end: points to the loop_start node id to repeat
|
|
57
|
+
*/
|
|
58
|
+
target_id?: string;
|
|
59
|
+
/**
|
|
60
|
+
* For fork: join node that collects parallel paths
|
|
61
|
+
*/
|
|
62
|
+
join?: WorkflowNode;
|
|
63
|
+
/**
|
|
64
|
+
* Custom metadata - can store domain-specific data
|
|
65
|
+
*/
|
|
66
|
+
metadata?: Record<string, any>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Complete workflow definition
|
|
71
|
+
*/
|
|
72
|
+
export interface Workflow {
|
|
73
|
+
workflow_id: string;
|
|
74
|
+
nodes: WorkflowNode; // Root node (usually trigger)
|
|
75
|
+
metadata?: Record<string, any>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Layout engine output - node with calculated position
|
|
80
|
+
*/
|
|
81
|
+
export interface PositionedNode {
|
|
82
|
+
node: WorkflowNode;
|
|
83
|
+
x: number; // Pixel X coordinate
|
|
84
|
+
y: number; // Pixel Y coordinate
|
|
85
|
+
width: number; // Node card width
|
|
86
|
+
height: number; // Node card height
|
|
87
|
+
lane: string; // Swimlane identifier (e.g., "yes_lane", "no_lane", "parallel_0")
|
|
88
|
+
depth: number; // Horizontal depth in layout
|
|
89
|
+
branchPath?: string; // For connectors: "yes" or "no" or branch name
|
|
90
|
+
parentId?: string; // Reference to parent node id
|
|
91
|
+
connectorPoints?: {
|
|
92
|
+
// For canvas connector rendering
|
|
93
|
+
from: { x: number; y: number };
|
|
94
|
+
to: { x: number; y: number };
|
|
95
|
+
type: 'straight' | 'curved' | 'branch' | 'fork' | 'join';
|
|
96
|
+
}[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validation error result
|
|
101
|
+
*/
|
|
102
|
+
export interface ValidationError {
|
|
103
|
+
nodeId: string;
|
|
104
|
+
type:
|
|
105
|
+
| 'circular_loop'
|
|
106
|
+
| 'orphaned_node'
|
|
107
|
+
| 'invalid_branch'
|
|
108
|
+
| 'missing_target'
|
|
109
|
+
| 'invalid_fork_join';
|
|
110
|
+
message: string;
|
|
111
|
+
severity: 'error' | 'warning';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* State for a single undo/redo operation
|
|
116
|
+
*/
|
|
117
|
+
export interface HistoryEntry {
|
|
118
|
+
command: WorkflowCommand;
|
|
119
|
+
workflow: Workflow;
|
|
120
|
+
timestamp: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Command interface for undo/redo pattern
|
|
125
|
+
*/
|
|
126
|
+
export interface WorkflowCommand {
|
|
127
|
+
execute(workflow: Workflow): Workflow;
|
|
128
|
+
undo(workflow: Workflow): Workflow;
|
|
129
|
+
description: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Swimlane configuration
|
|
134
|
+
*/
|
|
135
|
+
export interface SwimlaneConfig {
|
|
136
|
+
id: string;
|
|
137
|
+
name: string;
|
|
138
|
+
nodes: PositionedNode[];
|
|
139
|
+
isParallel: boolean;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Editor state
|
|
144
|
+
*/
|
|
145
|
+
export interface EditorState {
|
|
146
|
+
selectedNodeId: string | null;
|
|
147
|
+
isEditing: boolean;
|
|
148
|
+
editingNode: Partial<WorkflowNode> | null;
|
|
149
|
+
hoveredNodeId: string | null;
|
|
150
|
+
isDragging: boolean;
|
|
151
|
+
draggedNodeId: string | null;
|
|
152
|
+
zoom: number;
|
|
153
|
+
panX: number;
|
|
154
|
+
panY: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Node template configuration for slots
|
|
159
|
+
*/
|
|
160
|
+
export interface NodeTemplate {
|
|
161
|
+
type: NodeType;
|
|
162
|
+
defaultIcon?: string;
|
|
163
|
+
defaultHeight?: number;
|
|
164
|
+
allowedChildren?: NodeType[];
|
|
165
|
+
allowedBranches?: string[];
|
|
166
|
+
}
|