bpmn-auto-layout 1.1.1 → 1.2.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/index.cjs +722 -467
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +722 -467
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var bpmnModdle = require('bpmn-moddle');
|
|
4
4
|
var minDash = require('min-dash');
|
|
5
5
|
|
|
6
6
|
function isConnection(element) {
|
|
@@ -32,576 +32,656 @@ function findElementInTree(currentElement, targetElement, visited = new Set()) {
|
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const DEFAULT_TASK_HEIGHT = 80;
|
|
36
|
+
const DEFAULT_TASK_WIDTH = 100;
|
|
37
|
+
|
|
38
|
+
function getDefaultSize(element) {
|
|
39
|
+
if (is(element, 'bpmn:SubProcess')) {
|
|
40
|
+
return { width: DEFAULT_TASK_WIDTH, height: DEFAULT_TASK_HEIGHT };
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
43
|
+
if (is(element, 'bpmn:Task')) {
|
|
44
|
+
return { width: DEFAULT_TASK_WIDTH, height: DEFAULT_TASK_HEIGHT };
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
47
|
+
if (is(element, 'bpmn:Gateway')) {
|
|
48
|
+
return { width: 50, height: 50 };
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (is(element, 'bpmn:Event')) {
|
|
52
|
+
return { width: 36, height: 36 };
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if (is(element, 'bpmn:Participant')) {
|
|
56
|
+
return { width: 400, height: 100 };
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
if (is(element, 'bpmn:Lane')) {
|
|
60
|
+
return { width: 400, height: 100 };
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.grid.push([]);
|
|
65
|
-
} else {
|
|
66
|
-
this.grid.splice(afterIndex + 1, 0, []);
|
|
67
|
-
}
|
|
63
|
+
if (is(element, 'bpmn:DataObjectReference')) {
|
|
64
|
+
return { width: 36, height: 50 };
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
if (is(element, 'bpmn:DataStoreReference')) {
|
|
68
|
+
return { width: 50, height: 50 };
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this._addStart(newElement);
|
|
77
|
-
}
|
|
78
|
-
const [ row, col ] = this.find(element);
|
|
79
|
-
this.grid[row].splice(col + 1, 0, newElement);
|
|
71
|
+
if (is(element, 'bpmn:TextAnnotation')) {
|
|
72
|
+
return { width: DEFAULT_TASK_WIDTH, height: 30 };
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this._addStart(newElement);
|
|
85
|
-
}
|
|
75
|
+
return { width: DEFAULT_TASK_WIDTH, height: DEFAULT_TASK_HEIGHT };
|
|
76
|
+
}
|
|
86
77
|
|
|
87
|
-
|
|
78
|
+
function is(element, type) {
|
|
79
|
+
return element.$instanceOf(type);
|
|
80
|
+
}
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.grid[row + 1] = [];
|
|
92
|
-
}
|
|
82
|
+
const DEFAULT_CELL_WIDTH = 150;
|
|
83
|
+
const DEFAULT_CELL_HEIGHT = 140;
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
function getMid(bounds) {
|
|
86
|
+
return {
|
|
87
|
+
x: bounds.x + bounds.width / 2,
|
|
88
|
+
y: bounds.y + bounds.height / 2
|
|
89
|
+
};
|
|
90
|
+
}
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
throw new Error('Grid is occupied and we could not find a place - this should not happen');
|
|
101
|
-
}
|
|
92
|
+
function getDockingPoint(point, rectangle, dockingDirection = 'r', targetOrientation = 'top-left') {
|
|
102
93
|
|
|
103
|
-
|
|
94
|
+
// ensure we end up with a specific docking direction
|
|
95
|
+
// based on the targetOrientation, if <h|v> is being passed
|
|
96
|
+
if (dockingDirection === 'h') {
|
|
97
|
+
dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r';
|
|
104
98
|
}
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
col = row.findIndex((el) => {
|
|
110
|
-
return el === element;
|
|
111
|
-
});
|
|
100
|
+
if (dockingDirection === 'v') {
|
|
101
|
+
dockingDirection = /top/.test(targetOrientation) ? 't' : 'b';
|
|
102
|
+
}
|
|
112
103
|
|
|
113
|
-
|
|
114
|
-
}
|
|
104
|
+
if (dockingDirection === 't') {
|
|
105
|
+
return { original: point, x: point.x, y: rectangle.y };
|
|
106
|
+
}
|
|
115
107
|
|
|
116
|
-
|
|
108
|
+
if (dockingDirection === 'r') {
|
|
109
|
+
return { original: point, x: rectangle.x + rectangle.width, y: point.y };
|
|
117
110
|
}
|
|
118
111
|
|
|
119
|
-
|
|
120
|
-
return
|
|
112
|
+
if (dockingDirection === 'b') {
|
|
113
|
+
return { original: point, x: point.x, y: rectangle.y + rectangle.height };
|
|
121
114
|
}
|
|
122
115
|
|
|
123
|
-
|
|
124
|
-
|
|
116
|
+
if (dockingDirection === 'l') {
|
|
117
|
+
return { original: point, x: rectangle.x, y: point.y };
|
|
118
|
+
}
|
|
125
119
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
120
|
+
throw new Error('unexpected dockingDirection: <' + dockingDirection + '>');
|
|
121
|
+
}
|
|
129
122
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Modified Manhattan layout: Uses space between grid columns to route connections
|
|
125
|
+
* if direct connection is not possible.
|
|
126
|
+
* @param {*} source
|
|
127
|
+
* @param {*} target
|
|
128
|
+
* @param layoutGrid
|
|
129
|
+
* @returns waypoints
|
|
130
|
+
*/
|
|
131
|
+
function connectElements(source, target, layoutGrid) {
|
|
132
|
+
const sourceDi = source.di;
|
|
133
|
+
const targetDi = target.di;
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const element = this.get(row, col);
|
|
135
|
+
const sourceBounds = sourceDi.get('bounds');
|
|
136
|
+
const targetBounds = targetDi.get('bounds');
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
138
|
+
const sourceMid = getMid(sourceBounds);
|
|
139
|
+
const targetMid = getMid(targetBounds);
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
const dX = target.gridPosition.col - source.gridPosition.col;
|
|
142
|
+
const dY = target.gridPosition.row - source.gridPosition.row;
|
|
143
|
+
|
|
144
|
+
const dockingSource = `${(dY > 0 ? 'bottom' : 'top')}-${dX > 0 ? 'right' : 'left'}`;
|
|
145
|
+
const dockingTarget = `${(dY > 0 ? 'top' : 'bottom')}-${dX > 0 ? 'left' : 'right'}`;
|
|
146
|
+
|
|
147
|
+
const baseSourceGrid = source.grid || source.attachedToRef?.grid;
|
|
148
|
+
const baseTargetGrid = target.grid;
|
|
149
|
+
|
|
150
|
+
// Source === Target ==> Build loop
|
|
151
|
+
if (dX === 0 && dY === 0) {
|
|
152
|
+
const { x, y } = coordinatesToPosition(source.gridPosition.row, source.gridPosition.col);
|
|
153
|
+
return [
|
|
154
|
+
getDockingPoint(sourceMid, sourceBounds, 'r', dockingSource),
|
|
155
|
+
{ x: baseSourceGrid ? x + (baseSourceGrid.getGridDimensions()[1] + 1) * DEFAULT_CELL_WIDTH : x + DEFAULT_CELL_WIDTH, y: sourceMid.y },
|
|
156
|
+
{ x: baseSourceGrid ? x + (baseSourceGrid.getGridDimensions()[1] + 1) * DEFAULT_CELL_WIDTH : x + DEFAULT_CELL_WIDTH, y: y },
|
|
157
|
+
{ x: targetMid.x, y: y },
|
|
158
|
+
getDockingPoint(targetMid, targetBounds, 't', dockingTarget)
|
|
159
|
+
];
|
|
145
160
|
}
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const [ , maxCol ] = this.getGridDimensions();
|
|
162
|
+
// negative dX indicates connection from future to past
|
|
163
|
+
if (dX < 0) {
|
|
150
164
|
|
|
151
|
-
|
|
165
|
+
const { y } = coordinatesToPosition(source.gridPosition.row, source.gridPosition.col);
|
|
152
166
|
|
|
153
|
-
|
|
154
|
-
this.grid[row].length = maxCol;
|
|
155
|
-
this.grid[row][maxCol] = element;
|
|
156
|
-
this.grid[row][col] = null;
|
|
167
|
+
const offsetY = DEFAULT_CELL_HEIGHT / 2;
|
|
157
168
|
|
|
158
|
-
|
|
159
|
-
}
|
|
169
|
+
if (sourceMid.y >= targetMid.y) {
|
|
160
170
|
|
|
161
|
-
|
|
162
|
-
|
|
171
|
+
// edge goes below
|
|
172
|
+
const maxExpanded = getMaxExpandedBetween(source, target, layoutGrid);
|
|
173
|
+
|
|
174
|
+
if (maxExpanded) {
|
|
175
|
+
return [
|
|
176
|
+
getDockingPoint(sourceMid, sourceBounds, 'b'),
|
|
177
|
+
{ x: sourceMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT + maxExpanded * DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
178
|
+
{ x: targetMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT + maxExpanded * DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
179
|
+
getDockingPoint(targetMid, targetBounds, 'b')
|
|
180
|
+
];
|
|
181
|
+
}
|
|
163
182
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
return [
|
|
184
|
+
getDockingPoint(sourceMid, sourceBounds, 'b'),
|
|
185
|
+
{ x: sourceMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
186
|
+
{ x: targetMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
187
|
+
getDockingPoint(targetMid, targetBounds, 'b')
|
|
188
|
+
];
|
|
189
|
+
} else {
|
|
168
190
|
|
|
169
|
-
|
|
191
|
+
// edge goes above
|
|
192
|
+
const bendY = sourceMid.y - offsetY;
|
|
170
193
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
return [
|
|
195
|
+
getDockingPoint(sourceMid, sourceBounds, 't'),
|
|
196
|
+
{ x: sourceMid.x, y: bendY },
|
|
197
|
+
{ x: targetMid.x, y: bendY },
|
|
198
|
+
getDockingPoint(targetMid, targetBounds, 't')
|
|
199
|
+
];
|
|
175
200
|
}
|
|
176
201
|
}
|
|
177
202
|
|
|
178
|
-
|
|
179
|
-
|
|
203
|
+
// connect horizontally
|
|
204
|
+
if (dY === 0) {
|
|
205
|
+
if (isDirectPathBlocked(source, target, layoutGrid)) {
|
|
206
|
+
const { y } = coordinatesToPosition(source.gridPosition.row, source.gridPosition.col);
|
|
180
207
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
208
|
+
// Route on bottom
|
|
209
|
+
return [
|
|
210
|
+
getDockingPoint(sourceMid, sourceBounds, 'b'),
|
|
211
|
+
{ x: sourceMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
212
|
+
{ x: targetMid.x, y: !baseSourceGrid ? y + DEFAULT_CELL_HEIGHT : y + (baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
213
|
+
getDockingPoint(targetMid, targetBounds, 'b')
|
|
214
|
+
];
|
|
215
|
+
} else {
|
|
185
216
|
|
|
186
|
-
|
|
217
|
+
// if space is clear, connect directly
|
|
218
|
+
const firstPoint = getDockingPoint(sourceMid, sourceBounds, 'h', dockingSource);
|
|
219
|
+
const lastPoint = getDockingPoint(targetMid, targetBounds, 'h', dockingTarget);
|
|
220
|
+
if (baseSourceGrid) {
|
|
221
|
+
firstPoint.y = sourceBounds.y + DEFAULT_TASK_HEIGHT / 2 ;
|
|
222
|
+
}
|
|
187
223
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
224
|
+
if (baseTargetGrid) {
|
|
225
|
+
lastPoint.y = targetBounds.y + DEFAULT_TASK_HEIGHT / 2 ;
|
|
226
|
+
}
|
|
227
|
+
return [
|
|
228
|
+
firstPoint,
|
|
229
|
+
lastPoint
|
|
230
|
+
];
|
|
192
231
|
}
|
|
193
232
|
}
|
|
194
233
|
|
|
195
|
-
|
|
196
|
-
|
|
234
|
+
// connect vertically
|
|
235
|
+
if (dX === 0) {
|
|
236
|
+
if (isDirectPathBlocked(source, target, layoutGrid)) {
|
|
197
237
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
238
|
+
// Route parallel
|
|
239
|
+
const yOffset = -Math.sign(dY) * DEFAULT_CELL_HEIGHT / 2;
|
|
240
|
+
return [
|
|
241
|
+
getDockingPoint(sourceMid, sourceBounds, 'r'),
|
|
242
|
+
{ x: sourceMid.x + DEFAULT_CELL_WIDTH / 2, y: sourceMid.y }, // out right
|
|
243
|
+
{ x: targetMid.x + DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset },
|
|
244
|
+
{ x: targetMid.x, y: targetMid.y + yOffset },
|
|
245
|
+
getDockingPoint(targetMid, targetBounds, Math.sign(yOffset) > 0 ? 'b' : 't')
|
|
246
|
+
];
|
|
247
|
+
} else {
|
|
201
248
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
249
|
+
// if space is clear, connect directly
|
|
250
|
+
return [ getDockingPoint(sourceMid, sourceBounds, 'v', dockingSource),
|
|
251
|
+
getDockingPoint(targetMid, targetBounds, 'v', dockingTarget)
|
|
252
|
+
];
|
|
206
253
|
}
|
|
207
|
-
|
|
208
|
-
return elements;
|
|
209
254
|
}
|
|
210
255
|
|
|
211
|
-
|
|
212
|
-
const numRows = this.grid.length;
|
|
213
|
-
let maxCols = 0;
|
|
256
|
+
const directManhattan = directManhattanConnect(source, target, layoutGrid);
|
|
214
257
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
maxCols = currentRowLength;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
258
|
+
if (directManhattan) {
|
|
259
|
+
const startPoint = getDockingPoint(sourceMid, sourceBounds, directManhattan[0], dockingSource);
|
|
260
|
+
const endPoint = getDockingPoint(targetMid, targetBounds, directManhattan[1], dockingTarget);
|
|
221
261
|
|
|
222
|
-
|
|
262
|
+
const midPoint = directManhattan[0] === 'h' ? { x: endPoint.x, y: startPoint.y } : { x: startPoint.x, y: endPoint.y };
|
|
263
|
+
|
|
264
|
+
return [
|
|
265
|
+
startPoint,
|
|
266
|
+
midPoint,
|
|
267
|
+
endPoint
|
|
268
|
+
];
|
|
223
269
|
}
|
|
270
|
+
const yOffset = -Math.sign(dY) * DEFAULT_CELL_HEIGHT / 2;
|
|
224
271
|
|
|
225
|
-
|
|
226
|
-
|
|
272
|
+
return [
|
|
273
|
+
getDockingPoint(sourceMid, sourceBounds, 'r', dockingSource),
|
|
274
|
+
{ x: sourceMid.x + DEFAULT_CELL_WIDTH / 2, y: sourceMid.y }, // out right
|
|
275
|
+
{ x: sourceMid.x + DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset }, // to target row
|
|
276
|
+
{ x: targetMid.x - DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset }, // to target column
|
|
277
|
+
{ x: targetMid.x - DEFAULT_CELL_WIDTH / 2, y: targetMid.y }, // to mid
|
|
278
|
+
getDockingPoint(targetMid, targetBounds, 'l', dockingTarget)
|
|
279
|
+
];
|
|
280
|
+
}
|
|
227
281
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|
|
282
|
+
// helpers /////
|
|
283
|
+
function coordinatesToPosition(row, col) {
|
|
284
|
+
return {
|
|
285
|
+
width: DEFAULT_CELL_WIDTH,
|
|
286
|
+
height: DEFAULT_CELL_HEIGHT,
|
|
287
|
+
x: col * DEFAULT_CELL_WIDTH,
|
|
288
|
+
y: row * DEFAULT_CELL_HEIGHT
|
|
289
|
+
};
|
|
290
|
+
}
|
|
240
291
|
|
|
241
|
-
|
|
242
|
-
}
|
|
292
|
+
function getBounds(element, row, col, shift, attachedTo) {
|
|
293
|
+
let { width, height } = getDefaultSize(element);
|
|
294
|
+
const { x, y } = shift;
|
|
243
295
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
296
|
+
// Center in cell
|
|
297
|
+
if (!attachedTo) {
|
|
298
|
+
return {
|
|
299
|
+
width: element.isExpanded ? element.grid.getGridDimensions()[1] * DEFAULT_CELL_WIDTH + width : width,
|
|
300
|
+
height: element.isExpanded ? element.grid.getGridDimensions()[0] * DEFAULT_CELL_HEIGHT + height : height,
|
|
301
|
+
x: (col * DEFAULT_CELL_WIDTH) + (DEFAULT_CELL_WIDTH - width) / 2 + x,
|
|
302
|
+
y: row * DEFAULT_CELL_HEIGHT + (DEFAULT_CELL_HEIGHT - height) / 2 + y
|
|
303
|
+
};
|
|
248
304
|
}
|
|
305
|
+
|
|
306
|
+
const hostBounds = attachedTo.di.bounds;
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
width, height,
|
|
310
|
+
x: Math.round(hostBounds.x + hostBounds.width / 2 - width / 2),
|
|
311
|
+
y: Math.round(hostBounds.y + hostBounds.height - height / 2)
|
|
312
|
+
};
|
|
249
313
|
}
|
|
250
314
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
315
|
+
function isDirectPathBlocked(source, target, layoutGrid) {
|
|
316
|
+
const { row: sourceRow, col: sourceCol } = source.gridPosition;
|
|
317
|
+
const { row: targetRow, col: targetCol } = target.gridPosition;
|
|
255
318
|
|
|
256
|
-
|
|
257
|
-
|
|
319
|
+
const dX = targetCol - sourceCol;
|
|
320
|
+
const dY = targetRow - sourceRow;
|
|
321
|
+
|
|
322
|
+
let totalElements = 0;
|
|
323
|
+
|
|
324
|
+
if (dX) {
|
|
325
|
+
totalElements += layoutGrid.getElementsInRange({ row: sourceRow, col: sourceCol }, { row: sourceRow, col: targetCol }).length;
|
|
258
326
|
}
|
|
259
327
|
|
|
260
|
-
|
|
261
|
-
|
|
328
|
+
if (dY) {
|
|
329
|
+
totalElements += layoutGrid.getElementsInRange({ row: sourceRow, col: targetCol }, { row: targetRow, col: targetCol }).length;
|
|
262
330
|
}
|
|
263
331
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
332
|
+
return totalElements > 2;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function directManhattanConnect(source, target, layoutGrid) {
|
|
336
|
+
const { row: sourceRow, col: sourceCol } = source.gridPosition;
|
|
337
|
+
const { row: targetRow, col: targetCol } = target.gridPosition;
|
|
338
|
+
|
|
339
|
+
const dX = targetCol - sourceCol;
|
|
340
|
+
const dY = targetRow - sourceRow;
|
|
341
|
+
|
|
342
|
+
// Only directly connect left-to-right flow
|
|
343
|
+
if (!(dX > 0 && dY !== 0)) {
|
|
344
|
+
return;
|
|
268
345
|
}
|
|
269
346
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
},
|
|
347
|
+
// If below, go down then horizontal
|
|
348
|
+
if (dY > 0) {
|
|
349
|
+
let totalElements = 0;
|
|
350
|
+
const bendPoint = { row: targetRow, col: sourceCol };
|
|
351
|
+
totalElements += layoutGrid.getElementsInRange({ row: sourceRow, col: sourceCol }, bendPoint).length;
|
|
352
|
+
totalElements += layoutGrid.getElementsInRange(bendPoint, { row: targetRow, col: targetCol }).length;
|
|
353
|
+
|
|
354
|
+
return totalElements > 2 ? false : [ 'v', 'h' ];
|
|
355
|
+
} else {
|
|
356
|
+
|
|
357
|
+
// If above, go horizontal than vertical
|
|
358
|
+
let totalElements = 0;
|
|
359
|
+
const bendPoint = { row: sourceRow, col: targetCol };
|
|
360
|
+
|
|
361
|
+
totalElements += layoutGrid.getElementsInRange({ row: sourceRow, col: sourceCol }, bendPoint).length;
|
|
362
|
+
totalElements += layoutGrid.getElementsInRange(bendPoint, { row: targetRow, col: targetCol }).length;
|
|
363
|
+
|
|
364
|
+
return totalElements > 2 ? false : [ 'h', 'v' ];
|
|
275
365
|
}
|
|
366
|
+
}
|
|
276
367
|
|
|
277
|
-
createDiWaypoints(waypoints) {
|
|
278
|
-
var self = this;
|
|
279
368
|
|
|
280
|
-
|
|
281
|
-
return self.createDiWaypoint(pos);
|
|
282
|
-
});
|
|
283
|
-
}
|
|
369
|
+
function getMaxExpandedBetween(source, target, layoutGrid) {
|
|
284
370
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
371
|
+
const hostSource = source.attachedToRef ? source.attachedToRef : source;
|
|
372
|
+
const hostTarget = target.attachedToRef ? target.attachedToRef : target;
|
|
288
373
|
|
|
289
|
-
createDiEdge(semantic, waypoints, attrs) {
|
|
290
|
-
return this.create('bpmndi:BPMNEdge', minDash.assign({
|
|
291
|
-
bpmnElement: semantic,
|
|
292
|
-
waypoint: this.createDiWaypoints(waypoints)
|
|
293
|
-
}, attrs));
|
|
294
|
-
}
|
|
295
374
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
375
|
+
const [ sourceRow, sourceCol ] = layoutGrid.find(hostSource);
|
|
376
|
+
const [ , targetCol ] = layoutGrid.find(hostTarget);
|
|
299
377
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
378
|
+
const firstCol = sourceCol < targetCol ? sourceCol : targetCol;
|
|
379
|
+
const lastCol = sourceCol < targetCol ? targetCol : sourceCol;
|
|
380
|
+
|
|
381
|
+
const elementsInRange = layoutGrid.getAllElements().filter(element => element.gridPosition.row === sourceRow && element.gridPosition.col > firstCol && element.gridPosition.col < lastCol);
|
|
382
|
+
|
|
383
|
+
return elementsInRange.reduce((acc, cur) => {
|
|
384
|
+
if (cur.grid?.getGridDimensions()[0] > acc) return cur.grid?.getGridDimensions()[0];
|
|
385
|
+
}, 0);
|
|
303
386
|
}
|
|
304
387
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
388
|
+
class Grid {
|
|
389
|
+
constructor() {
|
|
390
|
+
this.grid = [];
|
|
308
391
|
}
|
|
309
392
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
393
|
+
add(element, position) {
|
|
394
|
+
if (!position) {
|
|
395
|
+
this._addStart(element);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
313
398
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
399
|
+
const [ row, col ] = position;
|
|
400
|
+
if (!row && !col) {
|
|
401
|
+
this._addStart(element);
|
|
402
|
+
}
|
|
317
403
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
404
|
+
if (!this.grid[row]) {
|
|
405
|
+
this.grid[row] = [];
|
|
406
|
+
}
|
|
321
407
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
408
|
+
if (this.grid[row][col]) {
|
|
409
|
+
throw new Error('Grid is occupied please ensure the place you insert at is not occupied');
|
|
410
|
+
}
|
|
325
411
|
|
|
326
|
-
|
|
327
|
-
return { width: 400, height: 100 };
|
|
412
|
+
this.grid[row][col] = element;
|
|
328
413
|
}
|
|
329
414
|
|
|
330
|
-
|
|
331
|
-
|
|
415
|
+
createRow(afterIndex) {
|
|
416
|
+
if (!afterIndex && !Number.isInteger(afterIndex)) {
|
|
417
|
+
this.grid.push([]);
|
|
418
|
+
} else {
|
|
419
|
+
this.grid.splice(afterIndex + 1, 0, []);
|
|
420
|
+
}
|
|
332
421
|
}
|
|
333
422
|
|
|
334
|
-
|
|
335
|
-
|
|
423
|
+
_addStart(element) {
|
|
424
|
+
this.grid.push([ element ]);
|
|
336
425
|
}
|
|
337
426
|
|
|
338
|
-
|
|
339
|
-
|
|
427
|
+
addAfter(element, newElement) {
|
|
428
|
+
if (!element) {
|
|
429
|
+
this._addStart(newElement);
|
|
430
|
+
}
|
|
431
|
+
const [ row, col ] = this.find(element);
|
|
432
|
+
this.grid[row].splice(col + 1, 0, newElement);
|
|
340
433
|
}
|
|
341
434
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return element.$instanceOf(type);
|
|
347
|
-
}
|
|
435
|
+
addBelow(element, newElement) {
|
|
436
|
+
if (!element) {
|
|
437
|
+
this._addStart(newElement);
|
|
438
|
+
}
|
|
348
439
|
|
|
349
|
-
const
|
|
350
|
-
const DEFAULT_CELL_HEIGHT = 140;
|
|
440
|
+
const [ row, col ] = this.find(element);
|
|
351
441
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
};
|
|
357
|
-
}
|
|
442
|
+
// We are at the bottom of the current grid - add empty row below
|
|
443
|
+
if (!this.grid[row + 1]) {
|
|
444
|
+
this.grid[row + 1] = [];
|
|
445
|
+
}
|
|
358
446
|
|
|
359
|
-
|
|
447
|
+
// The element below is already occupied - insert new row
|
|
448
|
+
if (this.grid[row + 1][col]) {
|
|
449
|
+
this.grid.splice(row + 1, 0, []);
|
|
450
|
+
}
|
|
360
451
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r';
|
|
365
|
-
}
|
|
452
|
+
if (this.grid[row + 1][col]) {
|
|
453
|
+
throw new Error('Grid is occupied and we could not find a place - this should not happen');
|
|
454
|
+
}
|
|
366
455
|
|
|
367
|
-
|
|
368
|
-
dockingDirection = /top/.test(targetOrientation) ? 't' : 'b';
|
|
456
|
+
this.grid[row + 1][col] = newElement;
|
|
369
457
|
}
|
|
370
458
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
459
|
+
find(element) {
|
|
460
|
+
let row, col;
|
|
461
|
+
row = this.grid.findIndex((row) => {
|
|
462
|
+
col = row.findIndex((el) => {
|
|
463
|
+
return el === element;
|
|
464
|
+
});
|
|
374
465
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
466
|
+
return col !== -1;
|
|
467
|
+
});
|
|
378
468
|
|
|
379
|
-
|
|
380
|
-
return { original: point, x: point.x, y: rectangle.y + rectangle.height };
|
|
469
|
+
return [ row, col ];
|
|
381
470
|
}
|
|
382
471
|
|
|
383
|
-
|
|
384
|
-
return
|
|
472
|
+
get(row, col) {
|
|
473
|
+
return (this.grid[row] || [])[col];
|
|
385
474
|
}
|
|
386
475
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Modified Manhattan layout: Uses space between grid coloumns to route connections
|
|
392
|
-
* if direct connection is not possible.
|
|
393
|
-
* @param {*} source
|
|
394
|
-
* @param {*} target
|
|
395
|
-
* @returns waypoints
|
|
396
|
-
*/
|
|
397
|
-
function connectElements(source, target, layoutGrid) {
|
|
398
|
-
const sourceDi = source.di;
|
|
399
|
-
const targetDi = target.di;
|
|
476
|
+
getElementsInRange({ row: startRow, col: startCol }, { row: endRow, col: endCol }) {
|
|
477
|
+
const elements = [];
|
|
400
478
|
|
|
401
|
-
|
|
402
|
-
|
|
479
|
+
if (startRow > endRow) {
|
|
480
|
+
[ startRow, endRow ] = [ endRow, startRow ];
|
|
481
|
+
}
|
|
403
482
|
|
|
404
|
-
|
|
405
|
-
|
|
483
|
+
if (startCol > endCol) {
|
|
484
|
+
[ startCol, endCol ] = [ endCol, startCol ];
|
|
485
|
+
}
|
|
406
486
|
|
|
407
|
-
|
|
408
|
-
|
|
487
|
+
for (let row = startRow; row <= endRow; row++) {
|
|
488
|
+
for (let col = startCol; col <= endCol; col++) {
|
|
489
|
+
const element = this.get(row, col);
|
|
409
490
|
|
|
410
|
-
|
|
411
|
-
|
|
491
|
+
if (element) {
|
|
492
|
+
elements.push(element);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
412
496
|
|
|
413
|
-
|
|
414
|
-
if (dX === 0 && dY === 0) {
|
|
415
|
-
const { x, y } = coordinatesToPosition(source.gridPosition.row, source.gridPosition.col);
|
|
416
|
-
return [
|
|
417
|
-
getDockingPoint(sourceMid, sourceBounds, 'r', dockingSource),
|
|
418
|
-
{ x: x + DEFAULT_CELL_WIDTH, y: sourceMid.y },
|
|
419
|
-
{ x: x + DEFAULT_CELL_WIDTH, y: y },
|
|
420
|
-
{ x: targetMid.x, y: y },
|
|
421
|
-
getDockingPoint(targetMid, targetBounds, 't', dockingTarget)
|
|
422
|
-
];
|
|
497
|
+
return elements;
|
|
423
498
|
}
|
|
424
499
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const { y } = coordinatesToPosition(source.gridPosition.row, source.gridPosition.col);
|
|
429
|
-
|
|
430
|
-
const offsetY = DEFAULT_CELL_HEIGHT / 2;
|
|
431
|
-
|
|
432
|
-
if (sourceMid.y >= targetMid.y) {
|
|
500
|
+
adjustGridPosition(element) {
|
|
501
|
+
let [ row, col ] = this.find(element);
|
|
502
|
+
const [ , maxCol ] = this.getGridDimensions();
|
|
433
503
|
|
|
434
|
-
|
|
435
|
-
return [
|
|
436
|
-
getDockingPoint(sourceMid, sourceBounds, 'b'),
|
|
437
|
-
{ x: sourceMid.x, y: y + DEFAULT_CELL_HEIGHT },
|
|
438
|
-
{ x: targetMid.x, y: y + DEFAULT_CELL_HEIGHT },
|
|
439
|
-
getDockingPoint(targetMid, targetBounds, 'b')
|
|
440
|
-
];
|
|
441
|
-
} else {
|
|
504
|
+
if (col < maxCol - 1) {
|
|
442
505
|
|
|
443
|
-
//
|
|
444
|
-
|
|
506
|
+
// add element in next column
|
|
507
|
+
this.grid[row].length = maxCol;
|
|
508
|
+
this.grid[row][maxCol] = element;
|
|
509
|
+
this.grid[row][col] = null;
|
|
445
510
|
|
|
446
|
-
return [
|
|
447
|
-
getDockingPoint(sourceMid, sourceBounds, 't'),
|
|
448
|
-
{ x: sourceMid.x, y: bendY },
|
|
449
|
-
{ x: targetMid.x, y: bendY },
|
|
450
|
-
getDockingPoint(targetMid, targetBounds, 't')
|
|
451
|
-
];
|
|
452
511
|
}
|
|
453
512
|
}
|
|
454
513
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (isDirectPathBlocked(source, target, layoutGrid)) {
|
|
514
|
+
adjustRowForMultipleIncoming(elements, currentElement) {
|
|
515
|
+
const results = elements.map(element => this.find(element));
|
|
458
516
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
{ x: targetMid.x, y: sourceMid.y + DEFAULT_CELL_HEIGHT / 2 },
|
|
464
|
-
getDockingPoint(targetMid, targetBounds, 'b')
|
|
465
|
-
];
|
|
466
|
-
} else {
|
|
517
|
+
// filter only rows that currently exist, excluding any future or non-existent rows
|
|
518
|
+
const lowestRow = Math.min(...results
|
|
519
|
+
.map(result => result[0])
|
|
520
|
+
.filter(row => row >= 0));
|
|
467
521
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
];
|
|
522
|
+
const [ row , col ] = this.find(currentElement);
|
|
523
|
+
|
|
524
|
+
// if element doesn't already exist in current row, add element
|
|
525
|
+
if (lowestRow < row && !this.grid[lowestRow][col]) {
|
|
526
|
+
this.grid[lowestRow][col] = currentElement;
|
|
527
|
+
this.grid[row][col] = null;
|
|
473
528
|
}
|
|
474
529
|
}
|
|
475
530
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (isDirectPathBlocked(source, target, layoutGrid)) {
|
|
531
|
+
adjustColumnForMultipleIncoming(elements, currentElement) {
|
|
532
|
+
const results = elements.map(element => this.find(element));
|
|
479
533
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
{ x: sourceMid.x + DEFAULT_CELL_WIDTH / 2, y: sourceMid.y }, // out right
|
|
485
|
-
{ x: targetMid.x + DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset },
|
|
486
|
-
{ x: targetMid.x, y: targetMid.y + yOffset },
|
|
487
|
-
getDockingPoint(targetMid, targetBounds, Math.sign(yOffset) > 0 ? 'b' : 't')
|
|
488
|
-
];
|
|
489
|
-
} else {
|
|
534
|
+
// filter only col that currently exist, excluding any future or non-existent col
|
|
535
|
+
const maxCol = Math.max(...results
|
|
536
|
+
.map(result => result[1])
|
|
537
|
+
.filter(col => col >= 0));
|
|
490
538
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
539
|
+
const [ row , col ] = this.find(currentElement);
|
|
540
|
+
|
|
541
|
+
// add to the next column
|
|
542
|
+
if (maxCol + 1 > col) {
|
|
543
|
+
this.grid[row][maxCol + 1] = currentElement;
|
|
544
|
+
this.grid[row][col] = null;
|
|
495
545
|
}
|
|
496
546
|
}
|
|
497
547
|
|
|
498
|
-
|
|
548
|
+
getAllElements() {
|
|
549
|
+
const elements = [];
|
|
499
550
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
551
|
+
for (let row = 0; row < this.grid.length; row++) {
|
|
552
|
+
for (let col = 0; col < this.grid[row].length; col++) {
|
|
553
|
+
const element = this.get(row, col);
|
|
503
554
|
|
|
504
|
-
|
|
555
|
+
if (element) {
|
|
556
|
+
elements.push(element);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
505
560
|
|
|
506
|
-
return
|
|
507
|
-
startPoint,
|
|
508
|
-
midPoint,
|
|
509
|
-
endPoint
|
|
510
|
-
];
|
|
561
|
+
return elements;
|
|
511
562
|
}
|
|
512
|
-
const yOffset = -Math.sign(dY) * DEFAULT_CELL_HEIGHT / 2;
|
|
513
563
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
{ x: sourceMid.x + DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset }, // to target row
|
|
518
|
-
{ x: targetMid.x - DEFAULT_CELL_WIDTH / 2, y: targetMid.y + yOffset }, // to target column
|
|
519
|
-
{ x: targetMid.x - DEFAULT_CELL_WIDTH / 2, y: targetMid.y }, // to mid
|
|
520
|
-
getDockingPoint(targetMid, targetBounds, 'l', dockingTarget)
|
|
521
|
-
];
|
|
522
|
-
}
|
|
564
|
+
getGridDimensions() {
|
|
565
|
+
const numRows = this.grid.length;
|
|
566
|
+
let maxCols = 0;
|
|
523
567
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
568
|
+
for (let i = 0; i < numRows; i++) {
|
|
569
|
+
const currentRowLength = this.grid[i].length;
|
|
570
|
+
if (currentRowLength > maxCols) {
|
|
571
|
+
maxCols = currentRowLength;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return [ numRows , maxCols ];
|
|
576
|
+
}
|
|
533
577
|
|
|
534
|
-
|
|
535
|
-
|
|
578
|
+
elementsByPosition() {
|
|
579
|
+
const elements = [];
|
|
536
580
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
581
|
+
this.grid.forEach((row, rowIndex) => {
|
|
582
|
+
row.forEach((element, colIndex) => {
|
|
583
|
+
if (!element) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
elements.push({
|
|
587
|
+
element,
|
|
588
|
+
row: rowIndex,
|
|
589
|
+
col: colIndex
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
return elements;
|
|
544
595
|
}
|
|
545
596
|
|
|
546
|
-
|
|
597
|
+
getElementsTotal() {
|
|
598
|
+
const flattenedGrid = this.grid.flat();
|
|
599
|
+
const uniqueElements = new Set(flattenedGrid.filter(value => value));
|
|
600
|
+
return uniqueElements.size;
|
|
601
|
+
}
|
|
547
602
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
603
|
+
/**
|
|
604
|
+
*
|
|
605
|
+
* @param {number} afterIndex - number is integer
|
|
606
|
+
* @param {number=} colCount - number is positive integer
|
|
607
|
+
*/
|
|
608
|
+
createCol(afterIndex, colCount) {
|
|
609
|
+
this.grid.forEach((row, rowIndex) => {
|
|
610
|
+
this.expandRow(rowIndex, afterIndex, colCount);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
554
613
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
614
|
+
/**
|
|
615
|
+
* @param {number} rowIndex - is positive integer
|
|
616
|
+
* @param {number} afterIndex - is integer
|
|
617
|
+
* @param {number=} colCount - is positive integer
|
|
618
|
+
*/
|
|
619
|
+
expandRow(rowIndex, afterIndex, colCount) {
|
|
620
|
+
if (!Number.isInteger(rowIndex) || rowIndex < 0 || rowIndex > this.rowCount - 1) return;
|
|
558
621
|
|
|
559
|
-
|
|
560
|
-
const dY = targetRow - sourceRow;
|
|
622
|
+
const placeholder = Number.isInteger(colCount) && colCount > 0 ? Array(colCount) : Array(1);
|
|
561
623
|
|
|
562
|
-
|
|
624
|
+
const row = this.grid[rowIndex];
|
|
563
625
|
|
|
564
|
-
|
|
565
|
-
|
|
626
|
+
if (!afterIndex && !Number.isInteger(afterIndex)) {
|
|
627
|
+
row.splice(row.length, 0, ...placeholder);
|
|
628
|
+
} else {
|
|
629
|
+
row.splice(afterIndex + 1, 0, ...placeholder);
|
|
630
|
+
}
|
|
566
631
|
}
|
|
632
|
+
}
|
|
567
633
|
|
|
568
|
-
|
|
569
|
-
|
|
634
|
+
class DiFactory {
|
|
635
|
+
constructor(moddle) {
|
|
636
|
+
this.moddle = moddle;
|
|
570
637
|
}
|
|
571
638
|
|
|
572
|
-
|
|
573
|
-
}
|
|
639
|
+
create(type, attrs) {
|
|
640
|
+
return this.moddle.create(type, attrs || {});
|
|
641
|
+
}
|
|
574
642
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
643
|
+
createDiBounds(bounds) {
|
|
644
|
+
return this.create('dc:Bounds', bounds);
|
|
645
|
+
}
|
|
578
646
|
|
|
579
|
-
|
|
580
|
-
|
|
647
|
+
createDiLabel() {
|
|
648
|
+
return this.create('bpmndi:BPMNLabel', {
|
|
649
|
+
bounds: this.createDiBounds()
|
|
650
|
+
});
|
|
651
|
+
}
|
|
581
652
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
653
|
+
createDiShape(semantic, bounds, attrs) {
|
|
654
|
+
return this.create('bpmndi:BPMNShape', minDash.assign({
|
|
655
|
+
bpmnElement: semantic,
|
|
656
|
+
bounds: this.createDiBounds(bounds)
|
|
657
|
+
}, attrs));
|
|
585
658
|
}
|
|
586
659
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
let totalElements = 0;
|
|
590
|
-
const bendPoint = { row: targetRow, col: sourceCol };
|
|
591
|
-
totalElements += layoutGrid.getElementsInRange({ row: sourceRow, col: sourceCol }, bendPoint).length;
|
|
592
|
-
totalElements += layoutGrid.getElementsInRange(bendPoint, { row: targetRow, col: targetCol }).length;
|
|
660
|
+
createDiWaypoints(waypoints) {
|
|
661
|
+
var self = this;
|
|
593
662
|
|
|
594
|
-
return
|
|
595
|
-
|
|
663
|
+
return minDash.map(waypoints, function(pos) {
|
|
664
|
+
return self.createDiWaypoint(pos);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
596
667
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
668
|
+
createDiWaypoint(point) {
|
|
669
|
+
return this.create('dc:Point', minDash.pick(point, [ 'x', 'y' ]));
|
|
670
|
+
}
|
|
600
671
|
|
|
601
|
-
|
|
602
|
-
|
|
672
|
+
createDiEdge(semantic, waypoints, attrs) {
|
|
673
|
+
return this.create('bpmndi:BPMNEdge', minDash.assign({
|
|
674
|
+
bpmnElement: semantic,
|
|
675
|
+
waypoint: this.createDiWaypoints(waypoints)
|
|
676
|
+
}, attrs));
|
|
677
|
+
}
|
|
603
678
|
|
|
604
|
-
|
|
679
|
+
createDiPlane(attrs) {
|
|
680
|
+
return this.create('bpmndi:BPMNPlane', attrs);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
createDiDiagram(attrs) {
|
|
684
|
+
return this.create('bpmndi:BPMNDiagram', attrs);
|
|
605
685
|
}
|
|
606
686
|
}
|
|
607
687
|
|
|
@@ -629,13 +709,13 @@ var attacherHandler = {
|
|
|
629
709
|
return nextElements;
|
|
630
710
|
},
|
|
631
711
|
|
|
632
|
-
'createElementDi': ({ element, row, col, diFactory }) => {
|
|
633
|
-
const hostBounds = getBounds(element, row, col);
|
|
712
|
+
'createElementDi': ({ element, row, col, diFactory, shift }) => {
|
|
713
|
+
const hostBounds = getBounds(element, row, col, shift);
|
|
634
714
|
|
|
635
715
|
const DIs = [];
|
|
636
716
|
(element.attachers || []).forEach((att, i, arr) => {
|
|
637
717
|
att.gridPosition = { row, col };
|
|
638
|
-
const bounds = getBounds(att, row, col, element);
|
|
718
|
+
const bounds = getBounds(att, row, col, shift, element);
|
|
639
719
|
|
|
640
720
|
// distribute along lower edge
|
|
641
721
|
bounds.x = hostBounds.x + (i + 1) * (hostBounds.width / (arr.length + 1)) - bounds.width / 2;
|
|
@@ -652,7 +732,7 @@ var attacherHandler = {
|
|
|
652
732
|
return DIs;
|
|
653
733
|
},
|
|
654
734
|
|
|
655
|
-
'createConnectionDi': ({ element, row, col, layoutGrid, diFactory }) => {
|
|
735
|
+
'createConnectionDi': ({ element, row, col, layoutGrid, diFactory, shift }) => {
|
|
656
736
|
const attachers = element.attachers || [];
|
|
657
737
|
|
|
658
738
|
return attachers.flatMap(att => {
|
|
@@ -665,11 +745,9 @@ var attacherHandler = {
|
|
|
665
745
|
// Correct waypoints if they don't automatically attach to the bottom
|
|
666
746
|
ensureExitBottom(att, waypoints, [ row, col ]);
|
|
667
747
|
|
|
668
|
-
|
|
748
|
+
return diFactory.createDiEdge(out, waypoints, {
|
|
669
749
|
id: out.id + '_di'
|
|
670
750
|
});
|
|
671
|
-
|
|
672
|
-
return connectionDi;
|
|
673
751
|
});
|
|
674
752
|
});
|
|
675
753
|
}
|
|
@@ -698,12 +776,14 @@ function ensureExitBottom(source, waypoints, [ row, col ]) {
|
|
|
698
776
|
return;
|
|
699
777
|
}
|
|
700
778
|
|
|
779
|
+
const baseSourceGrid = source.grid || source.attachedToRef?.grid;
|
|
780
|
+
|
|
701
781
|
if (waypoints.length === 2) {
|
|
702
782
|
const newStart = [
|
|
703
783
|
dockingPoint,
|
|
704
|
-
{ x: dockingPoint.x, y: (row + 1) * DEFAULT_CELL_HEIGHT },
|
|
705
|
-
{ x: (col + 1) * DEFAULT_CELL_WIDTH, y: (row + 1) * DEFAULT_CELL_HEIGHT },
|
|
706
|
-
{ x: (col + 1) * DEFAULT_CELL_WIDTH, y: (row + 0.5) * DEFAULT_CELL_HEIGHT },
|
|
784
|
+
{ x: dockingPoint.x, y: !baseSourceGrid ? (row + 1) * DEFAULT_CELL_HEIGHT : (row + baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
785
|
+
{ x: !baseSourceGrid ? (col + 1) * DEFAULT_CELL_WIDTH : (col + baseSourceGrid.getGridDimensions()[1] + 1) * DEFAULT_CELL_WIDTH, y: !baseSourceGrid ? (row + 1) * DEFAULT_CELL_HEIGHT : (row + baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
786
|
+
{ x: !baseSourceGrid ? (col + 1) * DEFAULT_CELL_WIDTH : (col + baseSourceGrid.getGridDimensions()[1] + 1) * DEFAULT_CELL_WIDTH, y: !baseSourceGrid ? (row + 0.5) * DEFAULT_CELL_HEIGHT : row * DEFAULT_CELL_HEIGHT + DEFAULT_CELL_HEIGHT / 2 },
|
|
707
787
|
];
|
|
708
788
|
|
|
709
789
|
waypoints.splice(0, 1, ...newStart);
|
|
@@ -713,18 +793,17 @@ function ensureExitBottom(source, waypoints, [ row, col ]) {
|
|
|
713
793
|
// add waypoints to exit bottom and connect to existing path
|
|
714
794
|
const newStart = [
|
|
715
795
|
dockingPoint,
|
|
716
|
-
{ x: dockingPoint.x, y: (row + 1) * DEFAULT_CELL_HEIGHT },
|
|
717
|
-
{ x: waypoints[1].x, y: (row + 1) * DEFAULT_CELL_HEIGHT },
|
|
796
|
+
{ x: dockingPoint.x, y: !baseSourceGrid ? (row + 1) * DEFAULT_CELL_HEIGHT : (row + baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
797
|
+
{ x: waypoints[1].x, y: !baseSourceGrid ? (row + 1) * DEFAULT_CELL_HEIGHT : (row + baseSourceGrid.getGridDimensions()[0] + 1) * DEFAULT_CELL_HEIGHT },
|
|
718
798
|
];
|
|
719
799
|
|
|
720
800
|
waypoints.splice(0, 1, ...newStart);
|
|
721
|
-
return;
|
|
722
801
|
}
|
|
723
802
|
|
|
724
803
|
var elementHandler = {
|
|
725
|
-
'createElementDi': ({ element, row, col, diFactory }) => {
|
|
804
|
+
'createElementDi': ({ element, row, col, diFactory, shift }) => {
|
|
726
805
|
|
|
727
|
-
const bounds = getBounds(element, row, col);
|
|
806
|
+
const bounds = getBounds(element, row, col, shift);
|
|
728
807
|
|
|
729
808
|
const options = {
|
|
730
809
|
id: element.id + '_di'
|
|
@@ -734,6 +813,10 @@ var elementHandler = {
|
|
|
734
813
|
options.isMarkerVisible = true;
|
|
735
814
|
}
|
|
736
815
|
|
|
816
|
+
if (element.isExpanded) {
|
|
817
|
+
options.isExpanded = true;
|
|
818
|
+
}
|
|
819
|
+
|
|
737
820
|
const shapeDi = diFactory.createDiShape(element, bounds, options);
|
|
738
821
|
element.di = shapeDi;
|
|
739
822
|
element.gridPosition = { row, col };
|
|
@@ -865,7 +948,7 @@ const handlers = [ elementHandler, incomingHandler, outgoingHandler, attacherHan
|
|
|
865
948
|
|
|
866
949
|
class Layouter {
|
|
867
950
|
constructor() {
|
|
868
|
-
this.moddle = new
|
|
951
|
+
this.moddle = new bpmnModdle.BpmnModdle();
|
|
869
952
|
this.diFactory = new DiFactory(this.moddle);
|
|
870
953
|
this._handlers = handlers;
|
|
871
954
|
}
|
|
@@ -878,23 +961,76 @@ class Layouter {
|
|
|
878
961
|
}
|
|
879
962
|
|
|
880
963
|
async layoutProcess(xml) {
|
|
881
|
-
const
|
|
964
|
+
const moddleObj = await this.moddle.fromXML(xml);
|
|
965
|
+
const { rootElement } = moddleObj;
|
|
882
966
|
|
|
883
967
|
this.diagram = rootElement;
|
|
884
968
|
|
|
885
|
-
const
|
|
969
|
+
const firstRootProcess = this.getProcess();
|
|
970
|
+
|
|
971
|
+
if (firstRootProcess) {
|
|
972
|
+
|
|
973
|
+
this.setExpandedPropertyToModdleElements(moddleObj);
|
|
974
|
+
|
|
975
|
+
this.setExecutedProcesses(firstRootProcess);
|
|
976
|
+
|
|
977
|
+
this.createGridsForProcesses();
|
|
886
978
|
|
|
887
|
-
if (root) {
|
|
888
979
|
this.cleanDi();
|
|
889
|
-
|
|
980
|
+
|
|
981
|
+
this.createRootDi(firstRootProcess);
|
|
982
|
+
|
|
983
|
+
this.drawProcesses();
|
|
890
984
|
}
|
|
891
985
|
|
|
892
986
|
return (await this.moddle.toXML(this.diagram, { format: true })).xml;
|
|
893
987
|
}
|
|
894
988
|
|
|
895
|
-
|
|
896
|
-
const
|
|
897
|
-
|
|
989
|
+
createGridsForProcesses() {
|
|
990
|
+
const processes = this.layoutedProcesses.sort((a, b) => b.level - a.level);
|
|
991
|
+
|
|
992
|
+
// create and add grids for each process
|
|
993
|
+
// root processes should be processed last for element expanding
|
|
994
|
+
for (const process of processes) {
|
|
995
|
+
|
|
996
|
+
// add base grid with collapsed elements
|
|
997
|
+
process.grid = this.createGridLayout(process);
|
|
998
|
+
|
|
999
|
+
expandGridHorizontally(process.grid);
|
|
1000
|
+
expandGridVertically(process.grid);
|
|
1001
|
+
|
|
1002
|
+
if (process.isExpanded) {
|
|
1003
|
+
const [ rowCount, colCount ] = process.grid.getGridDimensions();
|
|
1004
|
+
if (rowCount === 0) process.grid.createRow();
|
|
1005
|
+
if (colCount === 0) process.grid.createCol();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
setExpandedPropertyToModdleElements(bpmnModel) {
|
|
1012
|
+
const allElements = bpmnModel.elementsById;
|
|
1013
|
+
if (allElements) {
|
|
1014
|
+
for (const element of Object.values(allElements)) {
|
|
1015
|
+
if (element.$type === 'bpmndi:BPMNShape' && element.isExpanded === true) element.bpmnElement.isExpanded = true;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
setExecutedProcesses(firstRootProcess) {
|
|
1021
|
+
this.layoutedProcesses = [];
|
|
1022
|
+
|
|
1023
|
+
const executionStack = [ firstRootProcess ];
|
|
1024
|
+
|
|
1025
|
+
while (executionStack.length > 0) {
|
|
1026
|
+
const executedProcess = executionStack.pop();
|
|
1027
|
+
this.layoutedProcesses.push(executedProcess);
|
|
1028
|
+
executedProcess.level = executedProcess.$parent === this.diagram ? 0 : executedProcess.$parent.level + 1;
|
|
1029
|
+
|
|
1030
|
+
const nextProcesses = executedProcess.flowElements?.filter(flowElement => flowElement.$type === 'bpmn:SubProcess') || [];
|
|
1031
|
+
|
|
1032
|
+
executionStack.splice(executionStack.length, 0, ...nextProcesses);
|
|
1033
|
+
}
|
|
898
1034
|
}
|
|
899
1035
|
|
|
900
1036
|
cleanDi() {
|
|
@@ -912,20 +1048,16 @@ class Layouter {
|
|
|
912
1048
|
return grid;
|
|
913
1049
|
}
|
|
914
1050
|
|
|
915
|
-
|
|
916
|
-
boundaryEvents.forEach(boundaryEvent => {
|
|
917
|
-
const attachedTask = boundaryEvent.attachedToRef;
|
|
918
|
-
const attachers = attachedTask.attachers || [];
|
|
919
|
-
attachers.push(boundaryEvent);
|
|
920
|
-
attachedTask.attachers = attachers;
|
|
921
|
-
});
|
|
1051
|
+
bindBoundaryEventsWithHosts (flowElements);
|
|
922
1052
|
|
|
923
1053
|
// Depth-first-search
|
|
924
1054
|
const visited = new Set();
|
|
925
1055
|
while (visited.size < elements.filter(element => !element.attachedToRef).length) {
|
|
926
|
-
|
|
927
1056
|
const startingElements = flowElements.filter(el => {
|
|
928
|
-
return !isConnection(el) &&
|
|
1057
|
+
return !isConnection(el) &&
|
|
1058
|
+
!isBoundaryEvent(el) &&
|
|
1059
|
+
(!el.incoming || !hasOtherIncoming(el)) &&
|
|
1060
|
+
!visited.has(el);
|
|
929
1061
|
});
|
|
930
1062
|
|
|
931
1063
|
const stack = [ ...startingElements ];
|
|
@@ -937,7 +1069,7 @@ class Layouter {
|
|
|
937
1069
|
|
|
938
1070
|
this.handleGrid(grid,visited,stack);
|
|
939
1071
|
|
|
940
|
-
if (grid.getElementsTotal()
|
|
1072
|
+
if (grid.getElementsTotal() !== elements.length) {
|
|
941
1073
|
const gridElements = grid.getAllElements();
|
|
942
1074
|
const missingElements = elements.filter(el => !gridElements.includes(el) && !isBoundaryEvent(el));
|
|
943
1075
|
if (missingElements.length > 0) {
|
|
@@ -951,30 +1083,17 @@ class Layouter {
|
|
|
951
1083
|
return grid;
|
|
952
1084
|
}
|
|
953
1085
|
|
|
954
|
-
generateDi(
|
|
1086
|
+
generateDi(layoutGrid , shift, procDi) {
|
|
955
1087
|
const diFactory = this.diFactory;
|
|
956
1088
|
|
|
957
|
-
|
|
958
|
-
const diagram = this.diagram;
|
|
959
|
-
|
|
960
|
-
var planeDi = diFactory.createDiPlane({
|
|
961
|
-
id: 'BPMNPlane_' + root.id,
|
|
962
|
-
bpmnElement: root
|
|
963
|
-
});
|
|
964
|
-
var diagramDi = diFactory.createDiDiagram({
|
|
965
|
-
id: 'BPMNDiagram_' + root.id,
|
|
966
|
-
plane: planeDi
|
|
967
|
-
});
|
|
968
|
-
|
|
969
|
-
// deepest subprocess is added first - insert at the front
|
|
970
|
-
diagram.diagrams.unshift(diagramDi);
|
|
1089
|
+
const prePlaneElement = procDi ? procDi : this.diagram.diagrams[0];
|
|
971
1090
|
|
|
972
|
-
const planeElement =
|
|
1091
|
+
const planeElement = prePlaneElement.plane.get('planeElement');
|
|
973
1092
|
|
|
974
1093
|
// Step 1: Create DI for all elements
|
|
975
1094
|
layoutGrid.elementsByPosition().forEach(({ element, row, col }) => {
|
|
976
1095
|
const dis = this
|
|
977
|
-
.handle('createElementDi', { element, row, col, layoutGrid, diFactory })
|
|
1096
|
+
.handle('createElementDi', { element, row, col, layoutGrid, diFactory, shift })
|
|
978
1097
|
.flat();
|
|
979
1098
|
|
|
980
1099
|
planeElement.push(...dis);
|
|
@@ -983,7 +1102,7 @@ class Layouter {
|
|
|
983
1102
|
// Step 2: Create DI for all connections
|
|
984
1103
|
layoutGrid.elementsByPosition().forEach(({ element, row, col }) => {
|
|
985
1104
|
const dis = this
|
|
986
|
-
.handle('createConnectionDi', { element, row, col, layoutGrid, diFactory })
|
|
1105
|
+
.handle('createConnectionDi', { element, row, col, layoutGrid, diFactory, shift })
|
|
987
1106
|
.flat();
|
|
988
1107
|
|
|
989
1108
|
planeElement.push(...dis);
|
|
@@ -994,10 +1113,6 @@ class Layouter {
|
|
|
994
1113
|
while (stack.length > 0) {
|
|
995
1114
|
const currentElement = stack.pop();
|
|
996
1115
|
|
|
997
|
-
if (is(currentElement, 'bpmn:SubProcess')) {
|
|
998
|
-
this.handlePlane(currentElement);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
1116
|
const nextElements = this.handle('addToGrid', { element: currentElement, grid, visited, stack });
|
|
1002
1117
|
|
|
1003
1118
|
nextElements.flat().forEach(el => {
|
|
@@ -1010,6 +1125,146 @@ class Layouter {
|
|
|
1010
1125
|
getProcess() {
|
|
1011
1126
|
return this.diagram.get('rootElements').find(el => el.$type === 'bpmn:Process');
|
|
1012
1127
|
}
|
|
1128
|
+
|
|
1129
|
+
createRootDi(processes) {
|
|
1130
|
+
this.createProcessDi(processes);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
createProcessDi(element) {
|
|
1134
|
+
const diFactory = this.diFactory;
|
|
1135
|
+
|
|
1136
|
+
const planeDi = diFactory.createDiPlane({
|
|
1137
|
+
id: 'BPMNPlane_' + element.id,
|
|
1138
|
+
bpmnElement: element
|
|
1139
|
+
});
|
|
1140
|
+
const diagramDi = diFactory.createDiDiagram({
|
|
1141
|
+
id: 'BPMNDiagram_' + element.id,
|
|
1142
|
+
plane: planeDi
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
const diagram = this.diagram;
|
|
1146
|
+
|
|
1147
|
+
diagram.diagrams.push(diagramDi);
|
|
1148
|
+
|
|
1149
|
+
return diagramDi;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Draw processes.
|
|
1154
|
+
* Root processes should be processed first for element expanding
|
|
1155
|
+
*/
|
|
1156
|
+
drawProcesses() {
|
|
1157
|
+
const sortedProcesses = this.layoutedProcesses.sort((a, b) => a.level - b.level);
|
|
1158
|
+
|
|
1159
|
+
for (const process of sortedProcesses) {
|
|
1160
|
+
|
|
1161
|
+
// draw processes in expanded elements
|
|
1162
|
+
if (process.isExpanded) {
|
|
1163
|
+
const baseProcDi = this.getElementDi(process);
|
|
1164
|
+
const diagram = this.getProcDi(baseProcDi);
|
|
1165
|
+
let { x, y } = baseProcDi.bounds;
|
|
1166
|
+
const { width, height } = getDefaultSize(process);
|
|
1167
|
+
x += DEFAULT_CELL_WIDTH / 2 - width / 4;
|
|
1168
|
+
y += DEFAULT_CELL_HEIGHT - height - height / 4;
|
|
1169
|
+
this.generateDi(process.grid, { x, y }, diagram);
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// draw other processes
|
|
1174
|
+
const diagram = this.diagram.diagrams.find(diagram => diagram.plane.bpmnElement === process);
|
|
1175
|
+
this.generateDi(process.grid, { x: 0, y: 0 }, diagram);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
getElementDi(element) {
|
|
1180
|
+
return this.diagram.diagrams
|
|
1181
|
+
.map(diagram => diagram.plane.planeElement).flat()
|
|
1182
|
+
.find(item => item.bpmnElement === element);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
getProcDi(element) {
|
|
1186
|
+
return this.diagram.diagrams.find(diagram => diagram.plane.planeElement.includes(element));
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
function bindBoundaryEventsWithHosts(elements) {
|
|
1191
|
+
const boundaryEvents = elements.filter(element => isBoundaryEvent(element));
|
|
1192
|
+
boundaryEvents.forEach(boundaryEvent => {
|
|
1193
|
+
const attachedTask = boundaryEvent.attachedToRef;
|
|
1194
|
+
const attachers = attachedTask.attachers || [];
|
|
1195
|
+
attachers.push(boundaryEvent);
|
|
1196
|
+
attachedTask.attachers = attachers;
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Check grid by columns.
|
|
1202
|
+
* If column has elements with isExpanded === true,
|
|
1203
|
+
* find the maximum size of elements grids and expand the parent grid horizontally.
|
|
1204
|
+
* @param grid
|
|
1205
|
+
*/
|
|
1206
|
+
function expandGridHorizontally(grid) {
|
|
1207
|
+
const [ numRows , maxCols ] = grid.getGridDimensions();
|
|
1208
|
+
for (let i = maxCols - 1 ; i >= 0; i--) {
|
|
1209
|
+
const elementsInCol = [];
|
|
1210
|
+
for (let j = 0; j < numRows; j++) {
|
|
1211
|
+
const candidate = grid.get(j, i);
|
|
1212
|
+
if (candidate && candidate.isExpanded) elementsInCol.push(candidate);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
if (elementsInCol.length === 0) continue;
|
|
1216
|
+
|
|
1217
|
+
const maxColCount = elementsInCol.reduce((acc,cur) => {
|
|
1218
|
+
const [ ,curCols ] = cur.grid.getGridDimensions();
|
|
1219
|
+
if (acc === undefined || curCols > acc) return curCols;
|
|
1220
|
+
return acc;
|
|
1221
|
+
}, undefined);
|
|
1222
|
+
|
|
1223
|
+
const shift = !maxColCount ? 2 : maxColCount;
|
|
1224
|
+
grid.createCol(i, shift);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Check grid by rows.
|
|
1230
|
+
* If row has elements with isExpanded === true,
|
|
1231
|
+
* find the maximum size of elements grids and expand the parent grid vertically.
|
|
1232
|
+
* @param grid
|
|
1233
|
+
*/
|
|
1234
|
+
function expandGridVertically(grid) {
|
|
1235
|
+
const [ numRows , maxCols ] = grid.getGridDimensions();
|
|
1236
|
+
|
|
1237
|
+
for (let i = numRows - 1 ; i >= 0; i--) {
|
|
1238
|
+
const elementsInRow = [];
|
|
1239
|
+
for (let j = 0; j < maxCols; j++) {
|
|
1240
|
+
const candidate = grid.get(i, j);
|
|
1241
|
+
if (candidate && candidate.isExpanded) elementsInRow.push(candidate);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (elementsInRow.length === 0) continue;
|
|
1245
|
+
|
|
1246
|
+
const maxRowCount = elementsInRow.reduce((acc,cur) => {
|
|
1247
|
+
const [ curRows ] = cur.grid.getGridDimensions();
|
|
1248
|
+
if (acc === undefined || curRows > acc) return curRows;
|
|
1249
|
+
return acc;
|
|
1250
|
+
}, undefined);
|
|
1251
|
+
|
|
1252
|
+
const shift = !maxRowCount ? 1 : maxRowCount;
|
|
1253
|
+
|
|
1254
|
+
// expand the parent grid vertically
|
|
1255
|
+
for (let index = 0; index < shift; index++) {
|
|
1256
|
+
grid.createRow(i);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
function hasOtherIncoming(element) {
|
|
1262
|
+
const fromHost = element.incoming?.filter(edge => edge.sourceRef !== element && edge.sourceRef.attachedToRef === undefined) || [];
|
|
1263
|
+
|
|
1264
|
+
const fromAttached = element.incoming?.filter(edge => edge.sourceRef !== element
|
|
1265
|
+
&& edge.sourceRef.attachedToRef !== element);
|
|
1266
|
+
|
|
1267
|
+
return fromHost?.length > 0 || fromAttached?.length > 0;
|
|
1013
1268
|
}
|
|
1014
1269
|
|
|
1015
1270
|
function layoutProcess(xml) {
|