@aranzatech/diagrams-bpmn 0.2.14 → 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.
@@ -0,0 +1,163 @@
1
+ import { getBpmnNodeSize } from './chunk-HOWK3ZOO.js';
2
+ import { elkLayout } from '@aranzatech/diagrams-core/layout';
3
+
4
+ var BPMN_CONTAINER_TYPES = /* @__PURE__ */ new Set([
5
+ "Pool",
6
+ "Lane",
7
+ "SubProcess",
8
+ "Transaction",
9
+ "EventSubProcess",
10
+ "AdHocSubProcess"
11
+ ]);
12
+ var CONTAINER_MIN_SIZE = {
13
+ Pool: { w: 720, h: 200 },
14
+ Lane: { w: 600, h: 160 },
15
+ SubProcess: { w: 240, h: 140 },
16
+ Transaction: { w: 240, h: 140 },
17
+ EventSubProcess: { w: 240, h: 140 },
18
+ AdHocSubProcess: { w: 240, h: 140 }
19
+ };
20
+ var CONTAINER_PADDING = {
21
+ Pool: "[top=30,left=60,bottom=30,right=50]",
22
+ Lane: "[top=40,left=70,bottom=40,right=50]",
23
+ SubProcess: "[top=30,left=40,bottom=30,right=40]",
24
+ Transaction: "[top=30,left=40,bottom=30,right=40]",
25
+ EventSubProcess: "[top=30,left=40,bottom=30,right=40]",
26
+ AdHocSubProcess: "[top=30,left=40,bottom=30,right=40]"
27
+ };
28
+ async function bpmnElkLayout(nodes, edges) {
29
+ const poolsWithLanes = /* @__PURE__ */ new Set();
30
+ for (const node of nodes) {
31
+ if (node.data.elementType === "Lane" && node.parentId) {
32
+ poolsWithLanes.add(node.parentId);
33
+ }
34
+ }
35
+ const result = await elkLayout(
36
+ nodes,
37
+ edges,
38
+ {
39
+ direction: "LR",
40
+ getNodeSize: (node) => {
41
+ const bNode = node;
42
+ if (BPMN_CONTAINER_TYPES.has(bNode.data.elementType)) return void 0;
43
+ return getBpmnNodeSize(bNode);
44
+ },
45
+ isContainerNode: (node) => BPMN_CONTAINER_TYPES.has(node.data.elementType),
46
+ getNodeLayoutOptions: (node) => {
47
+ const bNode = node;
48
+ const type = bNode.data.elementType;
49
+ if (!BPMN_CONTAINER_TYPES.has(type)) return void 0;
50
+ const min = CONTAINER_MIN_SIZE[type];
51
+ const isPoolWithLanes = type === "Pool" && poolsWithLanes.has(node.id);
52
+ const dir = isPoolWithLanes ? "DOWN" : "RIGHT";
53
+ return {
54
+ "elk.direction": dir,
55
+ "elk.nodeSize.constraints": "MINIMUM_SIZE",
56
+ // Each container handles its own layout pass so cross-lane edges
57
+ // participate in the internal layout without the global direction
58
+ // forcing Lane containers to align sideways.
59
+ "elk.hierarchyHandling": "INCLUDE_CHILDREN",
60
+ // Lanes within a Pool share a border — zero gap between them.
61
+ // Other containers keep the tighter inner spacing.
62
+ ...isPoolWithLanes ? { "elk.spacing.nodeNode": "0", "elk.spacing.componentComponent": "0" } : {
63
+ "elk.layered.spacing.nodeNodeBetweenLayers": "100",
64
+ "elk.layered.spacing.edgeNodeBetweenLayers": "45",
65
+ "elk.spacing.nodeNode": "50"
66
+ },
67
+ ...min ? { "elk.nodeSize.minimum": `[w=${min.w},h=${min.h}]` } : {},
68
+ ...CONTAINER_PADDING[type] ? { "elk.padding": CONTAINER_PADDING[type] } : {}
69
+ };
70
+ },
71
+ elk: {
72
+ "elk.algorithm": "layered",
73
+ "elk.direction": "RIGHT",
74
+ // INHERIT: each container handles its own INCLUDE_CHILDREN pass so
75
+ // the global RIGHT direction does not force Lane containers sideways.
76
+ "elk.hierarchyHandling": "INHERIT",
77
+ "elk.edgeRouting": "ORTHOGONAL",
78
+ // Global spacing — generous values for visual breathing room.
79
+ "elk.layered.spacing.nodeNodeBetweenLayers": "120",
80
+ "elk.layered.spacing.edgeNodeBetweenLayers": "50",
81
+ "elk.spacing.nodeNode": "70",
82
+ "elk.spacing.componentComponent": "100"
83
+ }
84
+ }
85
+ );
86
+ const nodeMap = new Map(result.nodes.map((n) => [n.id, n]));
87
+ const edgeMap = new Map(result.edges.map((e) => [e.id, e]));
88
+ const COMPONENT_GAP = 100;
89
+ const rootNodeOrder = nodes.filter((n) => !n.parentId).map((n) => n.id);
90
+ const rootHeights = /* @__PURE__ */ new Map();
91
+ for (const id of rootNodeOrder) {
92
+ const laid = nodeMap.get(id);
93
+ const orig = nodes.find((n) => n.id === id);
94
+ rootHeights.set(id, laid?.height ?? orig?.height ?? 200);
95
+ }
96
+ let stackY = 0;
97
+ const rootY = /* @__PURE__ */ new Map();
98
+ for (const id of rootNodeOrder) {
99
+ rootY.set(id, stackY);
100
+ stackY += (rootHeights.get(id) ?? 200) + COMPONENT_GAP;
101
+ }
102
+ const updatedNodes = nodes.map((node) => {
103
+ const laid = nodeMap.get(node.id);
104
+ if (!laid) return node;
105
+ const pos = node.parentId ? laid.position : { x: laid.position.x, y: rootY.get(node.id) ?? laid.position.y };
106
+ return { ...node, position: pos, width: laid.width, height: laid.height };
107
+ });
108
+ const lanesByPool = /* @__PURE__ */ new Map();
109
+ for (const node of updatedNodes) {
110
+ if (node.data.elementType === "Lane" && node.parentId) {
111
+ const arr = lanesByPool.get(node.parentId) ?? [];
112
+ arr.push(node.id);
113
+ lanesByPool.set(node.parentId, arr);
114
+ }
115
+ }
116
+ const nodeIndex = new Map(updatedNodes.map((n, i) => [n.id, i]));
117
+ for (const laneIds of lanesByPool.values()) {
118
+ const widths = laneIds.map((id) => updatedNodes[nodeIndex.get(id)]?.width ?? 0);
119
+ const maxWidth = Math.max(...widths);
120
+ if (maxWidth <= 0) continue;
121
+ for (const id of laneIds) {
122
+ const idx = nodeIndex.get(id);
123
+ if (idx !== void 0) {
124
+ updatedNodes[idx] = { ...updatedNodes[idx], width: maxWidth };
125
+ }
126
+ }
127
+ }
128
+ const nodeRootContainer = /* @__PURE__ */ new Map();
129
+ for (const node of updatedNodes) {
130
+ nodeRootContainer.set(node.id, node.parentId ?? null);
131
+ }
132
+ const getRootContainer = (nodeId) => {
133
+ let current = nodeId;
134
+ const visited = /* @__PURE__ */ new Set();
135
+ while (current) {
136
+ if (visited.has(current)) break;
137
+ visited.add(current);
138
+ const parent = nodeRootContainer.get(current);
139
+ if (!parent) return current;
140
+ current = parent;
141
+ }
142
+ return null;
143
+ };
144
+ const updatedEdges = edges.map((edge) => {
145
+ const laid = edgeMap.get(edge.id);
146
+ if (!laid) return edge;
147
+ const nextData = { ...edge.data };
148
+ const sourceRoot = getRootContainer(edge.source);
149
+ const targetRoot = getRootContainer(edge.target);
150
+ const isCrossPool = sourceRoot !== targetRoot;
151
+ if (!isCrossPool && laid.points && laid.points.length > 0) {
152
+ nextData.routingPoints = laid.points;
153
+ } else {
154
+ delete nextData.routingPoints;
155
+ }
156
+ return { ...edge, data: nextData };
157
+ });
158
+ return { nodes: updatedNodes, edges: updatedEdges };
159
+ }
160
+
161
+ export { bpmnElkLayout };
162
+ //# sourceMappingURL=chunk-XMVV7FRZ.js.map
163
+ //# sourceMappingURL=chunk-XMVV7FRZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/layout/elk.ts"],"names":[],"mappings":";;;AAOA,IAAM,oBAAA,uBAA2B,GAAA,CAAqB;AAAA,EACpD,MAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,IAAM,kBAAA,GAAiF;AAAA,EACrF,IAAA,EAAM,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA,EAAI;AAAA,EACvB,IAAA,EAAM,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA,EAAI;AAAA,EACvB,UAAA,EAAY,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA,EAAI;AAAA,EAC7B,WAAA,EAAa,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA,EAAI;AAAA,EAC9B,eAAA,EAAiB,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA,EAAI;AAAA,EAClC,eAAA,EAAiB,EAAE,CAAA,EAAG,GAAA,EAAK,GAAG,GAAA;AAChC,CAAA;AAIA,IAAM,iBAAA,GAA8D;AAAA,EAClE,IAAA,EAAM,qCAAA;AAAA,EACN,IAAA,EAAM,qCAAA;AAAA,EACN,UAAA,EAAY,qCAAA;AAAA,EACZ,WAAA,EAAa,qCAAA;AAAA,EACb,eAAA,EAAiB,qCAAA;AAAA,EACjB,eAAA,EAAiB;AACnB,CAAA;AAqBA,eAAsB,aAAA,CACpB,OACA,KAAA,EACuD;AAEvD,EAAA,MAAM,cAAA,uBAAqB,GAAA,EAAY;AACvC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,WAAA,KAAgB,MAAA,IAAU,KAAK,QAAA,EAAU;AACrD,MAAA,cAAA,CAAe,GAAA,CAAI,KAAK,QAAQ,CAAA;AAAA,IAClC;AAAA,EACF;AAEA,EAAA,MAAM,SAAS,MAAM,SAAA;AAAA,IACnB,KAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,MACE,SAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAa,CAAC,IAAA,KAAS;AACrB,QAAA,MAAM,KAAA,GAAQ,IAAA;AACd,QAAA,IAAI,qBAAqB,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,WAAW,GAAG,OAAO,MAAA;AAC7D,QAAA,OAAO,gBAAgB,KAAK,CAAA;AAAA,MAC9B,CAAA;AAAA,MACA,iBAAiB,CAAC,IAAA,KAChB,qBAAqB,GAAA,CAAK,IAAA,CAA+B,KAAK,WAAW,CAAA;AAAA,MAC3E,oBAAA,EAAsB,CAAC,IAAA,KAAS;AAC9B,QAAA,MAAM,KAAA,GAAQ,IAAA;AACd,QAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,WAAA;AACxB,QAAA,IAAI,CAAC,oBAAA,CAAqB,GAAA,CAAI,IAAI,GAAG,OAAO,MAAA;AAE5C,QAAA,MAAM,GAAA,GAAM,mBAAmB,IAAI,CAAA;AACnC,QAAA,MAAM,kBAAkB,IAAA,KAAS,MAAA,IAAU,cAAA,CAAe,GAAA,CAAI,KAAK,EAAE,CAAA;AAIrE,QAAA,MAAM,GAAA,GAAM,kBAAkB,MAAA,GAAS,OAAA;AAEvC,QAAA,OAAO;AAAA,UACL,eAAA,EAAiB,GAAA;AAAA,UACjB,0BAAA,EAA4B,cAAA;AAAA;AAAA;AAAA;AAAA,UAI5B,uBAAA,EAAyB,kBAAA;AAAA;AAAA;AAAA,UAGzB,GAAI,eAAA,GACA,EAAE,wBAAwB,GAAA,EAAK,gCAAA,EAAkC,KAAI,GACrE;AAAA,YACE,2CAAA,EAA6C,KAAA;AAAA,YAC7C,2CAAA,EAA6C,IAAA;AAAA,YAC7C,sBAAA,EAAwB;AAAA,WAC1B;AAAA,UACJ,GAAI,GAAA,GAAM,EAAE,sBAAA,EAAwB,CAAA,GAAA,EAAM,GAAA,CAAI,CAAC,CAAA,GAAA,EAAM,GAAA,CAAI,CAAC,CAAA,CAAA,CAAA,EAAI,GAAI,EAAC;AAAA,UACnE,GAAI,iBAAA,CAAkB,IAAI,CAAA,GAAI,EAAE,eAAe,iBAAA,CAAkB,IAAI,CAAA,EAAE,GAAI;AAAC,SAC9E;AAAA,MACF,CAAA;AAAA,MACA,GAAA,EAAK;AAAA,QACH,eAAA,EAAiB,SAAA;AAAA,QACjB,eAAA,EAAiB,OAAA;AAAA;AAAA;AAAA,QAGjB,uBAAA,EAAyB,SAAA;AAAA,QACzB,iBAAA,EAAmB,YAAA;AAAA;AAAA,QAEnB,2CAAA,EAA6C,KAAA;AAAA,QAC7C,2CAAA,EAA6C,IAAA;AAAA,QAC7C,sBAAA,EAAwB,IAAA;AAAA,QACxB,gCAAA,EAAkC;AAAA;AACpC;AACF,GACF;AAEA,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAK1D,EAAA,MAAM,aAAA,GAAgB,GAAA;AACtB,EAAA,MAAM,aAAA,GAAgB,KAAA,CACnB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,QAAQ,CAAA,CACzB,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AAGlB,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,KAAA,MAAW,MAAM,aAAA,EAAe;AAC9B,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAC3B,IAAA,MAAM,OAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AAC1C,IAAA,WAAA,CAAY,IAAI,EAAA,EAAI,IAAA,EAAM,MAAA,IAAU,IAAA,EAAM,UAAU,GAAG,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAoB;AACtC,EAAA,KAAA,MAAW,MAAM,aAAA,EAAe;AAC9B,IAAA,KAAA,CAAM,GAAA,CAAI,IAAI,MAAM,CAAA;AACpB,IAAA,MAAA,IAAA,CAAW,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA,IAAK,GAAA,IAAO,aAAA;AAAA,EAC3C;AAEA,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACvC,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AAChC,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,IAAA,MAAM,MAAM,IAAA,CAAK,QAAA,GACb,KAAK,QAAA,GACL,EAAE,GAAG,IAAA,CAAK,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,MAAM,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,IAAK,IAAA,CAAK,SAAS,CAAA,EAAE;AACnE,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,QAAA,EAAU,GAAA,EAAK,OAAO,IAAA,CAAK,KAAA,EAAO,MAAA,EAAQ,IAAA,CAAK,MAAA,EAAO;AAAA,EAC1E,CAAC,CAAA;AAID,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAsB;AAC9C,EAAA,KAAA,MAAW,QAAQ,YAAA,EAAc;AAC/B,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,WAAA,KAAgB,MAAA,IAAU,KAAK,QAAA,EAAU;AACrD,MAAA,MAAM,MAAM,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAQ,KAAK,EAAC;AAC/C,MAAA,GAAA,CAAI,IAAA,CAAK,KAAK,EAAE,CAAA;AAChB,MAAA,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,QAAA,EAAU,GAAG,CAAA;AAAA,IACpC;AAAA,EACF;AACA,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAC/D,EAAA,KAAA,MAAW,OAAA,IAAW,WAAA,CAAY,MAAA,EAAO,EAAG;AAC1C,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,CAAC,EAAA,KAAO,YAAA,CAAa,SAAA,CAAU,GAAA,CAAI,EAAE,CAAE,CAAA,EAAG,KAAA,IAAS,CAAC,CAAA;AAC/E,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,GAAG,MAAM,CAAA;AACnC,IAAA,IAAI,YAAY,CAAA,EAAG;AACnB,IAAA,KAAA,MAAW,MAAM,OAAA,EAAS;AACxB,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA;AAC5B,MAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,QAAA,YAAA,CAAa,GAAG,IAAI,EAAE,GAAG,aAAa,GAAG,CAAA,EAAG,OAAO,QAAA,EAAS;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AAKA,EAAA,MAAM,iBAAA,uBAAwB,GAAA,EAA2B;AACzD,EAAA,KAAA,MAAW,QAAQ,YAAA,EAAc;AAC/B,IAAA,iBAAA,CAAkB,GAAA,CAAI,IAAA,CAAK,EAAA,EAAI,IAAA,CAAK,YAAY,IAAI,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,gBAAA,GAAmB,CAAC,MAAA,KAAkC;AAC1D,IAAA,IAAI,OAAA,GAAyB,MAAA;AAC7B,IAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,IAAA,OAAO,OAAA,EAAS;AACd,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AAC1B,MAAA,OAAA,CAAQ,IAAI,OAAO,CAAA;AACnB,MAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,GAAA,CAAI,OAAO,CAAA;AAC5C,MAAA,IAAI,CAAC,QAAQ,OAAO,OAAA;AACpB,MAAA,OAAA,GAAU,MAAA;AAAA,IACZ;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACvC,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AAChC,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,IAAA,MAAM,QAAA,GAAW,EAAE,GAAG,IAAA,CAAK,IAAA,EAAK;AAChC,IAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,IAAA,CAAK,MAAM,CAAA;AAC/C,IAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,IAAA,CAAK,MAAM,CAAA;AAC/C,IAAA,MAAM,cAAc,UAAA,KAAe,UAAA;AACnC,IAAA,IAAI,CAAC,WAAA,IAAe,IAAA,CAAK,UAAU,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACzD,MAAA,QAAA,CAAS,gBAAgB,IAAA,CAAK,MAAA;AAAA,IAChC,CAAA,MAAO;AACL,MAAA,OAAO,QAAA,CAAS,aAAA;AAAA,IAClB;AACA,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAS;AAAA,EACnC,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,YAAA,EAA6B;AACpE","file":"chunk-XMVV7FRZ.js","sourcesContent":["import { elkLayout } from \"@aranzatech/diagrams-core/layout\";\nimport type { DiagramNode, DiagramEdge } from \"@aranzatech/diagrams-core/types\";\nimport type { BpmnRFNode, BpmnRFEdge } from \"../xml/types\";\nimport type { BpmnElementType } from \"../elements/types\";\nimport { getBpmnNodeSize } from \"../modeling\";\n\n// Container types that ELK auto-sizes based on their children.\nconst BPMN_CONTAINER_TYPES = new Set<BpmnElementType>([\n \"Pool\",\n \"Lane\",\n \"SubProcess\",\n \"Transaction\",\n \"EventSubProcess\",\n \"AdHocSubProcess\",\n]);\n\nconst CONTAINER_MIN_SIZE: Partial<Record<BpmnElementType, { w: number; h: number }>> = {\n Pool: { w: 720, h: 200 },\n Lane: { w: 600, h: 160 },\n SubProcess: { w: 240, h: 140 },\n Transaction: { w: 240, h: 140 },\n EventSubProcess: { w: 240, h: 140 },\n AdHocSubProcess: { w: 240, h: 140 },\n};\n\n// Padding inside each container type.\n// Left padding for Pool/Lane accounts for the vertical label strip (~30px) + breathing room.\nconst CONTAINER_PADDING: Partial<Record<BpmnElementType, string>> = {\n Pool: \"[top=30,left=60,bottom=30,right=50]\",\n Lane: \"[top=40,left=70,bottom=40,right=50]\",\n SubProcess: \"[top=30,left=40,bottom=30,right=40]\",\n Transaction: \"[top=30,left=40,bottom=30,right=40]\",\n EventSubProcess: \"[top=30,left=40,bottom=30,right=40]\",\n AdHocSubProcess: \"[top=30,left=40,bottom=30,right=40]\",\n};\n\n/**\n * ELK-based auto-layout for BPMN diagrams.\n *\n * Spacing philosophy: fixed, generous values that scale consistently\n * regardless of diagram size. \"Air\" between elements is not waste —\n * it is what makes the flow readable to the human eye.\n *\n * Direction policy:\n * - Root: RIGHT — free nodes and pools flow LR on the canvas.\n * - Pool with Lanes: DOWN internally so lanes stack top-to-bottom;\n * the pool itself is placed LR on the canvas.\n * - Lane / SubProcess / Pool-without-lanes: RIGHT — content flows LR.\n * - Each container runs its own INCLUDE_CHILDREN pass (per-container\n * hierarchyHandling) so cross-lane edges are routed correctly without\n * the global RIGHT direction forcing Lane containers sideways.\n * - Root uses INHERIT so each pool is laid out independently.\n * - Post-processing equalises Lane widths within each Pool so all Lanes\n * span the full Pool width.\n */\nexport async function bpmnElkLayout(\n nodes: BpmnRFNode[],\n edges: BpmnRFEdge[],\n): Promise<{ nodes: BpmnRFNode[]; edges: BpmnRFEdge[] }> {\n // Pools that directly contain at least one Lane.\n const poolsWithLanes = new Set<string>();\n for (const node of nodes) {\n if (node.data.elementType === \"Lane\" && node.parentId) {\n poolsWithLanes.add(node.parentId);\n }\n }\n\n const result = await elkLayout(\n nodes as unknown as DiagramNode[],\n edges as unknown as DiagramEdge[],\n {\n direction: \"LR\",\n getNodeSize: (node) => {\n const bNode = node as unknown as BpmnRFNode;\n if (BPMN_CONTAINER_TYPES.has(bNode.data.elementType)) return undefined;\n return getBpmnNodeSize(bNode);\n },\n isContainerNode: (node) =>\n BPMN_CONTAINER_TYPES.has((node as unknown as BpmnRFNode).data.elementType),\n getNodeLayoutOptions: (node) => {\n const bNode = node as unknown as BpmnRFNode;\n const type = bNode.data.elementType;\n if (!BPMN_CONTAINER_TYPES.has(type)) return undefined;\n\n const min = CONTAINER_MIN_SIZE[type];\n const isPoolWithLanes = type === \"Pool\" && poolsWithLanes.has(node.id);\n\n // Pool with Lanes stacks Lanes top-to-bottom (standard horizontal BPMN).\n // Everything else flows left-to-right.\n const dir = isPoolWithLanes ? \"DOWN\" : \"RIGHT\";\n\n return {\n \"elk.direction\": dir,\n \"elk.nodeSize.constraints\": \"MINIMUM_SIZE\",\n // Each container handles its own layout pass so cross-lane edges\n // participate in the internal layout without the global direction\n // forcing Lane containers to align sideways.\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n // Lanes within a Pool share a border — zero gap between them.\n // Other containers keep the tighter inner spacing.\n ...(isPoolWithLanes\n ? { \"elk.spacing.nodeNode\": \"0\", \"elk.spacing.componentComponent\": \"0\" }\n : {\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"100\",\n \"elk.layered.spacing.edgeNodeBetweenLayers\": \"45\",\n \"elk.spacing.nodeNode\": \"50\",\n }),\n ...(min ? { \"elk.nodeSize.minimum\": `[w=${min.w},h=${min.h}]` } : {}),\n ...(CONTAINER_PADDING[type] ? { \"elk.padding\": CONTAINER_PADDING[type] } : {}),\n };\n },\n elk: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"RIGHT\",\n // INHERIT: each container handles its own INCLUDE_CHILDREN pass so\n // the global RIGHT direction does not force Lane containers sideways.\n \"elk.hierarchyHandling\": \"INHERIT\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // Global spacing — generous values for visual breathing room.\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"120\",\n \"elk.layered.spacing.edgeNodeBetweenLayers\": \"50\",\n \"elk.spacing.nodeNode\": \"70\",\n \"elk.spacing.componentComponent\": \"100\",\n },\n },\n );\n\n const nodeMap = new Map(result.nodes.map((n) => [n.id, n]));\n const edgeMap = new Map(result.edges.map((e) => [e.id, e]));\n\n // ── Fix 1: Preserve original vertical order of root nodes (Pools, free nodes) ──\n // ELK may reorder disconnected components based on cross-pool edge direction.\n // We restore the original top-to-bottom order so Pools never swap positions.\n const COMPONENT_GAP = 100;\n const rootNodeOrder = nodes\n .filter((n) => !n.parentId)\n .map((n) => n.id);\n\n // Compute ELK heights for each root node, then re-stack top-to-bottom.\n const rootHeights = new Map<string, number>();\n for (const id of rootNodeOrder) {\n const laid = nodeMap.get(id);\n const orig = nodes.find((n) => n.id === id);\n rootHeights.set(id, laid?.height ?? orig?.height ?? 200);\n }\n let stackY = 0;\n const rootY = new Map<string, number>();\n for (const id of rootNodeOrder) {\n rootY.set(id, stackY);\n stackY += (rootHeights.get(id) ?? 200) + COMPONENT_GAP;\n }\n\n const updatedNodes = nodes.map((node) => {\n const laid = nodeMap.get(node.id);\n if (!laid) return node;\n const pos = node.parentId\n ? laid.position // children: ELK relative position\n : { x: laid.position.x, y: rootY.get(node.id) ?? laid.position.y }; // root: preserved order\n return { ...node, position: pos, width: laid.width, height: laid.height };\n });\n\n // Post-processing: equalise Lane widths within each Pool so all Lanes\n // span the full Pool width regardless of their individual content width.\n const lanesByPool = new Map<string, string[]>();\n for (const node of updatedNodes) {\n if (node.data.elementType === \"Lane\" && node.parentId) {\n const arr = lanesByPool.get(node.parentId) ?? [];\n arr.push(node.id);\n lanesByPool.set(node.parentId, arr);\n }\n }\n const nodeIndex = new Map(updatedNodes.map((n, i) => [n.id, i]));\n for (const laneIds of lanesByPool.values()) {\n const widths = laneIds.map((id) => updatedNodes[nodeIndex.get(id)!]?.width ?? 0);\n const maxWidth = Math.max(...widths);\n if (maxWidth <= 0) continue;\n for (const id of laneIds) {\n const idx = nodeIndex.get(id);\n if (idx !== undefined) {\n updatedNodes[idx] = { ...updatedNodes[idx], width: maxWidth };\n }\n }\n }\n\n // ── Fix 2: Identify cross-pool edges (source and target live in different root containers) ──\n // ELK routes them incorrectly when pools are laid out independently.\n // Clear their routingPoints so ReactFlow renders smooth default curves instead.\n const nodeRootContainer = new Map<string, string | null>();\n for (const node of updatedNodes) {\n nodeRootContainer.set(node.id, node.parentId ?? null);\n }\n const getRootContainer = (nodeId: string): string | null => {\n let current: string | null = nodeId;\n const visited = new Set<string>();\n while (current) {\n if (visited.has(current)) break;\n visited.add(current);\n const parent = nodeRootContainer.get(current);\n if (!parent) return current;\n current = parent;\n }\n return null;\n };\n\n const updatedEdges = edges.map((edge) => {\n const laid = edgeMap.get(edge.id);\n if (!laid) return edge;\n const nextData = { ...edge.data };\n const sourceRoot = getRootContainer(edge.source);\n const targetRoot = getRootContainer(edge.target);\n const isCrossPool = sourceRoot !== targetRoot;\n if (!isCrossPool && laid.points && laid.points.length > 0) {\n nextData.routingPoints = laid.points;\n } else {\n delete nextData.routingPoints;\n }\n return { ...edge, data: nextData };\n });\n\n return { nodes: updatedNodes, edges: updatedEdges as BpmnRFEdge[] };\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { bpmnElkLayout } from './chunk-XMVV7FRZ.js';
2
+ import './chunk-HOWK3ZOO.js';
3
+ import './chunk-RLAJNRF2.js';
4
+ import './chunk-L5Z22RLX.js';
5
+ //# sourceMappingURL=elk-FSFIEL6O.js.map
6
+ //# sourceMappingURL=elk-FSFIEL6O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"elk-FSFIEL6O.js"}
package/dist/index.cjs CHANGED
@@ -762,6 +762,7 @@ var BPMN_THEME = {
762
762
  fillSoft: "#f8fbff",
763
763
  fillSubtle: "#eef6ff",
764
764
  stroke: "#334155",
765
+ strokeMuted: "#64748b",
765
766
  strokeSelected: "#2563eb",
766
767
  strokeWidth: 1.5,
767
768
  strokeWidthSelected: 2.5,
@@ -1401,71 +1402,33 @@ function SubProcessNode({ data, selected }) {
1401
1402
  }
1402
1403
  );
