@aranzatech/diagrams-bpmn 0.3.0 → 0.3.2

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 (36) hide show
  1. package/dist/{catalog-DAGDhO-D.d.cts → catalog-DG-sz0VM.d.cts} +1 -1
  2. package/dist/{catalog-Q1QmKLDD.d.ts → catalog-DNIyjHbl.d.ts} +1 -1
  3. package/dist/{chunk-334WN4JZ.js → chunk-NYIYQUGX.js} +104 -23
  4. package/dist/chunk-NYIYQUGX.js.map +1 -0
  5. package/dist/elements/index.d.cts +3 -3
  6. package/dist/elements/index.d.ts +3 -3
  7. package/dist/extensions/index.d.cts +2 -2
  8. package/dist/extensions/index.d.ts +2 -2
  9. package/dist/index.cjs +102 -21
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +4 -4
  12. package/dist/index.d.ts +4 -4
  13. package/dist/index.js +1 -1
  14. package/dist/layout/index.cjs +392 -95
  15. package/dist/layout/index.cjs.map +1 -1
  16. package/dist/layout/index.d.cts +4 -4
  17. package/dist/layout/index.d.ts +4 -4
  18. package/dist/layout/index.js +392 -95
  19. package/dist/layout/index.js.map +1 -1
  20. package/dist/modeling/index.d.cts +4 -4
  21. package/dist/modeling/index.d.ts +4 -4
  22. package/dist/{types-CggktCqr.d.cts → types-CDp9kWQ4.d.cts} +2 -2
  23. package/dist/{types-DmDODKlh.d.ts → types-CuDL2YGL.d.ts} +2 -2
  24. package/dist/{types-D7zel9dq.d.ts → types-X5FyP8oS.d.ts} +1 -1
  25. package/dist/{types-BX_o95GC.d.cts → types-dQUuSnV5.d.cts} +1 -1
  26. package/dist/{types-BYN4Zuee.d.cts → types-nvF59RGF.d.cts} +12 -0
  27. package/dist/{types-BYN4Zuee.d.ts → types-nvF59RGF.d.ts} +12 -0
  28. package/dist/validation/index.d.cts +3 -3
  29. package/dist/validation/index.d.ts +3 -3
  30. package/dist/xml/index.cjs +102 -21
  31. package/dist/xml/index.cjs.map +1 -1
  32. package/dist/xml/index.d.cts +4 -4
  33. package/dist/xml/index.d.ts +4 -4
  34. package/dist/xml/index.js +1 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-334WN4JZ.js.map +0 -1
@@ -249,63 +249,221 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
249
249
 
250
250
  // src/layout/bpmn-custom-layout.ts
251
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;
252
+ var LANE_H_PAD = 32;
253
+ var COL_GAP = 60;
254
+ var ROW_HEIGHT = 90;
255
+ var ROW_GAP = 50;
256
+ var LANE_V_PAD = 48;
257
+ var POOL_H_PAD = 64;
258
+ var POOL_V_GAP = 48;
259
+ var LANE_MIN_H = 180;
260
260
  var POOL_MIN_W = 720;
261
- var POOL_INNER_PAD = 8;
261
+ var POOL_INNER_PAD = 10;
262
+ var BACK_EDGE_CLEARANCE = 56;
263
+ var LAYOUT_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
264
+ "DataObject",
265
+ "DataObjectReference",
266
+ "DataInput",
267
+ "DataOutput",
268
+ "DataStore",
269
+ "DataStoreReference",
270
+ "Annotation",
271
+ "Group"
272
+ ]);
273
+ var COLLAPSED_SUBPROCESS_TYPES = /* @__PURE__ */ new Set([
274
+ "SubProcess",
275
+ "Transaction",
276
+ "EventSubProcess",
277
+ "AdHocSubProcess"
278
+ ]);
279
+ var LAYOUT_TASK_TYPES = /* @__PURE__ */ new Set([
280
+ "Task",
281
+ "UserTask",
282
+ "ServiceTask",
283
+ "ScriptTask",
284
+ "ManualTask",
285
+ "BusinessRuleTask",
286
+ "ReceiveTask",
287
+ "SendTask",
288
+ "CallActivity"
289
+ ]);
290
+ var TASK_LAYOUT_MIN_W = 130;
291
+ var TASK_LAYOUT_MIN_H = 80;
262
292
  function nW(node) {
263
293
  return node.width ?? node.measured?.width ?? 120;
264
294
  }
