@aranzatech/diagrams-bpmn 0.3.2 → 0.3.3

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.
@@ -1197,6 +1197,17 @@ function detectGatewayPairs(nodes, forwardEdges) {
1197
1197
  }
1198
1198
  return pairs;
1199
1199
  }
1200
+ function createSecondaryOffsets(count) {
1201
+ if (count <= 0) return [];
1202
+ const offsets = [];
1203
+ let step = 1;
1204
+ while (offsets.length < count) {
1205
+ offsets.push(-step);
1206
+ if (offsets.length < count) offsets.push(step);
1207
+ step++;
1208
+ }
1209
+ return offsets;
1210
+ }
1200
1211
  function handleToRowBias(handle) {
1201
1212
  if (!handle) return null;
1202
1213
  if (handle.includes("top")) return -1;
@@ -1212,6 +1223,52 @@ function buildHandleMap(edges) {
1212
1223
  }
1213
1224
  return map;
1214
1225
  }
1226
+ function pickMainBranch(branchData) {
1227
+ if (branchData.length === 0) return void 0;
1228
+ return [...branchData].sort((a, b) => {
1229
+ const aRight = a.bias === 0 ? 1 : 0;
1230
+ const bRight = b.bias === 0 ? 1 : 0;
1231
+ if (aRight !== bRight) return bRight - aRight;
1232
+ if (a.nodeIds.length !== b.nodeIds.length) return b.nodeIds.length - a.nodeIds.length;
1233
+ return a.start.localeCompare(b.start);
1234
+ })[0]?.start;
1235
+ }
1236
+ function resolveDirectionalRow(splitRow, preferredOffset, usedRows) {
1237
+ if (preferredOffset === 0 && !usedRows.has(splitRow)) return splitRow;
1238
+ const direction = preferredOffset < 0 ? -1 : 1;
1239
+ let distance = Math.max(1, Math.abs(preferredOffset));
1240
+ while (usedRows.has(splitRow + direction * distance)) distance++;
1241
+ return splitRow + direction * distance;
1242
+ }
1243
+ function assignBranchRowsAroundMain(branchData, splitRow) {
1244
+ const assigned = /* @__PURE__ */ new Map();
1245
+ const mainBranchStart = pickMainBranch(branchData);
1246
+ if (mainBranchStart) assigned.set(mainBranchStart, splitRow);
1247
+ const usedRows = new Set(assigned.values());
1248
+ const remaining = branchData.filter((branch) => branch.start !== mainBranchStart);
1249
+ const withBias = remaining.filter((branch) => branch.bias !== null && branch.bias !== 0).sort((a, b) => {
1250
+ const aMag = Math.abs(a.bias ?? 0);
1251
+ const bMag = Math.abs(b.bias ?? 0);
1252
+ if (aMag !== bMag) return aMag - bMag;
1253
+ return a.start.localeCompare(b.start);
1254
+ });
1255
+ const withoutBias = remaining.filter((branch) => branch.bias === null || branch.bias === 0).sort((a, b) => b.nodeIds.length - a.nodeIds.length || a.start.localeCompare(b.start));
1256
+ for (const branch of withBias) {
1257
+ const row = resolveDirectionalRow(splitRow, branch.bias ?? 0, usedRows);
1258
+ assigned.set(branch.start, row);
1259
+ usedRows.add(row);
1260
+ }
1261
+ const preferredOffsets = createSecondaryOffsets(withoutBias.length);
1262
+ for (let i = 0; i < withoutBias.length; i++) {
1263
+ let row = splitRow + preferredOffsets[i];
1264
+ if (usedRows.has(row)) {
1265
+ row = resolveDirectionalRow(splitRow, preferredOffsets[i], usedRows);
1266
+ }
1267
+ assigned.set(withoutBias[i].start, row);
1268
+ usedRows.add(row);
1269
+ }
1270
+ return assigned;
1271
+ }
1215
1272
  function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
1216
1273
  const rows = new Map(nodes.map((n) => [n.id, 0]));
1217
1274
  const succs = new Map(nodes.map((n) => [n.id, []]));
@@ -1245,24 +1302,14 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
1245
1302
  nodeIds: getBranchNodes(start),
1246
1303
  bias: handleToRowBias(splitHandles?.get(start) ?? null)
1247
1304
  }));