1403
1404
  }
1404
- var HEADER_WIDTH = 30;
1405
- var HEADER_HEIGHT = 28;
1406
- function PoolNode({ data, selected }) {
1407
- const d = data;
1408
- const stroke = resolveStroke(selected, d.color?.stroke);
1409
- const sw = resolveStrokeWidth(selected);
1410
- const orientation = d.orientation ?? "horizontal";
1411
- const fill = d.color?.fill ?? "#f8fbff";
1412
- const headerFill = d.color?.fill ?? "#eaf2ff";
1413
- if (orientation === "vertical") {
1414
- return /* @__PURE__ */ jsxRuntime.jsxs(
1415
- "div",
1416
- {
1417
- style: {
1418
- width: "100%",
1419
- height: "100%",
1420
- display: "flex",
1421
- flexDirection: "column",
1422
- border: `${sw}px solid ${stroke}`,
1423
- borderRadius: 4,
1424
- boxSizing: "border-box",
1425
- background: fill,
1426
- boxShadow: resolveNodeShadow(selected),
1427
- overflow: "hidden",
1428
- fontFamily: BPMN_THEME.fontFamily,
1429
- transition: BPMN_THEME.transition
1430
- },
1431
- children: [
1432
- /* @__PURE__ */ jsxRuntime.jsx(
1433
- "div",
1434
- {
1435
- "aria-label": "pool-header",
1436
- className: "pool-drag-handle",
1437
- style: {
1438
- height: HEADER_HEIGHT,
1439
- minHeight: HEADER_HEIGHT,
1440
- borderBottom: `${sw}px solid ${stroke}`,
1441
- display: "flex",
1442
- alignItems: "center",
1443
- justifyContent: "center",
1444
- background: headerFill,
1445
- padding: "0 8px",
1446
- cursor: "grab"
1447
- },
1448
- children: /* @__PURE__ */ jsxRuntime.jsx(
1449
- "span",
1450
- {
1451
- style: {
1452
- fontSize: BPMN_THEME.fontSize,
1453
- fontWeight: 600,
1454
- color: BPMN_THEME.labelColor,
1455
- whiteSpace: "nowrap",
1456
- overflow: "hidden",
1457
- textOverflow: "ellipsis"
1458
- },
1459
- children: d.label
1460
- }
1461
- )
1462
- }
1463
- ),
1464
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, position: "relative" } })
1465
- ]
1405
+ function PoolNode({ selected }) {
1406
+ const sw = selected ? 2.5 : 2;
1407
+ const borderColor = selected ? "var(--color-accent)" : "color-mix(in srgb, var(--color-accent) 40%, transparent)";
1408
+ const bg = "color-mix(in srgb, var(--color-accent) 3%, white)";
1409
+ return /* @__PURE__ */ jsxRuntime.jsx(
1410
+ "div",
1411
+ {
1412
+ style: {
1413
+ width: "100%",
1414
+ height: "100%",
1415
+ border: `${sw}px solid ${borderColor}`,
1416
+ borderRadius: 6,
1417
+ boxSizing: "border-box",
1418
+ background: bg,
1419
+ fontFamily: BPMN_THEME.fontFamily,
1420
+ transition: "border-color 140ms ease",
1421
+ overflow: "visible"
1466
1422
  }
1467
- );
1468
- }
1423
+ }
1424
+ );
1425
+ }
1426
+ var LABEL_W = 28;
1427
+ function LaneNode({ data, selected }) {
1428
+ const d = data;
1429
+ const stroke = selected ? BPMN_THEME.strokeSelected : d.color?.stroke ?? BPMN_THEME.strokeMuted;
1430
+ const bodyFill = "#fbfdff";
1431
+ const labelFill = d.color?.fill ?? "#eef6ff";
1469
1432
  return /* @__PURE__ */ jsxRuntime.jsxs(
1470
1433
  "div",
1471
1434
  {
@@ -1473,11 +1436,11 @@ function PoolNode({ data, selected }) {
1473
1436
  width: "100%",
1474
1437
  height: "100%",
1475
1438
  display: "flex",
1476
- border: `${sw}px solid ${stroke}`,
1477
- borderRadius: 4,
1439
+ flexDirection: "row",
1440
+ border: `1px solid ${stroke}`,
1441
+ borderRadius: 3,
1478
1442
  boxSizing: "border-box",
1479
- background: fill,
1480
- boxShadow: resolveNodeShadow(selected),
1443
+ background: bodyFill,
1481
1444
  overflow: "hidden",
1482
1445
  fontFamily: BPMN_THEME.fontFamily,
1483
1446
  transition: BPMN_THEME.transition
@@ -1486,17 +1449,19 @@ function PoolNode({ data, selected }) {
1486
1449
  /* @__PURE__ */ jsxRuntime.jsx(
1487
1450
  "div",
1488
1451
  {
1489
- "aria-label": "pool-header",
1490
- className: "pool-drag-handle",
1452
+ "aria-label": "lane-header",
1453
+ className: "lane-drag-handle",
1491
1454
  style: {
1492
- width: HEADER_WIDTH,
1493
- minWidth: HEADER_WIDTH,
1494
- borderRight: `${sw}px solid ${stroke}`,
1455
+ width: LABEL_W,
1456
+ minWidth: LABEL_W,
1457
+ borderRight: `1px solid ${stroke}`,
1495
1458
  display: "flex",
1496
1459
  alignItems: "center",
1497
1460
  justifyContent: "center",
1498
- background: headerFill,
1499
- cursor: "grab"
1461
+ background: labelFill,
1462
+ cursor: "grab",
1463
+ userSelect: "none",
1464
+ flexShrink: 0
1500
1465
  },
1501
1466
  children: /* @__PURE__ */ jsxRuntime.jsx(
1502
1467
  "span",
@@ -1505,7 +1470,7 @@ function PoolNode({ data, selected }) {
1505
1470
  writingMode: "vertical-rl",
1506
1471
  transform: "rotate(180deg)",
1507
1472
  fontSize: BPMN_THEME.fontSize,
1508
- fontWeight: 600,
1473
+ fontWeight: 500,
1509
1474
  color: BPMN_THEME.labelColor,
1510
1475
  whiteSpace: "nowrap",
1511
1476
  overflow: "hidden",
@@ -1517,118 +1482,7 @@ function PoolNode({ data, selected }) {
1517
1482
  )
1518
1483
  }
1519
1484
  ),
1520
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, position: "relative" } })
1521
- ]
1522
- }
1523
- );
1524
- }
1525
- var HEADER_HEIGHT2 = 24;
1526
- var HEADER_WIDTH2 = 28;
1527
- function LaneNode({ data, selected }) {
1528
- const d = data;
1529
- const stroke = resolveStroke(selected, d.color?.stroke);
1530
- const sw = resolveStrokeWidth(selected);
1531
- const orientation = d.orientation ?? "horizontal";
1532
- const fill = d.color?.fill ?? "#fbfdff";
1533
- const headerFill = d.color?.fill ?? "#eef6ff";
1534
- if (orientation === "vertical") {
1535
- return /* @__PURE__ */ jsxRuntime.jsxs(
1536
- "div",
1537
- {
1538
- style: {
1539
- width: "100%",
1540
- height: "100%",
1541
- display: "flex",
1542
- border: `${sw}px solid ${stroke}`,
1543
- borderRadius: 2,
1544
- boxSizing: "border-box",
1545
- background: fill,
1546
- boxShadow: resolveNodeShadow(selected),
1547
- overflow: "hidden",
1548
- fontFamily: BPMN_THEME.fontFamily,
1549
- transition: BPMN_THEME.transition
1550
- },
1551
- children: [
1552
- /* @__PURE__ */ jsxRuntime.jsx(
1553
- "div",
1554
- {
1555
- "aria-label": "lane-header",
1556
- className: "lane-drag-handle",
1557
- style: {
1558
- width: HEADER_WIDTH2,
1559
- minWidth: HEADER_WIDTH2,
1560
- borderRight: `1px solid ${stroke}`,
1561
- display: "flex",
1562
- alignItems: "center",
1563
- justifyContent: "center",
1564
- background: headerFill,
1565
- padding: "6px 0",
1566
- cursor: "grab"
1567
- },
1568
- children: /* @__PURE__ */ jsxRuntime.jsx(
1569
- "span",
1570
- {
1571
- style: {
1572
- writingMode: "vertical-rl",
1573
- transform: "rotate(180deg)",
1574
- fontSize: BPMN_THEME.fontSize,
1575
- fontWeight: 500,
1576
- color: BPMN_THEME.labelColor,
1577
- whiteSpace: "nowrap",
1578
- overflow: "hidden",
1579
- textOverflow: "ellipsis",
1580
- maxHeight: "90%"
1581
- },
1582
- children: d.label
1583
- }
1584
- )
1585
- }
1586
- ),
1587
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, position: "relative" } })
1588
- ]
1589
- }
1590
- );
1591
- }
1592
- return /* @__PURE__ */ jsxRuntime.jsxs(
1593
- "div",
1594
- {
1595
- style: {
1596
- width: "100%",
1597
- height: "100%",
1598
- display: "flex",
1599
- flexDirection: "column",
1600
- border: `${sw}px solid ${stroke}`,
1601
- borderRadius: 2,
1602
- boxSizing: "border-box",
1603
- background: fill,
1604
- boxShadow: resolveNodeShadow(selected),
1605
- overflow: "hidden",
1606
- fontFamily: BPMN_THEME.fontFamily,
1607
- transition: BPMN_THEME.transition
1608
- },
1609
- children: [
1610
- /* @__PURE__ */ jsxRuntime.jsx(
1611
- "div",
1612
- {
1613
- "aria-label": "lane-header",
1614
- className: "lane-drag-handle",
1615
- style: {
1616
- height: HEADER_HEIGHT2,
1617
- minHeight: HEADER_HEIGHT2,
1618
- borderBottom: `1px solid ${stroke}`,
1619
- display: "flex",
1620
- alignItems: "center",
1621
- paddingLeft: 8,
1622
- background: headerFill,
1623
- fontSize: BPMN_THEME.fontSize,
1624
- fontWeight: 500,
1625
- color: BPMN_THEME.labelColor,
1626
- cursor: "grab"
1627
- },
1628
- children: d.label
1629
- }
1630
- ),
1631
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, position: "relative" } })
1485
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nodrag", style: { flex: 1, position: "relative" } })
1632
1486
  ]