265
295
  function nH(node) {
266
296
  return node.height ?? node.measured?.height ?? 60;
267
297
  }
298
+ function layoutW(node) {
299
+ return LAYOUT_TASK_TYPES.has(node.data.elementType) ? Math.max(nW(node), TASK_LAYOUT_MIN_W) : nW(node);
300
+ }
301
+ function layoutH(node) {
302
+ return LAYOUT_TASK_TYPES.has(node.data.elementType) ? Math.max(nH(node), TASK_LAYOUT_MIN_H) : nH(node);
303
+ }
304
+ function applyLayoutMinSize(node) {
305
+ if (!LAYOUT_TASK_TYPES.has(node.data.elementType)) return node;
306
+ const w = layoutW(node);
307
+ const h = layoutH(node);
308
+ if (w === nW(node) && h === nH(node)) return node;
309
+ return { ...node, width: w, height: h, measured: { width: w, height: h } };
310
+ }
311
+ var BOUNDARY_SPACING = 10;
312
+ function repositionBoundaryEvents(boundaryEvents, positionedContent) {
313
+ if (boundaryEvents.length === 0) return [];
314
+ const hostById = new Map(positionedContent.map((n) => [n.id, n]));
315
+ const byHost = /* @__PURE__ */ new Map();
316
+ for (const be of boundaryEvents) {
317
+ const hostId = be.data.attachedToRef;
318
+ if (!hostId) continue;
319
+ if (!byHost.has(hostId)) byHost.set(hostId, []);
320
+ byHost.get(hostId).push(be);
321
+ }
322
+ const result = [];
323
+ for (const be of boundaryEvents) {
324
+ const hostId = be.data.attachedToRef;
325
+ if (!hostId) {
326
+ result.push(be);
327
+ continue;
328
+ }
329
+ const host = hostById.get(hostId);
330
+ if (!host) {
331
+ result.push(be);
332
+ continue;
333
+ }
334
+ const hostGroup = byHost.get(hostId);
335
+ const siblingIdx = hostGroup.findIndex((n) => n.id === be.id);
336
+ const hostW = layoutW(host);
337
+ const hostH = layoutH(host);
338
+ const beH = nH(be);
339
+ const totalGroupW = hostGroup.reduce((s, b) => s + nW(b), 0) + (hostGroup.length - 1) * BOUNDARY_SPACING;
340
+ const groupStartX = host.position.x + hostW / 2 - totalGroupW / 2;
341
+ const offsetX = hostGroup.slice(0, siblingIdx).reduce((s, b) => s + nW(b) + BOUNDARY_SPACING, 0);
342
+ result.push({
343
+ ...be,
344
+ position: {
345
+ x: groupStartX + offsetX,
346
+ y: host.position.y + hostH - beH / 2
347
+ }
348
+ });
349
+ }
350
+ return result;
351
+ }
352
+ var SP_PAD = 48;
353
+ function layoutSubProcess(children, edges) {
354
+ if (children.length === 0) {
355
+ return { children: [], width: 280, height: 160 };
356
+ }
357
+ const boundaryEvents = children.filter((n) => n.data.elementType === "BoundaryEvent");
358
+ const mainChildren = children.filter((n) => n.data.elementType !== "BoundaryEvent");
359
+ if (mainChildren.length === 0) {
360
+ return {
361
+ children: repositionBoundaryEvents(boundaryEvents, []),
362
+ width: 280,
363
+ height: 160
364
+ };
365
+ }
366
+ const contentIds = new Set(mainChildren.map((n) => n.id));
367
+ const seqEdges = extractSeqEdges(edges, contentIds);
368
+ const backIds = detectBackEdges([...contentIds], seqEdges);
369
+ const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
370
+ const columns = assignColumns([...contentIds], fwdEdges);
371
+ const pairs = detectGatewayPairs(mainChildren, fwdEdges);
372
+ const rows = assignRows(mainChildren, fwdEdges, columns, pairs);
373
+ const maxCol = Math.max(0, ...[...columns.values()]);
374
+ const colW = /* @__PURE__ */ new Map();
375
+ for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
376
+ for (const node of mainChildren) {
377
+ const c = columns.get(node.id) ?? 0;
378
+ colW.set(c, Math.max(colW.get(c) ?? 0, layoutW(node)));
379
+ }
380
+ const colX = /* @__PURE__ */ new Map();
381
+ let cumX = 0;
382
+ for (let c = 0; c <= maxCol; c++) {
383
+ colX.set(c, cumX);
384
+ cumX += (colW.get(c) ?? 120) + COL_GAP;
385
+ }
386
+ const contentW = cumX - COL_GAP;
387
+ const rowVals = [...rows.values()];
388
+ const minRow = Math.min(0, ...rowVals);
389
+ const maxRow = Math.max(0, ...rowVals);
390
+ const rowCount = maxRow - minRow + 1;
391
+ const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
392
+ const spW = Math.max(280, SP_PAD * 2 + contentW);
393
+ const spH = Math.max(160, SP_PAD * 2 + contentH);
394
+ const positionedChildren = mainChildren.map((node) => {
395
+ const c = columns.get(node.id) ?? 0;
396
+ const r = rows.get(node.id) ?? 0;
397
+ const lw = layoutW(node);
398
+ const lh = layoutH(node);
399
+ const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - lw / 2;
400
+ const rowOffset = (r - minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - lh / 2;
401
+ return applyLayoutMinSize({ ...node, position: { x: SP_PAD + colOffset, y: SP_PAD + rowOffset } });
402
+ });
403
+ const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, positionedChildren);
404
+ return {
405
+ children: [...positionedChildren, ...positionedBoundaries],
406
+ width: spW,
407
+ height: spH
408
+ };
409
+ }
268
410
  function layoutPool(pool, lanes, content, allEdges) {
269
- if (content.length === 0) {
411
+ const boundaryEvents = content.filter((n) => n.data.elementType === "BoundaryEvent");
412
+ const mainContent = content.filter((n) => n.data.elementType !== "BoundaryEvent");
413
+ if (mainContent.length === 0) {
414
+ const positionedBoundaries2 = repositionBoundaryEvents(boundaryEvents, []);
270
415
  if (lanes.length === 0) {
271
- return { nodes: [], width: 240, height: 120 };
416
+ return { nodes: positionedBoundaries2, width: 300, height: 140 };
272
417
  }
273
418
  const laneH = LANE_MIN_H;
274
419
  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
420
  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,
421
+ nodes: [
422
+ ...positionedBoundaries2,
423
+ ...lanes.map((lane, i) => ({
424
+ ...lane,
425
+ position: { x: POOL_INNER_PAD, y: POOL_INNER_PAD + i * (laneH + POOL_INNER_PAD) },
426
+ width: POOL_MIN_W - POOL_INNER_PAD * 2,
427
+ height: laneH
428
+ }))
429
+ ],
430
+ width: POOL_MIN_W,
284
431
  height: h
285
432
  };
286
433
  }
287
- const contentIds = new Set(content.map((n) => n.id));
434
+ const sortedLanes = [...lanes].sort((a, b) => a.position.y - b.position.y);
435
+ const hasLanes = sortedLanes.length > 0;
436
+ const laneIdSet = new Set(sortedLanes.map((l) => l.id));
437
+ const nodeLaneId = /* @__PURE__ */ new Map();
438
+ for (const node of mainContent) {
439
+ const lId = hasLanes && node.parentId && laneIdSet.has(node.parentId) ? node.parentId : "_pool_";
440
+ nodeLaneId.set(node.id, lId);
441
+ }
442
+ const contentIds = new Set(mainContent.map((n) => n.id));
288
443
  const seqEdges = extractSeqEdges(allEdges, contentIds);
289
444
  const backIds = detectBackEdges([...contentIds], seqEdges);
290
445
  const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
291
446
  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
- }
447
+ const rows = /* @__PURE__ */ new Map();
448
+ const contentByLane = /* @__PURE__ */ new Map();
449
+ for (const node of mainContent) {
450
+ const lId = nodeLaneId.get(node.id) ?? "_pool_";
451
+ if (!contentByLane.has(lId)) contentByLane.set(lId, []);
452
+ contentByLane.get(lId).push(node);
453
+ }
454
+ for (const [, laneNodes] of contentByLane) {
455
+ const laneNodeIds = new Set(laneNodes.map((n) => n.id));
456
+ const intraEdges = fwdEdges.filter(
457
+ (e) => laneNodeIds.has(e.source) && laneNodeIds.has(e.target)
458
+ );
459
+ const lanePairs = detectGatewayPairs(laneNodes, intraEdges);
460
+ const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs);
461
+ for (const [id, row] of laneRows) rows.set(id, row);
304
462
  }
