@ngroznykh/papirus 0.9.3 → 0.9.5
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 +4 -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/core/ResizeManager.d.ts +7 -0
- package/dist/core/ResizeManager.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 +488 -169
- 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
|
@@ -1255,6 +1255,9 @@ var DragManager = class extends EventEmitter {
|
|
|
1255
1255
|
};
|
|
1256
1256
|
//#endregion
|
|
1257
1257
|
//#region src/core/ResizeManager.ts
|
|
1258
|
+
function cursorForResizeHandle(handle) {
|
|
1259
|
+
return handle === "nw" || handle === "se" ? "nwse-resize" : "nesw-resize";
|
|
1260
|
+
}
|
|
1258
1261
|
/**
|
|
1259
1262
|
* Manages node resizing via corner handles
|
|
1260
1263
|
*/
|
|
@@ -1320,6 +1323,16 @@ var ResizeManager = class extends EventEmitter {
|
|
|
1320
1323
|
return true;
|
|
1321
1324
|
}
|
|
1322
1325
|
/**
|
|
1326
|
+
* Set canvas cursor when the pointer is over a selected node's resize handle.
|
|
1327
|
+
* Call after badge/connection hover so the resize cursor wins on corners.
|
|
1328
|
+
*/
|
|
1329
|
+
updateHoverCursor(event) {
|
|
1330
|
+
if (this.renderer.blocksDiagramPointerAtScreen(event.screenX, event.screenY)) return;
|
|
1331
|
+
const handle = this.isResizing ? this.handle : this.hitTestSelectedHandle(event);
|
|
1332
|
+
if (!handle) return;
|
|
1333
|
+
this.setCursor(cursorForResizeHandle(handle));
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1323
1336
|
* Handle mouse move - update resize
|
|
1324
1337
|
*/
|
|
1325
1338
|
handleMouseMove(event) {
|
|
@@ -1436,6 +1449,20 @@ var ResizeManager = class extends EventEmitter {
|
|
|
1436
1449
|
height
|
|
1437
1450
|
};
|
|
1438
1451
|
}
|
|
1452
|
+
hitTestSelectedHandle(event) {
|
|
1453
|
+
const selectedIds = Array.from(this.selectionManager.selectedIds);
|
|
1454
|
+
if (selectedIds.length !== 1) return null;
|
|
1455
|
+
const node = this.renderer.getNode(selectedIds[0]);
|
|
1456
|
+
if (!node) return null;
|
|
1457
|
+
return node.hitTestResizeHandle({
|
|
1458
|
+
x: event.worldX,
|
|
1459
|
+
y: event.worldY
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
setCursor(cursor) {
|
|
1463
|
+
const canvas = this.renderer.getCanvas();
|
|
1464
|
+
if (canvas.style.cursor !== cursor) canvas.style.cursor = cursor;
|
|
1465
|
+
}
|
|
1439
1466
|
reset() {
|
|
1440
1467
|
this.isResizing = false;
|
|
1441
1468
|
this.resizedNodeId = null;
|
|
@@ -1456,7 +1483,7 @@ function getContentBounds({ nodes, edges, groups, includeInvisible = false }) {
|
|
|
1456
1483
|
let hasContent = false;
|
|
1457
1484
|
const consider = (source) => {
|
|
1458
1485
|
if (!includeInvisible && !source.visible) return;
|
|
1459
|
-
const bounds = source.getBounds();
|
|
1486
|
+
const bounds = source.getVisualBounds?.() ?? source.getBounds();
|
|
1460
1487
|
minX = Math.min(minX, bounds.x);
|
|
1461
1488
|
minY = Math.min(minY, bounds.y);
|
|
1462
1489
|
maxX = Math.max(maxX, bounds.x + bounds.width);
|
|
@@ -1671,6 +1698,133 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1671
1698
|
}
|
|
1672
1699
|
};
|
|
1673
1700
|
//#endregion
|
|
1701
|
+
//#region src/utils/edgePath.ts
|
|
1702
|
+
/**
|
|
1703
|
+
* Point and tangent angle at a normalized position along an edge path (0..1 by length).
|
|
1704
|
+
*/
|
|
1705
|
+
function getPathPointAt(path, type, position) {
|
|
1706
|
+
if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
|
|
1707
|
+
const samples = [];
|
|
1708
|
+
const steps = 20;
|
|
1709
|
+
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
1710
|
+
const start = path[i - 1];
|
|
1711
|
+
const control1 = path[i];
|
|
1712
|
+
const control2 = path[i + 1];
|
|
1713
|
+
const end = path[i + 2];
|
|
1714
|
+
for (let sample = 0; sample <= steps; sample++) {
|
|
1715
|
+
const t = sample / steps;
|
|
1716
|
+
if (samples.length > 0 && t === 0) continue;
|
|
1717
|
+
samples.push(bezierPoint(start, control1, control2, end, t));
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
return getPointAlongPolyline(samples, position);
|
|
1721
|
+
}
|
|
1722
|
+
function getPointAlongPolyline(path, position) {
|
|
1723
|
+
if (path.length === 0) return {
|
|
1724
|
+
point: {
|
|
1725
|
+
x: 0,
|
|
1726
|
+
y: 0
|
|
1727
|
+
},
|
|
1728
|
+
angle: 0
|
|
1729
|
+
};
|
|
1730
|
+
if (path.length === 1) return {
|
|
1731
|
+
point: path[0],
|
|
1732
|
+
angle: 0
|
|
1733
|
+
};
|
|
1734
|
+
let totalLength = 0;
|
|
1735
|
+
const segments = [];
|
|
1736
|
+
for (let i = 1; i < path.length; i++) {
|
|
1737
|
+
const start = path[i - 1];
|
|
1738
|
+
const end = path[i];
|
|
1739
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
1740
|
+
segments.push({
|
|
1741
|
+
start,
|
|
1742
|
+
end,
|
|
1743
|
+
length
|
|
1744
|
+
});
|
|
1745
|
+
totalLength += length;
|
|
1746
|
+
}
|
|
1747
|
+
const targetLength = totalLength * Math.max(0, Math.min(1, position));
|
|
1748
|
+
let accumulated = 0;
|
|
1749
|
+
for (const segment of segments) {
|
|
1750
|
+
if (accumulated + segment.length >= targetLength) {
|
|
1751
|
+
const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
|
|
1752
|
+
return {
|
|
1753
|
+
point: {
|
|
1754
|
+
x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
|
|
1755
|
+
y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
|
|
1756
|
+
},
|
|
1757
|
+
angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
accumulated += segment.length;
|
|
1761
|
+
}
|
|
1762
|
+
const lastSegment = segments[segments.length - 1];
|
|
1763
|
+
return {
|
|
1764
|
+
point: lastSegment.end,
|
|
1765
|
+
angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
/** Map tangent angle to a cardinal direction used by polyline routing. */
|
|
1769
|
+
function directionFromAngle(angle) {
|
|
1770
|
+
const twoPi = Math.PI * 2;
|
|
1771
|
+
const a = (angle % twoPi + twoPi) % twoPi;
|
|
1772
|
+
if (a >= Math.PI * .25 && a < Math.PI * .75) return "bottom";
|
|
1773
|
+
if (a >= Math.PI * .75 && a < Math.PI * 1.25) return "left";
|
|
1774
|
+
if (a >= Math.PI * 1.25 && a < Math.PI * 1.75) return "top";
|
|
1775
|
+
return "right";
|
|
1776
|
+
}
|
|
1777
|
+
function closestPointOnPolyline(polyline, point) {
|
|
1778
|
+
if (polyline.length < 2) return null;
|
|
1779
|
+
let totalLength = 0;
|
|
1780
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
1781
|
+
const start = polyline[i - 1];
|
|
1782
|
+
const end = polyline[i];
|
|
1783
|
+
totalLength += Math.hypot(end.x - start.x, end.y - start.y);
|
|
1784
|
+
}
|
|
1785
|
+
let bestPoint = polyline[0];
|
|
1786
|
+
let bestParam = 0;
|
|
1787
|
+
let bestDist = Infinity;
|
|
1788
|
+
let accumulated = 0;
|
|
1789
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
1790
|
+
const start = polyline[i - 1];
|
|
1791
|
+
const end = polyline[i];
|
|
1792
|
+
const dx = end.x - start.x;
|
|
1793
|
+
const dy = end.y - start.y;
|
|
1794
|
+
const lengthSq = dx * dx + dy * dy;
|
|
1795
|
+
const segLen = Math.sqrt(lengthSq);
|
|
1796
|
+
const t = lengthSq === 0 ? 0 : Math.max(0, Math.min(1, ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSq));
|
|
1797
|
+
const proj = {
|
|
1798
|
+
x: start.x + t * dx,
|
|
1799
|
+
y: start.y + t * dy
|
|
1800
|
+
};
|
|
1801
|
+
const dist = Math.hypot(point.x - proj.x, point.y - proj.y);
|
|
1802
|
+
if (dist < bestDist) {
|
|
1803
|
+
bestDist = dist;
|
|
1804
|
+
bestPoint = proj;
|
|
1805
|
+
bestParam = totalLength > 0 ? (accumulated + t * segLen) / totalLength : 0;
|
|
1806
|
+
}
|
|
1807
|
+
accumulated += segLen;
|
|
1808
|
+
}
|
|
1809
|
+
return {
|
|
1810
|
+
point: bestPoint,
|
|
1811
|
+
pathParam: bestParam,
|
|
1812
|
+
distance: bestDist
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Nearest point on an edge path to `point`.
|
|
1817
|
+
* Polylines project onto real segments so a click on a long orthogonal stroke
|
|
1818
|
+
* is not lost between length samples. Bezier paths are densified, then projected.
|
|
1819
|
+
*/
|
|
1820
|
+
function getClosestPointOnPath(path, type, point, samples = 48) {
|
|
1821
|
+
if (path.length < 2) return null;
|
|
1822
|
+
if (type !== "bezier" || path.length < 4) return closestPointOnPolyline(path, point);
|
|
1823
|
+
const densified = [];
|
|
1824
|
+
for (let i = 0; i <= samples; i++) densified.push(getPathPointAt(path, type, i / samples).point);
|
|
1825
|
+
return closestPointOnPolyline(densified, point);
|
|
1826
|
+
}
|
|
1827
|
+
//#endregion
|
|
1674
1828
|
//#region src/utils/direction.ts
|
|
1675
1829
|
/**
|
|
1676
1830
|
* Map outline param [0,1) to cardinal direction.
|
|
@@ -2048,7 +2202,18 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2048
2202
|
let snappedPoint = point;
|
|
2049
2203
|
let snappedDir;
|
|
2050
2204
|
let targetNode = this.getNodeAtPoint(point, true);
|
|
2051
|
-
|
|
2205
|
+
const edgeDrop = this.originalEdgeEndpoint?.edgeId ? this.findEdgeDropTarget(point, {
|
|
2206
|
+
maxScreenPx: 40,
|
|
2207
|
+
skipEdgeIds: new Set([edge.id])
|
|
2208
|
+
}) : null;
|
|
2209
|
+
if (edgeDrop) {
|
|
2210
|
+
const at = edgeDrop.edge.getPointAt(edgeDrop.pathParam);
|
|
2211
|
+
snappedPoint = edgeDrop.point;
|
|
2212
|
+
snappedDir = at ? directionFromAngle(at.angle) : void 0;
|
|
2213
|
+
targetNode = null;
|
|
2214
|
+
this.reconnectingOutlineParam = null;
|
|
2215
|
+
this.reconnectingTargetNodeId = null;
|
|
2216
|
+
} else if (this.attachToOutline) {
|
|
2052
2217
|
const tolerance = 40 / Math.max(this.renderer.zoom, 1e-4);
|
|
2053
2218
|
const toleranceSq = tolerance * tolerance;
|
|
2054
2219
|
let bestDistSq = Infinity;
|
|
@@ -2215,6 +2380,9 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2215
2380
|
this.previewTargetNodeId = null;
|
|
2216
2381
|
this.previewTargetEdgeId = null;
|
|
2217
2382
|
this.previewTargetPathParam = null;
|
|
2383
|
+
const rawNode = this.getRawNodeAtPoint(cursorPoint);
|
|
2384
|
+
const forbiddenFill = !!this._connectionValidator && !!this.sourceNode && !!rawNode && !this._connectionValidator(this.sourceNode.id, rawNode.id);
|
|
2385
|
+
const searchGroupFill = !!rawNode && (forbiddenFill || this.nodeContainsSource(rawNode) || this.nodeLooksLikeContainer(rawNode));
|
|
2218
2386
|
let targetNode = this.getNodeAtPoint(cursorPoint, false);
|
|
2219
2387
|
if (this.attachToOutline) {
|
|
2220
2388
|
const tolerance = 40 / Math.max(this.renderer.zoom, 1e-4);
|
|
@@ -2258,7 +2426,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2258
2426
|
}
|
|
2259
2427
|
}
|
|
2260
2428
|
}
|
|
2261
|
-
const edgeDrop = this.findEdgeDropTarget(cursorPoint);
|
|
2429
|
+
const edgeDrop = this.findEdgeDropTarget(cursorPoint, searchGroupFill ? { maxScreenPx: 40 } : void 0);
|
|
2262
2430
|
if (edgeDrop) {
|
|
2263
2431
|
let nodeDistance = Infinity;
|
|
2264
2432
|
if (targetNode) {
|
|
@@ -2266,7 +2434,7 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2266
2434
|
const dy = cursorPoint.y - snappedPoint.y;
|
|
2267
2435
|
nodeDistance = Math.sqrt(dx * dx + dy * dy);
|
|
2268
2436
|
}
|
|
2269
|
-
if (!targetNode || edgeDrop.distance
|
|
2437
|
+
if (searchGroupFill || !targetNode || edgeDrop.distance <= nodeDistance) {
|
|
2270
2438
|
targetNode = null;
|
|
2271
2439
|
this.previewTargetNodeId = null;
|
|
2272
2440
|
this.previewTargetOutlineParam = null;
|
|
@@ -2275,6 +2443,12 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2275
2443
|
this.previewTargetEdgeId = edgeDrop.edge.id;
|
|
2276
2444
|
this.previewTargetPathParam = edgeDrop.pathParam;
|
|
2277
2445
|
}
|
|
2446
|
+
} else if (searchGroupFill) {
|
|
2447
|
+
targetNode = null;
|
|
2448
|
+
this.previewTargetNodeId = null;
|
|
2449
|
+
this.previewTargetOutlineParam = null;
|
|
2450
|
+
this.previewTargetAnchorId = null;
|
|
2451
|
+
snappedPoint = cursorPoint;
|
|
2278
2452
|
}
|
|
2279
2453
|
if (targetNode) {
|
|
2280
2454
|
const forbidden = this._connectionValidator && this.sourceNode && !this._connectionValidator(this.sourceNode.id, targetNode.id);
|
|
@@ -2336,9 +2510,23 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2336
2510
|
y: event.worldY
|
|
2337
2511
|
};
|
|
2338
2512
|
let reconnected = false;
|
|
2513
|
+
const edgeDrop = this.originalEdgeEndpoint?.edgeId ? this.findEdgeDropTarget(point, {
|
|
2514
|
+
maxScreenPx: 40,
|
|
2515
|
+
skipEdgeIds: new Set([this.reconnectingEdge.id])
|
|
2516
|
+
}) : null;
|
|
2517
|
+
if (edgeDrop) {
|
|
2518
|
+
const endpoint = {
|
|
2519
|
+
edgeId: edgeDrop.edgeId,
|
|
2520
|
+
pathParam: edgeDrop.pathParam
|
|
2521
|
+
};
|
|
2522
|
+
if (this.reconnectingEndpoint === "start") this.reconnectingEdge.from = endpoint;
|
|
2523
|
+
else this.reconnectingEdge.to = endpoint;
|
|
2524
|
+
reconnected = true;
|
|
2525
|
+
this.emit("edgeReconnect", this.reconnectingEdge, this.reconnectingEndpoint);
|
|
2526
|
+
}
|
|
2339
2527
|
let node = this.getNodeAtPoint(point, true);
|
|
2340
2528
|
if (!node && this.attachToOutline && this.reconnectingTargetNodeId) node = this.renderer.getNode(this.reconnectingTargetNodeId) ?? null;
|
|
2341
|
-
if (node) {
|
|
2529
|
+
if (!reconnected && node) {
|
|
2342
2530
|
if (this.attachToOutline && this.reconnectingOutlineParam !== null) {
|
|
2343
2531
|
const endpoint = {
|
|
2344
2532
|
nodeId: node.id,
|
|
@@ -2543,11 +2731,12 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2543
2731
|
/**
|
|
2544
2732
|
* Find the nearest edge under the cursor for junction / note→relation drops.
|
|
2545
2733
|
*/
|
|
2546
|
-
findEdgeDropTarget(point) {
|
|
2547
|
-
const tolerance = Math.max(14, 20 / Math.max(this.renderer.zoom, 1e-4));
|
|
2734
|
+
findEdgeDropTarget(point, options) {
|
|
2735
|
+
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);
|
|
2548
2736
|
let best = null;
|
|
2549
2737
|
for (const edge of this.renderer.edges.values()) {
|
|
2550
2738
|
if (!edge.visible || edge.path.length < 2) continue;
|
|
2739
|
+
if (options?.skipEdgeIds?.has(edge.id)) continue;
|
|
2551
2740
|
const closest = edge.getClosestPointOnPath(point);
|
|
2552
2741
|
if (!closest || closest.distance > tolerance) continue;
|
|
2553
2742
|
if (!best || closest.distance < best.distance) best = {
|
|
@@ -2806,6 +2995,26 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2806
2995
|
}
|
|
2807
2996
|
return null;
|
|
2808
2997
|
}
|
|
2998
|
+
getRawNodeAtPoint(point) {
|
|
2999
|
+
const nodes = Array.from(this.renderer.nodes.values());
|
|
3000
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
3001
|
+
const node = nodes[i];
|
|
3002
|
+
if (node.visible && node.hitTest(point)) return node;
|
|
3003
|
+
}
|
|
3004
|
+
return null;
|
|
3005
|
+
}
|
|
3006
|
+
nodeLooksLikeContainer(node, source = this.sourceNode) {
|
|
3007
|
+
if (!source || source.id === node.id) return false;
|
|
3008
|
+
return node.width * node.height >= Math.max(source.width * source.height, 1) * 4;
|
|
3009
|
+
}
|
|
3010
|
+
nodeContainsSource(node, source = this.sourceNode) {
|
|
3011
|
+
if (!source || source.id === node.id) return false;
|
|
3012
|
+
const sourceBounds = source.getBounds();
|
|
3013
|
+
const nodeBounds = node.getBounds();
|
|
3014
|
+
const cx = sourceBounds.x + sourceBounds.width / 2;
|
|
3015
|
+
const cy = sourceBounds.y + sourceBounds.height / 2;
|
|
3016
|
+
return cx >= nodeBounds.x && cx <= nodeBounds.x + nodeBounds.width && cy >= nodeBounds.y && cy <= nodeBounds.y + nodeBounds.height;
|
|
3017
|
+
}
|
|
2809
3018
|
startOutlineConnection(node, attach) {
|
|
2810
3019
|
const { point: outlinePoint, param } = node.getClosestPointOnOutline(attach);
|
|
2811
3020
|
this.sourceNode = node;
|
|
@@ -2940,6 +3149,9 @@ function getHistoryShortcut(event, keys = DEFAULT_HISTORY_SHORTCUT_KEYS) {
|
|
|
2940
3149
|
}
|
|
2941
3150
|
//#endregion
|
|
2942
3151
|
//#region src/utils/style.ts
|
|
3152
|
+
function canvasFontFromTextStyle(style) {
|
|
3153
|
+
return `${style.fontStyle ?? "normal"} ${style.fontWeight ?? "normal"} ${style.fontSize ?? 14}px ${style.fontFamily ?? "sans-serif"}`;
|
|
3154
|
+
}
|
|
2943
3155
|
/**
|
|
2944
3156
|
* Shallow compare two objects
|
|
2945
3157
|
*/
|
|
@@ -3039,6 +3251,7 @@ var DEFAULT_STYLE = {
|
|
|
3039
3251
|
fontSize: 14,
|
|
3040
3252
|
fontFamily: "sans-serif",
|
|
3041
3253
|
fontWeight: "normal",
|
|
3254
|
+
fontStyle: "normal",
|
|
3042
3255
|
color: "#000000",
|
|
3043
3256
|
opacity: 1,
|
|
3044
3257
|
align: "center",
|
|
@@ -3283,9 +3496,7 @@ var TextLabel = class {
|
|
|
3283
3496
|
this.render(ctx, bounds);
|
|
3284
3497
|
}
|
|
3285
3498
|
applyStyle(ctx) {
|
|
3286
|
-
|
|
3287
|
-
const fontFamily = this._style.fontFamily ?? "sans-serif";
|
|
3288
|
-
ctx.font = `${this._style.fontWeight ?? "normal"} ${fontSize}px ${fontFamily}`;
|
|
3499
|
+
ctx.font = canvasFontFromTextStyle(this._style);
|
|
3289
3500
|
ctx.textAlign = this._style.align ?? "center";
|
|
3290
3501
|
ctx.textBaseline = this._style.baseline ?? "middle";
|
|
3291
3502
|
}
|
|
@@ -3919,109 +4130,6 @@ var Element = class {
|
|
|
3919
4130
|
}
|
|
3920
4131
|
};
|
|
3921
4132
|
//#endregion
|
|
3922
|
-
//#region src/utils/edgePath.ts
|
|
3923
|
-
/**
|
|
3924
|
-
* Point and tangent angle at a normalized position along an edge path (0..1 by length).
|
|
3925
|
-
*/
|
|
3926
|
-
function getPathPointAt(path, type, position) {
|
|
3927
|
-
if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
|
|
3928
|
-
const samples = [];
|
|
3929
|
-
const steps = 20;
|
|
3930
|
-
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
3931
|
-
const start = path[i - 1];
|
|
3932
|
-
const control1 = path[i];
|
|
3933
|
-
const control2 = path[i + 1];
|
|
3934
|
-
const end = path[i + 2];
|
|
3935
|
-
for (let sample = 0; sample <= steps; sample++) {
|
|
3936
|
-
const t = sample / steps;
|
|
3937
|
-
if (samples.length > 0 && t === 0) continue;
|
|
3938
|
-
samples.push(bezierPoint(start, control1, control2, end, t));
|
|
3939
|
-
}
|
|
3940
|
-
}
|
|
3941
|
-
return getPointAlongPolyline(samples, position);
|
|
3942
|
-
}
|
|
3943
|
-
function getPointAlongPolyline(path, position) {
|
|
3944
|
-
if (path.length === 0) return {
|
|
3945
|
-
point: {
|
|
3946
|
-
x: 0,
|
|
3947
|
-
y: 0
|
|
3948
|
-
},
|
|
3949
|
-
angle: 0
|
|
3950
|
-
};
|
|
3951
|
-
if (path.length === 1) return {
|
|
3952
|
-
point: path[0],
|
|
3953
|
-
angle: 0
|
|
3954
|
-
};
|
|
3955
|
-
let totalLength = 0;
|
|
3956
|
-
const segments = [];
|
|
3957
|
-
for (let i = 1; i < path.length; i++) {
|
|
3958
|
-
const start = path[i - 1];
|
|
3959
|
-
const end = path[i];
|
|
3960
|
-
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
3961
|
-
segments.push({
|
|
3962
|
-
start,
|
|
3963
|
-
end,
|
|
3964
|
-
length
|
|
3965
|
-
});
|
|
3966
|
-
totalLength += length;
|
|
3967
|
-
}
|
|
3968
|
-
const targetLength = totalLength * Math.max(0, Math.min(1, position));
|
|
3969
|
-
let accumulated = 0;
|
|
3970
|
-
for (const segment of segments) {
|
|
3971
|
-
if (accumulated + segment.length >= targetLength) {
|
|
3972
|
-
const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
|
|
3973
|
-
return {
|
|
3974
|
-
point: {
|
|
3975
|
-
x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
|
|
3976
|
-
y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
|
|
3977
|
-
},
|
|
3978
|
-
angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
|
|
3979
|
-
};
|
|
3980
|
-
}
|
|
3981
|
-
accumulated += segment.length;
|
|
3982
|
-
}
|
|
3983
|
-
const lastSegment = segments[segments.length - 1];
|
|
3984
|
-
return {
|
|
3985
|
-
point: lastSegment.end,
|
|
3986
|
-
angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
|
|
3987
|
-
};
|
|
3988
|
-
}
|
|
3989
|
-
/** Map tangent angle to a cardinal direction used by polyline routing. */
|
|
3990
|
-
function directionFromAngle(angle) {
|
|
3991
|
-
const twoPi = Math.PI * 2;
|
|
3992
|
-
const a = (angle % twoPi + twoPi) % twoPi;
|
|
3993
|
-
if (a >= Math.PI * .25 && a < Math.PI * .75) return "bottom";
|
|
3994
|
-
if (a >= Math.PI * .75 && a < Math.PI * 1.25) return "left";
|
|
3995
|
-
if (a >= Math.PI * 1.25 && a < Math.PI * 1.75) return "top";
|
|
3996
|
-
return "right";
|
|
3997
|
-
}
|
|
3998
|
-
/**
|
|
3999
|
-
* Nearest point on an edge path to `point`, by sampling along normalized length.
|
|
4000
|
-
*/
|
|
4001
|
-
function getClosestPointOnPath(path, type, point, samples = 48) {
|
|
4002
|
-
if (path.length < 2) return null;
|
|
4003
|
-
let bestPoint = getPathPointAt(path, type, 0).point;
|
|
4004
|
-
let bestParam = 0;
|
|
4005
|
-
let bestDistSq = Infinity;
|
|
4006
|
-
for (let i = 0; i <= samples; i++) {
|
|
4007
|
-
const pathParam = i / samples;
|
|
4008
|
-
const at = getPathPointAt(path, type, pathParam).point;
|
|
4009
|
-
const dx = at.x - point.x;
|
|
4010
|
-
const dy = at.y - point.y;
|
|
4011
|
-
const distSq = dx * dx + dy * dy;
|
|
4012
|
-
if (distSq < bestDistSq) {
|
|
4013
|
-
bestDistSq = distSq;
|
|
4014
|
-
bestPoint = at;
|
|
4015
|
-
bestParam = pathParam;
|
|
4016
|
-
}
|
|
4017
|
-
}
|
|
4018
|
-
return {
|
|
4019
|
-
point: bestPoint,
|
|
4020
|
-
pathParam: bestParam,
|
|
4021
|
-
distance: Math.sqrt(bestDistSq)
|
|
4022
|
-
};
|
|
4023
|
-
}
|
|
4024
|
-
//#endregion
|
|
4025
4133
|
//#region src/elements/paths/StraightPathStrategy.ts
|
|
4026
4134
|
/**
|
|
4027
4135
|
* Straight line path strategy
|
|
@@ -7083,7 +7191,12 @@ var Edge = class extends Element {
|
|
|
7083
7191
|
* Nearest point on this edge's path to `point` (world coordinates).
|
|
7084
7192
|
*/
|
|
7085
7193
|
getClosestPointOnPath(point) {
|
|
7086
|
-
|
|
7194
|
+
const onPath = getClosestPointOnPath(this._path, this._type, point);
|
|
7195
|
+
if (onPath) return onPath;
|
|
7196
|
+
const start = this._fromPoint;
|
|
7197
|
+
const end = this._toPoint;
|
|
7198
|
+
if (start.x === end.x && start.y === end.y) return null;
|
|
7199
|
+
return getClosestPointOnPath([start, end], "straight", point);
|
|
7087
7200
|
}
|
|
7088
7201
|
/**
|
|
7089
7202
|
* Get world position of label center along path.
|
|
@@ -7839,9 +7952,13 @@ var InteractionManager = class {
|
|
|
7839
7952
|
if (overOverlayPointerSink && !this.connectionManager.connecting && !this.connectionManager.isEditingEdgeControlPoint) this.connectionManager.clearPointerHover();
|
|
7840
7953
|
if (overScrollbar || blockDiagramRoutesUnderOverlay) return;
|
|
7841
7954
|
if (!this.navigationOnly) {
|
|
7842
|
-
if (this.resizeManager.handleMouseMove(event))
|
|
7955
|
+
if (this.resizeManager.handleMouseMove(event)) {
|
|
7956
|
+
this.resizeManager.updateHoverCursor(event);
|
|
7957
|
+
return;
|
|
7958
|
+
}
|
|
7843
7959
|
if (this.connectionManager.handleMouseMove(event)) return;
|
|
7844
7960
|
if (this.dragManager.handleMouseMove(event)) return;
|
|
7961
|
+
this.resizeManager.updateHoverCursor(event);
|
|
7845
7962
|
}
|
|
7846
7963
|
if (this.selectionManager.selectionRectangle !== null) {
|
|
7847
7964
|
this.selectionManager.updateSelectionRect({
|
|
@@ -7940,7 +8057,13 @@ var InteractionManager = class {
|
|
|
7940
8057
|
}
|
|
7941
8058
|
if ("typeName" in hitElement) {
|
|
7942
8059
|
if (hitElement.typeName === "composite") {
|
|
7943
|
-
|
|
8060
|
+
const composite = hitElement;
|
|
8061
|
+
if (composite.usesExternalLabel()) {
|
|
8062
|
+
const editText = composite.label?.editableText ?? composite.label?.text ?? "";
|
|
8063
|
+
this.startLabelEdit("node", composite.id, editText, composite.getLabelPosition());
|
|
8064
|
+
return;
|
|
8065
|
+
}
|
|
8066
|
+
this.startCompositeNameEdit(composite);
|
|
7944
8067
|
return;
|
|
7945
8068
|
}
|
|
7946
8069
|
const editText = hitElement.label?.editableText ?? hitElement.label?.text ?? "";
|
|
@@ -9607,19 +9730,27 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9607
9730
|
return this._groups.get(groupId);
|
|
9608
9731
|
}
|
|
9609
9732
|
/**
|
|
9610
|
-
* Find element at a point
|
|
9733
|
+
* Find element at a point. Edges are painted above nodes, so a stroke over a
|
|
9734
|
+
* nested pool/lane fill is hit before the covering container.
|
|
9611
9735
|
*/
|
|
9612
9736
|
getElementAtPoint(worldPoint) {
|
|
9737
|
+
let bestEdge;
|
|
9738
|
+
let bestDist = Infinity;
|
|
9739
|
+
for (const edge of this._edges.values()) {
|
|
9740
|
+
if (!edge.visible) continue;
|
|
9741
|
+
const tolerance = Math.max((edge.style.strokeWidth ?? 2) * 2, 8) / Math.max(this._zoom, 1e-4);
|
|
9742
|
+
const closest = edge.getClosestPointOnPath(worldPoint);
|
|
9743
|
+
if (closest && closest.distance <= tolerance && closest.distance < bestDist) {
|
|
9744
|
+
bestDist = closest.distance;
|
|
9745
|
+
bestEdge = edge;
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9748
|
+
if (bestEdge) return bestEdge;
|
|
9613
9749
|
const nodesArray = Array.from(this._nodes.values());
|
|
9614
9750
|
for (let i = nodesArray.length - 1; i >= 0; i--) {
|
|
9615
9751
|
const node = nodesArray[i];
|
|
9616
9752
|
if (node.visible && node.hitTest(worldPoint)) return node;
|
|
9617
9753
|
}
|
|
9618
|
-
for (const edge of this._edges.values()) {
|
|
9619
|
-
if (!edge.visible) continue;
|
|
9620
|
-
const tolerance = Math.max((edge.style.strokeWidth ?? 2) * 2, 8) / Math.max(this._zoom, 1e-4);
|
|
9621
|
-
if (edge.hitTestWithTolerance(worldPoint, tolerance)) return edge;
|
|
9622
|
-
}
|
|
9623
9754
|
for (const group of this._groups.values()) if (group.visible && group.hitTest(worldPoint)) return group;
|
|
9624
9755
|
}
|
|
9625
9756
|
/**
|
|
@@ -10635,6 +10766,52 @@ var NodeImage = class {
|
|
|
10635
10766
|
}
|
|
10636
10767
|
};
|
|
10637
10768
|
//#endregion
|
|
10769
|
+
//#region src/utils/labelPlacement.ts
|
|
10770
|
+
var DEFAULT_LABEL_GAP = 4;
|
|
10771
|
+
function isExternalLabelPlacement(placement) {
|
|
10772
|
+
return placement === "top" || placement === "bottom" || placement === "left" || placement === "right";
|
|
10773
|
+
}
|
|
10774
|
+
function externalLabelWrapWidth(shapeWidth) {
|
|
10775
|
+
return Math.max(shapeWidth, 80);
|
|
10776
|
+
}
|
|
10777
|
+
function resolveExternalLabelBounds(shape, placement, gap, measured) {
|
|
10778
|
+
const width = Math.max(0, measured.width);
|
|
10779
|
+
const height = Math.max(0, measured.height);
|
|
10780
|
+
const safeGap = Number.isFinite(gap) ? gap : 4;
|
|
10781
|
+
switch (placement) {
|
|
10782
|
+
case "top": return {
|
|
10783
|
+
x: shape.x + (shape.width - width) / 2,
|
|
10784
|
+
y: shape.y - safeGap - height,
|
|
10785
|
+
width,
|
|
10786
|
+
height
|
|
10787
|
+
};
|
|
10788
|
+
case "bottom": return {
|
|
10789
|
+
x: shape.x + (shape.width - width) / 2,
|
|
10790
|
+
y: shape.y + shape.height + safeGap,
|
|
10791
|
+
width,
|
|
10792
|
+
height
|
|
10793
|
+
};
|
|
10794
|
+
case "left": return {
|
|
10795
|
+
x: shape.x - safeGap - width,
|
|
10796
|
+
y: shape.y + (shape.height - height) / 2,
|
|
10797
|
+
width,
|
|
10798
|
+
height
|
|
10799
|
+
};
|
|
10800
|
+
case "right": return {
|
|
10801
|
+
x: shape.x + shape.width + safeGap,
|
|
10802
|
+
y: shape.y + (shape.height - height) / 2,
|
|
10803
|
+
width,
|
|
10804
|
+
height
|
|
10805
|
+
};
|
|
10806
|
+
}
|
|
10807
|
+
}
|
|
10808
|
+
function unionBounds(a, b) {
|
|
10809
|
+
return rectUnion(a, b);
|
|
10810
|
+
}
|
|
10811
|
+
function pointInBounds(point, bounds) {
|
|
10812
|
+
return pointInRect(point, bounds);
|
|
10813
|
+
}
|
|
10814
|
+
//#endregion
|
|
10638
10815
|
//#region src/utils/resolveContentInset.ts
|
|
10639
10816
|
function axisFactor(node, base) {
|
|
10640
10817
|
if (base === void 0 || !(base > 0) || !Number.isFinite(base)) return 1;
|
|
@@ -10707,6 +10884,8 @@ var Node = class extends Element {
|
|
|
10707
10884
|
this._contentInset = this.normalizeContentInset(options.contentInset);
|
|
10708
10885
|
this._contentInsetScale = this.normalizeContentInsetScale(options.contentInsetScale);
|
|
10709
10886
|
this._contentInsetBaseSize = this.normalizeContentInsetBaseSize(options.contentInsetBaseSize);
|
|
10887
|
+
this._labelPlacement = options.labelPlacement ?? "center";
|
|
10888
|
+
this._labelGap = options.labelGap !== void 0 && Number.isFinite(options.labelGap) ? Math.max(0, options.labelGap) : 4;
|
|
10710
10889
|
if (options.label !== void 0) if (typeof options.label === "string") this._label = new TextLabel({
|
|
10711
10890
|
text: options.label,
|
|
10712
10891
|
onChange: () => this.markDirty()
|
|
@@ -10757,6 +10936,33 @@ var Node = class extends Element {
|
|
|
10757
10936
|
get ports() {
|
|
10758
10937
|
return this._ports;
|
|
10759
10938
|
}
|
|
10939
|
+
get labelPlacement() {
|
|
10940
|
+
return this._labelPlacement;
|
|
10941
|
+
}
|
|
10942
|
+
set labelPlacement(value) {
|
|
10943
|
+
if (this._labelPlacement !== value) {
|
|
10944
|
+
this._labelPlacement = value;
|
|
10945
|
+
this.markDirty();
|
|
10946
|
+
}
|
|
10947
|
+
}
|
|
10948
|
+
get labelGap() {
|
|
10949
|
+
return this._labelGap;
|
|
10950
|
+
}
|
|
10951
|
+
set labelGap(value) {
|
|
10952
|
+
const next = Number.isFinite(value) ? Math.max(0, value) : 4;
|
|
10953
|
+
if (this._labelGap !== next) {
|
|
10954
|
+
this._labelGap = next;
|
|
10955
|
+
this.markDirty();
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
usesExternalLabel() {
|
|
10959
|
+
return isExternalLabelPlacement(this._labelPlacement) && this._label !== void 0;
|
|
10960
|
+
}
|
|
10961
|
+
getVisualBounds() {
|
|
10962
|
+
const shape = this.getBounds();
|
|
10963
|
+
if (!this.usesExternalLabel()) return shape;
|
|
10964
|
+
return unionBounds(shape, this.getResolvedLabelBounds());
|
|
10965
|
+
}
|
|
10760
10966
|
/**
|
|
10761
10967
|
* Text label
|
|
10762
10968
|
*/
|
|
@@ -10980,13 +11186,15 @@ var Node = class extends Element {
|
|
|
10980
11186
|
* Calculate layout for icon and label.
|
|
10981
11187
|
* Label always gets full content area; icon is placed within the same content area.
|
|
10982
11188
|
*/
|
|
10983
|
-
calculateContentLayout(bounds, iconBoxSize,
|
|
11189
|
+
calculateContentLayout(bounds, iconBoxSize, labelSize) {
|
|
11190
|
+
const iconContainer = this.getIconContainerBounds(bounds);
|
|
10984
11191
|
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
10985
|
-
let iconBounds =
|
|
10986
|
-
if (this._icon && iconBoxSize) iconBounds = this.getIconBounds(
|
|
11192
|
+
let iconBounds = iconContainer;
|
|
11193
|
+
if (this._icon && iconBoxSize) iconBounds = this.getIconBounds(iconContainer, iconBoxSize, this._icon.placement);
|
|
11194
|
+
const labelBounds = isExternalLabelPlacement(this._labelPlacement) && this._label && labelSize ? resolveExternalLabelBounds(bounds, this._labelPlacement, this._labelGap, labelSize) : contentBounds;
|
|
10987
11195
|
return {
|
|
10988
11196
|
iconBounds,
|
|
10989
|
-
labelBounds
|
|
11197
|
+
labelBounds
|
|
10990
11198
|
};
|
|
10991
11199
|
}
|
|
10992
11200
|
/**
|
|
@@ -10996,14 +11204,14 @@ var Node = class extends Element {
|
|
|
10996
11204
|
getLabelBoundsForExport(ctx) {
|
|
10997
11205
|
if (!this._label) return null;
|
|
10998
11206
|
const bounds = this.getBounds();
|
|
10999
|
-
const
|
|
11207
|
+
const wrapWidth = this.getLabelWrapWidth(bounds);
|
|
11000
11208
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
11001
|
-
this._label.setAutoMaxWidth(
|
|
11209
|
+
this._label.setAutoMaxWidth(wrapWidth);
|
|
11002
11210
|
const labelSize = this._label.measure(ctx);
|
|
11003
11211
|
const { labelBounds } = this.calculateContentLayout(bounds, iconBoxSize, labelSize);
|
|
11004
11212
|
return {
|
|
11005
11213
|
bounds: labelBounds,
|
|
11006
|
-
lines: this._label.getWrappedLines(ctx, Math.max(0,
|
|
11214
|
+
lines: this._label.getWrappedLines(ctx, Math.max(0, wrapWidth))
|
|
11007
11215
|
};
|
|
11008
11216
|
}
|
|
11009
11217
|
/**
|
|
@@ -11016,10 +11224,10 @@ var Node = class extends Element {
|
|
|
11016
11224
|
ctx.globalAlpha = 1;
|
|
11017
11225
|
}
|
|
11018
11226
|
/**
|
|
11019
|
-
* Get world position of label center (
|
|
11227
|
+
* Get world position of label center (content area or external label box).
|
|
11020
11228
|
*/
|
|
11021
11229
|
getLabelPosition() {
|
|
11022
|
-
const bounds = this.
|
|
11230
|
+
const bounds = this.getResolvedLabelBounds();
|
|
11023
11231
|
return {
|
|
11024
11232
|
x: bounds.x + bounds.width / 2,
|
|
11025
11233
|
y: bounds.y + bounds.height / 2
|
|
@@ -11034,13 +11242,14 @@ var Node = class extends Element {
|
|
|
11034
11242
|
let bounds = this.getBounds();
|
|
11035
11243
|
this.renderBadges(ctx, bounds);
|
|
11036
11244
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
11037
|
-
|
|
11245
|
+
const externalLabel = this.usesExternalLabel();
|
|
11246
|
+
if (this._label) this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
11038
11247
|
const labelSize = this._label ? this._label.measure(ctx) : void 0;
|
|
11039
|
-
if (labelSize || iconBoxSize) {
|
|
11248
|
+
if ((labelSize || iconBoxSize) && !externalLabel) {
|
|
11040
11249
|
this.ensureContentsFit(labelSize, iconBoxSize);
|
|
11041
11250
|
if (this._label && iconBoxSize) {
|
|
11042
11251
|
bounds = this.getBounds();
|
|
11043
|
-
this._label.setAutoMaxWidth(this.
|
|
11252
|
+
this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
11044
11253
|
}
|
|
11045
11254
|
}
|
|
11046
11255
|
const { iconBounds, labelBounds } = this.calculateContentLayout(this.getBounds(), iconBoxSize, labelSize);
|
|
@@ -11117,10 +11326,10 @@ var Node = class extends Element {
|
|
|
11117
11326
|
return this.calculateContentMinSize(labelSize, iconBoxSize, bounds);
|
|
11118
11327
|
}
|
|
11119
11328
|
/**
|
|
11120
|
-
* Render resize handles when
|
|
11329
|
+
* Render the selection frame when selected, and resize handles when they are enabled.
|
|
11121
11330
|
*/
|
|
11122
11331
|
renderResizeHandles(ctx) {
|
|
11123
|
-
if (this._state !== "selected"
|
|
11332
|
+
if (this._state !== "selected") return;
|
|
11124
11333
|
const bounds = this.getBounds();
|
|
11125
11334
|
const offset = 6;
|
|
11126
11335
|
const size = 8;
|
|
@@ -11130,6 +11339,10 @@ var Node = class extends Element {
|
|
|
11130
11339
|
ctx.lineWidth = 1;
|
|
11131
11340
|
ctx.setLineDash([4, 4]);
|
|
11132
11341
|
ctx.strokeRect(bounds.x - offset, bounds.y - offset, bounds.width + offset * 2, bounds.height + offset * 2);
|
|
11342
|
+
if (!this._resizeHandlesEnabled) {
|
|
11343
|
+
ctx.restore();
|
|
11344
|
+
return;
|
|
11345
|
+
}
|
|
11133
11346
|
ctx.fillStyle = "#ffffff";
|
|
11134
11347
|
ctx.strokeStyle = DEFAULT_SELECTION_COLOR;
|
|
11135
11348
|
ctx.lineWidth = 1.5;
|
|
@@ -11146,7 +11359,7 @@ var Node = class extends Element {
|
|
|
11146
11359
|
* Hit test a resize handle
|
|
11147
11360
|
*/
|
|
11148
11361
|
hitTestResizeHandle(point) {
|
|
11149
|
-
if (this._state !== "selected") return null;
|
|
11362
|
+
if (this._state !== "selected" || !this._resizeHandlesEnabled) return null;
|
|
11150
11363
|
const half = 8 / 2;
|
|
11151
11364
|
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;
|
|
11152
11365
|
return null;
|
|
@@ -11157,7 +11370,25 @@ var Node = class extends Element {
|
|
|
11157
11370
|
hitTest(point) {
|
|
11158
11371
|
const bounds = this.getBounds();
|
|
11159
11372
|
const padding = 10;
|
|
11160
|
-
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;
|
|
11373
|
+
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);
|
|
11374
|
+
}
|
|
11375
|
+
hitTestExternalLabel(point) {
|
|
11376
|
+
if (!this.usesExternalLabel()) return false;
|
|
11377
|
+
return pointInBounds(point, this.getResolvedLabelBounds());
|
|
11378
|
+
}
|
|
11379
|
+
getLabelWrapWidth(bounds = this.getBounds()) {
|
|
11380
|
+
if (this.usesExternalLabel()) return externalLabelWrapWidth(bounds.width);
|
|
11381
|
+
return this.getLabelContainerBounds(bounds).width;
|
|
11382
|
+
}
|
|
11383
|
+
getResolvedLabelBounds(measured) {
|
|
11384
|
+
const shape = this.getBounds();
|
|
11385
|
+
if (!this.usesExternalLabel() || !this._label) return this.getLabelContainerBounds(shape);
|
|
11386
|
+
const size = measured ?? {
|
|
11387
|
+
width: this._label.measuredWidth || this.getLabelWrapWidth(shape),
|
|
11388
|
+
height: this._label.measuredHeight || (this._label.style.fontSize ?? 14) * 1.2
|
|
11389
|
+
};
|
|
11390
|
+
if (!isExternalLabelPlacement(this._labelPlacement)) return this.getLabelContainerBounds(shape);
|
|
11391
|
+
return resolveExternalLabelBounds(shape, this._labelPlacement, this._labelGap, size);
|
|
11161
11392
|
}
|
|
11162
11393
|
/**
|
|
11163
11394
|
* Get a connection point on the node boundary toward a target point,
|
|
@@ -11391,16 +11622,28 @@ var Node = class extends Element {
|
|
|
11391
11622
|
}
|
|
11392
11623
|
calculateContentMinSize(labelSize, iconBoxSize, bounds) {
|
|
11393
11624
|
const labelContainer = this.getLabelContainerBounds(bounds);
|
|
11394
|
-
const
|
|
11395
|
-
const
|
|
11396
|
-
const
|
|
11397
|
-
const
|
|
11625
|
+
const iconContainer = this.getIconContainerBounds(bounds);
|
|
11626
|
+
const labelWidthFactor = labelContainer.width > 0 ? bounds.width / labelContainer.width : 1;
|
|
11627
|
+
const labelHeightFactor = labelContainer.height > 0 ? bounds.height / labelContainer.height : 1;
|
|
11628
|
+
const iconWidthFactor = iconContainer.width > 0 ? bounds.width / iconContainer.width : 1;
|
|
11629
|
+
const iconHeightFactor = iconContainer.height > 0 ? bounds.height / iconContainer.height : 1;
|
|
11398
11630
|
return {
|
|
11399
|
-
width:
|
|
11400
|
-
height:
|
|
11631
|
+
width: Math.max((labelSize?.width ?? 0) * labelWidthFactor, (iconBoxSize?.width ?? 0) * iconWidthFactor),
|
|
11632
|
+
height: Math.max((labelSize?.height ?? 0) * labelHeightFactor, (iconBoxSize?.height ?? 0) * iconHeightFactor)
|
|
11401
11633
|
};
|
|
11402
11634
|
}
|
|
11635
|
+
/**
|
|
11636
|
+
* Content box for the node icon: AABB minus contentInset.
|
|
11637
|
+
* Unlike {@link getLabelContainerBounds}, this is not shrunk to the
|
|
11638
|
+
* inscribed rectangle of a circle/diamond, so a centered icon can fill the shape.
|
|
11639
|
+
*/
|
|
11640
|
+
getIconContainerBounds(bounds) {
|
|
11641
|
+
return this.applyContentInset(bounds);
|
|
11642
|
+
}
|
|
11403
11643
|
getLabelContainerBounds(bounds) {
|
|
11644
|
+
return this.applyContentInset(bounds);
|
|
11645
|
+
}
|
|
11646
|
+
applyContentInset(bounds) {
|
|
11404
11647
|
const { top, right, bottom, left } = resolveContentInset(this._contentInset, this._contentInsetScale, {
|
|
11405
11648
|
width: bounds.width,
|
|
11406
11649
|
height: bounds.height
|
|
@@ -11797,7 +12040,7 @@ var CircleNode = class extends Node {
|
|
|
11797
12040
|
const ry = this._height / 2 + padding;
|
|
11798
12041
|
const dx = point.x - center.x;
|
|
11799
12042
|
const dy = point.y - center.y;
|
|
11800
|
-
return dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1;
|
|
12043
|
+
return dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1 || this.hitTestExternalLabel(point);
|
|
11801
12044
|
}
|
|
11802
12045
|
getOutlineDirection(param) {
|
|
11803
12046
|
return this.outlineDirectionFromCenter(param);
|
|
@@ -11886,7 +12129,7 @@ var DiamondNode = class extends Node {
|
|
|
11886
12129
|
const hh = this._height / 2 + padding;
|
|
11887
12130
|
const dx = Math.abs(point.x - center.x);
|
|
11888
12131
|
const dy = Math.abs(point.y - center.y);
|
|
11889
|
-
return dx / hw + dy / hh <= 1;
|
|
12132
|
+
return dx / hw + dy / hh <= 1 || this.hitTestExternalLabel(point);
|
|
11890
12133
|
}
|
|
11891
12134
|
getOutlineDirection(param) {
|
|
11892
12135
|
return this.outlineDirectionFromCenter(param);
|
|
@@ -12287,7 +12530,7 @@ var CustomShapeNode = class extends Node {
|
|
|
12287
12530
|
const path = this.getPath();
|
|
12288
12531
|
if (ctx.isPointInPath(path, localX, localY)) return true;
|
|
12289
12532
|
ctx.lineWidth = 20;
|
|
12290
|
-
return ctx.isPointInStroke(path, localX, localY);
|
|
12533
|
+
return ctx.isPointInStroke(path, localX, localY) || this.hitTestExternalLabel(point);
|
|
12291
12534
|
}
|
|
12292
12535
|
render(ctx) {
|
|
12293
12536
|
const style = this.style;
|
|
@@ -13117,6 +13360,9 @@ function isCContainer(component) {
|
|
|
13117
13360
|
function isCShape(component) {
|
|
13118
13361
|
return component.type === "shape";
|
|
13119
13362
|
}
|
|
13363
|
+
function isCText(component) {
|
|
13364
|
+
return component.type === "text";
|
|
13365
|
+
}
|
|
13120
13366
|
/**
|
|
13121
13367
|
* Flex container component for CompositeNode.
|
|
13122
13368
|
* Lays out children using a flexbox-like algorithm.
|
|
@@ -13127,6 +13373,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13127
13373
|
this.type = "container";
|
|
13128
13374
|
this._cachedBounds = null;
|
|
13129
13375
|
this._cachedContainerBounds = null;
|
|
13376
|
+
this._layoutChildren = null;
|
|
13377
|
+
this._suppressBoundName = false;
|
|
13130
13378
|
this.id = options.id;
|
|
13131
13379
|
this._direction = options.direction ?? "column";
|
|
13132
13380
|
this._justifyContent = options.justifyContent ?? "start";
|
|
@@ -13155,6 +13403,27 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13155
13403
|
get children() {
|
|
13156
13404
|
return this._children;
|
|
13157
13405
|
}
|
|
13406
|
+
/**
|
|
13407
|
+
* When true, CText bound to `__name__` is omitted from measure/render/hit/SVG.
|
|
13408
|
+
* Used when CompositeNode draws the name as an external Node.label.
|
|
13409
|
+
*/
|
|
13410
|
+
setSuppressBoundName(value) {
|
|
13411
|
+
if (this._suppressBoundName === value) return;
|
|
13412
|
+
this._suppressBoundName = value;
|
|
13413
|
+
this._cachedBounds = null;
|
|
13414
|
+
this._layoutChildren = null;
|
|
13415
|
+
for (const child of this._children) {
|
|
13416
|
+
if (isCContainer(child)) child.setSuppressBoundName(value);
|
|
13417
|
+
if (isCShape(child) && child.content) child.content.setSuppressBoundName(value);
|
|
13418
|
+
}
|
|
13419
|
+
}
|
|
13420
|
+
isLayoutSkipped(child) {
|
|
13421
|
+
if (child.style.visible === false) return true;
|
|
13422
|
+
return this._suppressBoundName && isCText(child) && child.bindToProperty === "__name__";
|
|
13423
|
+
}
|
|
13424
|
+
getLayoutChildren() {
|
|
13425
|
+
return this._children.filter((child) => !this.isLayoutSkipped(child));
|
|
13426
|
+
}
|
|
13158
13427
|
addChild(child) {
|
|
13159
13428
|
child.setOnChange(() => this.handleChildChange());
|
|
13160
13429
|
this._children.push(child);
|
|
@@ -13186,8 +13455,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13186
13455
|
padding: this._padding
|
|
13187
13456
|
};
|
|
13188
13457
|
}
|
|
13189
|
-
buildFlexChildren(ctx) {
|
|
13190
|
-
return
|
|
13458
|
+
buildFlexChildren(ctx, children = this.getLayoutChildren()) {
|
|
13459
|
+
return children.map((child) => {
|
|
13191
13460
|
const s = child.style;
|
|
13192
13461
|
return {
|
|
13193
13462
|
measure: child.measure(ctx),
|
|
@@ -13220,11 +13489,13 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13220
13489
|
}
|
|
13221
13490
|
render(ctx, bounds) {
|
|
13222
13491
|
if (this.style.visible === false) return;
|
|
13223
|
-
const
|
|
13492
|
+
const layoutChildren = this.getLayoutChildren();
|
|
13493
|
+
const flexChildren = this.buildFlexChildren(ctx, layoutChildren);
|
|
13224
13494
|
const result = flexLayout({
|
|
13225
13495
|
width: bounds.width,
|
|
13226
13496
|
height: bounds.height
|
|
13227
13497
|
}, this.getFlexConfig(), flexChildren);
|
|
13498
|
+
this._layoutChildren = layoutChildren;
|
|
13228
13499
|
this._cachedBounds = result.childBounds;
|
|
13229
13500
|
this._cachedContainerBounds = bounds;
|
|
13230
13501
|
const opacity = this.style.opacity ?? 1;
|
|
@@ -13232,9 +13503,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13232
13503
|
ctx.save();
|
|
13233
13504
|
ctx.globalAlpha *= opacity;
|
|
13234
13505
|
}
|
|
13235
|
-
for (let i = 0; i <
|
|
13236
|
-
const child =
|
|
13237
|
-
if (child.style.visible === false) continue;
|
|
13506
|
+
for (let i = 0; i < layoutChildren.length; i++) {
|
|
13507
|
+
const child = layoutChildren[i];
|
|
13238
13508
|
const cb = result.childBounds[i];
|
|
13239
13509
|
child.render(ctx, {
|
|
13240
13510
|
x: bounds.x + cb.x,
|
|
@@ -13247,10 +13517,9 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13247
13517
|
}
|
|
13248
13518
|
hitTest(point, bounds) {
|
|
13249
13519
|
if (this.style.visible === false) return null;
|
|
13250
|
-
if (!this._cachedBounds) return null;
|
|
13251
|
-
for (let i = this.
|
|
13252
|
-
const child = this.
|
|
13253
|
-
if (child.style.visible === false) continue;
|
|
13520
|
+
if (!this._cachedBounds || !this._layoutChildren) return null;
|
|
13521
|
+
for (let i = this._layoutChildren.length - 1; i >= 0; i--) {
|
|
13522
|
+
const child = this._layoutChildren[i];
|
|
13254
13523
|
const cb = this._cachedBounds[i];
|
|
13255
13524
|
const absBounds = {
|
|
13256
13525
|
x: bounds.x + cb.x,
|
|
@@ -13264,6 +13533,22 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13264
13533
|
return null;
|
|
13265
13534
|
}
|
|
13266
13535
|
/**
|
|
13536
|
+
* First CText bound to `__name__`, including suppressed name text.
|
|
13537
|
+
*/
|
|
13538
|
+
findBoundNameText() {
|
|
13539
|
+
for (const child of this._children) {
|
|
13540
|
+
if (isCText(child) && child.bindToProperty === "__name__") return child;
|
|
13541
|
+
if (isCContainer(child)) {
|
|
13542
|
+
const found = child.findBoundNameText();
|
|
13543
|
+
if (found) return found;
|
|
13544
|
+
}
|
|
13545
|
+
if (isCShape(child) && child.content) {
|
|
13546
|
+
const found = child.content.findBoundNameText();
|
|
13547
|
+
if (found) return found;
|
|
13548
|
+
}
|
|
13549
|
+
}
|
|
13550
|
+
}
|
|
13551
|
+
/**
|
|
13267
13552
|
* Find a component by id recursively.
|
|
13268
13553
|
*/
|
|
13269
13554
|
findById(id) {
|
|
@@ -13300,8 +13585,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13300
13585
|
if (this.style.visible === false) return "";
|
|
13301
13586
|
const cachedBounds = this._cachedBounds;
|
|
13302
13587
|
const cachedContainer = this._cachedContainerBounds;
|
|
13303
|
-
|
|
13304
|
-
|
|
13588
|
+
const layoutChildren = this._layoutChildren ?? this.getLayoutChildren();
|
|
13589
|
+
return `<g>${layoutChildren.map((child, i) => {
|
|
13305
13590
|
if (cachedBounds && cachedContainer) {
|
|
13306
13591
|
const cb = cachedBounds[i];
|
|
13307
13592
|
const dx = bounds.x - cachedContainer.x;
|
|
@@ -13313,7 +13598,7 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13313
13598
|
height: cb.height
|
|
13314
13599
|
});
|
|
13315
13600
|
}
|
|
13316
|
-
const h = bounds.height / Math.max(1,
|
|
13601
|
+
const h = bounds.height / Math.max(1, layoutChildren.length);
|
|
13317
13602
|
return child.toSVG({
|
|
13318
13603
|
x: bounds.x,
|
|
13319
13604
|
y: bounds.y + i * h,
|
|
@@ -13620,8 +13905,8 @@ var CompositeNode = class extends Node {
|
|
|
13620
13905
|
return this.getLabelContainerBounds(this.getBounds());
|
|
13621
13906
|
}
|
|
13622
13907
|
hitTest(point) {
|
|
13623
|
-
if (this._shapeType === "circle") return this.hitTestEllipse(point);
|
|
13624
|
-
if (this._shapeType === "diamond") return this.hitTestDiamond(point);
|
|
13908
|
+
if (this._shapeType === "circle") return this.hitTestEllipse(point) || this.hitTestExternalLabel(point);
|
|
13909
|
+
if (this._shapeType === "diamond") return this.hitTestDiamond(point) || this.hitTestExternalLabel(point);
|
|
13625
13910
|
return super.hitTest(point);
|
|
13626
13911
|
}
|
|
13627
13912
|
hitTestEllipse(point) {
|
|
@@ -13642,7 +13927,27 @@ var CompositeNode = class extends Node {
|
|
|
13642
13927
|
const dy = Math.abs(point.y - center.y);
|
|
13643
13928
|
return dx / hw + dy / hh <= 1;
|
|
13644
13929
|
}
|
|
13930
|
+
/**
|
|
13931
|
+
* Copy color/font from the suppressed `__name__` CText onto the external Node.label.
|
|
13932
|
+
*/
|
|
13933
|
+
syncExternalBoundNameLabelStyle() {
|
|
13934
|
+
if (!this.usesExternalLabel() || !this._label) return;
|
|
13935
|
+
const nameText = this._content.findBoundNameText();
|
|
13936
|
+
if (!nameText) return;
|
|
13937
|
+
const current = this._label.styleOverrides;
|
|
13938
|
+
if (current.color === nameText.color && current.fontFamily === nameText.fontFamily && current.fontWeight === nameText.fontWeight && current.fontStyle === nameText.fontStyle && current.fontSize === nameText.fontSize) return;
|
|
13939
|
+
this._label.style = {
|
|
13940
|
+
...current,
|
|
13941
|
+
color: nameText.color,
|
|
13942
|
+
fontFamily: nameText.fontFamily,
|
|
13943
|
+
fontWeight: nameText.fontWeight,
|
|
13944
|
+
fontStyle: nameText.fontStyle,
|
|
13945
|
+
fontSize: nameText.fontSize
|
|
13946
|
+
};
|
|
13947
|
+
}
|
|
13645
13948
|
render(ctx) {
|
|
13949
|
+
this._content.setSuppressBoundName(this.usesExternalLabel());
|
|
13950
|
+
this.syncExternalBoundNameLabelStyle();
|
|
13646
13951
|
if (this._autoSize) this.applyAutoSize(ctx);
|
|
13647
13952
|
const { x, y, width, height } = this.getBounds();
|
|
13648
13953
|
const style = this.style;
|
|
@@ -13715,6 +14020,11 @@ var CompositeNode = class extends Node {
|
|
|
13715
14020
|
this.renderBadges(ctx, bounds);
|
|
13716
14021
|
const contentBounds = this.getContentBounds();
|
|
13717
14022
|
this._content.render(ctx, contentBounds);
|
|
14023
|
+
if (this.usesExternalLabel() && this._label) {
|
|
14024
|
+
this._label.setAutoMaxWidth(this.getLabelWrapWidth(bounds));
|
|
14025
|
+
const labelSize = this._label.measure(ctx);
|
|
14026
|
+
this.renderLabel(ctx, this.getResolvedLabelBounds(labelSize));
|
|
14027
|
+
}
|
|
13718
14028
|
this.renderPorts(ctx);
|
|
13719
14029
|
}
|
|
13720
14030
|
applyAutoSize(ctx) {
|
|
@@ -14348,6 +14658,8 @@ var Serializer = class {
|
|
|
14348
14658
|
styleClass: node.styleClass,
|
|
14349
14659
|
label,
|
|
14350
14660
|
labelStyleClass: typeof label === "string" ? node.label?.styleClass : void 0,
|
|
14661
|
+
labelPlacement: node.labelPlacement !== "center" ? node.labelPlacement : void 0,
|
|
14662
|
+
labelGap: node.labelGap !== 4 ? node.labelGap : void 0,
|
|
14351
14663
|
icon,
|
|
14352
14664
|
contentInset: hasContentInset ? contentInset : void 0,
|
|
14353
14665
|
anchorPoints,
|
|
@@ -14654,9 +14966,16 @@ var SvgExporter = class {
|
|
|
14654
14966
|
break;
|
|
14655
14967
|
}
|
|
14656
14968
|
}
|
|
14969
|
+
cn.content.setSuppressBoundName(cn.usesExternalLabel());
|
|
14970
|
+
cn.syncExternalBoundNameLabelStyle();
|
|
14657
14971
|
const contentBounds = cn.getLabelContainerBounds(bounds);
|
|
14658
14972
|
const contentSvg = cn.content.toSVG(contentBounds);
|
|
14659
|
-
|
|
14973
|
+
const labelSvg = cn.usesExternalLabel() ? this.renderNodeLabel(cn, bounds) : "";
|
|
14974
|
+
return [
|
|
14975
|
+
`${shape} fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="${strokeWidth}" stroke-opacity="${strokeOpacity}"${dash}${dashOffset}/>`,
|
|
14976
|
+
contentSvg,
|
|
14977
|
+
labelSvg
|
|
14978
|
+
].join("");
|
|
14660
14979
|
}
|
|
14661
14980
|
default: shape = `<rect x="${bounds.x}" y="${bounds.y}" width="${bounds.width}" height="${bounds.height}"`;
|
|
14662
14981
|
}
|
|
@@ -14729,15 +15048,13 @@ var SvgExporter = class {
|
|
|
14729
15048
|
}
|
|
14730
15049
|
measureTextLabel(text, style = {}, inset = 8) {
|
|
14731
15050
|
const fontSize = style.fontSize ?? 14;
|
|
14732
|
-
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
14733
|
-
const fontWeight = style.fontWeight ?? "normal";
|
|
14734
15051
|
const lineHeight = fontSize * 1.2;
|
|
14735
15052
|
const lines = text.split("\n");
|
|
14736
15053
|
let maxWidth = 0;
|
|
14737
15054
|
if (typeof document !== "undefined") {
|
|
14738
15055
|
const ctx = document.createElement("canvas").getContext("2d");
|
|
14739
15056
|
if (ctx) {
|
|
14740
|
-
ctx.font =
|
|
15057
|
+
ctx.font = canvasFontFromTextStyle(style);
|
|
14741
15058
|
for (const line of lines) maxWidth = Math.max(maxWidth, ctx.measureText(line).width);
|
|
14742
15059
|
}
|
|
14743
15060
|
}
|
|
@@ -14848,16 +15165,18 @@ var SvgExporter = class {
|
|
|
14848
15165
|
const fontSize = style.fontSize ?? 14;
|
|
14849
15166
|
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
14850
15167
|
const fontWeight = style.fontWeight ?? "normal";
|
|
15168
|
+
const fontStyle = style.fontStyle ?? "normal";
|
|
15169
|
+
const fontStyleAttr = fontStyle !== "normal" ? ` font-style="${fontStyle}"` : "";
|
|
14851
15170
|
const opacity = style.opacity ?? 1;
|
|
14852
15171
|
const anchor = style.align === "left" ? "start" : style.align === "right" ? "end" : "middle";
|
|
14853
15172
|
const baseline = style.baseline === "top" ? "text-before-edge" : style.baseline === "bottom" ? "text-after-edge" : "middle";
|
|
14854
15173
|
const transform = rotation !== 0 ? ` transform="rotate(${rotation * 180 / Math.PI} ${point.x} ${point.y})"` : "";
|
|
14855
15174
|
const lines = text.split("\n");
|
|
14856
|
-
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>`;
|
|
15175
|
+
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>`;
|
|
14857
15176
|
const lineHeight = fontSize * 1.2;
|
|
14858
15177
|
const startY = point.y - (lines.length - 1) * lineHeight / 2;
|
|
14859
15178
|
const tspans = lines.map((line, index) => `<tspan x="${point.x}" y="${startY + index * lineHeight}">${this.escapeText(line)}</tspan>`).join("");
|
|
14860
|
-
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>`;
|
|
15179
|
+
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>`;
|
|
14861
15180
|
}
|
|
14862
15181
|
buildEdgePath(edge, edgeLabelOffset) {
|
|
14863
15182
|
if (!edge.label || !edge.labelLineGap) return this.buildPath(edge);
|
|
@@ -15572,6 +15891,6 @@ function applyEdgeStyle(ctx, style) {
|
|
|
15572
15891
|
return baseOpacity * strokeOpacity;
|
|
15573
15892
|
}
|
|
15574
15893
|
//#endregion
|
|
15575
|
-
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 };
|
|
15894
|
+
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 };
|
|
15576
15895
|
|
|
15577
15896
|
//# sourceMappingURL=papirus.js.map
|