1633
1487
  }
1634
1488
  );
@@ -4418,7 +4272,9 @@ function createBpmnNode(options) {
4418
4272
  height: options.height ?? size.height,
4419
4273
  zIndex: getBpmnNodeZIndex(options.elementType),
4420
4274
  ...options.parentId ? { parentId: options.parentId } : {},
4421
- ...options.elementType === "Lane" && options.parentId ? { extent: "parent" } : {},
4275
+ // extent:"parent" is intentionally omitted for Lane it blocks resize beyond
4276
+ // the Pool boundary before the Pool/Lane coupling can expand the Pool.
4277
+ // Lane containment during drag is handled programmatically in handleNodeDragStop.
4422
4278
  ...dragHandle ? { dragHandle } : {}
4423
4279
  };
4424
4280
  }
@@ -4435,8 +4291,13 @@ function withBpmnNodeZIndexes(nodes) {
4435
4291
  });
4436
4292
  }
4437
4293
  var BPMN_POOL_LANE_LAYOUT = {
4438
- poolHeaderSize: 30,
4439
- laneHeaderSize: 24,
4294
+ // Pool inner padding on all sides (no left label strip).
4295
+ // 8px = pool border (2px) + 6px visible gap.
4296
+ poolPad: 8,
4297
+ /** @deprecated use poolPad */
4298
+ poolHeaderSize: 8,
4299
+ // Lane left label strip (vertical text).
4300
+ laneHeaderSize: 28,
4440
4301
  laneGap: 0,
4441
4302
  verticalPoolHeaderSize: 28,
4442
4303
  minLaneSize: 96
@@ -4560,6 +4421,30 @@ function getBpmnPoolLanes(state, poolId) {
4560
4421
  (node) => node.data.elementType === "Lane"
4561
4422
  );
4562
4423
  }
