@aranzatech/diagrams-bpmn 0.2.13 → 0.2.15
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/{catalog-Di2nzGs9.d.ts → catalog-CK3_4cOb.d.ts} +1 -1
- package/dist/{catalog-BiLXVn-2.d.cts → catalog-xOMF2ifW.d.cts} +1 -1
- package/dist/{chunk-UAWLUDKC.js → chunk-HOWK3ZOO.js} +105 -13
- package/dist/chunk-HOWK3ZOO.js.map +1 -0
- package/dist/{chunk-O3NWJ5H7.js → chunk-QSMP34CT.js} +38 -5
- package/dist/chunk-QSMP34CT.js.map +1 -0
- package/dist/{chunk-IMW6RG6F.js → chunk-X54NHLBA.js} +43 -190
- package/dist/chunk-X54NHLBA.js.map +1 -0
- package/dist/chunk-XMVV7FRZ.js +163 -0
- package/dist/chunk-XMVV7FRZ.js.map +1 -0
- package/dist/edges/index.cjs +35 -2
- package/dist/edges/index.cjs.map +1 -1
- package/dist/edges/index.js +1 -1
- package/dist/elements/index.d.cts +4 -4
- package/dist/elements/index.d.ts +4 -4
- package/dist/elk-FSFIEL6O.js +6 -0
- package/dist/elk-FSFIEL6O.js.map +1 -0
- package/dist/{guards-DPHXfpY8.d.cts → guards-C70uIY_O.d.cts} +1 -1
- package/dist/{guards-qgSeZEU4.d.ts → guards-foB6XIfZ.d.ts} +1 -1
- package/dist/index.cjs +180 -200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/layout/index.cjs +1428 -755
- package/dist/layout/index.cjs.map +1 -1
- package/dist/layout/index.d.cts +23 -12
- package/dist/layout/index.d.ts +23 -12
- package/dist/layout/index.js +547 -72
- package/dist/layout/index.js.map +1 -1
- package/dist/modeling/index.cjs +103 -10
- package/dist/modeling/index.cjs.map +1 -1
- package/dist/modeling/index.d.cts +23 -6
- package/dist/modeling/index.d.ts +23 -6
- package/dist/modeling/index.js +1 -1
- package/dist/nodes/index.cjs +42 -188
- package/dist/nodes/index.cjs.map +1 -1
- package/dist/nodes/index.d.cts +1 -1
- package/dist/nodes/index.d.ts +1 -1
- package/dist/nodes/index.js +1 -1
- package/dist/{types-rEfHsPr5.d.ts → types-DG5yPKld.d.ts} +1 -1
- package/dist/{types-s2_VvPGf.d.cts → types-jIDz306Y.d.cts} +1 -1
- package/dist/{types-Dfrt0wVs.d.cts → types-y-ZbX-ff.d.cts} +3 -0
- package/dist/{types-Dfrt0wVs.d.ts → types-y-ZbX-ff.d.ts} +3 -0
- package/dist/validation/index.d.cts +2 -2
- package/dist/validation/index.d.ts +2 -2
- package/dist/xml/index.d.cts +3 -3
- package/dist/xml/index.d.ts +3 -3
- package/package.json +2 -2
- package/dist/chunk-IMW6RG6F.js.map +0 -1
- package/dist/chunk-O3NWJ5H7.js.map +0 -1
- package/dist/chunk-UAWLUDKC.js.map +0 -1
package/dist/layout/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
export { bpmnElkLayout } from '../chunk-XMVV7FRZ.js';
|
|
2
|
+
import { getBpmnNodeSize } from '../chunk-HOWK3ZOO.js';
|
|
2
3
|
import '../chunk-RLAJNRF2.js';
|
|
3
4
|
import '../chunk-L5Z22RLX.js';
|
|
4
|
-
import {
|
|
5
|
+
import { dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
|
|
5
6
|
export { applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
// src/layout/bpmn-layout-graph.ts
|
|
9
|
+
var LAYOUT_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
8
10
|
"Pool",
|
|
9
11
|
"Lane",
|
|
10
12
|
"SubProcess",
|
|
@@ -12,83 +14,556 @@ var BPMN_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
|
12
14
|
"EventSubProcess",
|
|
13
15
|
"AdHocSubProcess"
|
|
14
16
|
]);
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
var LAYOUT_GATEWAY_TYPES = /* @__PURE__ */ new Set([
|
|
18
|
+
"ExclusiveGateway",
|
|
19
|
+
"InclusiveGateway",
|
|
20
|
+
"ParallelGateway",
|
|
21
|
+
"EventBasedGateway",
|
|
22
|
+
"ComplexGateway"
|
|
23
|
+
]);
|
|
24
|
+
function detectBackEdges(nodeIds, seqEdges) {
|
|
25
|
+
const backEdgeIds = /* @__PURE__ */ new Set();
|
|
26
|
+
const state = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const id of nodeIds) state.set(id, 0);
|
|
28
|
+
const adj = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const id of nodeIds) adj.set(id, []);
|
|
30
|
+
for (const e of seqEdges) {
|
|
31
|
+
adj.get(e.source)?.push({ target: e.target, edgeId: e.id });
|
|
32
|
+
}
|
|
33
|
+
for (const startId of nodeIds) {
|
|
34
|
+
if (state.get(startId) !== 0) continue;
|
|
35
|
+
const stack = [{ id: startId, idx: 0 }];
|
|
36
|
+
state.set(startId, 1);
|
|
37
|
+
while (stack.length > 0) {
|
|
38
|
+
const top = stack[stack.length - 1];
|
|
39
|
+
const neighbors = adj.get(top.id) ?? [];
|
|
40
|
+
if (top.idx >= neighbors.length) {
|
|
41
|
+
state.set(top.id, 2);
|
|
42
|
+
stack.pop();
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const { target, edgeId } = neighbors[top.idx++];
|
|
46
|
+
const tState = state.get(target);
|
|
47
|
+
if (tState === 1) {
|
|
48
|
+
backEdgeIds.add(edgeId);
|
|
49
|
+
} else if (tState === 0) {
|
|
50
|
+
state.set(target, 1);
|
|
51
|
+
stack.push({ id: target, idx: 0 });
|
|
52
|
+
}
|
|
36
53
|
}
|
|
37
54
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
return backEdgeIds;
|
|
56
|
+
}
|
|
57
|
+
function extractSeqEdges(edges, nodeIds) {
|
|
58
|
+
return edges.filter((e) => {
|
|
59
|
+
const t = e.data?.edgeType ?? e.type;
|
|
60
|
+
return t === "sequenceFlow" && nodeIds.has(e.source) && nodeIds.has(e.target);
|
|
61
|
+
}).map((e) => ({ id: e.id, source: e.source, target: e.target, sourceHandle: e.sourceHandle ?? null }));
|
|
62
|
+
}
|
|
63
|
+
function topologicalSort(nodeIds, forwardEdges) {
|
|
64
|
+
const inDeg = new Map(nodeIds.map((id) => [id, 0]));
|
|
65
|
+
const adj = new Map(nodeIds.map((id) => [id, []]));
|
|
66
|
+
for (const e of forwardEdges) {
|
|
67
|
+
if (!inDeg.has(e.source) || !inDeg.has(e.target)) continue;
|
|
68
|
+
adj.get(e.source).push(e.target);
|
|
69
|
+
inDeg.set(e.target, (inDeg.get(e.target) ?? 0) + 1);
|
|
70
|
+
}
|
|
71
|
+
const queue = nodeIds.filter((id) => (inDeg.get(id) ?? 0) === 0);
|
|
72
|
+
const result = [];
|
|
73
|
+
while (queue.length > 0) {
|
|
74
|
+
const id = queue.shift();
|
|
75
|
+
result.push(id);
|
|
76
|
+
for (const next of adj.get(id) ?? []) {
|
|
77
|
+
const d = (inDeg.get(next) ?? 1) - 1;
|
|
78
|
+
inDeg.set(next, d);
|
|
79
|
+
if (d === 0) queue.push(next);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const seen = new Set(result);
|
|
83
|
+
for (const id of nodeIds) {
|
|
84
|
+
if (!seen.has(id)) result.push(id);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function assignColumns(nodeIds, forwardEdges) {
|
|
89
|
+
const order = topologicalSort(nodeIds, forwardEdges);
|
|
90
|
+
const col = new Map(nodeIds.map((id) => [id, 0]));
|
|
91
|
+
const preds = new Map(nodeIds.map((id) => [id, []]));
|
|
92
|
+
for (const e of forwardEdges) {
|
|
93
|
+
preds.get(e.target)?.push(e.source);
|
|
94
|
+
}
|
|
95
|
+
for (const id of order) {
|
|
96
|
+
const predCols = (preds.get(id) ?? []).map((p) => col.get(p) ?? 0);
|
|
97
|
+
col.set(id, predCols.length > 0 ? Math.max(...predCols) + 1 : 0);
|
|
98
|
+
}
|
|
99
|
+
return col;
|
|
100
|
+
}
|
|
101
|
+
function detectGatewayPairs(nodes, forwardEdges) {
|
|
102
|
+
const pairs = /* @__PURE__ */ new Map();
|
|
103
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
104
|
+
const succs = new Map(nodes.map((n) => [n.id, []]));
|
|
105
|
+
for (const e of forwardEdges) {
|
|
106
|
+
succs.get(e.source)?.push(e.target);
|
|
107
|
+
}
|
|
108
|
+
const order = topologicalSort(nodeIds, forwardEdges);
|
|
109
|
+
const topoPos = new Map(order.map((id, i) => [id, i]));
|
|
110
|
+
const splits = nodes.filter(
|
|
111
|
+
(n) => LAYOUT_GATEWAY_TYPES.has(n.data.elementType) && (succs.get(n.id)?.length ?? 0) > 1
|
|
112
|
+
);
|
|
113
|
+
for (const split of splits) {
|
|
114
|
+
const branches = succs.get(split.id) ?? [];
|
|
115
|
+
if (branches.length < 2) continue;
|
|
116
|
+
const branchSets = branches.map((start) => {
|
|
117
|
+
const visited = /* @__PURE__ */ new Set();
|
|
118
|
+
const q = [start];
|
|
119
|
+
while (q.length) {
|
|
120
|
+
const curr = q.shift();
|
|
121
|
+
if (visited.has(curr)) continue;
|
|
122
|
+
visited.add(curr);
|
|
123
|
+
for (const next of succs.get(curr) ?? []) q.push(next);
|
|
124
|
+
}
|
|
125
|
+
return visited;
|
|
126
|
+
});
|
|
127
|
+
const common = [...branchSets[0]].filter(
|
|
128
|
+
(id) => branchSets.every((s) => s.has(id))
|
|
129
|
+
);
|
|
130
|
+
if (common.length === 0) continue;
|
|
131
|
+
const merge = common.reduce(
|
|
132
|
+
(a, b) => (topoPos.get(a) ?? Infinity) < (topoPos.get(b) ?? Infinity) ? a : b
|
|
133
|
+
);
|
|
134
|
+
pairs.set(split.id, merge);
|
|
135
|
+
}
|
|
136
|
+
return pairs;
|
|
137
|
+
}
|
|
138
|
+
function handleToRowBias(handle) {
|
|
139
|
+
if (!handle) return null;
|
|
140
|
+
if (handle.includes("top")) return -1;
|
|
141
|
+
if (handle.includes("bottom")) return 1;
|
|
142
|
+
if (handle.includes("right")) return 0;
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
function buildHandleMap(edges) {
|
|
146
|
+
const map = /* @__PURE__ */ new Map();
|
|
147
|
+
for (const e of edges) {
|
|
148
|
+
if (!map.has(e.source)) map.set(e.source, /* @__PURE__ */ new Map());
|
|
149
|
+
map.get(e.source).set(e.target, e.sourceHandle ?? null);
|
|
150
|
+
}
|
|
151
|
+
return map;
|
|
152
|
+
}
|
|
153
|
+
function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
|
|
154
|
+
const rows = new Map(nodes.map((n) => [n.id, 0]));
|
|
155
|
+
const succs = new Map(nodes.map((n) => [n.id, []]));
|
|
156
|
+
for (const e of forwardEdges) {
|
|
157
|
+
succs.get(e.source)?.push(e.target);
|
|
158
|
+
}
|
|
159
|
+
const handleMap = buildHandleMap(forwardEdges);
|
|
160
|
+
const sortedPairs = [...gatewayPairs.entries()].sort(
|
|
161
|
+
(a, b) => (columns.get(b[0]) ?? 0) - (columns.get(a[0]) ?? 0)
|
|
162
|
+
);
|
|
163
|
+
for (const [splitId, mergeId] of sortedPairs) {
|
|
164
|
+
const branches = succs.get(splitId) ?? [];
|
|
165
|
+
if (branches.length < 2) continue;
|
|
166
|
+
const splitRow = rows.get(splitId) ?? 0;
|
|
167
|
+
const getBranchNodes = (branchStart) => {
|
|
168
|
+
const result = [];
|
|
169
|
+
const visited = /* @__PURE__ */ new Set();
|
|
170
|
+
const q = [branchStart];
|
|
171
|
+
while (q.length) {
|
|
172
|
+
const curr = q.shift();
|
|
173
|
+
if (visited.has(curr) || curr === mergeId) continue;
|
|
174
|
+
visited.add(curr);
|
|
175
|
+
result.push(curr);
|
|
176
|
+
for (const next of succs.get(curr) ?? []) q.push(next);
|
|
70
177
|
}
|
|
178
|
+
return result;
|
|
179
|
+
};
|
|
180
|
+
const splitHandles = handleMap.get(splitId);
|
|
181
|
+
const branchData = branches.map((start) => ({
|
|
182
|
+
start,
|
|
183
|
+
nodeIds: getBranchNodes(start),
|
|
184
|
+
bias: handleToRowBias(splitHandles?.get(start) ?? null)
|
|
185
|
+
}));
|
|
186
|
+
const withBias = branchData.filter((b) => b.bias !== null && b.bias !== 0);
|
|
187
|
+
const withoutBias = branchData.filter((b) => b.bias === null || b.bias === 0).sort((a, b) => b.nodeIds.length - a.nodeIds.length);
|
|
188
|
+
const assigned = /* @__PURE__ */ new Map();
|
|
189
|
+
for (const b of withBias) {
|
|
190
|
+
assigned.set(b.start, splitRow + b.bias);
|
|
71
191
|
}
|
|
192
|
+
let nextOffset = 0;
|
|
193
|
+
for (const b of withoutBias) {
|
|
194
|
+
while ([...assigned.values()].includes(splitRow + nextOffset)) nextOffset++;
|
|
195
|
+
assigned.set(b.start, splitRow + nextOffset);
|
|
196
|
+
nextOffset++;
|
|
197
|
+
}
|
|
198
|
+
for (const b of branchData) {
|
|
199
|
+
const row = assigned.get(b.start) ?? splitRow;
|
|
200
|
+
for (const id of b.nodeIds) {
|
|
201
|
+
rows.set(id, row);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const pairedSplits = new Set(gatewayPairs.keys());
|
|
206
|
+
const unpairedSplits = nodes.filter(
|
|
207
|
+
(n) => LAYOUT_GATEWAY_TYPES.has(n.data.elementType) && !pairedSplits.has(n.id) && (succs.get(n.id)?.length ?? 0) > 1
|
|
72
208
|
);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
209
|
+
for (const split of unpairedSplits) {
|
|
210
|
+
const branches = succs.get(split.id) ?? [];
|
|
211
|
+
if (branches.length < 2) continue;
|
|
212
|
+
const splitRow = rows.get(split.id) ?? 0;
|
|
213
|
+
const getAllReachable = (start) => {
|
|
214
|
+
const result = [];
|
|
215
|
+
const visited = /* @__PURE__ */ new Set();
|
|
216
|
+
const q = [start];
|
|
217
|
+
while (q.length) {
|
|
218
|
+
const curr = q.shift();
|
|
219
|
+
if (visited.has(curr)) continue;
|
|
220
|
+
visited.add(curr);
|
|
221
|
+
result.push(curr);
|
|
222
|
+
for (const next of succs.get(curr) ?? []) q.push(next);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
};
|
|
226
|
+
const splitHandles2 = handleMap.get(split.id);
|
|
227
|
+
const branchData = branches.map((start) => ({
|
|
228
|
+
start,
|
|
229
|
+
nodeIds: getAllReachable(start),
|
|
230
|
+
bias: handleToRowBias(splitHandles2?.get(start) ?? null)
|
|
231
|
+
}));
|
|
232
|
+
const withBias2 = branchData.filter((b) => b.bias !== null && b.bias !== 0);
|
|
233
|
+
const withoutBias2 = branchData.filter((b) => b.bias === null || b.bias === 0).sort((a, b) => b.nodeIds.length - a.nodeIds.length);
|
|
234
|
+
const assigned2 = /* @__PURE__ */ new Map();
|
|
235
|
+
for (const b of withBias2) assigned2.set(b.start, splitRow + b.bias);
|
|
236
|
+
let nextOff = 0;
|
|
237
|
+
for (const b of withoutBias2) {
|
|
238
|
+
while ([...assigned2.values()].includes(splitRow + nextOff)) nextOff++;
|
|
239
|
+
assigned2.set(b.start, splitRow + nextOff);
|
|
240
|
+
nextOff++;
|
|
241
|
+
}
|
|
242
|
+
for (const b of branchData) {
|
|
243
|
+
const row = assigned2.get(b.start) ?? splitRow;
|
|
244
|
+
for (const id of b.nodeIds) rows.set(id, row);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return rows;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/layout/bpmn-custom-layout.ts
|
|
251
|
+
var LANE_LABEL_W = 28;
|
|
252
|
+
var LANE_H_PAD = 20;
|
|
253
|
+
var COL_GAP = 80;
|
|
254
|
+
var ROW_HEIGHT = 80;
|
|
255
|
+
var ROW_GAP = 60;
|
|
256
|
+
var LANE_V_PAD = 50;
|
|
257
|
+
var POOL_H_PAD = 60;
|
|
258
|
+
var POOL_V_GAP = 50;
|
|
259
|
+
var LANE_MIN_H = 160;
|
|
260
|
+
var POOL_MIN_W = 720;
|
|
261
|
+
var POOL_INNER_PAD = 8;
|
|
262
|
+
function nW(node) {
|
|
263
|
+
return node.width ?? node.measured?.width ?? 120;
|
|
264
|
+
}
|
|
265
|
+
function nH(node) {
|
|
266
|
+
return node.height ?? node.measured?.height ?? 60;
|
|
267
|
+
}
|
|
268
|
+
function layoutPool(pool, lanes, content, allEdges) {
|
|
269
|
+
if (content.length === 0) {
|
|
270
|
+
if (lanes.length === 0) {
|
|
271
|
+
return { nodes: [], width: 240, height: 120 };
|
|
272
|
+
}
|
|
273
|
+
const laneH = LANE_MIN_H;
|
|
274
|
+
const h = POOL_INNER_PAD * 2 + lanes.length * laneH + Math.max(0, lanes.length - 1) * POOL_INNER_PAD;
|
|
275
|
+
const w = POOL_MIN_W;
|
|
276
|
+
return {
|
|
277
|
+
nodes: lanes.map((lane, i) => ({
|
|
278
|
+
...lane,
|
|
279
|
+
position: { x: POOL_INNER_PAD, y: POOL_INNER_PAD + i * (laneH + POOL_INNER_PAD) },
|
|
280
|
+
width: w - POOL_INNER_PAD * 2,
|
|
281
|
+
height: laneH
|
|
282
|
+
})),
|
|
283
|
+
width: w,
|
|
284
|
+
height: h
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const contentIds = new Set(content.map((n) => n.id));
|
|
288
|
+
const seqEdges = extractSeqEdges(allEdges, contentIds);
|
|
289
|
+
const backIds = detectBackEdges([...contentIds], seqEdges);
|
|
290
|
+
const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
|
|
291
|
+
const columns = assignColumns([...contentIds], fwdEdges);
|
|
292
|
+
const pairs = detectGatewayPairs(content, fwdEdges);
|
|
293
|
+
const rows = assignRows(content, fwdEdges, columns, pairs);
|
|
294
|
+
const lanePositionsDistinct = lanes.some((l) => Math.abs(l.position.y) > 10);
|
|
295
|
+
const sortedLanes = lanePositionsDistinct ? [...lanes].sort((a, b) => a.position.y - b.position.y) : [...lanes];
|
|
296
|
+
const hasLanes = sortedLanes.length > 0;
|
|
297
|
+
const nodeLaneId = /* @__PURE__ */ new Map();
|
|
298
|
+
for (const node of content) {
|
|
299
|
+
if (hasLanes && node.parentId && node.parentId !== pool.id) {
|
|
300
|
+
nodeLaneId.set(node.id, node.parentId);
|
|
301
|
+
} else {
|
|
302
|
+
nodeLaneId.set(node.id, "_pool_");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const laneIds = hasLanes ? sortedLanes.map((l) => l.id) : ["_pool_"];
|
|
306
|
+
const laneStats = /* @__PURE__ */ new Map();
|
|
307
|
+
for (const laneId of laneIds) {
|
|
308
|
+
const laneRows = content.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
|
|
309
|
+
if (laneRows.length === 0) {
|
|
310
|
+
laneStats.set(laneId, { minRow: 0, maxRow: 0, rowCount: 1, height: LANE_MIN_H });
|
|
311
|
+
} else {
|
|
312
|
+
const minRow = Math.min(...laneRows);
|
|
313
|
+
const maxRow = Math.max(...laneRows);
|
|
314
|
+
const rowCount = maxRow - minRow + 1;
|
|
315
|
+
const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
|
|
316
|
+
const height = Math.max(LANE_MIN_H, LANE_V_PAD * 2 + contentH);
|
|
317
|
+
laneStats.set(laneId, { minRow, maxRow, rowCount, height });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const maxCol = Math.max(0, ...[...columns.values()]);
|
|
321
|
+
const colW = /* @__PURE__ */ new Map();
|
|
322
|
+
for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
|
|
323
|
+
for (const node of content) {
|
|
324
|
+
const c = columns.get(node.id) ?? 0;
|
|
325
|
+
colW.set(c, Math.max(colW.get(c) ?? 0, nW(node)));
|
|
326
|
+
}
|
|
327
|
+
const colX = /* @__PURE__ */ new Map();
|
|
328
|
+
let cumX = 0;
|
|
329
|
+
for (let c = 0; c <= maxCol; c++) {
|
|
330
|
+
colX.set(c, cumX);
|
|
331
|
+
cumX += (colW.get(c) ?? 120) + COL_GAP;
|
|
332
|
+
}
|
|
333
|
+
const contentW = cumX - COL_GAP;
|
|
334
|
+
const innerW = LANE_LABEL_W + LANE_H_PAD + contentW + LANE_H_PAD;
|
|
335
|
+
const poolW = Math.max(POOL_MIN_W, innerW + POOL_INNER_PAD * 2);
|
|
336
|
+
const laneW = poolW - POOL_INNER_PAD * 2;
|
|
337
|
+
const laneY = /* @__PURE__ */ new Map();
|
|
338
|
+
let cumY = POOL_INNER_PAD;
|
|
339
|
+
for (let i = 0; i < laneIds.length; i++) {
|
|
340
|
+
const laneId = laneIds[i];
|
|
341
|
+
laneY.set(laneId, cumY);
|
|
342
|
+
cumY += laneStats.get(laneId)?.height ?? LANE_MIN_H;
|
|
343
|
+
if (i < laneIds.length - 1) cumY += POOL_INNER_PAD;
|
|
344
|
+
}
|
|
345
|
+
const poolH = cumY + POOL_INNER_PAD;
|
|
346
|
+
const positionedContent = content.map((node) => {
|
|
347
|
+
const c = columns.get(node.id) ?? 0;
|
|
348
|
+
const r = rows.get(node.id) ?? 0;
|
|
349
|
+
const laneId = nodeLaneId.get(node.id) ?? "_pool_";
|
|
350
|
+
const stat = laneStats.get(laneId);
|
|
351
|
+
const lYOff = laneY.get(laneId) ?? 0;
|
|
352
|
+
const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - nW(node) / 2;
|
|
353
|
+
const rowOffset = (r - stat.minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - nH(node) / 2;
|
|
354
|
+
const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
|
|
355
|
+
const contentH_stat = stat.rowCount * ROW_HEIGHT + Math.max(0, stat.rowCount - 1) * ROW_GAP;
|
|
356
|
+
const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 2);
|
|
357
|
+
const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
|
|
358
|
+
const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
|
|
359
|
+
return { ...node, position: { x, y } };
|
|
79
360
|
});
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
361
|
+
const NODE_MIN_GAP = 20;
|
|
362
|
+
const resolvedContent = [...positionedContent];
|
|
363
|
+
for (const laneId of laneIds) {
|
|
364
|
+
const laneNodeIndices = resolvedContent.map((n, i) => ({ n, i })).filter(({ n }) => (nodeLaneId.get(n.id) ?? "_pool_") === laneId).sort((a, b) => a.n.position.x - b.n.position.x);
|
|
365
|
+
for (let k = 1; k < laneNodeIndices.length; k++) {
|
|
366
|
+
const prev = resolvedContent[laneNodeIndices[k - 1].i];
|
|
367
|
+
const curr = resolvedContent[laneNodeIndices[k].i];
|
|
368
|
+
const prevBottom = prev.position.y + nH(prev);
|
|
369
|
+
const currTop = curr.position.y;
|
|
370
|
+
const prevTop = prev.position.y;
|
|
371
|
+
const currBottom = curr.position.y + nH(curr);
|
|
372
|
+
const yOverlap = prevBottom + NODE_MIN_GAP > currTop && currBottom + NODE_MIN_GAP > prevTop;
|
|
373
|
+
if (!yOverlap) continue;
|
|
374
|
+
const prevRight = prev.position.x + nW(prev);
|
|
375
|
+
if (prevRight + NODE_MIN_GAP > curr.position.x) {
|
|
376
|
+
resolvedContent[laneNodeIndices[k].i] = {
|
|
377
|
+
...curr,
|
|
378
|
+
position: {
|
|
379
|
+
x: prevRight + NODE_MIN_GAP,
|
|
380
|
+
y: curr.position.y
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const positionedLanes = hasLanes ? sortedLanes.map((lane) => ({
|
|
387
|
+
...lane,
|
|
388
|
+
// x: after pool label strip + left inner padding
|
|
389
|
+
// y: laneY already includes top POOL_INNER_PAD offset
|
|
390
|
+
position: { x: POOL_INNER_PAD, y: laneY.get(lane.id) ?? POOL_INNER_PAD },
|
|
391
|
+
width: laneW,
|
|
392
|
+
height: laneStats.get(lane.id)?.height ?? LANE_MIN_H
|
|
393
|
+
})) : [];
|
|
394
|
+
return {
|
|
395
|
+
nodes: [...resolvedContent, ...positionedLanes],
|
|
396
|
+
width: poolW,
|
|
397
|
+
height: poolH
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
var BACK_EDGE_CLEARANCE = 50;
|
|
401
|
+
var SAME_ROW_THRESHOLD = 15;
|
|
402
|
+
function absolutePos(nodeId, byId, cache) {
|
|
403
|
+
const cached = cache.get(nodeId);
|
|
404
|
+
if (cached) return cached;
|
|
405
|
+
const node = byId.get(nodeId);
|
|
406
|
+
if (!node) return { x: 0, y: 0 };
|
|
407
|
+
const own = { ...node.position };
|
|
408
|
+
if (!node.parentId) {
|
|
409
|
+
cache.set(nodeId, own);
|
|
410
|
+
return own;
|
|
411
|
+
}
|
|
412
|
+
const parent = absolutePos(node.parentId, byId, cache);
|
|
413
|
+
const abs = { x: parent.x + own.x, y: parent.y + own.y };
|
|
414
|
+
cache.set(nodeId, abs);
|
|
415
|
+
return abs;
|
|
416
|
+
}
|
|
417
|
+
function laneOf(node, laneIds) {
|
|
418
|
+
return node.parentId && laneIds.has(node.parentId) ? node.parentId : void 0;
|
|
419
|
+
}
|
|
420
|
+
function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
|
|
421
|
+
const byId = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
422
|
+
const cache = /* @__PURE__ */ new Map();
|
|
423
|
+
const abs = (id) => absolutePos(id, byId, cache);
|
|
424
|
+
return edges.map((edge) => {
|
|
425
|
+
const edgeType = edge.data?.edgeType ?? edge.type;
|
|
426
|
+
if (edgeType !== "sequenceFlow") {
|
|
427
|
+
const d = { ...edge.data };
|
|
428
|
+
delete d.routingPoints;
|
|
429
|
+
return { ...edge, data: d };
|
|
430
|
+
}
|
|
431
|
+
const src = byId.get(edge.source);
|
|
432
|
+
const tgt = byId.get(edge.target);
|
|
433
|
+
if (!src || !tgt) return edge;
|
|
434
|
+
const sAbs = abs(src.id);
|
|
435
|
+
const tAbs = abs(tgt.id);
|
|
436
|
+
const sW = nW(src), sH = nH(src);
|
|
437
|
+
const tW = nW(tgt), tH = nH(tgt);
|
|
438
|
+
const sCX = sAbs.x + sW / 2, sCY = sAbs.y + sH / 2;
|
|
439
|
+
const tCX = tAbs.x + tW / 2, tCY = tAbs.y + tH / 2;
|
|
440
|
+
const srcPool = poolIds.has(src.parentId ?? "") ? src.parentId : poolIds.has(byId.get(src.parentId ?? "")?.parentId ?? "") ? byId.get(src.parentId ?? "")?.parentId : src.parentId;
|
|
441
|
+
const tgtPool = poolIds.has(tgt.parentId ?? "") ? tgt.parentId : poolIds.has(byId.get(tgt.parentId ?? "")?.parentId ?? "") ? byId.get(tgt.parentId ?? "")?.parentId : tgt.parentId;
|
|
442
|
+
if (srcPool !== tgtPool) {
|
|
443
|
+
const d = { ...edge.data };
|
|
444
|
+
delete d.routingPoints;
|
|
445
|
+
return { ...edge, data: d };
|
|
446
|
+
}
|
|
447
|
+
let routingPoints;
|
|
448
|
+
if (backEdgeIds.has(edge.id)) {
|
|
449
|
+
const topY = Math.min(sAbs.y, tAbs.y) - BACK_EDGE_CLEARANCE;
|
|
450
|
+
routingPoints = [
|
|
451
|
+
{ x: sCX, y: sAbs.y },
|
|
452
|
+
// [0] discarded
|
|
453
|
+
{ x: sCX, y: topY },
|
|
454
|
+
// [1] go up
|
|
455
|
+
{ x: tCX, y: topY },
|
|
456
|
+
// [2] go left/right
|
|
457
|
+
{ x: tCX, y: tAbs.y }
|
|
458
|
+
// [3] discarded
|
|
459
|
+
];
|
|
460
|
+
} else if (laneOf(src, laneIds) !== laneOf(tgt, laneIds)) {
|
|
461
|
+
const goingDown = tAbs.y > sAbs.y;
|
|
462
|
+
const sharedX = Math.abs(sCX - tCX) < 10 ? sCX : (sCX + tCX) / 2;
|
|
463
|
+
const srcLane = byId.get(src.parentId ?? "");
|
|
464
|
+
const tgtLane = byId.get(tgt.parentId ?? "");
|
|
465
|
+
if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
|
|
466
|
+
const srcLaneAbs = abs(srcLane.id);
|
|
467
|
+
const borderY = goingDown ? srcLaneAbs.y + (srcLane.height ?? 160) : srcLaneAbs.y;
|
|
468
|
+
routingPoints = [
|
|
469
|
+
{ x: sCX, y: goingDown ? sAbs.y + sH : sAbs.y },
|
|
470
|
+
// [0] discarded
|
|
471
|
+
{ x: sharedX, y: goingDown ? sAbs.y + sH : sAbs.y },
|
|
472
|
+
// [1] bend: horizontal exit
|
|
473
|
+
{ x: sharedX, y: borderY },
|
|
474
|
+
// [2] bend: lane border
|
|
475
|
+
{ x: sharedX, y: goingDown ? tAbs.y : tAbs.y + tH }
|
|
476
|
+
// [3] discarded
|
|
477
|
+
];
|
|
478
|
+
} else {
|
|
479
|
+
routingPoints = [
|
|
480
|
+
{ x: sCX, y: sCY },
|
|
481
|
+
{ x: sharedX, y: sCY },
|
|
482
|
+
{ x: sharedX, y: tCY },
|
|
483
|
+
{ x: tCX, y: tCY }
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
} else if (Math.abs(sCY - tCY) <= SAME_ROW_THRESHOLD) {
|
|
487
|
+
const d = { ...edge.data };
|
|
488
|
+
delete d.routingPoints;
|
|
489
|
+
return { ...edge, data: d };
|
|
86
490
|
} else {
|
|
87
|
-
|
|
491
|
+
const handle = edge.sourceHandle ?? "";
|
|
492
|
+
const exitsTop = handle.includes("top");
|
|
493
|
+
const exitsBottom = handle.includes("bottom");
|
|
494
|
+
if (exitsTop || exitsBottom) {
|
|
495
|
+
routingPoints = [
|
|
496
|
+
{ x: sCX, y: exitsTop ? sAbs.y : sAbs.y + sH },
|
|
497
|
+
// [0] discarded
|
|
498
|
+
{ x: sCX, y: tCY },
|
|
499
|
+
// [1] vertical to target row
|
|
500
|
+
{ x: tCX, y: tCY },
|
|
501
|
+
// [2] horizontal to target
|
|
502
|
+
{ x: tCX, y: exitsTop ? tAbs.y + tH : tAbs.y }
|
|
503
|
+
// [3] discarded
|
|
504
|
+
];
|
|
505
|
+
} else {
|
|
506
|
+
const goingRight = tAbs.x >= sAbs.x;
|
|
507
|
+
const midX = goingRight ? sAbs.x + sW + COL_GAP / 2 : sAbs.x - COL_GAP / 2;
|
|
508
|
+
routingPoints = [
|
|
509
|
+
{ x: sAbs.x + sW, y: sCY },
|
|
510
|
+
// [0] discarded
|
|
511
|
+
{ x: midX, y: sCY },
|
|
512
|
+
// [1] horizontal exit
|
|
513
|
+
{ x: midX, y: tCY },
|
|
514
|
+
// [2] vertical to target row
|
|
515
|
+
{ x: tAbs.x, y: tCY }
|
|
516
|
+
// [3] discarded
|
|
517
|
+
];
|
|
518
|
+
}
|
|
88
519
|
}
|
|
89
|
-
return { ...edge, data:
|
|
520
|
+
return { ...edge, data: { ...edge.data, routingPoints } };
|
|
90
521
|
});
|
|
91
|
-
|
|
522
|
+
}
|
|
523
|
+
async function bpmnCustomLayout(nodes, edges) {
|
|
524
|
+
const pools = nodes.filter((n) => n.data.elementType === "Pool");
|
|
525
|
+
const lanes = nodes.filter((n) => n.data.elementType === "Lane");
|
|
526
|
+
const content = nodes.filter((n) => !LAYOUT_CONTAINER_TYPES.has(n.data.elementType));
|
|
527
|
+
if (pools.length === 0) {
|
|
528
|
+
const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-FSFIEL6O.js');
|
|
529
|
+
return bpmnElkLayout2(nodes, edges);
|
|
530
|
+
}
|
|
531
|
+
const poolIds = new Set(pools.map((p) => p.id));
|
|
532
|
+
const allLaneIds = new Set(lanes.map((l) => l.id));
|
|
533
|
+
const allContentIds = new Set(content.map((n) => n.id));
|
|
534
|
+
const allSeqEdges = edges.filter((e) => {
|
|
535
|
+
const t = e.data?.edgeType ?? e.type;
|
|
536
|
+
return t === "sequenceFlow" && allContentIds.has(e.source) && allContentIds.has(e.target);
|
|
537
|
+
}).map((e) => ({ id: e.id, source: e.source, target: e.target }));
|
|
538
|
+
const allBackEdgeIds = detectBackEdges([...allContentIds], allSeqEdges);
|
|
539
|
+
const poolPositionsDistinct = pools.some((p) => Math.abs(p.position.y) > 10);
|
|
540
|
+
const sortedPools = poolPositionsDistinct ? [...pools].sort((a, b) => a.position.y - b.position.y) : [...pools];
|
|
541
|
+
const resultNodes = [];
|
|
542
|
+
let stackY = 0;
|
|
543
|
+
for (const pool of sortedPools) {
|
|
544
|
+
const poolLanes = lanes.filter((l) => l.parentId === pool.id);
|
|
545
|
+
const laneIds = new Set(poolLanes.map((l) => l.id));
|
|
546
|
+
const poolContent = content.filter(
|
|
547
|
+
(n) => n.parentId === pool.id || n.parentId != null && laneIds.has(n.parentId)
|
|
548
|
+
);
|
|
549
|
+
const result = layoutPool(pool, poolLanes, poolContent, edges);
|
|
550
|
+
resultNodes.push({
|
|
551
|
+
...pool,
|
|
552
|
+
position: { x: 0, y: stackY },
|
|
553
|
+
width: result.width,
|
|
554
|
+
height: result.height
|
|
555
|
+
});
|
|
556
|
+
for (const node of result.nodes) {
|
|
557
|
+
resultNodes.push(node);
|
|
558
|
+
}
|
|
559
|
+
stackY += result.height + POOL_V_GAP;
|
|
560
|
+
}
|
|
561
|
+
const layoutted = new Set(resultNodes.map((n) => n.id));
|
|
562
|
+
for (const node of nodes) {
|
|
563
|
+
if (!layoutted.has(node.id)) resultNodes.push(node);
|
|
564
|
+
}
|
|
565
|
+
const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
|
|
566
|
+
return { nodes: resultNodes, edges: routedEdges };
|
|
92
567
|
}
|
|
93
568
|
|
|
94
569
|
// src/layout/index.ts
|
|
@@ -102,6 +577,6 @@ function applyBpmnLayoutResult(state, result) {
|
|
|
102
577
|
return applyLayoutResultToDiagram(state, result);
|
|
103
578
|
}
|
|
104
579
|
|
|
105
|
-
export { applyBpmnLayoutResult,
|
|
580
|
+
export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout };
|
|
106
581
|
//# sourceMappingURL=index.js.map
|
|
107
582
|
//# sourceMappingURL=index.js.map
|