305
463
  const laneIds = hasLanes ? sortedLanes.map((l) => l.id) : ["_pool_"];
306
464
  const laneStats = /* @__PURE__ */ new Map();
307
465
  for (const laneId of laneIds) {
308
- const laneRows = content.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
466
+ const laneRows = mainContent.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
309
467
  if (laneRows.length === 0) {
310
468
  laneStats.set(laneId, { minRow: 0, maxRow: 0, rowCount: 1, height: LANE_MIN_H });
311
469
  } else {
@@ -320,9 +478,9 @@ function layoutPool(pool, lanes, content, allEdges) {
320
478
  const maxCol = Math.max(0, ...[...columns.values()]);
321
479
  const colW = /* @__PURE__ */ new Map();
322
480
  for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
323
- for (const node of content) {
481
+ for (const node of mainContent) {
324
482
  const c = columns.get(node.id) ?? 0;
325
- colW.set(c, Math.max(colW.get(c) ?? 0, nW(node)));
483
+ colW.set(c, Math.max(colW.get(c) ?? 0, layoutW(node)));
326
484
  }
327
485
  const colX = /* @__PURE__ */ new Map();
328
486
  let cumX = 0;
@@ -343,62 +501,58 @@ function layoutPool(pool, lanes, content, allEdges) {
343
501
  if (i < laneIds.length - 1) cumY += POOL_INNER_PAD;
344
502
  }
345
503
  const poolH = cumY + POOL_INNER_PAD;
346
- const positionedContent = content.map((node) => {
504
+ const positionedContent = mainContent.map((node) => {
347
505
  const c = columns.get(node.id) ?? 0;
348
506
  const r = rows.get(node.id) ?? 0;
349
507
  const laneId = nodeLaneId.get(node.id) ?? "_pool_";
350
508
  const stat = laneStats.get(laneId);
351
509
  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;
510
+ const lw = layoutW(node);
511
+ const lh = layoutH(node);
512
+ const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - lw / 2;
513
+ const rowOffset = (r - stat.minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - lh / 2;
355
514
  const contentH_stat = stat.rowCount * ROW_HEIGHT + Math.max(0, stat.rowCount - 1) * ROW_GAP;
356
515
  const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 2);
516
+ const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
357
517
  const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
358
518
  const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
359
- return { ...node, position: { x, y } };
519
+ return applyLayoutMinSize({ ...node, position: { x, y } });
360
520
  });
361
- const NODE_MIN_GAP = 20;
521
+ const NODE_MIN_GAP = 24;
362
522
  const resolvedContent = [...positionedContent];
363
523
  for (const laneId of laneIds) {
364
524
  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
525
  for (let k = 1; k < laneNodeIndices.length; k++) {
366
526
  const prev = resolvedContent[laneNodeIndices[k - 1].i];
367
527
  const curr = resolvedContent[laneNodeIndices[k].i];
368
- const prevBottom = prev.position.y + nH(prev);
528
+ const prevBottom = prev.position.y + layoutH(prev);
369
529
  const currTop = curr.position.y;
370
530
  const prevTop = prev.position.y;
371
- const currBottom = curr.position.y + nH(curr);
531
+ const currBottom = curr.position.y + layoutH(curr);
372
532
  const yOverlap = prevBottom + NODE_MIN_GAP > currTop && currBottom + NODE_MIN_GAP > prevTop;
373
533
  if (!yOverlap) continue;
374
- const prevRight = prev.position.x + nW(prev);
534
+ const prevRight = prev.position.x + layoutW(prev);
375
535
  if (prevRight + NODE_MIN_GAP > curr.position.x) {
376
536
  resolvedContent[laneNodeIndices[k].i] = {
377
537
  ...curr,
378
- position: {
379
- x: prevRight + NODE_MIN_GAP,
380
- y: curr.position.y
381
- }
538
+ position: { x: prevRight + NODE_MIN_GAP, y: curr.position.y }
382
539
  };
383
540
  }
384
541
  }
385
542
  }
543
+ const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, resolvedContent);
386
544
  const positionedLanes = hasLanes ? sortedLanes.map((lane) => ({
387
545
  ...lane,
388
- // x: after pool label strip + left inner padding
389
- // y: laneY already includes top POOL_INNER_PAD offset
390
546
  position: { x: POOL_INNER_PAD, y: laneY.get(lane.id) ?? POOL_INNER_PAD },
391
547
  width: laneW,
392
548
  height: laneStats.get(lane.id)?.height ?? LANE_MIN_H
393
549
  })) : [];
394
550
  return {
395
- nodes: [...resolvedContent, ...positionedLanes],
551
+ nodes: [...resolvedContent, ...positionedBoundaries, ...positionedLanes],
396
552
  width: poolW,
397
553
  height: poolH
398
554
  };
399
555
  }
400
- var BACK_EDGE_CLEARANCE = 50;
401
- var SAME_ROW_THRESHOLD = 15;
402
556
  function absolutePos(nodeId, byId, cache) {
403
557
  const cached = cache.get(nodeId);
404
558
  if (cached) return cached;
@@ -417,10 +571,22 @@ function absolutePos(nodeId, byId, cache) {
417
571
  function laneOf(node, laneIds) {
418
572
  return node.parentId && laneIds.has(node.parentId) ? node.parentId : void 0;
419
573
  }
574
+ function gapMidX(sAbs, sW, tAbs, tW) {
575
+ const leftRightEdge = Math.min(sAbs.x + sW, tAbs.x + tW);
576
+ const rightLeftEdge = Math.max(sAbs.x, tAbs.x);
577
+ return leftRightEdge < rightLeftEdge ? (leftRightEdge + rightLeftEdge) / 2 : sAbs.x + sW / 2;
578
+ }
579
+ function routeMidX(sAbs, sW, tAbs, tW) {
580
+ const leftEdge = Math.min(sAbs.x + sW, tAbs.x + tW);
581
+ const rightEdge = Math.max(sAbs.x, tAbs.x);
582
+ return leftEdge < rightEdge ? leftEdge + (rightEdge - leftEdge) * 0.35 : sAbs.x + sW / 2;
583
+ }
584
+ var SAME_ROW_THRESHOLD = 15;
420
585
  function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
421
586
  const byId = new Map(layoutNodes.map((n) => [n.id, n]));
422
587
  const cache = /* @__PURE__ */ new Map();
423
588
  const abs = (id) => absolutePos(id, byId, cache);
589
+ const sortedLanes = [...laneIds].map((id) => byId.get(id)).filter((n) => !!n).sort((a, b) => abs(a.id).y - abs(b.id).y);
424
590
  return edges.map((edge) => {
425
591
  const edgeType = edge.data?.edgeType ?? edge.type;
426
592
  if (edgeType !== "sequenceFlow") {
@@ -435,11 +601,22 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
435
601
  const tAbs = abs(tgt.id);
436
602
  const sW = nW(src), sH = nH(src);
437
603
  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) {
604
+ const sCX = sAbs.x + sW / 2;
605
+ const tCX = tAbs.x + tW / 2;
606
+ const sCY = sAbs.y + sH / 2;
607
+ const tCY = tAbs.y + tH / 2;
608
+ const getPool = (nodeId) => {
609
+ const node = byId.get(nodeId);
610
+ if (!node) return null;
611
+ if (poolIds.has(nodeId)) return nodeId;
612
+ if (node.parentId && poolIds.has(node.parentId)) return node.parentId;
613
+ if (node.parentId) {
614
+ const gp = byId.get(node.parentId)?.parentId ?? null;
615
+ if (gp && poolIds.has(gp)) return gp;
616
+ }
617
+ return node.parentId ?? null;
618
+ };
619
+ if (getPool(edge.source) !== getPool(edge.target)) {
443
620
  const d = { ...edge.data };
444
621
  delete d.routingPoints;
445
622
  return { ...edge, data: d };
@@ -449,33 +626,49 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
449
626
  const topY = Math.min(sAbs.y, tAbs.y) - BACK_EDGE_CLEARANCE;
450
627
  routingPoints = [
451
628
  { x: sCX, y: sAbs.y },
452
- // [0] discarded
453
629
  { x: sCX, y: topY },
454
- // [1] go up
455
630
  { x: tCX, y: topY },
456
- // [2] go left/right
457
- { x: tCX, y: tAbs.y }
458
- // [3] discarded
631
+ { x: tCX, y: tAbs.y + tH }
459
632
  ];
460
633
  } 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
634
  const srcLane = byId.get(src.parentId ?? "");
464
635
  const tgtLane = byId.get(tgt.parentId ?? "");
465
636
  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
- ];
637
+ const goingDown = tAbs.y > sAbs.y;
638
+ const sharedX = gapMidX(sAbs, sW, tAbs, tW);
639
+ const srcLaneIdx = sortedLanes.findIndex((l) => l.id === srcLane.id);
640
+ const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
641
+ const pts = [];
642
+ if (goingDown) {
643
+ pts.push({ x: sCX, y: sAbs.y + sH });
644
+ pts.push({ x: sharedX, y: sAbs.y + sH });
645
+ const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
646
+ const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
647
+ for (let i = fromIdx; i < toIdx; i++) {
648
+ const lane = sortedLanes[i];
649
+ const laneBot = abs(lane.id).y + (lane.height ?? LANE_MIN_H);
650
+ pts.push({ x: sharedX, y: laneBot });
651
+ }
652
+ const lastY = pts[pts.length - 1].y;
653
+ pts.push({ x: tCX, y: lastY });
654
+ pts.push({ x: tCX, y: tAbs.y + tH });
655
+ } else {
656
+ pts.push({ x: sCX, y: sAbs.y });
657
+ pts.push({ x: sharedX, y: sAbs.y });
658
+ const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
659
+ const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
660
+ for (let i = toIdx; i > fromIdx; i--) {
661
+ const lane = sortedLanes[i];
662
+ const laneTop = abs(lane.id).y;
663
+ pts.push({ x: sharedX, y: laneTop });
664
+ }
665
+ const lastY = pts[pts.length - 1].y;
666
+ pts.push({ x: tCX, y: lastY });
667
+ pts.push({ x: tCX, y: tAbs.y });
668
+ }
669
+ routingPoints = pts;
478
670
  } else {
671
+ const sharedX = gapMidX(sAbs, sW, tAbs, tW);
479
672
  routingPoints = [
480
673
  { x: sCX, y: sCY },
481
674
  { x: sharedX, y: sCY },
@@ -494,39 +687,108 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
494
687
  if (exitsTop || exitsBottom) {
495
688
  routingPoints = [
496
689
  { x: sCX, y: exitsTop ? sAbs.y : sAbs.y + sH },
497
- // [0] discarded
498
690
  { x: sCX, y: tCY },
499
- // [1] vertical to target row
500
691
  { x: tCX, y: tCY },
501
- // [2] horizontal to target
502
692
  { x: tCX, y: exitsTop ? tAbs.y + tH : tAbs.y }
503
- // [3] discarded
504
693
  ];
505
694
  } else {
506
- const goingRight = tAbs.x >= sAbs.x;
507
- const midX = goingRight ? sAbs.x + sW + COL_GAP / 2 : sAbs.x - COL_GAP / 2;
695
+ const midX = routeMidX(sAbs, sW, tAbs, tW);
508
696
  routingPoints = [
509
697
  { x: sAbs.x + sW, y: sCY },
510
- // [0] discarded
511
698
  { x: midX, y: sCY },
512
- // [1] horizontal exit
513
699
  { x: midX, y: tCY },
514
- // [2] vertical to target row
515
700
  { x: tAbs.x, y: tCY }
516
- // [3] discarded
517
701
  ];
518
702
  }
519
703
  }
520
704
  return { ...edge, data: { ...edge.data, routingPoints } };
521
705
  });
522
706
  }
707
+ var ARTIFACT_ABOVE_GAP = 16;
708
+ var ARTIFACT_H_SPACING = 12;
709
+ function positionArtifacts(artifacts, resultNodes, edges) {
710
+ if (artifacts.length === 0) return [];
711
+ const byId = new Map(resultNodes.map((n) => [n.id, n]));
712
+ const cache = /* @__PURE__ */ new Map();
713
+ const absPos = (id) => absolutePos(id, byId, cache);
714
+ const artifactsByNode = /* @__PURE__ */ new Map();
715
+ const ungrouped = [];
716
+ for (const artifact of artifacts) {
717
+ if (artifact.data.elementType === "Group") {
718
+ ungrouped.push(artifact);
719
+ continue;
720
+ }
721
+ const connEdge = edges.find((e) => {
722
+ const t = e.data?.edgeType ?? e.type;
723
+ return (t === "association" || t === "dataAssociation") && (e.source === artifact.id || e.target === artifact.id);
724
+ });
725
+ if (!connEdge) {
726
+ ungrouped.push(artifact);
727
+ continue;
728
+ }
729
+ const connId = connEdge.source === artifact.id ? connEdge.target : connEdge.source;
730
+ if (!byId.has(connId)) {
731
+ ungrouped.push(artifact);
732
+ continue;
733
+ }
734
+ if (!artifactsByNode.has(connId)) artifactsByNode.set(connId, []);
735
+ artifactsByNode.get(connId).push(artifact);
736
+ }
737
+ const positioned = [...ungrouped];
738
+ for (const [connId, arts] of artifactsByNode) {
739
+ const connNode = byId.get(connId);
740
+ const connAbsP = absPos(connId);
741
+ const totalW = arts.reduce((s, a) => s + nW(a), 0) + (arts.length - 1) * ARTIFACT_H_SPACING;
742
+ const desiredAbsY = connAbsP.y - ARTIFACT_ABOVE_GAP - Math.max(...arts.map(nH));
743
+ let desiredAbsX = connAbsP.x + nW(connNode) / 2 - totalW / 2;
744
+ for (const artifact of arts) {
745
+ const parentAbsP = artifact.parentId ? absPos(artifact.parentId) : { x: 0, y: 0 };
746
+ positioned.push({
747
+ ...artifact,
748
+ position: {
749
+ x: desiredAbsX - parentAbsP.x,
750
+ y: desiredAbsY - parentAbsP.y
751
+ }
752
+ });
753
+ desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
754
+ }
755
+ }
756
+ return positioned;
757
+ }
523
758
  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));
759
+ const artifacts = nodes.filter((n) => LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
760
+ const mainNodes = nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
761
+ const pools = mainNodes.filter((n) => n.data.elementType === "Pool");
762
+ const lanes = mainNodes.filter((n) => n.data.elementType === "Lane");
763
+ const expandedSubProcesses = mainNodes.filter(
764
+ (n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded
765
+ );
766
+ const subProcessLayouts = /* @__PURE__ */ new Map();
767
+ const workingMainNodes = [...mainNodes];
768
+ for (const sp of expandedSubProcesses) {
769
+ const spChildren = workingMainNodes.filter((n) => n.parentId === sp.id);
770
+ const result = layoutSubProcess(spChildren, edges);
771
+ subProcessLayouts.set(sp.id, result);
772
+ const spIdx = workingMainNodes.findIndex((n) => n.id === sp.id);
773
+ if (spIdx >= 0) {
774
+ workingMainNodes[spIdx] = {
775
+ ...workingMainNodes[spIdx],
776
+ width: result.width,
777
+ height: result.height,
778
+ measured: { width: result.width, height: result.height }
779
+ };
780
+ }
781
+ }
782
+ const content = workingMainNodes.filter((n) => {
783
+ if (!LAYOUT_CONTAINER_TYPES.has(n.data.elementType)) return true;
784
+ if (COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)) return true;
785
+ return false;
786
+ });
527
787
  if (pools.length === 0) {
528
788
  const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
529
- return bpmnElkLayout2(nodes, edges);
789
+ const elkResult = await bpmnElkLayout2(nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType)), edges);
790
+ const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges);
791
+ return { nodes: [...elkResult.nodes, ...posArtifacts], edges: elkResult.edges };
530
792
  }
531
793
  const poolIds = new Set(pools.map((p) => p.id));
532
794
  const allLaneIds = new Set(lanes.map((l) => l.id));
@@ -546,7 +808,10 @@ async function bpmnCustomLayout(nodes, edges) {
546
808
  const poolContent = content.filter(
547
809
  (n) => n.parentId === pool.id || n.parentId != null && laneIds.has(n.parentId)
548
810
  );
549
- const result = layoutPool(pool, poolLanes, poolContent, edges);
811
+ const poolBoundaries = workingMainNodes.filter(
812
+ (n) => n.data.elementType === "BoundaryEvent" && (n.parentId === pool.id || n.parentId != null && laneIds.has(n.parentId))
813
+ );
814
+ const result = layoutPool(pool, poolLanes, [...poolContent, ...poolBoundaries], edges);
550
815
  resultNodes.push({
551
816
  ...pool,
552
817
  position: { x: 0, y: stackY },
@@ -558,12 +823,44 @@ async function bpmnCustomLayout(nodes, edges) {
558
823
  }
559
824
  stackY += result.height + POOL_V_GAP;
560
825
  }
826
+ for (const [, spLayout] of subProcessLayouts) {
827
+ for (const child of spLayout.children) {
828
+ resultNodes.push(child);
829
+ }
830
+ }
561
831
  const layoutted = new Set(resultNodes.map((n) => n.id));
562
- for (const node of nodes) {
563
- if (!layoutted.has(node.id)) resultNodes.push(node);
832
+ const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
833
+ if (freeNodes.length > 0) {
834
+ const freeEdges = edges.filter(
835
+ (e) => freeNodes.some((n) => n.id === e.source || n.id === e.target)
836
+ );
837
+ try {
838
+ const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
839
+ const elkFree = await bpmnElkLayout2(freeNodes, freeEdges);
840
+ const offsetY = stackY;
841
+ for (const node of elkFree.nodes) {
842
+ resultNodes.push({
843
+ ...node,
844
+ position: { x: node.position.x, y: node.position.y + offsetY }
845
+ });
846
+ }
847
+ const freeEdgeIds = new Set(freeEdges.map((e) => e.id));
848
+ const elkEdgeMap = new Map(elkFree.edges.map((e) => [e.id, e]));
849
+ for (let i = 0; i < edges.length; i++) {
850
+ if (freeEdgeIds.has(edges[i].id) && elkEdgeMap.has(edges[i].id)) {
851
+ edges[i] = elkEdgeMap.get(edges[i].id);
852
+ }
853
+ }
854
+ } catch {
855
+ for (const node of freeNodes) resultNodes.push(node);
856
+ }
564
857
  }
565
858
  const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
566
- return { nodes: resultNodes, edges: routedEdges };
859
+ const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges);
860
+ return {
861
+ nodes: [...resultNodes, ...positionedArtifacts],
862
+ edges: routedEdges
863
+ };
567
864
  }
568
865
 
569
866
  // src/layout/index.ts