4424
+ function getBpmnNodeBounds(state, node) {
4425
+ const position = getBpmnNodeAbsolutePosition(state, node) ?? node.position;
4426
+ const size = getBpmnNodeSize(node);
4427
+ return {
4428
+ ...position,
4429
+ width: size.width,
4430
+ height: size.height
4431
+ };
4432
+ }
4433
+ function findBpmnLaneAt(state, position) {
4434
+ return state.nodes.find((node) => {
4435
+ if (node.data.elementType !== "Lane") return false;
4436
+ const bounds = getBpmnNodeBounds(state, node);
4437
+ return position.x >= bounds.x && position.x <= bounds.x + bounds.width && position.y >= bounds.y && position.y <= bounds.y + bounds.height;
4438
+ });
4439
+ }
4440
+ function getAncestorLaneId(state, node) {
4441
+ let current = node;
4442
+ while (current) {
4443
+ if (current.data.elementType === "Lane") return current.id;
4444
+ current = current.parentId ? diagramsCore.getNode(state, current.parentId) : void 0;
4445
+ }
4446
+ return void 0;
4447
+ }
4563
4448
  function getNodeDimension(node, axis) {
4564
4449
  return node[axis] ?? node.measured?.[axis] ?? getBpmnElementSize(node.data.elementType)[axis];
4565
4450
  }
