@ngroznykh/papirus 0.9.2 → 0.9.4
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/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ConnectionManager.d.ts +3 -0
- package/dist/core/ConnectionManager.d.ts.map +1 -1
- package/dist/core/DiagramRenderer.d.ts +2 -1
- package/dist/core/DiagramRenderer.d.ts.map +1 -1
- package/dist/core/InteractionManager.d.ts.map +1 -1
- package/dist/elements/Edge.d.ts.map +1 -1
- package/dist/elements/Node.d.ts +29 -4
- package/dist/elements/Node.d.ts.map +1 -1
- package/dist/elements/TextLabel.d.ts.map +1 -1
- package/dist/elements/composite/CContainer.d.ts +14 -0
- package/dist/elements/composite/CContainer.d.ts.map +1 -1
- package/dist/elements/composite/CompositeNode.d.ts +4 -0
- package/dist/elements/composite/CompositeNode.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/papirus.js +462 -173
- package/dist/papirus.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/Serializer.d.ts.map +1 -1
- package/dist/utils/SvgExporter.d.ts.map +1 -1
- package/dist/utils/contentBounds.d.ts +1 -0
- package/dist/utils/contentBounds.d.ts.map +1 -1
- package/dist/utils/edgePath.d.ts +3 -1
- package/dist/utils/edgePath.d.ts.map +1 -1
- package/dist/utils/labelPlacement.d.ts +13 -0
- package/dist/utils/labelPlacement.d.ts.map +1 -0
- package/dist/utils/style.d.ts +2 -0
- package/dist/utils/style.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/papirus.js
CHANGED
|
@@ -1456,7 +1456,7 @@ function getContentBounds({ nodes, edges, groups, includeInvisible = false }) {
|
|
|
1456
1456
|
let hasContent = false;
|
|
1457
1457
|
const consider = (source) => {
|
|
1458
1458
|
if (!includeInvisible && !source.visible) return;
|
|
1459
|
-
const bounds = source.getBounds();
|
|
1459
|
+
const bounds = source.getVisualBounds?.() ?? source.getBounds();
|
|
1460
1460
|
minX = Math.min(minX, bounds.x);
|
|
1461
1461
|
minY = Math.min(minY, bounds.y);
|
|
1462
1462
|
maxX = Math.max(maxX, bounds.x + bounds.width);
|
|
@@ -1671,6 +1671,133 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1671
1671
|
}
|
|
1672
1672
|
};
|
|
1673
1673
|
//#endregion
|
|
1674
|
+
//#region src/utils/edgePath.ts
|
|
1675
|
+
/**
|
|
1676
|
+
* Point and tangent angle at a normalized position along an edge path (0..1 by length).
|
|
1677
|
+
*/
|
|
1678
|
+
function getPathPointAt(path, type, position) {
|
|
1679
|
+
if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
|
|
1680
|
+
const samples = [];
|
|
1681
|
+
const steps = 20;
|
|
1682
|
+
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
1683
|
+
const start = path[i - 1];
|
|
1684
|
+
const control1 = path[i];
|
|
1685
|
+
const control2 = path[i + 1];
|
|
1686
|
+
const end = path[i + 2];
|
|
1687
|
+
for (let sample = 0; sample <= steps; sample++) {
|
|
1688
|
+
const t = sample / steps;
|
|
1689
|
+
if (samples.length > 0 && t === 0) continue;
|
|
1690
|
+
samples.push(bezierPoint(start, control1, control2, end, t));
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return getPointAlongPolyline(samples, position);
|
|
1694
|
+
}
|
|
1695
|
+
function getPointAlongPolyline(path, position) {
|
|
1696
|
+
if (path.length === 0) return {
|
|
1697
|
+
point: {
|
|
1698
|
+
x: 0,
|
|
1699
|
+
y: 0
|
|
1700
|
+
},
|
|
1701
|
+
angle: 0
|
|
1702
|
+
};
|
|
1703
|
+
if (path.length === 1) return {
|
|
1704
|
+
point: path[0],
|
|
1705
|
+
angle: 0
|
|
1706
|
+
};
|
|
1707
|
+
let totalLength = 0;
|
|
1708
|
+
const segments = [];
|
|
1709
|
+
for (let i = 1; i < path.length; i++) {
|
|
1710
|
+
const start = path[i - 1];
|
|
1711
|
+
const end = path[i];
|
|
1712
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
1713
|
+
segments.push({
|
|
1714
|
+
start,
|
|
1715
|
+
end,
|
|
1716
|
+
length
|
|
1717
|
+
});
|
|
1718
|
+
totalLength += length;
|
|
1719
|
+
}
|
|
1720
|
+
const targetLength = totalLength * Math.max(0, Math.min(1, position));
|
|
1721
|
+
let accumulated = 0;
|
|
1722
|
+
for (const segment of segments) {
|
|
1723
|
+
if (accumulated + segment.length >= targetLength) {
|
|
1724
|
+
const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
|
|
1725
|
+
return {
|
|
1726
|
+
point: {
|
|
1727
|
+
x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
|
|
1728
|
+
y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
|
|
1729
|
+
},
|
|
1730
|
+
angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
accumulated += segment.length;
|
|
1734
|
+
}
|
|
1735
|
+
const lastSegment = segments[segments.length - 1];
|
|
1736
|
+
return {
|
|
1737
|
+
point: lastSegment.end,
|
|
1738
|
+
angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
/** Map tangent angle to a cardinal direction used by polyline routing. */
|
|
1742
|
+
function directionFromAngle(angle) {
|
|
1743
|
+
const twoPi = Math.PI * 2;
|
|
1744
|
+
const a = (angle % twoPi + twoPi) % twoPi;
|
|
1745
|
+
if (a >= Math.PI * .25 && a < Math.PI * .75) return "bottom";
|
|
1746
|
+
if (a >= Math.PI * .75 && a < Math.PI * 1.25) return "left";
|
|
1747
|
+
if (a >= Math.PI * 1.25 && a < Math.PI * 1.75) return "top";
|
|
1748
|
+
return "right";
|
|
1749
|
+
}
|
|
1750
|
+
function closestPointOnPolyline(polyline, point) {
|
|
1751
|
+
if (polyline.length < 2) return null;
|
|
1752
|
+
let totalLength = 0;
|
|
1753
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
1754
|
+
const start = polyline[i - 1];
|
|
1755
|
+
const end = polyline[i];
|
|
1756
|
+
totalLength += Math.hypot(end.x - start.x, end.y - start.y);
|
|
1757
|
+
}
|
|
1758
|
+
let bestPoint = polyline[0];
|
|
1759
|
+
let bestParam = 0;
|
|
1760
|
+
let bestDist = Infinity;
|
|
1761
|
+
let accumulated = 0;
|
|
1762
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
1763
|
+
const start = polyline[i - 1];
|
|
1764
|
+
const end = polyline[i];
|
|
1765
|
+
const dx = end.x - start.x;
|
|
1766
|
+
const dy = end.y - start.y;
|
|
1767
|
+
const lengthSq = dx * dx + dy * dy;
|
|
1768
|
+
const segLen = Math.sqrt(lengthSq);
|
|
1769
|
+
const t = lengthSq === 0 ? 0 : Math.max(0, Math.min(1, ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSq));
|
|
1770
|
+
const proj = {
|
|
1771
|
+
x: start.x + t * dx,
|
|
1772
|
+
y: start.y + t * dy
|
|
1773
|
+
};
|
|
1774
|
+
const dist = Math.hypot(point.x - proj.x, point.y - proj.y);
|
|
1775
|
+
if (dist < bestDist) {
|
|
1776
|
+
bestDist = dist;
|
|
1777
|
+
bestPoint = proj;
|
|
1778
|
+
bestParam = totalLength > 0 ? (accumulated + t * segLen) / totalLength : 0;
|
|
1779
|
+
}
|
|
1780
|
+
accumulated += segLen;
|
|
1781
|
+
}
|
|
1782
|
+
return {
|
|
1783
|
+
point: bestPoint,
|
|
1784
|
+
pathParam: bestParam,
|
|
1785
|
+
distance: bestDist
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Nearest point on an edge path to `point`.
|
|
1790
|
+
* Polylines project onto real segments so a click on a long orthogonal stroke
|
|
1791
|
+
* is not lost between length samples. Bezier paths are densified, then projected.
|
|
1792
|
+
*/
|
|
1793
|
+
function getClosestPointOnPath(path, type, point, samples = 48) {
|
|
1794
|
+
if (path.length < 2) return null;
|
|
1795
|
+
if (type !== "bezier" || path.length < 4) return closestPointOnPolyline(path, point);
|
|
1796
|
+
const densified = [];
|
|
1797
|
+
for (let i = 0; i <= samples; i++) densified.push(getPathPointAt(path, type, i / samples).point);
|
|
1798
|
+
return closestPointOnPolyline(densified, point);
|
|
1799
|
+
}
|
|
1800
|
+
//#endregion
|
|
1674
1801
|
//#region src/utils/direction.ts
|
|
1675
1802
|
/**
|
|
1676
1803
|
* Map outline param [0,1) to cardinal direction.
|
|
@@ -1983,6 +2110,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
1983
2110
|
this.startOutlineConnection(node, outlinePoint);
|
|
1984
2111
|
return true;
|
|
1985
2112
|
}
|
|
2113
|
+
if (this.attachToOutline) return false;
|
|
1986
2114
|
const hover = this.getAnchorAtPoint(node, point);
|
|
1987
2115
|
if (hover) {
|
|
1988
2116
|
this.sourceNode = node;
|
|
@@ -2047,7 +2175,18 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2047
2175
|
let snappedPoint = point;
|
|
2048
2176
|
let snappedDir;
|
|
2049
2177
|
let targetNode = this.getNodeAtPoint(point, true);
|
|
2050
|
-
|
|
2178
|
+
const edgeDrop = this.originalEdgeEndpoint?.edgeId ? this.findEdgeDropTarget(point, {
|
|
2179
|
+
maxScreenPx: 40,
|
|
2180
|
+
skipEdgeIds: new Set([edge.id])
|
|
2181
|
+
}) : null;
|
|
2182
|
+
if (edgeDrop) {
|
|
2183
|
+
const at = edgeDrop.edge.getPointAt(edgeDrop.pathParam);
|
|
2184
|
+
snappedPoint = edgeDrop.point;
|
|
2185
|
+
snappedDir = at ? directionFromAngle(at.angle) : void 0;
|
|
2186
|
+
targetNode = null;
|
|
2187
|
+
this.reconnectingOutlineParam = null;
|
|
2188
|
+
this.reconnectingTargetNodeId = null;
|
|
2189
|
+
} else if (this.attachToOutline) {
|
|
2051
2190
|
const tolerance = 40 / Math.max(this.renderer.zoom, 1e-4);
|
|
2052
2191
|
const toleranceSq = tolerance * tolerance;
|
|
2053
2192
|
let bestDistSq = Infinity;
|
|
@@ -2214,6 +2353,9 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2214
2353
|
this.previewTargetNodeId = null;
|
|
2215
2354
|
this.previewTargetEdgeId = null;
|
|
2216
2355
|
this.previewTargetPathParam = null;
|
|
2356
|
+
const rawNode = this.getRawNodeAtPoint(cursorPoint);
|
|
2357
|
+
const forbiddenFill = !!this._connectionValidator && !!this.sourceNode && !!rawNode && !this._connectionValidator(this.sourceNode.id, rawNode.id);
|
|
2358
|
+
const searchGroupFill = !!rawNode && (forbiddenFill || this.nodeContainsSource(rawNode) || this.nodeLooksLikeContainer(rawNode));
|
|
2217
2359
|
let targetNode = this.getNodeAtPoint(cursorPoint, false);
|
|
2218
2360
|
if (this.attachToOutline) {
|
|
2219
2361
|
const tolerance = 40 / Math.max(this.renderer.zoom, 1e-4);
|
|
@@ -2257,7 +2399,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2257
2399
|
}
|
|
2258
2400
|
}
|
|
2259
2401
|
}
|
|
2260
|
-
const edgeDrop = this.findEdgeDropTarget(cursorPoint);
|
|
2402
|
+
const edgeDrop = this.findEdgeDropTarget(cursorPoint, searchGroupFill ? { maxScreenPx: 40 } : void 0);
|
|
2261
2403
|
if (edgeDrop) {
|
|
2262
2404
|
let nodeDistance = Infinity;
|
|
2263
2405
|
if (targetNode) {
|
|
@@ -2265,7 +2407,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2265
2407
|
const dy = cursorPoint.y - snappedPoint.y;
|
|
2266
2408
|
nodeDistance = Math.sqrt(dx * dx + dy * dy);
|
|
2267
2409
|
}
|
|
2268
|
-
if (!targetNode || edgeDrop.distance
|
|
2410
|
+
if (searchGroupFill || !targetNode || edgeDrop.distance <= nodeDistance) {
|
|
2269
2411
|
targetNode = null;
|
|
2270
2412
|
this.previewTargetNodeId = null;
|
|
2271
2413
|
this.previewTargetOutlineParam = null;
|
|
@@ -2274,6 +2416,12 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2274
2416
|
this.previewTargetEdgeId = edgeDrop.edge.id;
|
|
2275
2417
|
this.previewTargetPathParam = edgeDrop.pathParam;
|
|
2276
2418
|
}
|
|
2419
|
+
} else if (searchGroupFill) {
|
|
2420
|
+
targetNode = null;
|
|
2421
|
+
this.previewTargetNodeId = null;
|
|
2422
|
+
this.previewTargetOutlineParam = null;
|
|
2423
|
+
this.previewTargetAnchorId = null;
|
|
2424
|
+
snappedPoint = cursorPoint;
|
|
2277
2425
|
}
|
|
2278
2426
|
if (targetNode) {
|
|
2279
2427
|
const forbidden = this._connectionValidator && this.sourceNode && !this._connectionValidator(this.sourceNode.id, targetNode.id);
|
|
@@ -2335,9 +2483,23 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2335
2483
|
y: event.worldY
|
|
2336
2484
|
};
|
|
2337
2485
|
let reconnected = false;
|
|
2486
|
+
const edgeDrop = this.originalEdgeEndpoint?.edgeId ? this.findEdgeDropTarget(point, {
|
|
2487
|
+
maxScreenPx: 40,
|
|
2488
|
+
skipEdgeIds: new Set([this.reconnectingEdge.id])
|
|
2489
|
+
}) : null;
|
|
2490
|
+
if (edgeDrop) {
|
|
2491
|
+
const endpoint = {
|
|
2492
|
+
edgeId: edgeDrop.edgeId,
|
|
2493
|
+
pathParam: edgeDrop.pathParam
|
|
2494
|
+
};
|
|
2495
|
+
if (this.reconnectingEndpoint === "start") this.reconnectingEdge.from = endpoint;
|
|
2496
|
+
else this.reconnectingEdge.to = endpoint;
|
|
2497
|
+
reconnected = true;
|
|
2498
|
+
this.emit("edgeReconnect", this.reconnectingEdge, this.reconnectingEndpoint);
|
|
2499
|
+
}
|
|
2338
2500
|
let node = this.getNodeAtPoint(point, true);
|
|
2339
2501
|
if (!node && this.attachToOutline && this.reconnectingTargetNodeId) node = this.renderer.getNode(this.reconnectingTargetNodeId) ?? null;
|
|
2340
|
-
if (node) {
|
|
2502
|
+
if (!reconnected && node) {
|
|
2341
2503
|
if (this.attachToOutline && this.reconnectingOutlineParam !== null) {
|
|
2342
2504
|
const endpoint = {
|
|
2343
2505
|
nodeId: node.id,
|
|
@@ -2542,11 +2704,12 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2542
2704
|
/**
|
|
2543
2705
|
* Find the nearest edge under the cursor for junction / note→relation drops.
|
|
2544
2706
|
*/
|
|
2545
|
-
findEdgeDropTarget(point) {
|
|
2546
|
-
const tolerance = Math.max(14, 20 / Math.max(this.renderer.zoom, 1e-4));
|
|
2707
|
+
findEdgeDropTarget(point, options) {
|
|
2708
|
+
const tolerance = options?.maxScreenPx === void 0 ? Math.max(14, 20 / Math.max(this.renderer.zoom, 1e-4)) : options.maxScreenPx / Math.max(this.renderer.zoom, 1e-4);
|
|
2547
2709
|
let best = null;
|
|
2548
2710
|
for (const edge of this.renderer.edges.values()) {
|
|
2549
2711
|
if (!edge.visible || edge.path.length < 2) continue;
|
|
2712
|
+
if (options?.skipEdgeIds?.has(edge.id)) continue;
|
|
2550
2713
|
const closest = edge.getClosestPointOnPath(point);
|
|
2551
2714
|
if (!closest || closest.distance > tolerance) continue;
|
|
2552
2715
|
if (!best || closest.distance < best.distance) best = {
|
|
@@ -2805,6 +2968,26 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2805
2968
|
}
|
|
2806
2969
|
return null;
|
|
2807
2970
|
}
|
|
2971
|
+
getRawNodeAtPoint(point) {
|
|
2972
|
+
const nodes = Array.from(this.renderer.nodes.values());
|
|
2973
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
2974
|
+
const node = nodes[i];
|
|
2975
|
+
if (node.visible && node.hitTest(point)) return node;
|
|
2976
|
+
}
|
|
2977
|
+
return null;
|
|
2978
|
+
}
|
|
2979
|
+
nodeLooksLikeContainer(node, source = this.sourceNode) {
|
|
2980
|
+
if (!source || source.id === node.id) return false;
|
|
2981
|
+
return node.width * node.height >= Math.max(source.width * source.height, 1) * 4;
|
|
2982
|
+
}
|
|
2983
|
+
nodeContainsSource(node, source = this.sourceNode) {
|
|
2984
|
+
if (!source || source.id === node.id) return false;
|
|
2985
|
+
const sourceBounds = source.getBounds();
|
|
2986
|
+
const nodeBounds = node.getBounds();
|
|
2987
|
+
const cx = sourceBounds.x + sourceBounds.width / 2;
|
|
2988
|
+
const cy = sourceBounds.y + sourceBounds.height / 2;
|
|
2989
|
+
return cx >= nodeBounds.x && cx <= nodeBounds.x + nodeBounds.width && cy >= nodeBounds.y && cy <= nodeBounds.y + nodeBounds.height;
|
|
2990
|
+
}
|
|
2808
2991
|
startOutlineConnection(node, attach) {
|
|
2809
2992
|
const { point: outlinePoint, param } = node.getClosestPointOnOutline(attach);
|
|
2810
2993
|
this.sourceNode = node;
|
|
@@ -2820,17 +3003,16 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2820
3003
|
return 8 / Math.max(this.renderer.zoom, 1e-4);
|
|
2821
3004
|
}
|
|
2822
3005
|
outlineConnectHandleFor(node, pointer) {
|
|
3006
|
+
const { point: onOutline } = node.getClosestPointOnOutline(pointer);
|
|
2823
3007
|
const prevSide = node.id === this.hoverNodeId ? this.hoverOutlineSide : null;
|
|
2824
|
-
const geom = getOutlineConnectHandle(node.getBounds(), pointer, 0, prevSide, this.outlineHandleHysteresis());
|
|
2825
|
-
const { point: onOutline } = node.getClosestPointOnOutline(geom.attach);
|
|
2826
3008
|
return {
|
|
2827
3009
|
attach: onOutline,
|
|
2828
3010
|
handle: onOutline,
|
|
2829
|
-
side:
|
|
3011
|
+
side: getOutlineConnectHandle(node.getBounds(), pointer, 0, prevSide, this.outlineHandleHysteresis()).side
|
|
2830
3012
|
};
|
|
2831
3013
|
}
|
|
2832
3014
|
outlineHandleHitRadius() {
|
|
2833
|
-
return
|
|
3015
|
+
return 4 / Math.max(this.renderer.zoom, 1e-4);
|
|
2834
3016
|
}
|
|
2835
3017
|
findOutlineConnectHandleAt(point) {
|
|
2836
3018
|
if (!this.attachToOutline) return null;
|
|
@@ -2839,6 +3021,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2839
3021
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
2840
3022
|
const node = nodes[i];
|
|
2841
3023
|
if (!node.visible || !this.isCompatibleTarget(node)) continue;
|
|
3024
|
+
if (node.hitTestResizeHandle(point)) continue;
|
|
2842
3025
|
const { attach, handle } = this.outlineConnectHandleFor(node, point);
|
|
2843
3026
|
const dx = point.x - handle.x;
|
|
2844
3027
|
const dy = point.y - handle.y;
|
|
@@ -2851,7 +3034,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2851
3034
|
return null;
|
|
2852
3035
|
}
|
|
2853
3036
|
renderOutlineConnectHandle(ctx) {
|
|
2854
|
-
if (!this.hoverNodeId || !this.hoverPoint) return;
|
|
3037
|
+
if (!this.hoverOutlineHandle || !this.hoverNodeId || !this.hoverPoint) return;
|
|
2855
3038
|
const node = this.renderer.getNode(this.hoverNodeId);
|
|
2856
3039
|
if (!node) return;
|
|
2857
3040
|
const { handle } = this.outlineConnectHandleFor(node, this.hoverPoint);
|
|
@@ -2939,6 +3122,9 @@ function getHistoryShortcut(event, keys = DEFAULT_HISTORY_SHORTCUT_KEYS) {
|
|
|
2939
3122
|
}
|
|
2940
3123
|
//#endregion
|
|
2941
3124
|
//#region src/utils/style.ts
|
|
3125
|
+
function canvasFontFromTextStyle(style) {
|
|
3126
|
+
return `${style.fontStyle ?? "normal"} ${style.fontWeight ?? "normal"} ${style.fontSize ?? 14}px ${style.fontFamily ?? "sans-serif"}`;
|
|
3127
|
+
}
|
|
2942
3128
|
/**
|
|
2943
3129
|
* Shallow compare two objects
|
|
2944
3130
|
*/
|
|
@@ -3038,6 +3224,7 @@ var DEFAULT_STYLE = {
|
|
|
3038
3224
|
fontSize: 14,
|
|
3039
3225
|
fontFamily: "sans-serif",
|
|
3040
3226
|
fontWeight: "normal",
|
|
3227
|
+
fontStyle: "normal",
|
|
3041
3228
|
color: "#000000",
|
|
3042
3229
|
opacity: 1,
|
|
3043
3230
|
align: "center",
|
|
@@ -3282,9 +3469,7 @@ var TextLabel = class {
|
|
|
3282
3469
|
this.render(ctx, bounds);
|
|
3283
3470
|
}
|
|
3284
3471
|
applyStyle(ctx) {
|
|
3285
|
-
|
|
3286
|
-
const fontFamily = this._style.fontFamily ?? "sans-serif";
|
|
3287
|
-
ctx.font = `${this._style.fontWeight ?? "normal"} ${fontSize}px ${fontFamily}`;
|
|
3472
|
+
ctx.font = canvasFontFromTextStyle(this._style);
|
|
3288
3473
|
ctx.textAlign = this._style.align ?? "center";
|
|
3289
3474
|
ctx.textBaseline = this._style.baseline ?? "middle";
|
|
3290
3475
|
}
|
|
@@ -3918,109 +4103,6 @@ var Element = class {
|
|
|
3918
4103
|
}
|
|
3919
4104
|
};
|
|
3920
4105
|
//#endregion
|
|
3921
|
-
//#region src/utils/edgePath.ts
|
|
3922
|
-
/**
|
|
3923
|
-
* Point and tangent angle at a normalized position along an edge path (0..1 by length).
|
|
3924
|
-
*/
|
|
3925
|
-
function getPathPointAt(path, type, position) {
|
|
3926
|
-
if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
|
|
3927
|
-
const samples = [];
|
|
3928
|
-
const steps = 20;
|
|
3929
|
-
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
3930
|
-
const start = path[i - 1];
|
|
3931
|
-
const control1 = path[i];
|
|
3932
|
-
const control2 = path[i + 1];
|
|
3933
|
-
const end = path[i + 2];
|
|
3934
|
-
for (let sample = 0; sample <= steps; sample++) {
|
|
3935
|
-
const t = sample / steps;
|
|
3936
|
-
if (samples.length > 0 && t === 0) continue;
|
|
3937
|
-
samples.push(bezierPoint(start, control1, control2, end, t));
|
|
3938
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
return getPointAlongPolyline(samples, position);
|
|
3941
|
-
}
|
|
3942
|
-
function getPointAlongPolyline(path, position) {
|
|
3943
|
-
if (path.length === 0) return {
|
|
3944
|
-
point: {
|
|
3945
|
-
x: 0,
|
|
3946
|
-
y: 0
|
|
3947
|
-
},
|
|
3948
|
-
angle: 0
|
|
3949
|
-
};
|
|
3950
|
-
if (path.length === 1) return {
|
|
3951
|
-
point: path[0],
|
|
3952
|
-
angle: 0
|
|
3953
|
-
};
|
|
3954
|
-
let totalLength = 0;
|
|
3955
|
-
const segments = [];
|
|
3956
|
-
for (let i = 1; i < path.length; i++) {
|
|
3957
|
-
const start = path[i - 1];
|
|
3958
|
-
const end = path[i];
|
|
3959
|
-
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
3960
|
-
segments.push({
|
|
3961
|
-
start,
|
|
3962
|
-
end,
|
|
3963
|
-
length
|
|
3964
|
-
});
|
|
3965
|
-
totalLength += length;
|
|
3966
|
-
}
|
|
3967
|
-
const targetLength = totalLength * Math.max(0, Math.min(1, position));
|
|
3968
|
-
let accumulated = 0;
|
|
3969
|
-
for (const segment of segments) {
|
|
3970
|
-
if (accumulated + segment.length >= targetLength) {
|
|
3971
|
-
const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
|
|
3972
|
-
return {
|
|
3973
|
-
point: {
|
|
3974
|
-
x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
|
|
3975
|
-
y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
|
|
3976
|
-
},
|
|
3977
|
-
angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
|
|
3978
|
-
};
|
|
3979
|
-
}
|
|
3980
|
-
accumulated += segment.length;
|
|
3981
|
-
}
|
|
3982
|
-
const lastSegment = segments[segments.length - 1];
|
|
3983
|
-
return {
|
|
3984
|
-
point: lastSegment.end,
|
|
3985
|
-
angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
|
|
3986
|
-
};
|
|
3987
|
-
}
|
|
3988
|
-
/** Map tangent angle to a cardinal direction used by polyline routing. */
|
|
3989
|
-
function directionFromAngle(angle) {
|
|
3990
|
-
const twoPi = Math.PI * 2;
|
|
3991
|
-
const a = (angle % twoPi + twoPi) % twoPi;
|
|
3992
|
-
if (a >= Math.PI * .25 && a < Math.PI * .75) return "bottom";
|
|
3993
|
-
if (a >= Math.PI * .75 && a < Math.PI * 1.25) return "left";
|
|
3994
|
-
if (a >= Math.PI * 1.25 && a < Math.PI * 1.75) return "top";
|
|
3995
|
-
return "right";
|
|
3996
|
-
}
|
|
3997
|
-
/**
|
|
3998
|
-
* Nearest point on an edge path to `point`, by sampling along normalized length.
|
|
3999
|
-
*/
|
|
4000
|
-
function getClosestPointOnPath(path, type, point, samples = 48) {
|
|
4001
|
-
if (path.length < 2) return null;
|
|
4002
|
-
let bestPoint = getPathPointAt(path, type, 0).point;
|
|
4003
|
-
let bestParam = 0;
|
|
4004
|
-
let bestDistSq = Infinity;
|
|
4005
|
-
for (let i = 0; i <= samples; i++) {
|
|
4006
|
-
const pathParam = i / samples;
|
|
4007
|
-
const at = getPathPointAt(path, type, pathParam).point;
|
|
4008
|
-
const dx = at.x - point.x;
|
|
4009
|
-
const dy = at.y - point.y;
|
|
4010
|
-
const distSq = dx * dx + dy * dy;
|
|
4011
|
-
if (distSq < bestDistSq) {
|
|
4012
|
-
bestDistSq = distSq;
|
|
4013
|
-
bestPoint = at;
|
|
4014
|
-
bestParam = pathParam;
|
|
4015
|
-
}
|
|
4016
|
-
}
|
|
4017
|
-
return {
|
|
4018
|
-
point: bestPoint,
|
|
4019
|
-
pathParam: bestParam,
|
|
4020
|
-
distance: Math.sqrt(bestDistSq)
|
|
4021
|
-
};
|
|
4022
|
-
}
|
|
4023
|
-
//#endregion
|
|
4024
4106
|
//#region src/elements/paths/StraightPathStrategy.ts
|
|
4025
4107
|
/**
|
|
4026
4108
|
* Straight line path strategy
|
|
@@ -7082,7 +7164,12 @@ var Edge = class extends Element {
|
|
|
7082
7164
|
* Nearest point on this edge's path to `point` (world coordinates).
|
|
7083
7165
|
*/
|
|
7084
7166
|
getClosestPointOnPath(point) {
|
|
7085
|
-
|
|
7167
|
+
const onPath = getClosestPointOnPath(this._path, this._type, point);
|
|
7168
|
+
if (onPath) return onPath;
|
|
7169
|
+
const start = this._fromPoint;
|
|
7170
|
+
const end = this._toPoint;
|
|
7171
|
+
if (start.x === end.x && start.y === end.y) return null;
|
|
7172
|
+
return getClosestPointOnPath([start, end], "straight", point);
|
|
7086
7173
|
}
|
|
7087
7174
|
/**
|
|
7088
7175
|
* Get world position of label center along path.
|
|
@@ -7939,7 +8026,13 @@ var InteractionManager = class {
|
|
|
7939
8026
|
}
|
|
7940
8027
|
if ("typeName" in hitElement) {
|
|
7941
8028
|
if (hitElement.typeName === "composite") {
|
|
7942
|
-
|
|
8029
|
+
const composite = hitElement;
|
|
8030
|
+
if (composite.usesExternalLabel()) {
|
|
8031
|
+
const editText = composite.label?.editableText ?? composite.label?.text ?? "";
|
|
8032
|
+
this.startLabelEdit("node", composite.id, editText, composite.getLabelPosition());
|
|
8033
|
+
return;
|
|
8034
|
+
}
|
|
8035
|
+
this.startCompositeNameEdit(composite);
|
|
7943
8036
|
return;
|
|
7944
8037
|
}
|
|
7945
8038
|
const editText = hitElement.label?.editableText ?? hitElement.label?.text ?? "";
|
|
@@ -9606,19 +9699,27 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9606
9699
|
return this._groups.get(groupId);
|
|
9607
9700
|
}
|
|
9608
9701
|
/**
|
|
9609
|
-
* Find element at a point
|
|
9702
|
+
* Find element at a point. Edges are painted above nodes, so a stroke over a
|
|
9703
|
+
* nested pool/lane fill is hit before the covering container.
|
|
9610
9704
|
*/
|
|
9611
9705
|
getElementAtPoint(worldPoint) {
|
|
9706
|
+
let bestEdge;
|
|
9707
|
+
let bestDist = Infinity;
|
|
9708
|
+
for (const edge of this._edges.values()) {
|
|
9709
|
+
if (!edge.visible) continue;
|
|
9710
|
+
const tolerance = Math.max((edge.style.strokeWidth ?? 2) * 2, 8) / Math.max(this._zoom, 1e-4);
|
|
9711
|
+
const closest = edge.getClosestPointOnPath(worldPoint);
|
|
9712
|
+
if (closest && closest.distance <= tolerance && closest.distance < bestDist) {
|
|
9713
|
+
bestDist = closest.distance;
|
|
9714
|
+
bestEdge = edge;
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
if (bestEdge) return bestEdge;
|
|
9612
9718
|
const nodesArray = Array.from(this._nodes.values());
|
|
9613
9719
|
for (let i = nodesArray.length - 1; i >= 0; i--) {
|
|
9614
9720
|
const node = nodesArray[i];
|
|
9615
9721
|
if (node.visible && node.hitTest(worldPoint)) return node;
|
|
9616
9722
|
}
|
|
9617
|
-
for (const edge of this._edges.values()) {
|
|
9618
|
-
if (!edge.visible) continue;
|
|
9619
|
-
const tolerance = Math.max((edge.style.strokeWidth ?? 2) * 2, 8) / Math.max(this._zoom, 1e-4);
|
|
9620
|
-
if (edge.hitTestWithTolerance(worldPoint, tolerance)) return edge;
|
|
9621
|
-
}
|
|
9622
9723
|
for (const group of this._groups.values()) if (group.visible && group.hitTest(worldPoint)) return group;
|
|
9623
9724
|
}
|
|
9624
9725
|
/**
|
|
@@ -10634,6 +10735,52 @@ var NodeImage = class {
|
|
|
10634
10735
|
}
|
|
10635
10736
|
};
|
|
10636
10737
|
//#endregion
|
|
10738
|
+
//#region src/utils/labelPlacement.ts
|
|
10739
|
+
var DEFAULT_LABEL_GAP = 4;
|
|
10740
|
+
function isExternalLabelPlacement(placement) {
|
|
10741
|
+
return placement === "top" || placement === "bottom" || placement === "left" || placement === "right";
|
|
10742
|
+
}
|
|
10743
|
+
function externalLabelWrapWidth(shapeWidth) {
|
|
10744
|
+
return Math.max(shapeWidth, 80);
|
|
10745
|
+
}
|
|
10746
|
+
function resolveExternalLabelBounds(shape, placement, gap, measured) {
|
|
10747
|
+
const width = Math.max(0, measured.width);
|
|
10748
|
+
const height = Math.max(0, measured.height);
|
|
10749
|
+
const safeGap = Number.isFinite(gap) ? gap : 4;
|
|
10750
|
+
switch (placement) {
|
|
10751
|
+
case "top": return {
|
|
10752
|
+
x: shape.x + (shape.width - width) / 2,
|
|
10753
|
+
y: shape.y - safeGap - height,
|
|
10754
|
+
width,
|
|
10755
|
+
height
|
|
10756
|
+
};
|
|
10757
|
+
case "bottom": return {
|
|
10758
|
+
x: shape.x + (shape.width - width) / 2,
|
|
10759
|
+
y: shape.y + shape.height + safeGap,
|
|
10760
|
+
width,
|
|
10761
|
+
height
|
|
10762
|
+
};
|
|
10763
|
+
case "left": return {
|
|
10764
|
+
x: shape.x - safeGap - width,
|
|
10765
|
+
y: shape.y + (shape.height - height) / 2,
|
|
10766
|
+
width,
|
|
10767
|
+
height
|
|
10768
|
+
};
|
|
10769
|
+
case "right": return {
|
|
10770
|
+
x: shape.x + shape.width + safeGap,
|
|
10771
|
+
y: shape.y + (shape.height - height) / 2,
|
|
10772
|
+
width,
|
|
10773
|
+
height
|
|
10774
|
+
};
|
|
10775
|
+
}
|
|
10776
|
+
}
|
|
10777
|
+
function unionBounds(a, b) {
|
|
10778
|
+
return rectUnion(a, b);
|
|
10779
|
+
}
|
|
10780
|
+
function pointInBounds(point, bounds) {
|
|
10781
|
+
return pointInRect(point, bounds);
|
|
10782
|
+
}
|
|
10783
|
+
//#endregion
|
|
10637
10784
|
//#region src/utils/resolveContentInset.ts
|
|
10638
10785
|
function axisFactor(node, base) {
|
|
10639
10786
|
if (base === void 0 || !(base > 0) || !Number.isFinite(base)) return 1;
|
|
@@ -10706,6 +10853,8 @@ var Node = class extends Element {
|
|
|
10706
10853
|
this._contentInset = this.normalizeContentInset(options.contentInset);
|
|
10707
10854
|
this._contentInsetScale = this.normalizeContentInsetScale(options.contentInsetScale);
|
|
10708
10855
|
this._contentInsetBaseSize = this.normalizeContentInsetBaseSize(options.contentInsetBaseSize);
|
|
10856
|
+
this._labelPlacement = options.labelPlacement ?? "center";
|
|
10857
|
+
this._labelGap = options.labelGap !== void 0 && Number.isFinite(options.labelGap) ? Math.max(0, options.labelGap) : 4;
|
|
10709
10858
|
if (options.label !== void 0) if (typeof options.label === "string") this._label = new TextLabel({
|
|
10710
10859
|
text: options.label,
|
|
10711
10860
|
onChange: () => this.markDirty()
|
|
@@ -10756,6 +10905,33 @@ var Node = class extends Element {
|
|
|
10756
10905
|
get ports() {
|
|
10757
10906
|
return this._ports;
|
|
10758
10907
|
}
|
|
10908
|
+
get labelPlacement() {
|
|
10909
|
+
return this._labelPlacement;
|
|
10910
|
+
}
|
|
10911
|
+
set labelPlacement(value) {
|
|
10912
|
+
if (this._labelPlacement !== value) {
|
|
10913
|
+
this._labelPlacement = value;
|
|
10914
|
+
this.markDirty();
|
|
10915
|
+
}
|
|
10916
|
+
}
|
|
10917
|
+
get labelGap() {
|
|
10918
|
+
return this._labelGap;
|
|
10919
|
+
}
|
|
10920
|
+
set labelGap(value) {
|
|
10921
|
+
const next = Number.isFinite(value) ? Math.max(0, value) : 4;
|
|
10922
|
+
if (this._labelGap !== next) {
|
|
10923
|
+
this._labelGap = next;
|
|
10924
|
+
this.markDirty();
|
|
10925
|
+
}
|
|
10926
|
+
}
|
|
10927
|
+
usesExternalLabel() {
|
|
10928
|
+
return isExternalLabelPlacement(this._labelPlacement) && this._label !== void 0;
|
|
10929
|
+
}
|
|
10930
|
+
getVisualBounds() {
|
|
10931
|
+
const shape = this.getBounds();
|
|
10932
|
+
if (!this.usesExternalLabel()) return shape;
|
|
10933
|
+
return unionBounds(shape, this.getResolvedLabelBounds());
|
|
10934
|
+
}
|
|
10759
10935
|
/**
|
|
10760
10936
|
* Text label
|
|
10761
10937
|
*/
|
|
@@ -10979,13 +11155,15 @@ var Node = class extends Element {
|
|
|
10979
11155
|
* Calculate layout for icon and label.
|
|
10980
11156
|
* Label always gets full content area; icon is placed within the same content area.
|
|
10981
11157
|
*/
|
|
10982
|
-
calculateContentLayout(bounds, iconBoxSize,
|
|
11158
|
+
calculateContentLayout(bounds, iconBoxSize, labelSize) {
|
|
11159
|
+
const iconContainer = this.getIconContainerBounds(bounds);
|
|
10983
11160
|
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
10984
|
-
let iconBounds =
|
|
10985
|
-
if (this._icon && iconBoxSize) iconBounds = this.getIconBounds(
|
|
11161
|
+
let iconBounds = iconContainer;
|
|
11162
|
+
if (this._icon && iconBoxSize) iconBounds = this.getIconBounds(iconContainer, iconBoxSize, this._icon.placement);
|
|
11163
|
+
const labelBounds = isExternalLabelPlacement(this._labelPlacement) && this._label && labelSize ? resolveExternalLabelBounds(bounds, this._labelPlacement, this._labelGap, labelSize) : contentBounds;
|
|
10986
11164
|
return {
|
|
10987
11165
|
iconBounds,
|
|
10988
|
-
labelBounds
|
|
11166
|
+
labelBounds
|
|
10989
11167
|
};
|
|
10990
11168
|
}
|
|
10991
11169
|
/**
|
|
@@ -10995,14 +11173,14 @@ var Node = class extends Element {
|
|
|
10995
11173
|
getLabelBoundsForExport(ctx) {
|
|
10996
11174
|
if (!this._label) return null;
|
|
10997
11175
|
const bounds = this.getBounds();
|
|
10998
|
-
const
|
|
11176
|
+
const wrapWidth = this.getLabelWrapWidth(bounds);
|
|
10999
11177
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
11000
|
-
this._label.setAutoMaxWidth(
|
|
11178
|
+
this._label.setAutoMaxWidth(wrapWidth);
|
|
11001
11179
|
const labelSize = this._label.measure(ctx);
|
|
11002
11180
|
const { labelBounds } = this.calculateContentLayout(bounds, iconBoxSize, labelSize);
|
|
11003
11181
|
return {
|
|
11004
11182
|
bounds: labelBounds,
|
|
11005
|
-
lines: this._label.getWrappedLines(ctx, Math.max(0,
|
|
11183
|
+
lines: this._label.getWrappedLines(ctx, Math.max(0, wrapWidth))
|
|
11006
11184
|
};
|
|
11007
11185
|
}
|
|
11008
11186
|
/**
|
|
@@ -11015,10 +11193,10 @@ var Node = class extends Element {
|
|
|
11015
11193
|
ctx.globalAlpha = 1;
|
|
11016
11194
|
}
|
|
11017
11195
|
/**
|
|
11018
|
-
* Get world position of label center (
|
|
11196
|
+
* Get world position of label center (content area or external label box).
|
|
11019
11197
|
*/
|
|
11020
11198
|
getLabelPosition() {
|
|
11021
|
-
const bounds = this.
|
|
11199
|
+
const bounds = this.getResolvedLabelBounds();
|
|
11022
11200
|
return {
|
|
11023
11201
|
x: bounds.x + bounds.width / 2,
|
|
11024
11202
|
y: bounds.y + bounds.height / 2
|
|
@@ -11033,13 +11211,14 @@ var Node = class extends Element {
|
|
|
11033
11211
|
let bounds = this.getBounds();
|
|
11034
11212
|
this.renderBadges(ctx, bounds);
|
|
11035
11213
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
11036
|
-
|
|
11214
|
+
const externalLabel = this.usesExternalLabel();
|
|
11215
|
+
if (this._label) this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
11037
11216
|
const labelSize = this._label ? this._label.measure(ctx) : void 0;
|
|
11038
|
-
if (labelSize || iconBoxSize) {
|
|
11217
|
+
if ((labelSize || iconBoxSize) && !externalLabel) {
|
|
11039
11218
|
this.ensureContentsFit(labelSize, iconBoxSize);
|
|
11040
11219
|
if (this._label && iconBoxSize) {
|
|
11041
11220
|
bounds = this.getBounds();
|
|
11042
|
-
this._label.setAutoMaxWidth(this.
|
|
11221
|
+
this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
11043
11222
|
}
|
|
11044
11223
|
}
|
|
11045
11224
|
const { iconBounds, labelBounds } = this.calculateContentLayout(this.getBounds(), iconBoxSize, labelSize);
|
|
@@ -11116,10 +11295,10 @@ var Node = class extends Element {
|
|
|
11116
11295
|
return this.calculateContentMinSize(labelSize, iconBoxSize, bounds);
|
|
11117
11296
|
}
|
|
11118
11297
|
/**
|
|
11119
|
-
* Render resize handles when
|
|
11298
|
+
* Render the selection frame when selected, and resize handles when they are enabled.
|
|
11120
11299
|
*/
|
|
11121
11300
|
renderResizeHandles(ctx) {
|
|
11122
|
-
if (this._state !== "selected"
|
|
11301
|
+
if (this._state !== "selected") return;
|
|
11123
11302
|
const bounds = this.getBounds();
|
|
11124
11303
|
const offset = 6;
|
|
11125
11304
|
const size = 8;
|
|
@@ -11129,6 +11308,10 @@ var Node = class extends Element {
|
|
|
11129
11308
|
ctx.lineWidth = 1;
|
|
11130
11309
|
ctx.setLineDash([4, 4]);
|
|
11131
11310
|
ctx.strokeRect(bounds.x - offset, bounds.y - offset, bounds.width + offset * 2, bounds.height + offset * 2);
|
|
11311
|
+
if (!this._resizeHandlesEnabled) {
|
|
11312
|
+
ctx.restore();
|
|
11313
|
+
return;
|
|
11314
|
+
}
|
|
11132
11315
|
ctx.fillStyle = "#ffffff";
|
|
11133
11316
|
ctx.strokeStyle = DEFAULT_SELECTION_COLOR;
|
|
11134
11317
|
ctx.lineWidth = 1.5;
|
|
@@ -11145,7 +11328,7 @@ var Node = class extends Element {
|
|
|
11145
11328
|
* Hit test a resize handle
|
|
11146
11329
|
*/
|
|
11147
11330
|
hitTestResizeHandle(point) {
|
|
11148
|
-
if (this._state !== "selected") return null;
|
|
11331
|
+
if (this._state !== "selected" || !this._resizeHandlesEnabled) return null;
|
|
11149
11332
|
const half = 8 / 2;
|
|
11150
11333
|
for (const handle of this.getResizeHandlePositions()) if (point.x >= handle.x - half && point.x <= handle.x + half && point.y >= handle.y - half && point.y <= handle.y + half) return handle.type;
|
|
11151
11334
|
return null;
|
|
@@ -11156,7 +11339,25 @@ var Node = class extends Element {
|
|
|
11156
11339
|
hitTest(point) {
|
|
11157
11340
|
const bounds = this.getBounds();
|
|
11158
11341
|
const padding = 10;
|
|
11159
|
-
return point.x >= bounds.x - padding && point.x <= bounds.x + bounds.width + padding && point.y >= bounds.y - padding && point.y <= bounds.y + bounds.height + padding;
|
|
11342
|
+
return point.x >= bounds.x - padding && point.x <= bounds.x + bounds.width + padding && point.y >= bounds.y - padding && point.y <= bounds.y + bounds.height + padding || this.hitTestExternalLabel(point);
|
|
11343
|
+
}
|
|
11344
|
+
hitTestExternalLabel(point) {
|
|
11345
|
+
if (!this.usesExternalLabel()) return false;
|
|
11346
|
+
return pointInBounds(point, this.getResolvedLabelBounds());
|
|
11347
|
+
}
|
|
11348
|
+
getLabelWrapWidth(bounds = this.getBounds()) {
|
|
11349
|
+
if (this.usesExternalLabel()) return externalLabelWrapWidth(bounds.width);
|
|
11350
|
+
return this.getLabelContainerBounds(bounds).width;
|
|
11351
|
+
}
|
|
11352
|
+
getResolvedLabelBounds(measured) {
|
|
11353
|
+
const shape = this.getBounds();
|
|
11354
|
+
if (!this.usesExternalLabel() || !this._label) return this.getLabelContainerBounds(shape);
|
|
11355
|
+
const size = measured ?? {
|
|
11356
|
+
width: this._label.measuredWidth || this.getLabelWrapWidth(shape),
|
|
11357
|
+
height: this._label.measuredHeight || (this._label.style.fontSize ?? 14) * 1.2
|
|
11358
|
+
};
|
|
11359
|
+
if (!isExternalLabelPlacement(this._labelPlacement)) return this.getLabelContainerBounds(shape);
|
|
11360
|
+
return resolveExternalLabelBounds(shape, this._labelPlacement, this._labelGap, size);
|
|
11160
11361
|
}
|
|
11161
11362
|
/**
|
|
11162
11363
|
* Get a connection point on the node boundary toward a target point,
|
|
@@ -11390,16 +11591,28 @@ var Node = class extends Element {
|
|
|
11390
11591
|
}
|
|
11391
11592
|
calculateContentMinSize(labelSize, iconBoxSize, bounds) {
|
|
11392
11593
|
const labelContainer = this.getLabelContainerBounds(bounds);
|
|
11393
|
-
const
|
|
11394
|
-
const
|
|
11395
|
-
const
|
|
11396
|
-
const
|
|
11594
|
+
const iconContainer = this.getIconContainerBounds(bounds);
|
|
11595
|
+
const labelWidthFactor = labelContainer.width > 0 ? bounds.width / labelContainer.width : 1;
|
|
11596
|
+
const labelHeightFactor = labelContainer.height > 0 ? bounds.height / labelContainer.height : 1;
|
|
11597
|
+
const iconWidthFactor = iconContainer.width > 0 ? bounds.width / iconContainer.width : 1;
|
|
11598
|
+
const iconHeightFactor = iconContainer.height > 0 ? bounds.height / iconContainer.height : 1;
|
|
11397
11599
|
return {
|
|
11398
|
-
width:
|
|
11399
|
-
height:
|
|
11600
|
+
width: Math.max((labelSize?.width ?? 0) * labelWidthFactor, (iconBoxSize?.width ?? 0) * iconWidthFactor),
|
|
11601
|
+
height: Math.max((labelSize?.height ?? 0) * labelHeightFactor, (iconBoxSize?.height ?? 0) * iconHeightFactor)
|
|
11400
11602
|
};
|
|
11401
11603
|
}
|
|
11604
|
+
/**
|
|
11605
|
+
* Content box for the node icon: AABB minus contentInset.
|
|
11606
|
+
* Unlike {@link getLabelContainerBounds}, this is not shrunk to the
|
|
11607
|
+
* inscribed rectangle of a circle/diamond, so a centered icon can fill the shape.
|
|
11608
|
+
*/
|
|
11609
|
+
getIconContainerBounds(bounds) {
|
|
11610
|
+
return this.applyContentInset(bounds);
|
|
11611
|
+
}
|
|
11402
11612
|
getLabelContainerBounds(bounds) {
|
|
11613
|
+
return this.applyContentInset(bounds);
|
|
11614
|
+
}
|
|
11615
|
+
applyContentInset(bounds) {
|
|
11403
11616
|
const { top, right, bottom, left } = resolveContentInset(this._contentInset, this._contentInsetScale, {
|
|
11404
11617
|
width: bounds.width,
|
|
11405
11618
|
height: bounds.height
|
|
@@ -11796,7 +12009,7 @@ var CircleNode = class extends Node {
|
|
|
11796
12009
|
const ry = this._height / 2 + padding;
|
|
11797
12010
|
const dx = point.x - center.x;
|
|
11798
12011
|
const dy = point.y - center.y;
|
|
11799
|
-
return dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1;
|
|
12012
|
+
return dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1 || this.hitTestExternalLabel(point);
|
|
11800
12013
|
}
|
|
11801
12014
|
getOutlineDirection(param) {
|
|
11802
12015
|
return this.outlineDirectionFromCenter(param);
|
|
@@ -11885,7 +12098,7 @@ var DiamondNode = class extends Node {
|
|
|
11885
12098
|
const hh = this._height / 2 + padding;
|
|
11886
12099
|
const dx = Math.abs(point.x - center.x);
|
|
11887
12100
|
const dy = Math.abs(point.y - center.y);
|
|
11888
|
-
return dx / hw + dy / hh <= 1;
|
|
12101
|
+
return dx / hw + dy / hh <= 1 || this.hitTestExternalLabel(point);
|
|
11889
12102
|
}
|
|
11890
12103
|
getOutlineDirection(param) {
|
|
11891
12104
|
return this.outlineDirectionFromCenter(param);
|
|
@@ -12286,7 +12499,7 @@ var CustomShapeNode = class extends Node {
|
|
|
12286
12499
|
const path = this.getPath();
|
|
12287
12500
|
if (ctx.isPointInPath(path, localX, localY)) return true;
|
|
12288
12501
|
ctx.lineWidth = 20;
|
|
12289
|
-
return ctx.isPointInStroke(path, localX, localY);
|
|
12502
|
+
return ctx.isPointInStroke(path, localX, localY) || this.hitTestExternalLabel(point);
|
|
12290
12503
|
}
|
|
12291
12504
|
render(ctx) {
|
|
12292
12505
|
const style = this.style;
|
|
@@ -13116,6 +13329,9 @@ function isCContainer(component) {
|
|
|
13116
13329
|
function isCShape(component) {
|
|
13117
13330
|
return component.type === "shape";
|
|
13118
13331
|
}
|
|
13332
|
+
function isCText(component) {
|
|
13333
|
+
return component.type === "text";
|
|
13334
|
+
}
|
|
13119
13335
|
/**
|
|
13120
13336
|
* Flex container component for CompositeNode.
|
|
13121
13337
|
* Lays out children using a flexbox-like algorithm.
|
|
@@ -13126,6 +13342,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13126
13342
|
this.type = "container";
|
|
13127
13343
|
this._cachedBounds = null;
|
|
13128
13344
|
this._cachedContainerBounds = null;
|
|
13345
|
+
this._layoutChildren = null;
|
|
13346
|
+
this._suppressBoundName = false;
|
|
13129
13347
|
this.id = options.id;
|
|
13130
13348
|
this._direction = options.direction ?? "column";
|
|
13131
13349
|
this._justifyContent = options.justifyContent ?? "start";
|
|
@@ -13154,6 +13372,27 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13154
13372
|
get children() {
|
|
13155
13373
|
return this._children;
|
|
13156
13374
|
}
|
|
13375
|
+
/**
|
|
13376
|
+
* When true, CText bound to `__name__` is omitted from measure/render/hit/SVG.
|
|
13377
|
+
* Used when CompositeNode draws the name as an external Node.label.
|
|
13378
|
+
*/
|
|
13379
|
+
setSuppressBoundName(value) {
|
|
13380
|
+
if (this._suppressBoundName === value) return;
|
|
13381
|
+
this._suppressBoundName = value;
|
|
13382
|
+
this._cachedBounds = null;
|
|
13383
|
+
this._layoutChildren = null;
|
|
13384
|
+
for (const child of this._children) {
|
|
13385
|
+
if (isCContainer(child)) child.setSuppressBoundName(value);
|
|
13386
|
+
if (isCShape(child) && child.content) child.content.setSuppressBoundName(value);
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
isLayoutSkipped(child) {
|
|
13390
|
+
if (child.style.visible === false) return true;
|
|
13391
|
+
return this._suppressBoundName && isCText(child) && child.bindToProperty === "__name__";
|
|
13392
|
+
}
|
|
13393
|
+
getLayoutChildren() {
|
|
13394
|
+
return this._children.filter((child) => !this.isLayoutSkipped(child));
|
|
13395
|
+
}
|
|
13157
13396
|
addChild(child) {
|
|
13158
13397
|
child.setOnChange(() => this.handleChildChange());
|
|
13159
13398
|
this._children.push(child);
|
|
@@ -13185,8 +13424,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13185
13424
|
padding: this._padding
|
|
13186
13425
|
};
|
|
13187
13426
|
}
|
|
13188
|
-
buildFlexChildren(ctx) {
|
|
13189
|
-
return
|
|
13427
|
+
buildFlexChildren(ctx, children = this.getLayoutChildren()) {
|
|
13428
|
+
return children.map((child) => {
|
|
13190
13429
|
const s = child.style;
|
|
13191
13430
|
return {
|
|
13192
13431
|
measure: child.measure(ctx),
|
|
@@ -13219,11 +13458,13 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13219
13458
|
}
|
|
13220
13459
|
render(ctx, bounds) {
|
|
13221
13460
|
if (this.style.visible === false) return;
|
|
13222
|
-
const
|
|
13461
|
+
const layoutChildren = this.getLayoutChildren();
|
|
13462
|
+
const flexChildren = this.buildFlexChildren(ctx, layoutChildren);
|
|
13223
13463
|
const result = flexLayout({
|
|
13224
13464
|
width: bounds.width,
|
|
13225
13465
|
height: bounds.height
|
|
13226
13466
|
}, this.getFlexConfig(), flexChildren);
|
|
13467
|
+
this._layoutChildren = layoutChildren;
|
|
13227
13468
|
this._cachedBounds = result.childBounds;
|
|
13228
13469
|
this._cachedContainerBounds = bounds;
|
|
13229
13470
|
const opacity = this.style.opacity ?? 1;
|
|
@@ -13231,9 +13472,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13231
13472
|
ctx.save();
|
|
13232
13473
|
ctx.globalAlpha *= opacity;
|
|
13233
13474
|
}
|
|
13234
|
-
for (let i = 0; i <
|
|
13235
|
-
const child =
|
|
13236
|
-
if (child.style.visible === false) continue;
|
|
13475
|
+
for (let i = 0; i < layoutChildren.length; i++) {
|
|
13476
|
+
const child = layoutChildren[i];
|
|
13237
13477
|
const cb = result.childBounds[i];
|
|
13238
13478
|
child.render(ctx, {
|
|
13239
13479
|
x: bounds.x + cb.x,
|
|
@@ -13246,10 +13486,9 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13246
13486
|
}
|
|
13247
13487
|
hitTest(point, bounds) {
|
|
13248
13488
|
if (this.style.visible === false) return null;
|
|
13249
|
-
if (!this._cachedBounds) return null;
|
|
13250
|
-
for (let i = this.
|
|
13251
|
-
const child = this.
|
|
13252
|
-
if (child.style.visible === false) continue;
|
|
13489
|
+
if (!this._cachedBounds || !this._layoutChildren) return null;
|
|
13490
|
+
for (let i = this._layoutChildren.length - 1; i >= 0; i--) {
|
|
13491
|
+
const child = this._layoutChildren[i];
|
|
13253
13492
|
const cb = this._cachedBounds[i];
|
|
13254
13493
|
const absBounds = {
|
|
13255
13494
|
x: bounds.x + cb.x,
|
|
@@ -13263,6 +13502,22 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13263
13502
|
return null;
|
|
13264
13503
|
}
|
|
13265
13504
|
/**
|
|
13505
|
+
* First CText bound to `__name__`, including suppressed name text.
|
|
13506
|
+
*/
|
|
13507
|
+
findBoundNameText() {
|
|
13508
|
+
for (const child of this._children) {
|
|
13509
|
+
if (isCText(child) && child.bindToProperty === "__name__") return child;
|
|
13510
|
+
if (isCContainer(child)) {
|
|
13511
|
+
const found = child.findBoundNameText();
|
|
13512
|
+
if (found) return found;
|
|
13513
|
+
}
|
|
13514
|
+
if (isCShape(child) && child.content) {
|
|
13515
|
+
const found = child.content.findBoundNameText();
|
|
13516
|
+
if (found) return found;
|
|
13517
|
+
}
|
|
13518
|
+
}
|
|
13519
|
+
}
|
|
13520
|
+
/**
|
|
13266
13521
|
* Find a component by id recursively.
|
|
13267
13522
|
*/
|
|
13268
13523
|
findById(id) {
|
|
@@ -13299,8 +13554,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13299
13554
|
if (this.style.visible === false) return "";
|
|
13300
13555
|
const cachedBounds = this._cachedBounds;
|
|
13301
13556
|
const cachedContainer = this._cachedContainerBounds;
|
|
13302
|
-
|
|
13303
|
-
|
|
13557
|
+
const layoutChildren = this._layoutChildren ?? this.getLayoutChildren();
|
|
13558
|
+
return `<g>${layoutChildren.map((child, i) => {
|
|
13304
13559
|
if (cachedBounds && cachedContainer) {
|
|
13305
13560
|
const cb = cachedBounds[i];
|
|
13306
13561
|
const dx = bounds.x - cachedContainer.x;
|
|
@@ -13312,7 +13567,7 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13312
13567
|
height: cb.height
|
|
13313
13568
|
});
|
|
13314
13569
|
}
|
|
13315
|
-
const h = bounds.height / Math.max(1,
|
|
13570
|
+
const h = bounds.height / Math.max(1, layoutChildren.length);
|
|
13316
13571
|
return child.toSVG({
|
|
13317
13572
|
x: bounds.x,
|
|
13318
13573
|
y: bounds.y + i * h,
|
|
@@ -13619,8 +13874,8 @@ var CompositeNode = class extends Node {
|
|
|
13619
13874
|
return this.getLabelContainerBounds(this.getBounds());
|
|
13620
13875
|
}
|
|
13621
13876
|
hitTest(point) {
|
|
13622
|
-
if (this._shapeType === "circle") return this.hitTestEllipse(point);
|
|
13623
|
-
if (this._shapeType === "diamond") return this.hitTestDiamond(point);
|
|
13877
|
+
if (this._shapeType === "circle") return this.hitTestEllipse(point) || this.hitTestExternalLabel(point);
|
|
13878
|
+
if (this._shapeType === "diamond") return this.hitTestDiamond(point) || this.hitTestExternalLabel(point);
|
|
13624
13879
|
return super.hitTest(point);
|
|
13625
13880
|
}
|
|
13626
13881
|
hitTestEllipse(point) {
|
|
@@ -13641,7 +13896,27 @@ var CompositeNode = class extends Node {
|
|
|
13641
13896
|
const dy = Math.abs(point.y - center.y);
|
|
13642
13897
|
return dx / hw + dy / hh <= 1;
|
|
13643
13898
|
}
|
|
13899
|
+
/**
|
|
13900
|
+
* Copy color/font from the suppressed `__name__` CText onto the external Node.label.
|
|
13901
|
+
*/
|
|
13902
|
+
syncExternalBoundNameLabelStyle() {
|
|
13903
|
+
if (!this.usesExternalLabel() || !this._label) return;
|
|
13904
|
+
const nameText = this._content.findBoundNameText();
|
|
13905
|
+
if (!nameText) return;
|
|
13906
|
+
const current = this._label.styleOverrides;
|
|
13907
|
+
if (current.color === nameText.color && current.fontFamily === nameText.fontFamily && current.fontWeight === nameText.fontWeight && current.fontStyle === nameText.fontStyle && current.fontSize === nameText.fontSize) return;
|
|
13908
|
+
this._label.style = {
|
|
13909
|
+
...current,
|
|
13910
|
+
color: nameText.color,
|
|
13911
|
+
fontFamily: nameText.fontFamily,
|
|
13912
|
+
fontWeight: nameText.fontWeight,
|
|
13913
|
+
fontStyle: nameText.fontStyle,
|
|
13914
|
+
fontSize: nameText.fontSize
|
|
13915
|
+
};
|
|
13916
|
+
}
|
|
13644
13917
|
render(ctx) {
|
|
13918
|
+
this._content.setSuppressBoundName(this.usesExternalLabel());
|
|
13919
|
+
this.syncExternalBoundNameLabelStyle();
|
|
13645
13920
|
if (this._autoSize) this.applyAutoSize(ctx);
|
|
13646
13921
|
const { x, y, width, height } = this.getBounds();
|
|
13647
13922
|
const style = this.style;
|
|
@@ -13714,6 +13989,11 @@ var CompositeNode = class extends Node {
|
|
|
13714
13989
|
this.renderBadges(ctx, bounds);
|
|
13715
13990
|
const contentBounds = this.getContentBounds();
|
|
13716
13991
|
this._content.render(ctx, contentBounds);
|
|
13992
|
+
if (this.usesExternalLabel() && this._label) {
|
|
13993
|
+
this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
13994
|
+
const labelSize = this._label.measure(ctx);
|
|
13995
|
+
this.renderLabel(ctx, this.getResolvedLabelBounds(labelSize));
|
|
13996
|
+
}
|
|
13717
13997
|
this.renderPorts(ctx);
|
|
13718
13998
|
}
|
|
13719
13999
|
applyAutoSize(ctx) {
|
|
@@ -14347,6 +14627,8 @@ var Serializer = class {
|
|
|
14347
14627
|
styleClass: node.styleClass,
|
|
14348
14628
|
label,
|
|
14349
14629
|
labelStyleClass: typeof label === "string" ? node.label?.styleClass : void 0,
|
|
14630
|
+
labelPlacement: node.labelPlacement !== "center" ? node.labelPlacement : void 0,
|
|
14631
|
+
labelGap: node.labelGap !== 4 ? node.labelGap : void 0,
|
|
14350
14632
|
icon,
|
|
14351
14633
|
contentInset: hasContentInset ? contentInset : void 0,
|
|
14352
14634
|
anchorPoints,
|
|
@@ -14653,9 +14935,16 @@ var SvgExporter = class {
|
|
|
14653
14935
|
break;
|
|
14654
14936
|
}
|
|
14655
14937
|
}
|
|
14938
|
+
cn.content.setSuppressBoundName(cn.usesExternalLabel());
|
|
14939
|
+
cn.syncExternalBoundNameLabelStyle();
|
|
14656
14940
|
const contentBounds = cn.getLabelContainerBounds(bounds);
|
|
14657
14941
|
const contentSvg = cn.content.toSVG(contentBounds);
|
|
14658
|
-
|
|
14942
|
+
const labelSvg = cn.usesExternalLabel() ? this.renderNodeLabel(cn, bounds) : "";
|
|
14943
|
+
return [
|
|
14944
|
+
`${shape} fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="${strokeWidth}" stroke-opacity="${strokeOpacity}"${dash}${dashOffset}/>`,
|
|
14945
|
+
contentSvg,
|
|
14946
|
+
labelSvg
|
|
14947
|
+
].join("");
|
|
14659
14948
|
}
|
|
14660
14949
|
default: shape = `<rect x="${bounds.x}" y="${bounds.y}" width="${bounds.width}" height="${bounds.height}"`;
|
|
14661
14950
|
}
|
|
@@ -14728,15 +15017,13 @@ var SvgExporter = class {
|
|
|
14728
15017
|
}
|
|
14729
15018
|
measureTextLabel(text, style = {}, inset = 8) {
|
|
14730
15019
|
const fontSize = style.fontSize ?? 14;
|
|
14731
|
-
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
14732
|
-
const fontWeight = style.fontWeight ?? "normal";
|
|
14733
15020
|
const lineHeight = fontSize * 1.2;
|
|
14734
15021
|
const lines = text.split("\n");
|
|
14735
15022
|
let maxWidth = 0;
|
|
14736
15023
|
if (typeof document !== "undefined") {
|
|
14737
15024
|
const ctx = document.createElement("canvas").getContext("2d");
|
|
14738
15025
|
if (ctx) {
|
|
14739
|
-
ctx.font =
|
|
15026
|
+
ctx.font = canvasFontFromTextStyle(style);
|
|
14740
15027
|
for (const line of lines) maxWidth = Math.max(maxWidth, ctx.measureText(line).width);
|
|
14741
15028
|
}
|
|
14742
15029
|
}
|
|
@@ -14847,16 +15134,18 @@ var SvgExporter = class {
|
|
|
14847
15134
|
const fontSize = style.fontSize ?? 14;
|
|
14848
15135
|
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
14849
15136
|
const fontWeight = style.fontWeight ?? "normal";
|
|
15137
|
+
const fontStyle = style.fontStyle ?? "normal";
|
|
15138
|
+
const fontStyleAttr = fontStyle !== "normal" ? ` font-style="${fontStyle}"` : "";
|
|
14850
15139
|
const opacity = style.opacity ?? 1;
|
|
14851
15140
|
const anchor = style.align === "left" ? "start" : style.align === "right" ? "end" : "middle";
|
|
14852
15141
|
const baseline = style.baseline === "top" ? "text-before-edge" : style.baseline === "bottom" ? "text-after-edge" : "middle";
|
|
14853
15142
|
const transform = rotation !== 0 ? ` transform="rotate(${rotation * 180 / Math.PI} ${point.x} ${point.y})"` : "";
|
|
14854
15143
|
const lines = text.split("\n");
|
|
14855
|
-
if (lines.length <= 1) return `<text x="${point.x}" y="${point.y}" fill="${fill}" fill-opacity="${opacity}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}" text-anchor="${anchor}" dominant-baseline="${baseline}"${transform}>${this.escapeText(text)}</text>`;
|
|
15144
|
+
if (lines.length <= 1) return `<text x="${point.x}" y="${point.y}" fill="${fill}" fill-opacity="${opacity}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}"${fontStyleAttr} text-anchor="${anchor}" dominant-baseline="${baseline}"${transform}>${this.escapeText(text)}</text>`;
|
|
14856
15145
|
const lineHeight = fontSize * 1.2;
|
|
14857
15146
|
const startY = point.y - (lines.length - 1) * lineHeight / 2;
|
|
14858
15147
|
const tspans = lines.map((line, index) => `<tspan x="${point.x}" y="${startY + index * lineHeight}">${this.escapeText(line)}</tspan>`).join("");
|
|
14859
|
-
return `<text x="${point.x}" y="${point.y}" fill="${fill}" fill-opacity="${opacity}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}" text-anchor="${anchor}" dominant-baseline="${baseline}"${transform}>${tspans}</text>`;
|
|
15148
|
+
return `<text x="${point.x}" y="${point.y}" fill="${fill}" fill-opacity="${opacity}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}"${fontStyleAttr} text-anchor="${anchor}" dominant-baseline="${baseline}"${transform}>${tspans}</text>`;
|
|
14860
15149
|
}
|
|
14861
15150
|
buildEdgePath(edge, edgeLabelOffset) {
|
|
14862
15151
|
if (!edge.label || !edge.labelLineGap) return this.buildPath(edge);
|
|
@@ -15571,6 +15860,6 @@ function applyEdgeStyle(ctx, style) {
|
|
|
15571
15860
|
return baseOpacity * strokeOpacity;
|
|
15572
15861
|
}
|
|
15573
15862
|
//#endregion
|
|
15574
|
-
export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, ResizeNodesCommand, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, directionFromAngle, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, getClosestPointOnPath, getPathPointAt, icon, isCornerPlacement, isEdgeEdgeEndpoint, isNodeEdgeEndpoint, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, resolveContentInset, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
|
|
15863
|
+
export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_LABEL_GAP, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, ResizeNodesCommand, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, directionFromAngle, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, getClosestPointOnPath, getPathPointAt, icon, isCornerPlacement, isEdgeEdgeEndpoint, isExternalLabelPlacement, isNodeEdgeEndpoint, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, resolveContentInset, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
|
|
15575
15864
|
|
|
15576
15865
|
//# sourceMappingURL=papirus.js.map
|