1248
- const withBias = branchData.filter((b) => b.bias !== null && b.bias !== 0);
1249
- const withoutBias = branchData.filter((b) => b.bias === null || b.bias === 0).sort((a, b) => b.nodeIds.length - a.nodeIds.length);
1250
- const assigned = /* @__PURE__ */ new Map();
1251
- for (const b of withBias) {
1252
- assigned.set(b.start, splitRow + b.bias);
1253
- }
1254
- let nextOffset = 0;
1255
- for (const b of withoutBias) {
1256
- while ([...assigned.values()].includes(splitRow + nextOffset)) nextOffset++;
1257
- assigned.set(b.start, splitRow + nextOffset);
1258
- nextOffset++;
1259
- }
1305
+ const assigned = assignBranchRowsAroundMain(branchData, splitRow);
1260
1306
  for (const b of branchData) {
1261
1307
  const row = assigned.get(b.start) ?? splitRow;
1262
1308
  for (const id of b.nodeIds) {
1263
1309
  rows.set(id, row);
1264
1310
  }
1265
1311
  }
1312
+ rows.set(mergeId, splitRow);
1266
1313
  }
1267
1314
  const pairedSplits = new Set(gatewayPairs.keys());
1268
1315
  const unpairedSplits = nodes.filter(
@@ -1291,16 +1338,7 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
1291
1338
  nodeIds: getAllReachable(start),
1292
1339
  bias: handleToRowBias(splitHandles2?.get(start) ?? null)
1293
1340
  }));