@@ -4568,16 +4453,18 @@ function getBpmnLaneOrderPosition(lane, orientation) {
4568
4453
  return orientation === "vertical" ? lane.position.x + size.width / 2 : lane.position.y + size.height / 2;
4569
4454
  }
4570
4455
  function resizeHorizontalBpmnLanes(lanes, pool) {
4456
+ const pad = BPMN_POOL_LANE_LAYOUT.poolPad;
4571
4457
  const poolSize = getBpmnNodeSize(pool);
4572
- const laneWidth = Math.max(BPMN_POOL_LANE_LAYOUT.minLaneSize, poolSize.width - BPMN_POOL_LANE_LAYOUT.poolHeaderSize);
4573
- const laneHeight = Math.max(BPMN_POOL_LANE_LAYOUT.minLaneSize, poolSize.height / Math.max(1, lanes.length));
4458
+ const laneWidth = Math.max(BPMN_POOL_LANE_LAYOUT.minLaneSize, poolSize.width - pad * 2);
4459
+ const availH = Math.max(0, poolSize.height - pad * 2);
4460
+ const laneHeight = Math.max(BPMN_POOL_LANE_LAYOUT.minLaneSize, availH / Math.max(1, lanes.length));
4574
4461
  return lanes.map((lane, index) => ({
4575
4462
  ...lane,
4576
- position: { x: BPMN_POOL_LANE_LAYOUT.poolHeaderSize, y: index * laneHeight },
4463
+ position: { x: pad, y: pad + index * laneHeight },
4577
4464
  width: laneWidth,
4578
4465
  height: laneHeight,
4579
4466
  parentId: pool.id,
4580
- extent: "parent",
4467
+ // extent:"parent" intentionally omitted — blocks resize→Pool coupling.
4581
4468
  zIndex: getBpmnNodeZIndex("Lane"),
4582
4469
  data: { ...lane.data, orientation: "horizontal", laneIndex: index }
4583
4470
  }));
@@ -4608,10 +4495,22 @@ function layoutBpmnPoolLaneNodes(nodes, poolId) {
4608
4495
  return getBpmnLaneOrderPosition(a, orientation) - getBpmnLaneOrderPosition(b, orientation);
4609
4496
  });
