@bpmnkit/core 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bpmn/auto-layout.js +190 -21
- package/dist/bpmn/bpmn-builder.d.ts +104 -16
- package/dist/bpmn/bpmn-builder.js +561 -374
- package/dist/bpmn/bpmn-model.d.ts +6 -0
- package/dist/bpmn/bpmn-parser.js +10 -0
- package/dist/bpmn/bpmn-serializer.js +10 -1
- package/dist/bpmn/compact.js +1 -0
- package/dist/bpmn/index.d.ts +15 -1
- package/dist/bpmn/index.js +17 -1
- package/dist/bpmn/optimize/tasks.js +1 -0
- package/dist/bpmn/zeebe-extensions.d.ts +2 -0
- package/dist/bpmn/zeebe-extensions.js +3 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/layout/astar.d.ts +15 -0
- package/dist/layout/astar.js +191 -0
- package/dist/layout/block-builder.d.ts +37 -0
- package/dist/layout/block-builder.js +154 -0
- package/dist/layout/block-layout.d.ts +9 -0
- package/dist/layout/block-layout.js +163 -0
- package/dist/layout/coordinates.d.ts +23 -0
- package/dist/layout/coordinates.js +612 -88
- package/dist/layout/index.d.ts +5 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/layout-engine.d.ts +2 -1
- package/dist/layout/layout-engine.js +209 -29
- package/dist/layout/overlap.js +3 -0
- package/dist/layout/routing.d.ts +10 -3
- package/dist/layout/routing.js +254 -71
- package/dist/layout/types.d.ts +6 -0
- package/package.json +4 -3
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { ELEMENT_SIZES } from "./types.js";
|
|
2
|
+
import { GRID_CELL_WIDTH } from "./types.js";
|
|
3
|
+
const H_GAP = 50;
|
|
4
|
+
const V_GAP = 80;
|
|
5
|
+
const OUTER_PADDING = 40;
|
|
6
|
+
/** Compute label bounds for a laid-out block node (same logic as coordinates.ts). */
|
|
7
|
+
function computeLabelBoundsForBlock(block, nodeIndex) {
|
|
8
|
+
const el = nodeIndex.get(block.id);
|
|
9
|
+
if (!el?.name)
|
|
10
|
+
return undefined;
|
|
11
|
+
const labelWidth = Math.min(Math.max(el.name.length * 7, 40), GRID_CELL_WIDTH);
|
|
12
|
+
const labelHeight = 14;
|
|
13
|
+
switch (block.type) {
|
|
14
|
+
case "startEvent":
|
|
15
|
+
case "endEvent":
|
|
16
|
+
case "intermediateThrowEvent":
|
|
17
|
+
case "intermediateCatchEvent":
|
|
18
|
+
case "exclusiveGateway":
|
|
19
|
+
case "parallelGateway":
|
|
20
|
+
case "inclusiveGateway":
|
|
21
|
+
case "eventBasedGateway":
|
|
22
|
+
return {
|
|
23
|
+
x: block.x + block.width / 2 - labelWidth / 2,
|
|
24
|
+
y: block.y + block.height + 4,
|
|
25
|
+
width: labelWidth,
|
|
26
|
+
height: labelHeight,
|
|
27
|
+
};
|
|
28
|
+
default:
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function countBlockNodes(block) {
|
|
33
|
+
if (block.kind === "node")
|
|
34
|
+
return 1;
|
|
35
|
+
if (block.kind === "sequence")
|
|
36
|
+
return block.items.reduce((s, b) => s + countBlockNodes(b), 0);
|
|
37
|
+
return 2 + block.branches.reduce((s, b) => s + countBlockNodes(b), 0);
|
|
38
|
+
}
|
|
39
|
+
/** Size a block bottom-up: sets width/height on all blocks in the tree. */
|
|
40
|
+
function sizeBlock(block) {
|
|
41
|
+
if (block.kind === "node") {
|
|
42
|
+
const size = ELEMENT_SIZES[block.type] ?? { width: 100, height: 80 };
|
|
43
|
+
block.width = size.width;
|
|
44
|
+
block.height = size.height;
|
|
45
|
+
}
|
|
46
|
+
else if (block.kind === "sequence") {
|
|
47
|
+
for (const item of block.items) {
|
|
48
|
+
sizeBlock(item);
|
|
49
|
+
}
|
|
50
|
+
const n = block.items.length;
|
|
51
|
+
block.width =
|
|
52
|
+
block.items.reduce((sum, item) => sum + item.width, 0) + Math.max(0, n - 1) * H_GAP;
|
|
53
|
+
block.height = block.items.reduce((max, item) => Math.max(max, item.height), 0);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// GatewayBlock
|
|
57
|
+
sizeBlock(block.split);
|
|
58
|
+
sizeBlock(block.join);
|
|
59
|
+
for (const branch of block.branches) {
|
|
60
|
+
sizeBlock(branch);
|
|
61
|
+
}
|
|
62
|
+
block.branchColumnWidth =
|
|
63
|
+
block.branches.length > 0 ? block.branches.reduce((max, b) => Math.max(max, b.width), 0) : 0;
|
|
64
|
+
block.width = block.split.width + H_GAP + block.branchColumnWidth + H_GAP + block.join.width;
|
|
65
|
+
// Each branch gets at least V_GAP height (for empty bypass edges).
|
|
66
|
+
// Use effectiveH consistently in both sizing and positioning.
|
|
67
|
+
const totalBranchH = block.branches.reduce((sum, b) => sum + Math.max(b.height, V_GAP), 0) +
|
|
68
|
+
Math.max(0, block.branches.length - 1) * V_GAP;
|
|
69
|
+
block.height = Math.max(totalBranchH, block.split.height, block.join.height);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Position a block top-down given its absolute (x,y) top-left origin. */
|
|
73
|
+
function positionBlock(block, x, y) {
|
|
74
|
+
block.x = x;
|
|
75
|
+
block.y = y;
|
|
76
|
+
if (block.kind === "node") {
|
|
77
|
+
// Nothing more to do — leaf
|
|
78
|
+
}
|
|
79
|
+
else if (block.kind === "sequence") {
|
|
80
|
+
let curX = x;
|
|
81
|
+
for (const item of block.items) {
|
|
82
|
+
// Center each item vertically within the sequence
|
|
83
|
+
const itemY = y + (block.height - item.height) / 2;
|
|
84
|
+
positionBlock(item, curX, itemY);
|
|
85
|
+
curX += item.width + H_GAP;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// GatewayBlock: split at left, join at right, branches stacked in middle
|
|
90
|
+
const splitY = y + block.height / 2 - block.split.height / 2;
|
|
91
|
+
positionBlock(block.split, x, splitY);
|
|
92
|
+
const joinX = x + block.width - block.join.width;
|
|
93
|
+
const joinY = y + block.height / 2 - block.join.height / 2;
|
|
94
|
+
positionBlock(block.join, joinX, joinY);
|
|
95
|
+
// Reorder branches: heaviest (most nodes) at center, alternating above/below
|
|
96
|
+
const sortedBySize = [...block.branches].sort((a, b) => countBlockNodes(b) - countBlockNodes(a));
|
|
97
|
+
const bc = block.branches.length;
|
|
98
|
+
const orderedBranches = new Array(bc);
|
|
99
|
+
const bm = Math.floor((bc - 1) / 2);
|
|
100
|
+
// biome-ignore lint/style/noNonNullAssertion: sortedBySize is non-empty (bc >= 1)
|
|
101
|
+
orderedBranches[bm] = sortedBySize[0];
|
|
102
|
+
let ba = bm - 1;
|
|
103
|
+
let bb = bm + 1;
|
|
104
|
+
for (let si = 1; si < bc;) {
|
|
105
|
+
// biome-ignore lint/style/noNonNullAssertion: si < bc ensures element exists
|
|
106
|
+
if (bb < bc && si < bc)
|
|
107
|
+
orderedBranches[bb++] = sortedBySize[si++];
|
|
108
|
+
// biome-ignore lint/style/noNonNullAssertion: si < bc ensures element exists
|
|
109
|
+
if (ba >= 0 && si < bc)
|
|
110
|
+
orderedBranches[ba--] = sortedBySize[si++];
|
|
111
|
+
}
|
|
112
|
+
block.branches = orderedBranches;
|
|
113
|
+
// Branches stacked top-to-bottom, using effectiveH matching sizeBlock
|
|
114
|
+
const totalBranchH = block.branches.reduce((sum, b) => sum + Math.max(b.height, V_GAP), 0) +
|
|
115
|
+
Math.max(0, block.branches.length - 1) * V_GAP;
|
|
116
|
+
const branchX = x + block.split.width + H_GAP;
|
|
117
|
+
let branchY = y + (block.height - totalBranchH) / 2;
|
|
118
|
+
for (const branch of block.branches) {
|
|
119
|
+
const effectiveH = Math.max(branch.height, V_GAP);
|
|
120
|
+
positionBlock(branch, branchX, branchY + (effectiveH - branch.height) / 2);
|
|
121
|
+
branchY += effectiveH + V_GAP;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Flatten block tree into LayoutNode list. */
|
|
126
|
+
function flattenBlock(block, nodeIndex, out) {
|
|
127
|
+
if (block.kind === "node") {
|
|
128
|
+
const layoutNode = {
|
|
129
|
+
id: block.id,
|
|
130
|
+
type: block.type,
|
|
131
|
+
bounds: { x: block.x, y: block.y, width: block.width, height: block.height },
|
|
132
|
+
layer: 0,
|
|
133
|
+
position: 0,
|
|
134
|
+
label: block.label,
|
|
135
|
+
};
|
|
136
|
+
layoutNode.labelBounds = computeLabelBoundsForBlock(block, nodeIndex);
|
|
137
|
+
out.push(layoutNode);
|
|
138
|
+
}
|
|
139
|
+
else if (block.kind === "sequence") {
|
|
140
|
+
for (const item of block.items) {
|
|
141
|
+
flattenBlock(item, nodeIndex, out);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
flattenBlock(block.split, nodeIndex, out);
|
|
146
|
+
flattenBlock(block.join, nodeIndex, out);
|
|
147
|
+
for (const branch of block.branches) {
|
|
148
|
+
flattenBlock(branch, nodeIndex, out);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Apply block-based layout: size (bottom-up) then position (top-down).
|
|
154
|
+
* Returns LayoutNode[] with absolute positions.
|
|
155
|
+
*/
|
|
156
|
+
export function applyBlockLayout(root, nodeIndex) {
|
|
157
|
+
sizeBlock(root);
|
|
158
|
+
positionBlock(root, OUTER_PADDING, OUTER_PADDING);
|
|
159
|
+
const nodes = [];
|
|
160
|
+
flattenBlock(root, nodeIndex, nodes);
|
|
161
|
+
return nodes;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=block-layout.js.map
|
|
@@ -22,6 +22,7 @@ export declare function reassignXCoordinates(layoutNodes: LayoutNode[], orderedL
|
|
|
22
22
|
* Align nodes in linear sequences to a common y-baseline.
|
|
23
23
|
* A "linear" node has ≤1 predecessor and ≤1 successor, and is not a gateway.
|
|
24
24
|
* Walks forward from each chain root, setting successors to the same center-y.
|
|
25
|
+
* Crosses split/join gateway pairs to align the full branch spine.
|
|
25
26
|
*/
|
|
26
27
|
export declare function alignBranchBaselines(layoutNodes: LayoutNode[], dag: DirectedGraph): void;
|
|
27
28
|
/**
|
|
@@ -53,6 +54,28 @@ export declare function alignBaselinePath(layoutNodes: LayoutNode[], dag: Direct
|
|
|
53
54
|
* Pass 2: single-branch gateways — placed one full grid row away, with peer-aware gap enforcement.
|
|
54
55
|
*/
|
|
55
56
|
export declare function distributeSplitBranches(layoutNodes: LayoutNode[], dag: DirectedGraph, backEdges?: BackEdge[]): void;
|
|
57
|
+
/**
|
|
58
|
+
* Assign integer grid-row indices to all nodes based on their final center-Y positions.
|
|
59
|
+
* Nodes whose center-Y values are within EPSILON pixels are placed in the same row.
|
|
60
|
+
* This eliminates pixel-tolerance guessing in port-side decisions: two nodes with the
|
|
61
|
+
* same gridRow are on the same horizontal row and should connect left-to-right.
|
|
62
|
+
*
|
|
63
|
+
* Called once, after ALL coordinate adjustments, just before edge routing.
|
|
64
|
+
*/
|
|
65
|
+
export declare function assignGridRows(layoutNodes: LayoutNode[]): void;
|
|
66
|
+
/**
|
|
67
|
+
* Snap nodes to common Y rows for matrix-like alignment.
|
|
68
|
+
* Groups nodes that share a CY (from alignment passes), then merges
|
|
69
|
+
* close groups into a single row — moving entire groups as units.
|
|
70
|
+
* Boundary events are excluded (they are repositioned later).
|
|
71
|
+
*/
|
|
72
|
+
export declare function snapToYRows(layoutNodes: LayoutNode[]): void;
|
|
73
|
+
/**
|
|
74
|
+
* Pull each branch subtree toward the baseline, closing unnecessary vertical gaps.
|
|
75
|
+
* Uses per-element 2D collision detection — only actual element-to-element overlaps
|
|
76
|
+
* constrain the movement, not the branch's full bounding box.
|
|
77
|
+
*/
|
|
78
|
+
export declare function compactBranches(layoutNodes: LayoutNode[], dag: DirectedGraph, backEdges?: BackEdge[]): void;
|
|
56
79
|
/**
|
|
57
80
|
* Resolve overlaps within each layer by pushing nodes apart.
|
|
58
81
|
* Sorts nodes by Y within each layer and ensures minimum gap.
|