@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.
Files changed (52) hide show
  1. package/dist/{catalog-Di2nzGs9.d.ts → catalog-CK3_4cOb.d.ts} +1 -1
  2. package/dist/{catalog-BiLXVn-2.d.cts → catalog-xOMF2ifW.d.cts} +1 -1
  3. package/dist/{chunk-UAWLUDKC.js → chunk-HOWK3ZOO.js} +105 -13
  4. package/dist/chunk-HOWK3ZOO.js.map +1 -0
  5. package/dist/{chunk-O3NWJ5H7.js → chunk-QSMP34CT.js} +38 -5
  6. package/dist/chunk-QSMP34CT.js.map +1 -0
  7. package/dist/{chunk-IMW6RG6F.js → chunk-X54NHLBA.js} +43 -190
  8. package/dist/chunk-X54NHLBA.js.map +1 -0
  9. package/dist/chunk-XMVV7FRZ.js +163 -0
  10. package/dist/chunk-XMVV7FRZ.js.map +1 -0
  11. package/dist/edges/index.cjs +35 -2
  12. package/dist/edges/index.cjs.map +1 -1
  13. package/dist/edges/index.js +1 -1
  14. package/dist/elements/index.d.cts +4 -4
  15. package/dist/elements/index.d.ts +4 -4
  16. package/dist/elk-FSFIEL6O.js +6 -0
  17. package/dist/elk-FSFIEL6O.js.map +1 -0
  18. package/dist/{guards-DPHXfpY8.d.cts → guards-C70uIY_O.d.cts} +1 -1
  19. package/dist/{guards-qgSeZEU4.d.ts → guards-foB6XIfZ.d.ts} +1 -1
  20. package/dist/index.cjs +180 -200
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +5 -5
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.js +3 -3
  25. package/dist/layout/index.cjs +1428 -755
  26. package/dist/layout/index.cjs.map +1 -1
  27. package/dist/layout/index.d.cts +23 -12
  28. package/dist/layout/index.d.ts +23 -12
  29. package/dist/layout/index.js +547 -72
  30. package/dist/layout/index.js.map +1 -1
  31. package/dist/modeling/index.cjs +103 -10
  32. package/dist/modeling/index.cjs.map +1 -1
  33. package/dist/modeling/index.d.cts +23 -6
  34. package/dist/modeling/index.d.ts +23 -6
  35. package/dist/modeling/index.js +1 -1
  36. package/dist/nodes/index.cjs +42 -188
  37. package/dist/nodes/index.cjs.map +1 -1
  38. package/dist/nodes/index.d.cts +1 -1
  39. package/dist/nodes/index.d.ts +1 -1
  40. package/dist/nodes/index.js +1 -1
  41. package/dist/{types-rEfHsPr5.d.ts → types-DG5yPKld.d.ts} +1 -1
  42. package/dist/{types-s2_VvPGf.d.cts → types-jIDz306Y.d.cts} +1 -1
  43. package/dist/{types-Dfrt0wVs.d.cts → types-y-ZbX-ff.d.cts} +3 -0
  44. package/dist/{types-Dfrt0wVs.d.ts → types-y-ZbX-ff.d.ts} +3 -0
  45. package/dist/validation/index.d.cts +2 -2
  46. package/dist/validation/index.d.ts +2 -2
  47. package/dist/xml/index.d.cts +3 -3
  48. package/dist/xml/index.d.ts +3 -3
  49. package/package.json +2 -2
  50. package/dist/chunk-IMW6RG6F.js.map +0 -1
  51. package/dist/chunk-O3NWJ5H7.js.map +0 -1
  52. package/dist/chunk-UAWLUDKC.js.map +0 -1