4610
4497
  if (lanes.length === 0) return withBpmnNodeZIndexes(nodes);
4611
- const laneMap = new Map(
4612
- (orientation === "vertical" ? resizeVerticalBpmnLanes(lanes, pool) : resizeHorizontalBpmnLanes(lanes, pool)).map((lane) => [lane.id, lane])
4498
+ const resizedLanes = orientation === "vertical" ? resizeVerticalBpmnLanes(lanes, pool) : resizeHorizontalBpmnLanes(lanes, pool);
4499
+ const laneMap = new Map(resizedLanes.map((lane) => [lane.id, lane]));
4500
+ const pad = BPMN_POOL_LANE_LAYOUT.poolPad;
4501
+ const totalLaneH = resizedLanes.reduce((sum, l) => sum + (l.height ?? 0), 0);
4502
+ const requiredPoolH = totalLaneH + pad * 2;
4503
+ const poolMap = /* @__PURE__ */ new Map();
4504
+ if (orientation === "horizontal") {
4505
+ const currentH = getBpmnNodeSize(pool).height;
4506
+ poolMap.set(pool.id, {
4507
+ ...pool,
4508
+ height: Math.max(requiredPoolH, currentH)
4509
+ });
4510
+ }
4511
+ return withBpmnNodeZIndexes(
4512
+ nodes.map((node) => laneMap.get(node.id) ?? poolMap.get(node.id) ?? node)
4613
4513
  );
4614
- return withBpmnNodeZIndexes(nodes.map((node) => laneMap.get(node.id) ?? node));
4615
4514
  }