1294
- const withBias2 = branchData.filter((b) => b.bias !== null && b.bias !== 0);
1295
- const withoutBias2 = branchData.filter((b) => b.bias === null || b.bias === 0).sort((a, b) => b.nodeIds.length - a.nodeIds.length);
1296
- const assigned2 = /* @__PURE__ */ new Map();
1297
- for (const b of withBias2) assigned2.set(b.start, splitRow + b.bias);
1298
- let nextOff = 0;
1299
- for (const b of withoutBias2) {
1300
- while ([...assigned2.values()].includes(splitRow + nextOff)) nextOff++;
1301
- assigned2.set(b.start, splitRow + nextOff);
1302
- nextOff++;
1303
- }
1341
+ const assigned2 = assignBranchRowsAroundMain(branchData, splitRow);
1304
1342
  for (const b of branchData) {
1305
1343
  const row = assigned2.get(b.start) ?? splitRow;
1306
1344
  for (const id of b.nodeIds) rows.set(id, row);
@@ -1322,6 +1360,7 @@ var LANE_MIN_H = 180;
1322
1360
  var POOL_MIN_W = 720;
1323
1361
  var POOL_INNER_PAD = 10;
1324
1362
  var BACK_EDGE_CLEARANCE = 56;
1363
+ var EDGE_ROUTE_PAD = 28;
1325
1364
  var LAYOUT_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
1326
1365
  "DataObject",
1327
1366
  "DataObjectReference",
@@ -1630,8 +1669,27 @@ function absolutePos(nodeId, byId, cache) {
1630
1669
  cache.set(nodeId, abs);
1631
1670
  return abs;
1632
1671
  }
1633
- function laneOf(node, laneIds) {
1634
- return node.parentId && laneIds.has(node.parentId) ? node.parentId : void 0;
1672
+ function findAncestorNodeId(node, candidateIds, byId) {
1673
+ let current = node;
1674
+ while (current?.parentId) {
1675
+ if (candidateIds.has(current.parentId)) return current.parentId;
1676
+ current = byId.get(current.parentId);
1677
+ }
1678
+ return void 0;
1679
+ }
1680
+ function laneOf(node, laneIds, byId) {
1681
+ return findAncestorNodeId(node, laneIds, byId);
1682
+ }
1683
+ function poolOf(node, poolIds, byId) {
1684
+ if (poolIds.has(node.id)) return node.id;
1685
+ return findAncestorNodeId(node, poolIds, byId);
1686
+ }
1687
+ function nodeRect(node, byId, cache) {
1688
+ const pos = absolutePos(node.id, byId, cache);
1689
+ return { x: pos.x, y: pos.y, width: nW(node), height: nH(node) };
1690
+ }
1691
+ function rectContainsRect(outer, inner) {
1692
+ return inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.width <= outer.x + outer.width && inner.y + inner.height <= outer.y + outer.height;
1635
1693
  }
1636
1694
  function gapMidX(sAbs, sW, tAbs, tW) {
1637
1695
  const leftRightEdge = Math.min(sAbs.x + sW, tAbs.x + tW);
@@ -1667,18 +1725,9 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1667
1725
  const tCX = tAbs.x + tW / 2;
1668
1726
  const sCY = sAbs.y + sH / 2;
1669
1727
  const tCY = tAbs.y + tH / 2;
1670
- const getPool = (nodeId) => {
1671
- const node = byId.get(nodeId);
1672
- if (!node) return null;
1673
- if (poolIds.has(nodeId)) return nodeId;
1674
- if (node.parentId && poolIds.has(node.parentId)) return node.parentId;
1675
- if (node.parentId) {
1676
- const gp = byId.get(node.parentId)?.parentId ?? null;
1677
- if (gp && poolIds.has(gp)) return gp;
1678
- }
1679
- return node.parentId ?? null;
1680
- };
1681
- if (getPool(edge.source) !== getPool(edge.target)) {
1728
+ const srcPool = poolOf(src, poolIds, byId) ?? null;
1729
+ const tgtPool = poolOf(tgt, poolIds, byId) ?? null;
1730
+ if (srcPool !== tgtPool) {
1682
1731
  const d = { ...edge.data };
1683
1732
  delete d.routingPoints;
1684
1733
  return { ...edge, data: d };
@@ -1687,14 +1736,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1687
1736
  if (backEdgeIds.has(edge.id)) {
1688
1737
  const topY = Math.min(sAbs.y, tAbs.y) - BACK_EDGE_CLEARANCE;
1689
1738
  routingPoints = [
1690
- { x: sCX, y: sAbs.y },
1691
- { x: sCX, y: topY },
1692
- { x: tCX, y: topY },
1693
- { x: tCX, y: tAbs.y + tH }
1739
+ { x: sAbs.x + sW, y: sCY },
1740
+ { x: sAbs.x + sW + EDGE_ROUTE_PAD, y: sCY },
1741
+ { x: sAbs.x + sW + EDGE_ROUTE_PAD, y: topY },
1742
+ { x: tAbs.x - EDGE_ROUTE_PAD, y: topY },
1743
+ { x: tAbs.x - EDGE_ROUTE_PAD, y: tCY },
1744
+ { x: tAbs.x, y: tCY }
1694
1745
  ];
1695
- } else if (laneOf(src, laneIds) !== laneOf(tgt, laneIds)) {
1696
- const srcLane = byId.get(src.parentId ?? "");
1697
- const tgtLane = byId.get(tgt.parentId ?? "");
1746
+ } else if (laneOf(src, laneIds, byId) !== laneOf(tgt, laneIds, byId)) {
1747
+ const srcLane = byId.get(laneOf(src, laneIds, byId) ?? "");
1748
+ const tgtLane = byId.get(laneOf(tgt, laneIds, byId) ?? "");
1698
1749
  if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
1699
1750
  const goingDown = tAbs.y > sAbs.y;
1700
1751
  const sharedX = gapMidX(sAbs, sW, tAbs, tW);
@@ -1702,8 +1753,8 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1702
1753
  const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
1703
1754
  const pts = [];
1704
1755
  if (goingDown) {
1705
- pts.push({ x: sCX, y: sAbs.y + sH });
1706
- pts.push({ x: sharedX, y: sAbs.y + sH });
1756
+ pts.push({ x: sAbs.x + sW, y: sCY });
1757
+ pts.push({ x: sharedX, y: sCY });
1707
1758
  const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
1708
1759
  const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
1709
1760
  for (let i = fromIdx; i < toIdx; i++) {
@@ -1712,11 +1763,12 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1712
1763
  pts.push({ x: sharedX, y: laneBot });
1713
1764
  }
1714
1765
  const lastY = pts[pts.length - 1].y;
1715
- pts.push({ x: tCX, y: lastY });
1716
- pts.push({ x: tCX, y: tAbs.y + tH });
1766
+ pts.push({ x: tAbs.x - EDGE_ROUTE_PAD, y: lastY });
1767
+ pts.push({ x: tAbs.x - EDGE_ROUTE_PAD, y: tCY });
1768
+ pts.push({ x: tAbs.x, y: tCY });
1717
1769
  } else {
1718
- pts.push({ x: sCX, y: sAbs.y });
1719
- pts.push({ x: sharedX, y: sAbs.y });
1770
+ pts.push({ x: sAbs.x + sW, y: sCY });
1771
+ pts.push({ x: sharedX, y: sCY });
1720
1772
  const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
1721
1773
  const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
1722
1774
  for (let i = toIdx; i > fromIdx; i--) {
@@ -1725,17 +1777,18 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1725
1777
  pts.push({ x: sharedX, y: laneTop });
1726
1778
  }
1727
1779
  const lastY = pts[pts.length - 1].y;
1728
- pts.push({ x: tCX, y: lastY });
1729
- pts.push({ x: tCX, y: tAbs.y });
1780
+ pts.push({ x: tAbs.x - EDGE_ROUTE_PAD, y: lastY });
1781
+ pts.push({ x: tAbs.x - EDGE_ROUTE_PAD, y: tCY });
1782
+ pts.push({ x: tAbs.x, y: tCY });
1730
1783
  }
1731
1784
  routingPoints = pts;
1732
1785
  } else {
1733
1786
  const sharedX = gapMidX(sAbs, sW, tAbs, tW);
1734
1787
  routingPoints = [
1735
- { x: sCX, y: sCY },
1788
+ { x: sAbs.x + sW, y: sCY },
1736
1789
  { x: sharedX, y: sCY },
1737
1790
  { x: sharedX, y: tCY },
1738
- { x: tCX, y: tCY }
1791
+ { x: tAbs.x, y: tCY }
1739
1792
  ];
1740
1793
  }
1741
1794
  } else if (Math.abs(sCY - tCY) <= SAME_ROW_THRESHOLD) {
@@ -1747,19 +1800,25 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1747
1800
  const exitsTop = handle.includes("top");
1748
1801
  const exitsBottom = handle.includes("bottom");
1749
1802
  if (exitsTop || exitsBottom) {
1803
+ const exitY = exitsTop ? sAbs.y : sAbs.y + sH;
1804
+ const entryY = exitsTop ? tAbs.y + tH : tAbs.y;
1805
+ const corridorY = exitsTop ? Math.min(exitY, entryY) - EDGE_ROUTE_PAD : Math.max(exitY, entryY) + EDGE_ROUTE_PAD;
1750
1806
  routingPoints = [
1751
- { x: sCX, y: exitsTop ? sAbs.y : sAbs.y + sH },
1752
- { x: sCX, y: tCY },
1753
- { x: tCX, y: tCY },
1754
- { x: tCX, y: exitsTop ? tAbs.y + tH : tAbs.y }
1807
+ { x: sCX, y: exitY },
1808
+ { x: sCX, y: corridorY },
1809
+ { x: tCX, y: corridorY },
1810
+ { x: tCX, y: entryY }
1755
1811
  ];
1756
1812
  } else {
1757
1813
  const midX = routeMidX(sAbs, sW, tAbs, tW);
1814
+ const exitX = sAbs.x + sW;
1815
+ const entryX = tAbs.x;
1816
+ const corridorX = Math.max(midX, exitX + EDGE_ROUTE_PAD);
1758
1817
  routingPoints = [
1759
- { x: sAbs.x + sW, y: sCY },
1760
- { x: midX, y: sCY },
1761
- { x: midX, y: tCY },
1762
- { x: tAbs.x, y: tCY }
1818
+ { x: exitX, y: sCY },
1819
+ { x: corridorX, y: sCY },
1820
+ { x: corridorX, y: tCY },
1821
+ { x: entryX, y: tCY }
1763
1822
  ];
1764
1823
  }
1765
1824
  }
@@ -1768,16 +1827,19 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1768
1827
  }
1769
1828
  var ARTIFACT_ABOVE_GAP = 16;
1770
1829
  var ARTIFACT_H_SPACING = 12;
1771
- function positionArtifacts(artifacts, resultNodes, edges) {
1830
+ function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
1772
1831
  if (artifacts.length === 0) return [];
1773
1832
  const byId = new Map(resultNodes.map((n) => [n.id, n]));
1774
1833
  const cache = /* @__PURE__ */ new Map();
1775
1834
  const absPos = (id) => absolutePos(id, byId, cache);
1835
+ const originalById = new Map(originalNodes.map((n) => [n.id, n]));
1836
+ const originalCache = /* @__PURE__ */ new Map();
1776
1837
  const artifactsByNode = /* @__PURE__ */ new Map();
1777
1838
  const ungrouped = [];
1839
+ const groups = [];
1778
1840
  for (const artifact of artifacts) {
1779
1841
  if (artifact.data.elementType === "Group") {
1780
- ungrouped.push(artifact);
1842
+ groups.push(artifact);
1781
1843
  continue;
1782
1844
  }
1783
1845
  const connEdge = edges.find((e) => {
@@ -1815,6 +1877,58 @@ function positionArtifacts(artifacts, resultNodes, edges) {
1815
1877
  desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
1816
1878
  }
1817
1879
  }
1880
+ const nonArtifactOriginalNodes = originalNodes.filter(
1881
+ (node) => !LAYOUT_ARTIFACT_TYPES.has(node.data.elementType)
1882
+ );
1883
+ for (const group of groups) {
1884
+ const originalGroup = originalById.get(group.id);
1885
+ if (!originalGroup) {
1886
+ positioned.push(group);
1887
+ continue;
1888
+ }
1889
+ const groupRect = nodeRect(originalGroup, originalById, originalCache);
1890
+ const containedOriginalIds = nonArtifactOriginalNodes.filter((node) => node.id !== group.id).filter((node) => {
1891
+ if (node.parentId !== group.parentId) return false;
1892
+ return rectContainsRect(groupRect, nodeRect(node, originalById, originalCache));
1893
+ }).map((node) => node.id);
1894
+ if (containedOriginalIds.length === 0) {
1895
+ positioned.push(group);
1896
+ continue;
1897
+ }
1898
+ const containedLaidOut = containedOriginalIds.map((id) => byId.get(id)).filter((node) => !!node);
1899
+ if (containedLaidOut.length === 0) {
1900
+ positioned.push(group);
1901
+ continue;
1902
+ }
1903
+ const GROUP_PAD_X = 24;
1904
+ const GROUP_PAD_Y = 20;
1905
+ const bounds = containedLaidOut.reduce(
1906
+ (acc, node) => {
1907
+ const rect = nodeRect(node, byId, cache);
1908
+ return {
1909
+ left: Math.min(acc.left, rect.x),
1910
+ top: Math.min(acc.top, rect.y),
1911
+ right: Math.max(acc.right, rect.x + rect.width),
1912
+ bottom: Math.max(acc.bottom, rect.y + rect.height)
1913
+ };
1914
+ },
1915
+ { left: Number.POSITIVE_INFINITY, top: Number.POSITIVE_INFINITY, right: Number.NEGATIVE_INFINITY, bottom: Number.NEGATIVE_INFINITY }
1916
+ );
1917
+ const parentAbsP = group.parentId ? absPos(group.parentId) : { x: 0, y: 0 };
1918
+ positioned.push({
1919
+ ...group,
1920
+ position: {
1921
+ x: bounds.left - GROUP_PAD_X - parentAbsP.x,
1922
+ y: bounds.top - GROUP_PAD_Y - parentAbsP.y
1923
+ },
1924
+ width: bounds.right - bounds.left + GROUP_PAD_X * 2,
1925
+ height: bounds.bottom - bounds.top + GROUP_PAD_Y * 2,
1926
+ measured: {
1927
+ width: bounds.right - bounds.left + GROUP_PAD_X * 2,
1928
+ height: bounds.bottom - bounds.top + GROUP_PAD_Y * 2
1929
+ }
1930
+ });
1931
+ }
1818
1932
  return positioned;
1819
1933
  }
1820
1934
  async function bpmnCustomLayout(nodes, edges) {
@@ -1849,7 +1963,7 @@ async function bpmnCustomLayout(nodes, edges) {
1849
1963
  if (pools.length === 0) {
1850
1964
  const { bpmnElkLayout: bpmnElkLayout2 } = await Promise.resolve().then(() => (init_elk(), elk_exports));
1851
1965
  const elkResult = await bpmnElkLayout2(nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType)), edges);
1852
- const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges);
1966
+ const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges, nodes);
1853
1967
  return { nodes: [...elkResult.nodes, ...posArtifacts], edges: elkResult.edges };
1854
1968
  }
1855
1969
  const poolIds = new Set(pools.map((p) => p.id));
@@ -1893,8 +2007,9 @@ async function bpmnCustomLayout(nodes, edges) {
1893
2007
  const layoutted = new Set(resultNodes.map((n) => n.id));
1894
2008
  const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
1895
2009
  if (freeNodes.length > 0) {
2010
+ const freeNodeIds = new Set(freeNodes.map((n) => n.id));
1896
2011
  const freeEdges = edges.filter(
1897
- (e) => freeNodes.some((n) => n.id === e.source || n.id === e.target)
2012
+ (e) => freeNodeIds.has(e.source) && freeNodeIds.has(e.target)
1898
2013
  );
1899
2014
  try {
1900
2015
  const { bpmnElkLayout: bpmnElkLayout2 } = await Promise.resolve().then(() => (init_elk(), elk_exports));
@@ -1918,7 +2033,7 @@ async function bpmnCustomLayout(nodes, edges) {
1918
2033
  }
1919
2034
  }
1920
2035
  const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
1921
- const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges);
2036
+ const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges, nodes);
1922
2037
  return {
1923
2038
  nodes: [...resultNodes, ...positionedArtifacts],
1924
2039
  edges: routedEdges