@@ -1,10 +1,12 @@
1
- import { getBpmnNodeSize } from '../chunk-UAWLUDKC.js';
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 { elkLayout, dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
5
+ import { dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
5
6
  export { applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
6
7
 
7
- var BPMN_CONTAINER_TYPES = /* @__PURE__ */ new Set([
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 CONTAINER_MIN_SIZE = {
16
- Pool: { w: 560, h: 160 },
17
- Lane: { w: 480, h: 100 },
18
- SubProcess: { w: 200, h: 120 },
19
- Transaction: { w: 200, h: 120 },
20
- EventSubProcess: { w: 200, h: 120 },
21
- AdHocSubProcess: { w: 200, h: 120 }
22
- };
23
- var CONTAINER_PADDING = {
24
- Pool: "[top=30,left=55,bottom=30,right=40]",
25
- Lane: "[top=25,left=60,bottom=25,right=40]",
26
- SubProcess: "[top=25,left=35,bottom=25,right=35]",
27
- Transaction: "[top=25,left=35,bottom=25,right=35]",
28
- EventSubProcess: "[top=25,left=35,bottom=25,right=35]",
29
- AdHocSubProcess: "[top=25,left=35,bottom=25,right=35]"
30
- };
31
- async function bpmnElkLayout(nodes, edges) {
32
- const poolsWithLanes = /* @__PURE__ */ new Set();
33
- for (const node of nodes) {
34
- if (node.data.elementType === "Lane" && node.parentId) {
35
- poolsWithLanes.add(node.parentId);
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
- const result = await elkLayout(
39
- nodes,
40
- edges,
41
- {
42
- direction: "LR",
43
- getNodeSize: (node) => {
44
- const bNode = node;
45
- if (BPMN_CONTAINER_TYPES.has(bNode.data.elementType)) return void 0;
46
- return getBpmnNodeSize(bNode);
47
- },
48
- isContainerNode: (node) => BPMN_CONTAINER_TYPES.has(node.data.elementType),
49
- getNodeLayoutOptions: (node) => {
50
- const type = node.data.elementType;
51
- if (!BPMN_CONTAINER_TYPES.has(type)) return void 0;
52
- const min = CONTAINER_MIN_SIZE[type];
53
- const dir = type === "Pool" && poolsWithLanes.has(node.id) ? "DOWN" : "RIGHT";
54
- return {
55
- "elk.direction": dir,
56
- "elk.nodeSize.constraints": "MINIMUM_SIZE",
57
- ...min ? { "elk.nodeSize.minimum": `[w=${min.w},h=${min.h}]` } : {},
58
- ...CONTAINER_PADDING[type] ? { "elk.padding": CONTAINER_PADDING[type] } : {}
59
- };
60
- },
61
- elk: {
62
- "elk.algorithm": "layered",
63
- "elk.direction": "RIGHT",
64
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
65
- "elk.edgeRouting": "ORTHOGONAL",
66
- "elk.layered.spacing.nodeNodeBetweenLayers": "100",
67
- "elk.layered.spacing.edgeNodeBetweenLayers": "40",
68
- "elk.spacing.nodeNode": "60",
69
- "elk.spacing.componentComponent": "80"
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 nodeMap = new Map(result.nodes.map((n) => [n.id, n]));
74
- const edgeMap = new Map(result.edges.map((e) => [e.id, e]));
75
- const updatedNodes = nodes.map((node) => {
76
- const laid = nodeMap.get(node.id);
77
- if (!laid) return node;
78
- return { ...node, position: laid.position, width: laid.width, height: laid.height };
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 updatedEdges = edges.map((edge) => {
81
- const laid = edgeMap.get(edge.id);
82
- if (!laid) return edge;
83
- const nextData = { ...edge.data };
84
- if (laid.points && laid.points.length > 0) {
85
- nextData.routingPoints = laid.points;
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
- delete nextData.routingPoints;
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: nextData };
520
+ return { ...edge, data: { ...edge.data, routingPoints } };
90
521
  });
91
- return { nodes: updatedNodes, edges: updatedEdges };
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, bpmnDagreLayout, bpmnElkLayout };
580
+ export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout };
106
581
  //# sourceMappingURL=index.js.map
107
582
  //# sourceMappingURL=index.js.map