4616
4515
  function reorderBpmnLaneAfterDrop(nodes, laneId) {
4617
4516
  const lane = nodes.find((node) => node.id === laneId && node.data.elementType === "Lane");
@@ -4670,6 +4569,53 @@ function findBpmnContainerAt(state, options) {
4670
4569
  }
4671
4570
  });
4672
4571
  }
4572
+ function resolveBpmnDropTarget(state, options) {
4573
+ if (options.elementType === "Pool") {
4574
+ return { highlightedContainerId: null, invalidContainerId: null, isValid: true };
4575
+ }
4576
+ if (options.elementType === "Lane") {
4577
+ const pool = state.nodes.find((node) => {
4578
+ if (node.data.elementType !== "Pool") return false;
4579
+ const bounds = getBpmnNodeBounds(state, node);
4580
+ return options.position.x >= bounds.x && options.position.x <= bounds.x + bounds.width && options.position.y >= bounds.y && options.position.y <= bounds.y + bounds.height;
4581
+ });
4582
+ return {
4583
+ ...pool ? { container: pool } : {},
4584
+ highlightedContainerId: pool?.id ?? null,
4585
+ invalidContainerId: null,
4586
+ isValid: Boolean(pool)
4587
+ };
4588
+ }
4589
+ const lane = findBpmnLaneAt(state, options.position);
4590
+ if (lane) {
4591
+ return {
4592
+ container: lane,
4593
+ highlightedContainerId: lane.id,
4594
+ invalidContainerId: null,
4595
+ isValid: true
4596
+ };
4597
+ }
4598
+ const container = findBpmnContainerAt(state, {
4599
+ position: options.position,
4600
+ ...options.excludeId ? { excludeId: options.excludeId } : {}
4601
+ });
4602
+ if (container?.data.elementType === "Pool") {
4603
+ const hasLanes = getBpmnPoolLanes(state, container.id).length > 0;
4604
+ if (hasLanes) {
4605
+ return {
4606
+ highlightedContainerId: null,
4607
+ invalidContainerId: container.id,
4608
+ isValid: false
4609
+ };
4610
+ }
4611
+ }
4612
+ return {
4613
+ ...container ? { container } : {},
4614
+ highlightedContainerId: getAncestorLaneId(state, container) ?? container?.id ?? null,
4615
+ invalidContainerId: null,
4616
+ isValid: true
4617
+ };
4618
+ }
4673
4619
  function getBpmnNodeCenter(node, absolutePosition) {
4674
4620
  return diagramsCore.getNodeCenterPosition(absolutePosition, getBpmnNodeSize(node));
4675
4621
  }
@@ -5340,6 +5286,7 @@ exports.reparentBpmnNodeCommand = reparentBpmnNodeCommand;
5340
5286
  exports.replaceBpmnNodeCommand = replaceBpmnNodeCommand;
5341
5287
  exports.resizeBpmnNodeByHandleCommand = resizeBpmnNodeByHandleCommand;
5342
5288
  exports.resizeBpmnNodeCommand = resizeBpmnNodeCommand;
5289
+ exports.resolveBpmnDropTarget = resolveBpmnDropTarget;
5343
5290
  exports.restoreBpmnHistory = restoreBpmnHistory;
5344
5291
  exports.routeBpmnEdgeCommand = routeBpmnEdgeCommand;
5345
5292
  exports.runBatchSimulation = runBatchSimulation;