@ngroznykh/papirus 0.9.13 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/README.ru.md +2 -1
- package/dist/core/AnimationManager.d.ts +5 -0
- package/dist/core/AnimationManager.d.ts.map +1 -1
- package/dist/core/ConnectionManager.d.ts +5 -0
- package/dist/core/ConnectionManager.d.ts.map +1 -1
- package/dist/core/DiagramRenderer.d.ts +6 -0
- package/dist/core/DiagramRenderer.d.ts.map +1 -1
- package/dist/core/DragManager.d.ts +2 -1
- package/dist/core/DragManager.d.ts.map +1 -1
- package/dist/core/InteractionManager.d.ts.map +1 -1
- package/dist/core/NavigationManager.d.ts +4 -0
- package/dist/core/NavigationManager.d.ts.map +1 -1
- package/dist/core/ResizeManager.d.ts +5 -0
- package/dist/core/ResizeManager.d.ts.map +1 -1
- package/dist/core/SearchManager.d.ts.map +1 -1
- package/dist/core/SelectionManager.d.ts +1 -0
- package/dist/core/SelectionManager.d.ts.map +1 -1
- package/dist/elements/Group.d.ts.map +1 -1
- package/dist/elements/Node.d.ts +4 -2
- package/dist/elements/Node.d.ts.map +1 -1
- package/dist/elements/composite/CComponent.d.ts +4 -2
- package/dist/elements/composite/CComponent.d.ts.map +1 -1
- package/dist/elements/composite/CContainer.d.ts +1 -1
- package/dist/elements/composite/CContainer.d.ts.map +1 -1
- package/dist/elements/composite/CShape.d.ts +1 -1
- package/dist/elements/composite/CShape.d.ts.map +1 -1
- package/dist/elements/composite/CText.d.ts +1 -1
- package/dist/elements/composite/CText.d.ts.map +1 -1
- package/dist/elements/composite/CompositeComponentBase.d.ts +1 -1
- package/dist/elements/composite/CompositeComponentBase.d.ts.map +1 -1
- package/dist/elements/composite/CompositeNode.d.ts +5 -1
- package/dist/elements/composite/CompositeNode.d.ts.map +1 -1
- package/dist/elements/composite/FlexLayout.d.ts.map +1 -1
- package/dist/elements/nodes/CircleNode.d.ts.map +1 -1
- package/dist/elements/nodes/DiamondNode.d.ts.map +1 -1
- package/dist/elements/paths/BezierPathStrategy.d.ts.map +1 -1
- package/dist/events/EventEmitter.d.ts +2 -3
- package/dist/events/EventEmitter.d.ts.map +1 -1
- package/dist/events/InputHandler.d.ts +11 -2
- package/dist/events/InputHandler.d.ts.map +1 -1
- package/dist/papirus.js +408 -167
- package/dist/papirus.js.map +1 -1
- package/dist/styles/StyleManager.d.ts.map +1 -1
- package/dist/types.d.ts +17 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/ImageExporter.d.ts.map +1 -1
- package/dist/utils/Serializer.d.ts +1 -0
- package/dist/utils/Serializer.d.ts.map +1 -1
- package/dist/utils/SvgExporter.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/papirus.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
//#region src/events/EventEmitter.ts
|
|
2
|
-
/**
|
|
3
|
-
* Type-safe event emitter with support for typed event maps
|
|
4
|
-
*/
|
|
5
2
|
var EventEmitter = class {
|
|
6
3
|
constructor() {
|
|
7
4
|
this.listeners = /* @__PURE__ */ new Map();
|
|
5
|
+
this.onceWrappers = /* @__PURE__ */ new Map();
|
|
8
6
|
}
|
|
9
7
|
/**
|
|
10
8
|
* Subscribe to an event
|
|
@@ -25,8 +23,20 @@ var EventEmitter = class {
|
|
|
25
23
|
* @param listener - Callback function to remove
|
|
26
24
|
*/
|
|
27
25
|
off(event, listener) {
|
|
26
|
+
const eventListener = listener;
|
|
28
27
|
const listeners = this.listeners.get(event);
|
|
29
|
-
|
|
28
|
+
listeners?.delete(eventListener);
|
|
29
|
+
const wrappersByListener = this.onceWrappers.get(event);
|
|
30
|
+
const wrappers = wrappersByListener?.get(eventListener);
|
|
31
|
+
if (wrappers !== void 0) {
|
|
32
|
+
for (const wrapper of wrappers) listeners?.delete(wrapper);
|
|
33
|
+
wrappersByListener?.delete(eventListener);
|
|
34
|
+
} else if (wrappersByListener !== void 0) for (const [original, originalWrappers] of wrappersByListener) {
|
|
35
|
+
originalWrappers.delete(eventListener);
|
|
36
|
+
if (originalWrappers.size === 0) wrappersByListener.delete(original);
|
|
37
|
+
}
|
|
38
|
+
if (listeners?.size === 0) this.listeners.delete(event);
|
|
39
|
+
if (wrappersByListener?.size === 0) this.onceWrappers.delete(event);
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
32
42
|
* Subscribe to an event for a single emission
|
|
@@ -39,6 +49,19 @@ var EventEmitter = class {
|
|
|
39
49
|
this.off(event, wrapper);
|
|
40
50
|
listener(...args);
|
|
41
51
|
};
|
|
52
|
+
const originalListener = listener;
|
|
53
|
+
const eventWrapper = wrapper;
|
|
54
|
+
let wrappersByListener = this.onceWrappers.get(event);
|
|
55
|
+
if (wrappersByListener === void 0) {
|
|
56
|
+
wrappersByListener = /* @__PURE__ */ new Map();
|
|
57
|
+
this.onceWrappers.set(event, wrappersByListener);
|
|
58
|
+
}
|
|
59
|
+
let wrappers = wrappersByListener.get(originalListener);
|
|
60
|
+
if (wrappers === void 0) {
|
|
61
|
+
wrappers = /* @__PURE__ */ new Set();
|
|
62
|
+
wrappersByListener.set(originalListener, wrappers);
|
|
63
|
+
}
|
|
64
|
+
wrappers.add(eventWrapper);
|
|
42
65
|
return this.on(event, wrapper);
|
|
43
66
|
}
|
|
44
67
|
/**
|
|
@@ -48,15 +71,20 @@ var EventEmitter = class {
|
|
|
48
71
|
*/
|
|
49
72
|
emit(event, ...args) {
|
|
50
73
|
const listeners = this.listeners.get(event);
|
|
51
|
-
if (listeners !== void 0) for (const listener of listeners) listener(...args);
|
|
74
|
+
if (listeners !== void 0) for (const listener of Array.from(listeners)) listener(...args);
|
|
52
75
|
}
|
|
53
76
|
/**
|
|
54
77
|
* Remove all listeners for an event or all events
|
|
55
78
|
* @param event - Optional event name. If not provided, removes all listeners
|
|
56
79
|
*/
|
|
57
80
|
removeAllListeners(event) {
|
|
58
|
-
if (event !== void 0)
|
|
59
|
-
|
|
81
|
+
if (event !== void 0) {
|
|
82
|
+
this.listeners.delete(event);
|
|
83
|
+
this.onceWrappers.delete(event);
|
|
84
|
+
} else {
|
|
85
|
+
this.listeners.clear();
|
|
86
|
+
this.onceWrappers.clear();
|
|
87
|
+
}
|
|
60
88
|
}
|
|
61
89
|
/**
|
|
62
90
|
* Get the number of listeners for an event
|
|
@@ -79,6 +107,9 @@ var InputHandler = class extends EventEmitter {
|
|
|
79
107
|
this.boundHandlers = /* @__PURE__ */ new Map();
|
|
80
108
|
this.lastTouchDistance = null;
|
|
81
109
|
this.lastTouchCenter = null;
|
|
110
|
+
this.windowPointerActive = false;
|
|
111
|
+
this.boundWindowMouseMove = (e) => this.handleWindowMouseMove(e);
|
|
112
|
+
this.boundWindowMouseUp = (e) => this.handleWindowMouseUp(e);
|
|
82
113
|
this.canvas = options.canvas;
|
|
83
114
|
this.screenToWorld = options.screenToWorld;
|
|
84
115
|
this.setupEventListeners();
|
|
@@ -93,6 +124,7 @@ var InputHandler = class extends EventEmitter {
|
|
|
93
124
|
* Clean up event listeners
|
|
94
125
|
*/
|
|
95
126
|
destroy() {
|
|
127
|
+
this.teardownWindowPointerListeners();
|
|
96
128
|
for (const [eventName, handler] of this.boundHandlers) if (eventName.startsWith("key")) window.removeEventListener(eventName, handler);
|
|
97
129
|
else this.canvas.removeEventListener(eventName, handler);
|
|
98
130
|
this.boundHandlers.clear();
|
|
@@ -102,8 +134,8 @@ var InputHandler = class extends EventEmitter {
|
|
|
102
134
|
this.addCanvasListener("click", this.handleClick.bind(this));
|
|
103
135
|
this.addCanvasListener("dblclick", this.handleDblClick.bind(this));
|
|
104
136
|
this.addCanvasListener("mousedown", this.handleMouseDown.bind(this));
|
|
105
|
-
this.addCanvasListener("mouseup", this.
|
|
106
|
-
this.addCanvasListener("mousemove", this.
|
|
137
|
+
this.addCanvasListener("mouseup", this.handleCanvasMouseUp.bind(this));
|
|
138
|
+
this.addCanvasListener("mousemove", this.handleCanvasMouseMove.bind(this));
|
|
107
139
|
this.addCanvasListener("wheel", this.handleWheel.bind(this), { passive: false });
|
|
108
140
|
this.addCanvasListener("touchstart", this.handleTouchStart.bind(this));
|
|
109
141
|
this.addCanvasListener("touchend", this.handleTouchEnd.bind(this));
|
|
@@ -120,6 +152,18 @@ var InputHandler = class extends EventEmitter {
|
|
|
120
152
|
window.addEventListener(eventName, handler);
|
|
121
153
|
this.boundHandlers.set(eventName, handler);
|
|
122
154
|
}
|
|
155
|
+
ensureWindowPointerListeners() {
|
|
156
|
+
if (this.windowPointerActive) return;
|
|
157
|
+
this.windowPointerActive = true;
|
|
158
|
+
window.addEventListener("mousemove", this.boundWindowMouseMove);
|
|
159
|
+
window.addEventListener("mouseup", this.boundWindowMouseUp);
|
|
160
|
+
}
|
|
161
|
+
teardownWindowPointerListeners() {
|
|
162
|
+
if (!this.windowPointerActive) return;
|
|
163
|
+
this.windowPointerActive = false;
|
|
164
|
+
window.removeEventListener("mousemove", this.boundWindowMouseMove);
|
|
165
|
+
window.removeEventListener("mouseup", this.boundWindowMouseUp);
|
|
166
|
+
}
|
|
123
167
|
handleClick(e) {
|
|
124
168
|
const event = this.normalizeMouseEvent(e);
|
|
125
169
|
this.emit("click", event);
|
|
@@ -130,16 +174,30 @@ var InputHandler = class extends EventEmitter {
|
|
|
130
174
|
}
|
|
131
175
|
handleMouseDown(e) {
|
|
132
176
|
const event = this.normalizeMouseEvent(e);
|
|
177
|
+
this.ensureWindowPointerListeners();
|
|
133
178
|
this.emit("mousedown", event);
|
|
134
179
|
}
|
|
135
|
-
|
|
180
|
+
handleCanvasMouseMove(e) {
|
|
136
181
|
const event = this.normalizeMouseEvent(e);
|
|
137
|
-
this.emit("
|
|
182
|
+
this.emit("mousemove", event);
|
|
138
183
|
}
|
|
139
|
-
|
|
184
|
+
handleWindowMouseMove(e) {
|
|
185
|
+
if (e.target === this.canvas || this.canvas.contains(e.target)) return;
|
|
140
186
|
const event = this.normalizeMouseEvent(e);
|
|
141
187
|
this.emit("mousemove", event);
|
|
142
188
|
}
|
|
189
|
+
handleCanvasMouseUp(e) {
|
|
190
|
+
this.finishPointerUp(e);
|
|
191
|
+
}
|
|
192
|
+
handleWindowMouseUp(e) {
|
|
193
|
+
if (e.target === this.canvas || this.canvas.contains(e.target)) return;
|
|
194
|
+
this.finishPointerUp(e);
|
|
195
|
+
}
|
|
196
|
+
finishPointerUp(mouseEvent) {
|
|
197
|
+
const event = this.normalizeMouseEvent(mouseEvent);
|
|
198
|
+
this.teardownWindowPointerListeners();
|
|
199
|
+
this.emit("mouseup", event);
|
|
200
|
+
}
|
|
143
201
|
handleWheel(e) {
|
|
144
202
|
e.preventDefault();
|
|
145
203
|
const wheelEvent = e;
|
|
@@ -748,6 +806,7 @@ var SelectionManager = class extends EventEmitter {
|
|
|
748
806
|
const rect = this.selectionRect;
|
|
749
807
|
const selected = [];
|
|
750
808
|
for (const node of this.renderer.nodes.values()) if (rectsIntersect(rect, node.getBounds())) selected.push(node.id);
|
|
809
|
+
for (const edge of this.renderer.edges.values()) if (rectsIntersect(rect, edge.getBounds())) selected.push(edge.id);
|
|
751
810
|
for (const group of this.renderer.groups.values()) if (rectsIntersect(rect, group.getBounds())) selected.push(group.id);
|
|
752
811
|
if (selected.length > 0) this.selectMultiple(selected);
|
|
753
812
|
this.emit("selectionEnd", rect);
|
|
@@ -779,6 +838,7 @@ var SelectionManager = class extends EventEmitter {
|
|
|
779
838
|
ctx.setLineDash([]);
|
|
780
839
|
}
|
|
781
840
|
updateElementStates() {
|
|
841
|
+
this.pruneStaleSelection();
|
|
782
842
|
const updateState = (elements) => {
|
|
783
843
|
for (const element of elements) if (this._selectedIds.has(element.id)) element.state = "selected";
|
|
784
844
|
else if (element.state === "selected") element.state = "normal";
|
|
@@ -789,6 +849,9 @@ var SelectionManager = class extends EventEmitter {
|
|
|
789
849
|
this.renderer.markDirty();
|
|
790
850
|
this.renderer.markStyleDirty();
|
|
791
851
|
}
|
|
852
|
+
pruneStaleSelection() {
|
|
853
|
+
for (const id of Array.from(this._selectedIds)) if (this.renderer.getNode(id) === void 0 && this.renderer.getEdge(id) === void 0 && this.renderer.getGroup(id) === void 0) this._selectedIds.delete(id);
|
|
854
|
+
}
|
|
792
855
|
emitSelect() {
|
|
793
856
|
this.emit("select", Array.from(this._selectedIds));
|
|
794
857
|
this.renderer.emit("select", Array.from(this._selectedIds));
|
|
@@ -810,9 +873,9 @@ var DragManager = class extends EventEmitter {
|
|
|
810
873
|
this.editableBendFollow = /* @__PURE__ */ new Map();
|
|
811
874
|
this.editablePolylineFullyConnectedEdges = /* @__PURE__ */ new Set();
|
|
812
875
|
this.initialControlPointsForFullyConnected = /* @__PURE__ */ new Map();
|
|
876
|
+
this.initialControlPointsForBendFollow = /* @__PURE__ */ new Map();
|
|
813
877
|
this.alignmentGuides = [];
|
|
814
878
|
this._handledMouseDown = false;
|
|
815
|
-
this.draggedGroupSelection = false;
|
|
816
879
|
this.renderer = options.renderer;
|
|
817
880
|
this.selectionManager = options.selectionManager;
|
|
818
881
|
this.snapToGrid = options.snapToGrid ?? false;
|
|
@@ -890,7 +953,6 @@ var DragManager = class extends EventEmitter {
|
|
|
890
953
|
this._handledMouseDown = true;
|
|
891
954
|
this.dragStartPoint = point;
|
|
892
955
|
this.lastDragPoint = point;
|
|
893
|
-
this.draggedGroupSelection = false;
|
|
894
956
|
this.draggedNodes = [];
|
|
895
957
|
this.initialPositions.clear();
|
|
896
958
|
for (const id of this.selectionManager.selectedIds) {
|
|
@@ -907,7 +969,6 @@ var DragManager = class extends EventEmitter {
|
|
|
907
969
|
}
|
|
908
970
|
this.dragStartPoint = point;
|
|
909
971
|
this.lastDragPoint = point;
|
|
910
|
-
this.draggedGroupSelection = false;
|
|
911
972
|
this.draggedNodes = [];
|
|
912
973
|
for (const id of this.selectionManager.selectedIds) {
|
|
913
974
|
const selectedNode = this.renderer.getNode(id);
|
|
@@ -916,12 +977,9 @@ var DragManager = class extends EventEmitter {
|
|
|
916
977
|
continue;
|
|
917
978
|
}
|
|
918
979
|
const selectedGroup = this.renderer.getGroup(id);
|
|
919
|
-
if (selectedGroup !== void 0) {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const childNode = this.renderer.getNode(child.id);
|
|
923
|
-
if (childNode) this.addDraggedNode(childNode);
|
|
924
|
-
}
|
|
980
|
+
if (selectedGroup !== void 0) for (const child of selectedGroup.getAllChildren()) {
|
|
981
|
+
const childNode = this.renderer.getNode(child.id);
|
|
982
|
+
if (childNode) this.addDraggedNode(childNode);
|
|
925
983
|
}
|
|
926
984
|
}
|
|
927
985
|
return true;
|
|
@@ -977,6 +1035,7 @@ var DragManager = class extends EventEmitter {
|
|
|
977
1035
|
}
|
|
978
1036
|
if (firstDelta !== null) this.followEditableBendForFullyConnectedEdges(firstDelta.x, firstDelta.y);
|
|
979
1037
|
this.emit("drag", this.draggedNodes.map((n) => n.id), point, delta);
|
|
1038
|
+
this.lastDragPoint = point;
|
|
980
1039
|
this.renderer.markDirty();
|
|
981
1040
|
return true;
|
|
982
1041
|
}
|
|
@@ -993,7 +1052,6 @@ var DragManager = class extends EventEmitter {
|
|
|
993
1052
|
if (this.isDragging) {
|
|
994
1053
|
for (const node of this.draggedNodes) node.state = "selected";
|
|
995
1054
|
this.emit("dragend", this.draggedNodes.map((n) => n.id), point);
|
|
996
|
-
if (this.draggedGroupSelection) this.selectionManager.clearSelection();
|
|
997
1055
|
}
|
|
998
1056
|
this.reset();
|
|
999
1057
|
this.renderer.markDirty();
|
|
@@ -1016,6 +1074,10 @@ var DragManager = class extends EventEmitter {
|
|
|
1016
1074
|
const edge = this.renderer.getEdge(edgeId);
|
|
1017
1075
|
if (edge) edge.controlPoints = clonePoints(points);
|
|
1018
1076
|
}
|
|
1077
|
+
for (const [edgeId, points] of this.initialControlPointsForBendFollow) {
|
|
1078
|
+
const edge = this.renderer.getEdge(edgeId);
|
|
1079
|
+
if (edge) edge.controlPoints = clonePoints(points);
|
|
1080
|
+
}
|
|
1019
1081
|
}
|
|
1020
1082
|
this.reset();
|
|
1021
1083
|
this.renderer.markDirty();
|
|
@@ -1037,13 +1099,14 @@ var DragManager = class extends EventEmitter {
|
|
|
1037
1099
|
this.editableBendFollow.clear();
|
|
1038
1100
|
this.editablePolylineFullyConnectedEdges.clear();
|
|
1039
1101
|
this.initialControlPointsForFullyConnected.clear();
|
|
1102
|
+
this.initialControlPointsForBendFollow.clear();
|
|
1040
1103
|
this.alignmentGuides = [];
|
|
1041
|
-
this.draggedGroupSelection = false;
|
|
1042
1104
|
}
|
|
1043
1105
|
buildEditableBendFollowBindings() {
|
|
1044
1106
|
this.editableBendFollow.clear();
|
|
1045
1107
|
this.editablePolylineFullyConnectedEdges.clear();
|
|
1046
1108
|
this.initialControlPointsForFullyConnected.clear();
|
|
1109
|
+
this.initialControlPointsForBendFollow.clear();
|
|
1047
1110
|
const draggedIds = new Set(this.draggedNodes.map((node) => node.id));
|
|
1048
1111
|
const nodeBindings = /* @__PURE__ */ new Map();
|
|
1049
1112
|
for (const edge of this.renderer.edges.values()) {
|
|
@@ -1080,6 +1143,7 @@ var DragManager = class extends EventEmitter {
|
|
|
1080
1143
|
axis
|
|
1081
1144
|
});
|
|
1082
1145
|
nodeBindings.set(node.id, bindings);
|
|
1146
|
+
if (!this.initialControlPointsForBendFollow.has(edge.id)) this.initialControlPointsForBendFollow.set(edge.id, clonePoints(edge.controlPoints));
|
|
1083
1147
|
}
|
|
1084
1148
|
}
|
|
1085
1149
|
for (const [nodeId, bindings] of nodeBindings) if (bindings.length > 0) this.editableBendFollow.set(nodeId, bindings);
|
|
@@ -1317,10 +1381,17 @@ var ResizeManager = class extends EventEmitter {
|
|
|
1317
1381
|
this.handle = hit;
|
|
1318
1382
|
this.startPoint = point;
|
|
1319
1383
|
this.startBounds = node.getBounds();
|
|
1320
|
-
|
|
1384
|
+
this.updateMinSize(node, this.startBounds.width);
|
|
1385
|
+
return true;
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Recompute content minimums at the given width: as the node narrows, text
|
|
1389
|
+
* wraps and the height floor grows to fit the wrapped lines.
|
|
1390
|
+
*/
|
|
1391
|
+
updateMinSize(node, width) {
|
|
1392
|
+
const contentMinSize = node.getContentMinSize(this.renderer.getContext(), width);
|
|
1321
1393
|
this.minWidth = Math.max(this.minSize, contentMinSize.width);
|
|
1322
1394
|
this.minHeight = Math.max(this.minSize, contentMinSize.height);
|
|
1323
|
-
return true;
|
|
1324
1395
|
}
|
|
1325
1396
|
/**
|
|
1326
1397
|
* Set canvas cursor when the pointer is over a selected node's resize handle.
|
|
@@ -1350,14 +1421,19 @@ var ResizeManager = class extends EventEmitter {
|
|
|
1350
1421
|
node.state = "dragging";
|
|
1351
1422
|
this.emit("resizeStart", node.id, this.handle, this.startBounds);
|
|
1352
1423
|
}
|
|
1424
|
+
const dx = point.x - this.startPoint.x;
|
|
1425
|
+
const prospectiveWidth = this.handle.includes("w") ? this.startBounds.width - dx : this.startBounds.width + dx;
|
|
1426
|
+
this.updateMinSize(node, prospectiveWidth);
|
|
1353
1427
|
const bounds = this.calculateBounds(point);
|
|
1354
|
-
node
|
|
1355
|
-
|
|
1356
|
-
node.
|
|
1357
|
-
node.
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1428
|
+
this.updateMinSize(node, bounds.width);
|
|
1429
|
+
const clamped = this.applyMinSize(bounds.x, bounds.y, bounds.width, bounds.height, this.startBounds, this.handle);
|
|
1430
|
+
node.x = clamped.x;
|
|
1431
|
+
node.y = clamped.y;
|
|
1432
|
+
node.width = clamped.width;
|
|
1433
|
+
node.height = clamped.height;
|
|
1434
|
+
this.emit("resize", node.id, clamped, {
|
|
1435
|
+
x: clamped.x - this.startBounds.x,
|
|
1436
|
+
y: clamped.y - this.startBounds.y
|
|
1361
1437
|
});
|
|
1362
1438
|
this.renderer.markDirty();
|
|
1363
1439
|
return true;
|
|
@@ -1512,9 +1588,23 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1512
1588
|
this.isPanning = false;
|
|
1513
1589
|
this.panStart = null;
|
|
1514
1590
|
this.spacePressed = false;
|
|
1591
|
+
this.boundBlur = () => this.handleWindowBlur();
|
|
1592
|
+
this.boundVisibilityChange = () => {
|
|
1593
|
+
if (document.visibilityState === "hidden") this.handleWindowBlur();
|
|
1594
|
+
};
|
|
1515
1595
|
this.renderer = options.renderer;
|
|
1516
1596
|
this.zoomSensitivity = options.zoomSensitivity ?? .001;
|
|
1517
1597
|
this.panButton = options.panButton ?? 1;
|
|
1598
|
+
window.addEventListener("blur", this.boundBlur);
|
|
1599
|
+
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1600
|
+
}
|
|
1601
|
+
destroy() {
|
|
1602
|
+
window.removeEventListener("blur", this.boundBlur);
|
|
1603
|
+
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1604
|
+
}
|
|
1605
|
+
handleWindowBlur() {
|
|
1606
|
+
this.spacePressed = false;
|
|
1607
|
+
if (this.isPanning) this.endPan();
|
|
1518
1608
|
}
|
|
1519
1609
|
/**
|
|
1520
1610
|
* Check if currently panning
|
|
@@ -1633,7 +1723,7 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1633
1723
|
const actualNewZoom = this.renderer.zoom;
|
|
1634
1724
|
this.renderer.offsetX = oldOffsetX + worldPos.x * (oldZoom - actualNewZoom);
|
|
1635
1725
|
this.renderer.offsetY = oldOffsetY + worldPos.y * (oldZoom - actualNewZoom);
|
|
1636
|
-
this.emit("zoomChange", this.renderer.zoom,
|
|
1726
|
+
this.emit("zoomChange", this.renderer.zoom, worldPos);
|
|
1637
1727
|
}
|
|
1638
1728
|
/**
|
|
1639
1729
|
* Fit all content in view
|
|
@@ -1650,6 +1740,7 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1650
1740
|
* Zoom to fit a specific rectangle
|
|
1651
1741
|
*/
|
|
1652
1742
|
zoomToRect(rect, padding = 50) {
|
|
1743
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
1653
1744
|
const viewWidth = this.renderer.width - padding * 2;
|
|
1654
1745
|
const viewHeight = this.renderer.height - padding * 2;
|
|
1655
1746
|
const scaleX = viewWidth / rect.width;
|
|
@@ -2626,12 +2717,24 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2626
2717
|
* Cancel current connection
|
|
2627
2718
|
*/
|
|
2628
2719
|
cancelConnection() {
|
|
2720
|
+
if (this.isReconnecting) {
|
|
2721
|
+
this.restoreReconnectingEndpoint();
|
|
2722
|
+
this.resetReconnection();
|
|
2723
|
+
this.renderer.markDirty();
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2629
2726
|
if (!this.isConnecting) return;
|
|
2630
2727
|
this.emit("connectionEnd", null);
|
|
2631
2728
|
this.reset();
|
|
2632
2729
|
this.renderer.markDirty();
|
|
2633
2730
|
}
|
|
2634
2731
|
/**
|
|
2732
|
+
* Cancel any in-flight connect or reconnect gesture and restore edge auto-updates.
|
|
2733
|
+
*/
|
|
2734
|
+
cancelAll() {
|
|
2735
|
+
this.cancelConnection();
|
|
2736
|
+
}
|
|
2737
|
+
/**
|
|
2635
2738
|
* Render connection preview
|
|
2636
2739
|
*/
|
|
2637
2740
|
renderPreview(ctx) {
|
|
@@ -2767,6 +2870,11 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2767
2870
|
this.reconnectPoint = null;
|
|
2768
2871
|
this.setCursor("");
|
|
2769
2872
|
}
|
|
2873
|
+
restoreReconnectingEndpoint() {
|
|
2874
|
+
if (!this.reconnectingEdge || !this.originalEdgeEndpoint || !this.reconnectingEndpoint) return;
|
|
2875
|
+
if (this.reconnectingEndpoint === "start") this.reconnectingEdge.from = this.originalEdgeEndpoint;
|
|
2876
|
+
else this.reconnectingEdge.to = this.originalEdgeEndpoint;
|
|
2877
|
+
}
|
|
2770
2878
|
/**
|
|
2771
2879
|
* Render anchors when hovering a node (not connecting)
|
|
2772
2880
|
*/
|
|
@@ -6058,12 +6166,18 @@ var BezierPathStrategy = class {
|
|
|
6058
6166
|
hitTest(point, path, tolerance) {
|
|
6059
6167
|
if (path.length < 4) return false;
|
|
6060
6168
|
const samples = 20;
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6169
|
+
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
6170
|
+
const start = path[i - 1];
|
|
6171
|
+
const control1 = path[i];
|
|
6172
|
+
const control2 = path[i + 1];
|
|
6173
|
+
const end = path[i + 2];
|
|
6174
|
+
let previous = start;
|
|
6175
|
+
for (let sample = 1; sample <= samples; sample++) {
|
|
6176
|
+
const current = bezierPoint(start, control1, control2, end, sample / samples);
|
|
6177
|
+
if (distanceToSegment(point, previous, current) <= tolerance) return true;
|
|
6178
|
+
previous = current;
|
|
6179
|
+
}
|
|
6065
6180
|
}
|
|
6066
|
-
for (let i = 1; i < points.length; i++) if (distanceToSegment(point, points[i - 1], points[i]) <= tolerance) return true;
|
|
6067
6181
|
return false;
|
|
6068
6182
|
}
|
|
6069
6183
|
};
|
|
@@ -7730,6 +7844,13 @@ var InteractionManager = class {
|
|
|
7730
7844
|
}
|
|
7731
7845
|
destroy() {
|
|
7732
7846
|
this.labelEditor.finish(false);
|
|
7847
|
+
this.propertyChangeBatcher.flush();
|
|
7848
|
+
this.dragManager.cancelDrag();
|
|
7849
|
+
this.resizeManager.cancelResize();
|
|
7850
|
+
this.connectionManager.cancelAll();
|
|
7851
|
+
this.selectionManager.cancelSelectionRect();
|
|
7852
|
+
this.navigationManager.destroy();
|
|
7853
|
+
this.resumeEditHover();
|
|
7733
7854
|
if (this.overlayDragSession) {
|
|
7734
7855
|
this.renderer.endOverlayDrag(this.overlayDragSession);
|
|
7735
7856
|
this.overlayDragSession = null;
|
|
@@ -8471,6 +8592,15 @@ var AnimationManager = class {
|
|
|
8471
8592
|
hasActive() {
|
|
8472
8593
|
return this.enter.size > 0 || this.exit.size > 0 || this.highlight.size > 0;
|
|
8473
8594
|
}
|
|
8595
|
+
/**
|
|
8596
|
+
* Drop all in-flight enter/exit/highlight timers without running exit callbacks.
|
|
8597
|
+
* Used by {@link DiagramRenderer.clear} so removed ids do not keep the render loop dirty.
|
|
8598
|
+
*/
|
|
8599
|
+
clear() {
|
|
8600
|
+
this.enter.clear();
|
|
8601
|
+
this.exit.clear();
|
|
8602
|
+
this.highlight.clear();
|
|
8603
|
+
}
|
|
8474
8604
|
update(now = performance.now()) {
|
|
8475
8605
|
if (!this.options.enabled) return false;
|
|
8476
8606
|
let changed = false;
|
|
@@ -9398,6 +9528,7 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9398
9528
|
this._contentDirty = true;
|
|
9399
9529
|
this._contentRevision = 0;
|
|
9400
9530
|
this._contentBoundsCache = null;
|
|
9531
|
+
this._edgeIndexEndpoints = /* @__PURE__ */ new Map();
|
|
9401
9532
|
this._destroyed = false;
|
|
9402
9533
|
this._contextLost = false;
|
|
9403
9534
|
this._canvasRect = null;
|
|
@@ -9754,10 +9885,12 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9754
9885
|
*/
|
|
9755
9886
|
addEdge(edge) {
|
|
9756
9887
|
edge.setDirtyListener(() => this.markDirty());
|
|
9757
|
-
edge.setBindingListener(() =>
|
|
9888
|
+
edge.setBindingListener(() => {
|
|
9889
|
+
this.syncEdgeNodeIndex(edge);
|
|
9890
|
+
this.markContentDirty();
|
|
9891
|
+
});
|
|
9758
9892
|
this._edges.set(edge.id, edge);
|
|
9759
|
-
|
|
9760
|
-
if (edge.to.nodeId) this._updateEdgeIndex(edge.id, edge.to.nodeId, true);
|
|
9893
|
+
this.syncEdgeNodeIndex(edge);
|
|
9761
9894
|
this.animationManager.registerEnter(edge.id);
|
|
9762
9895
|
this.markContentDirty();
|
|
9763
9896
|
this.emit("edgeAdd", edge);
|
|
@@ -9778,6 +9911,22 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9778
9911
|
}
|
|
9779
9912
|
}
|
|
9780
9913
|
/**
|
|
9914
|
+
* Keep `_nodeEdgeIndex` accurate when an edge is added or its from/to node ids change.
|
|
9915
|
+
*/
|
|
9916
|
+
syncEdgeNodeIndex(edge) {
|
|
9917
|
+
const prev = this._edgeIndexEndpoints.get(edge.id);
|
|
9918
|
+
const nextFrom = edge.from.nodeId;
|
|
9919
|
+
const nextTo = edge.to.nodeId;
|
|
9920
|
+
if (prev?.from !== void 0 && prev.from !== nextFrom) this._updateEdgeIndex(edge.id, prev.from, false);
|
|
9921
|
+
if (prev?.to !== void 0 && prev.to !== nextTo) this._updateEdgeIndex(edge.id, prev.to, false);
|
|
9922
|
+
if (nextFrom !== void 0 && nextFrom !== prev?.from) this._updateEdgeIndex(edge.id, nextFrom, true);
|
|
9923
|
+
if (nextTo !== void 0 && nextTo !== prev?.to) this._updateEdgeIndex(edge.id, nextTo, true);
|
|
9924
|
+
this._edgeIndexEndpoints.set(edge.id, {
|
|
9925
|
+
from: nextFrom,
|
|
9926
|
+
to: nextTo
|
|
9927
|
+
});
|
|
9928
|
+
}
|
|
9929
|
+
/**
|
|
9781
9930
|
* Remove an edge from the diagram
|
|
9782
9931
|
*/
|
|
9783
9932
|
removeEdge(edgeId) {
|
|
@@ -9813,8 +9962,12 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9813
9962
|
if (other.from.edgeId === edgeId || other.to.edgeId === edgeId) dependents.push(other.id);
|
|
9814
9963
|
}
|
|
9815
9964
|
for (const dependentId of dependents) this.removeEdgeImmediate(dependentId);
|
|
9816
|
-
|
|
9817
|
-
if (
|
|
9965
|
+
const indexed = this._edgeIndexEndpoints.get(edgeId);
|
|
9966
|
+
if (indexed?.from !== void 0) this._updateEdgeIndex(edgeId, indexed.from, false);
|
|
9967
|
+
else if (edge.from.nodeId) this._updateEdgeIndex(edgeId, edge.from.nodeId, false);
|
|
9968
|
+
if (indexed?.to !== void 0) this._updateEdgeIndex(edgeId, indexed.to, false);
|
|
9969
|
+
else if (edge.to.nodeId) this._updateEdgeIndex(edgeId, edge.to.nodeId, false);
|
|
9970
|
+
this._edgeIndexEndpoints.delete(edgeId);
|
|
9818
9971
|
this._edges.delete(edgeId);
|
|
9819
9972
|
edge.setDirtyListener(void 0);
|
|
9820
9973
|
edge.setBindingListener(void 0);
|
|
@@ -9984,19 +10137,25 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9984
10137
|
this._edges.clear();
|
|
9985
10138
|
this._groups.clear();
|
|
9986
10139
|
this._nodeEdgeIndex.clear();
|
|
10140
|
+
this._edgeIndexEndpoints.clear();
|
|
9987
10141
|
}
|
|
9988
10142
|
/**
|
|
9989
10143
|
* Clear all elements from the diagram
|
|
9990
10144
|
*/
|
|
9991
10145
|
clear() {
|
|
9992
10146
|
for (const node of this._nodes.values()) node.setDirtyListener(void 0);
|
|
9993
|
-
for (const edge of this._edges.values())
|
|
10147
|
+
for (const edge of this._edges.values()) {
|
|
10148
|
+
edge.setDirtyListener(void 0);
|
|
10149
|
+
edge.setBindingListener(void 0);
|
|
10150
|
+
}
|
|
9994
10151
|
for (const group of this._groups.values()) group.setDirtyListener(void 0);
|
|
9995
10152
|
this._nodes.clear();
|
|
9996
10153
|
this._edges.clear();
|
|
9997
10154
|
this._groups.clear();
|
|
9998
10155
|
this._nodeEdgeIndex.clear();
|
|
9999
|
-
this.
|
|
10156
|
+
this._edgeIndexEndpoints.clear();
|
|
10157
|
+
this.animationManager.clear();
|
|
10158
|
+
this.markContentDirty();
|
|
10000
10159
|
}
|
|
10001
10160
|
nativePixelRatio() {
|
|
10002
10161
|
return this.options.retina ? window.devicePixelRatio || 1 : 1;
|
|
@@ -10227,6 +10386,7 @@ var SearchManager = class {
|
|
|
10227
10386
|
this.overlayCleanup = this.renderer.addOverlayRenderer((ctx) => this.renderHighlights(ctx));
|
|
10228
10387
|
}
|
|
10229
10388
|
destroy() {
|
|
10389
|
+
if (this.filterActive) this.clearFilter();
|
|
10230
10390
|
this.overlayCleanup?.();
|
|
10231
10391
|
this.overlayCleanup = null;
|
|
10232
10392
|
this.highlightIds.clear();
|
|
@@ -11490,9 +11650,11 @@ var Node = class extends Element {
|
|
|
11490
11650
|
}
|
|
11491
11651
|
}
|
|
11492
11652
|
/**
|
|
11493
|
-
* Minimal size required to fit current contents
|
|
11653
|
+
* Minimal size required to fit current contents.
|
|
11654
|
+
* Optional availableWidth enables width-constrained measurement
|
|
11655
|
+
* (composite content wraps text to fit the given width).
|
|
11494
11656
|
*/
|
|
11495
|
-
getContentMinSize(ctx) {
|
|
11657
|
+
getContentMinSize(ctx, _availableWidth) {
|
|
11496
11658
|
const bounds = this.getBounds();
|
|
11497
11659
|
const labelSize = this._label ? this._label.measure(ctx) : void 0;
|
|
11498
11660
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
@@ -12015,7 +12177,7 @@ var Group = class Group extends Element {
|
|
|
12015
12177
|
}
|
|
12016
12178
|
applyStyleManager(styleManager) {
|
|
12017
12179
|
const mergedStyle = {
|
|
12018
|
-
...styleManager.getGroupStyle(
|
|
12180
|
+
...styleManager.getGroupStyle(this._state === "selected", this._styleClass),
|
|
12019
12181
|
...this._style
|
|
12020
12182
|
};
|
|
12021
12183
|
if (!shallowEqual(this._groupStyle, mergedStyle)) {
|
|
@@ -12123,7 +12285,6 @@ var Group = class Group extends Element {
|
|
|
12123
12285
|
ctx.fillText(this._label, x + 8, y + 4);
|
|
12124
12286
|
}
|
|
12125
12287
|
ctx.globalAlpha = 1;
|
|
12126
|
-
for (const child of this._children) if (child.visible) child.render(ctx);
|
|
12127
12288
|
}
|
|
12128
12289
|
getBounds() {
|
|
12129
12290
|
return {
|
|
@@ -12260,11 +12421,12 @@ var CircleNode = class extends Node {
|
|
|
12260
12421
|
};
|
|
12261
12422
|
}
|
|
12262
12423
|
getLabelContainerBounds(bounds) {
|
|
12263
|
-
const
|
|
12264
|
-
const
|
|
12424
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
12425
|
+
const width = insetBounds.width / Math.SQRT2;
|
|
12426
|
+
const height = insetBounds.height / Math.SQRT2;
|
|
12265
12427
|
return {
|
|
12266
|
-
x:
|
|
12267
|
-
y:
|
|
12428
|
+
x: insetBounds.x + (insetBounds.width - width) / 2,
|
|
12429
|
+
y: insetBounds.y + (insetBounds.height - height) / 2,
|
|
12268
12430
|
width,
|
|
12269
12431
|
height
|
|
12270
12432
|
};
|
|
@@ -12421,11 +12583,12 @@ var DiamondNode = class extends Node {
|
|
|
12421
12583
|
};
|
|
12422
12584
|
}
|
|
12423
12585
|
getLabelContainerBounds(bounds) {
|
|
12424
|
-
const
|
|
12425
|
-
const
|
|
12586
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
12587
|
+
const width = insetBounds.width / 2;
|
|
12588
|
+
const height = insetBounds.height / 2;
|
|
12426
12589
|
return {
|
|
12427
|
-
x:
|
|
12428
|
-
y:
|
|
12590
|
+
x: insetBounds.x + (insetBounds.width - width) / 2,
|
|
12591
|
+
y: insetBounds.y + (insetBounds.height - height) / 2,
|
|
12429
12592
|
width,
|
|
12430
12593
|
height
|
|
12431
12594
|
};
|
|
@@ -12961,14 +13124,30 @@ var CText = class extends CompositeComponentBase {
|
|
|
12961
13124
|
appendEllipsis: true
|
|
12962
13125
|
});
|
|
12963
13126
|
}
|
|
12964
|
-
measure(ctx) {
|
|
13127
|
+
measure(ctx, maxWidth) {
|
|
12965
13128
|
ctx.font = this.getFont();
|
|
12966
13129
|
const lineHeightPx = this.getLineHeightPx();
|
|
13130
|
+
if (maxWidth !== void 0) {
|
|
13131
|
+
const wrapped = wrapMeasuredText(ctx, this._text, maxWidth, {
|
|
13132
|
+
maxLines: this._maxLines,
|
|
13133
|
+
appendEllipsis: true
|
|
13134
|
+
});
|
|
13135
|
+
const width = measureTextLines(ctx, wrapped);
|
|
13136
|
+
const height = wrapped.length * lineHeightPx;
|
|
13137
|
+
if (this._rotation === 90 || this._rotation === -90) return {
|
|
13138
|
+
width: height,
|
|
13139
|
+
height: width
|
|
13140
|
+
};
|
|
13141
|
+
return {
|
|
13142
|
+
width,
|
|
13143
|
+
height
|
|
13144
|
+
};
|
|
13145
|
+
}
|
|
12967
13146
|
const rawLines = this._text.split("\n");
|
|
12968
|
-
const
|
|
13147
|
+
const oneLineWidth = measureTextLines(ctx, rawLines);
|
|
12969
13148
|
let lineCount = rawLines.length;
|
|
12970
13149
|
if (this._maxLines !== void 0 && lineCount > this._maxLines) lineCount = this._maxLines;
|
|
12971
|
-
const width =
|
|
13150
|
+
const width = oneLineWidth;
|
|
12972
13151
|
const height = lineCount * lineHeightPx;
|
|
12973
13152
|
if (this._rotation === 90 || this._rotation === -90) return {
|
|
12974
13153
|
width: height,
|
|
@@ -13443,16 +13622,33 @@ function flexLayout(container, config, children) {
|
|
|
13443
13622
|
finalSizes[i] = finalSizes[i] + remaining * child.flexGrow / totalGrow;
|
|
13444
13623
|
}
|
|
13445
13624
|
} else if (remaining < 0) {
|
|
13446
|
-
|
|
13447
|
-
for (let i = 0; i < children.length; i++)
|
|
13448
|
-
|
|
13449
|
-
const
|
|
13450
|
-
|
|
13625
|
+
const active = /* @__PURE__ */ new Set();
|
|
13626
|
+
for (let i = 0; i < children.length; i++) {
|
|
13627
|
+
const child = children[i];
|
|
13628
|
+
const minMain = isRow ? child.minSize.width : child.minSize.height;
|
|
13629
|
+
if (child.flexShrink > 0 && finalSizes[i] > minMain) active.add(i);
|
|
13630
|
+
}
|
|
13631
|
+
let deficit = -remaining;
|
|
13632
|
+
const epsilon = 1e-9;
|
|
13633
|
+
while (deficit > epsilon && active.size > 0) {
|
|
13634
|
+
let totalShrink = 0;
|
|
13635
|
+
for (const i of active) totalShrink += children[i].flexShrink * baseSizes[i];
|
|
13636
|
+
if (totalShrink <= 0) break;
|
|
13637
|
+
let distributed = 0;
|
|
13638
|
+
for (const i of active) {
|
|
13451
13639
|
const child = children[i];
|
|
13452
13640
|
const shrinkRatio = child.flexShrink * baseSizes[i] / totalShrink;
|
|
13453
13641
|
const minMain = isRow ? child.minSize.width : child.minSize.height;
|
|
13454
|
-
|
|
13642
|
+
const shrink = Math.min(deficit * shrinkRatio, finalSizes[i] - minMain);
|
|
13643
|
+
finalSizes[i] = finalSizes[i] - shrink;
|
|
13644
|
+
distributed += shrink;
|
|
13645
|
+
if (finalSizes[i] - minMain <= epsilon) {
|
|
13646
|
+
finalSizes[i] = minMain;
|
|
13647
|
+
active.delete(i);
|
|
13648
|
+
}
|
|
13455
13649
|
}
|
|
13650
|
+
if (distributed <= epsilon) break;
|
|
13651
|
+
deficit -= distributed;
|
|
13456
13652
|
}
|
|
13457
13653
|
}
|
|
13458
13654
|
let actualTotal = totalGaps + totalMargins;
|
|
@@ -13641,11 +13837,13 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13641
13837
|
padding: this._padding
|
|
13642
13838
|
};
|
|
13643
13839
|
}
|
|
13644
|
-
buildFlexChildren(ctx, children = this.getLayoutChildren()) {
|
|
13840
|
+
buildFlexChildren(ctx, availableWidth, children = this.getLayoutChildren()) {
|
|
13841
|
+
const pad = normalizeSides(this._padding);
|
|
13842
|
+
const innerWidth = availableWidth !== void 0 && this._direction === "column" ? Math.max(0, availableWidth - pad.left - pad.right) : void 0;
|
|
13645
13843
|
return children.map((child) => {
|
|
13646
13844
|
const s = child.style;
|
|
13647
13845
|
return {
|
|
13648
|
-
measure: child.measure(ctx),
|
|
13846
|
+
measure: child.measure(ctx, innerWidth),
|
|
13649
13847
|
minSize: {
|
|
13650
13848
|
width: 0,
|
|
13651
13849
|
height: 0
|
|
@@ -13658,8 +13856,8 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13658
13856
|
};
|
|
13659
13857
|
});
|
|
13660
13858
|
}
|
|
13661
|
-
measure(ctx) {
|
|
13662
|
-
const flexChildren = this.buildFlexChildren(ctx);
|
|
13859
|
+
measure(ctx, availableWidth) {
|
|
13860
|
+
const flexChildren = this.buildFlexChildren(ctx, availableWidth);
|
|
13663
13861
|
return flexLayout({
|
|
13664
13862
|
width: 1e5,
|
|
13665
13863
|
height: 1e5
|
|
@@ -13676,7 +13874,7 @@ var CContainer = class extends CompositeComponentBase {
|
|
|
13676
13874
|
render(ctx, bounds) {
|
|
13677
13875
|
if (this.style.visible === false) return;
|
|
13678
13876
|
const layoutChildren = this.getLayoutChildren();
|
|
13679
|
-
const flexChildren = this.buildFlexChildren(ctx, layoutChildren);
|
|
13877
|
+
const flexChildren = this.buildFlexChildren(ctx, void 0, layoutChildren);
|
|
13680
13878
|
const result = flexLayout({
|
|
13681
13879
|
width: bounds.width,
|
|
13682
13880
|
height: bounds.height
|
|
@@ -13851,11 +14049,12 @@ var CShape = class extends CompositeComponentBase {
|
|
|
13851
14049
|
get content() {
|
|
13852
14050
|
return this._content;
|
|
13853
14051
|
}
|
|
13854
|
-
measure(ctx) {
|
|
14052
|
+
measure(ctx, maxWidth) {
|
|
13855
14053
|
const pad = normalizeSides(this._padding);
|
|
13856
14054
|
const bw = this._borderWidth;
|
|
13857
14055
|
if (this._content) {
|
|
13858
|
-
const
|
|
14056
|
+
const innerWidth = maxWidth === void 0 ? void 0 : Math.max(0, maxWidth - pad.left - pad.right - bw * 2);
|
|
14057
|
+
const contentSize = this._content.measure(ctx, innerWidth);
|
|
13859
14058
|
return {
|
|
13860
14059
|
width: contentSize.width + pad.left + pad.right + bw * 2,
|
|
13861
14060
|
height: contentSize.height + pad.top + pad.bottom + bw * 2
|
|
@@ -14141,13 +14340,24 @@ var CompositeNode = class extends Node {
|
|
|
14141
14340
|
const fillOpacity = style.fillOpacity ?? 1;
|
|
14142
14341
|
const strokeOpacity = style.strokeOpacity ?? 1;
|
|
14143
14342
|
this.applyStyle(ctx);
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14343
|
+
if (this._shapeType === "custom" && this._pathFactory) {
|
|
14344
|
+
const path = this._pathFactory(width, height);
|
|
14345
|
+
ctx.save();
|
|
14346
|
+
ctx.translate(x, y);
|
|
14347
|
+
ctx.globalAlpha = baseOpacity * fillOpacity;
|
|
14348
|
+
ctx.fill(path);
|
|
14349
|
+
ctx.globalAlpha = baseOpacity * strokeOpacity;
|
|
14350
|
+
ctx.stroke(path);
|
|
14351
|
+
ctx.restore();
|
|
14352
|
+
} else {
|
|
14353
|
+
ctx.beginPath();
|
|
14354
|
+
this.buildShapePath(ctx, x, y, width, height);
|
|
14355
|
+
ctx.closePath();
|
|
14356
|
+
ctx.globalAlpha = baseOpacity * fillOpacity;
|
|
14357
|
+
ctx.fill();
|
|
14358
|
+
ctx.globalAlpha = baseOpacity * strokeOpacity;
|
|
14359
|
+
ctx.stroke();
|
|
14360
|
+
}
|
|
14151
14361
|
ctx.globalAlpha = 1;
|
|
14152
14362
|
this.renderContents(ctx);
|
|
14153
14363
|
}
|
|
@@ -14169,15 +14379,6 @@ var CompositeNode = class extends Node {
|
|
|
14169
14379
|
break;
|
|
14170
14380
|
}
|
|
14171
14381
|
case "custom":
|
|
14172
|
-
if (this._pathFactory) {
|
|
14173
|
-
const path = this._pathFactory(w, h);
|
|
14174
|
-
ctx.save();
|
|
14175
|
-
ctx.translate(x, y);
|
|
14176
|
-
ctx.fill(path);
|
|
14177
|
-
ctx.stroke(path);
|
|
14178
|
-
ctx.restore();
|
|
14179
|
-
return;
|
|
14180
|
-
}
|
|
14181
14382
|
this.buildRectPath(ctx, x, y, w, h);
|
|
14182
14383
|
break;
|
|
14183
14384
|
default:
|
|
@@ -14214,43 +14415,64 @@ var CompositeNode = class extends Node {
|
|
|
14214
14415
|
this.renderPorts(ctx);
|
|
14215
14416
|
}
|
|
14216
14417
|
applyAutoSize(ctx) {
|
|
14217
|
-
const
|
|
14218
|
-
const inset = this.contentInset
|
|
14219
|
-
|
|
14220
|
-
|
|
14418
|
+
const bounds = this.getBounds();
|
|
14419
|
+
const inset = resolveContentInset(this.contentInset, this.contentInsetScale, {
|
|
14420
|
+
width: bounds.width,
|
|
14421
|
+
height: bounds.height
|
|
14422
|
+
}, this.contentInsetBaseSize);
|
|
14423
|
+
const floorContentWidth = this._content.measure(ctx, 0).width;
|
|
14424
|
+
const neededWidth = Math.max(this._minWidth, floorContentWidth + inset.left + inset.right);
|
|
14221
14425
|
if (neededWidth > this._width) this._width = neededWidth;
|
|
14426
|
+
const innerWidth = Math.max(0, this._width - inset.left - inset.right);
|
|
14427
|
+
const contentSize = this._content.measure(ctx, innerWidth);
|
|
14428
|
+
const neededHeight = Math.max(this._minHeight, contentSize.height + inset.top + inset.bottom);
|
|
14222
14429
|
if (neededHeight > this._height) this._height = neededHeight;
|
|
14223
14430
|
}
|
|
14224
14431
|
getLabelContainerBounds(bounds) {
|
|
14432
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
14225
14433
|
if (this._shapeType === "circle") {
|
|
14226
14434
|
const factor = 1 / Math.SQRT2;
|
|
14227
|
-
const w =
|
|
14228
|
-
const h =
|
|
14435
|
+
const w = insetBounds.width * factor;
|
|
14436
|
+
const h = insetBounds.height * factor;
|
|
14229
14437
|
return {
|
|
14230
|
-
x:
|
|
14231
|
-
y:
|
|
14438
|
+
x: insetBounds.x + (insetBounds.width - w) / 2,
|
|
14439
|
+
y: insetBounds.y + (insetBounds.height - h) / 2,
|
|
14232
14440
|
width: w,
|
|
14233
14441
|
height: h
|
|
14234
14442
|
};
|
|
14235
14443
|
}
|
|
14236
14444
|
if (this._shapeType === "diamond") {
|
|
14237
|
-
const w =
|
|
14238
|
-
const h =
|
|
14445
|
+
const w = insetBounds.width / 2;
|
|
14446
|
+
const h = insetBounds.height / 2;
|
|
14239
14447
|
return {
|
|
14240
|
-
x:
|
|
14241
|
-
y:
|
|
14448
|
+
x: insetBounds.x + (insetBounds.width - w) / 2,
|
|
14449
|
+
y: insetBounds.y + (insetBounds.height - h) / 2,
|
|
14242
14450
|
width: w,
|
|
14243
14451
|
height: h
|
|
14244
14452
|
};
|
|
14245
14453
|
}
|
|
14246
|
-
return
|
|
14454
|
+
return insetBounds;
|
|
14247
14455
|
}
|
|
14248
14456
|
/**
|
|
14249
14457
|
* Get the minimum content size needed for the content tree.
|
|
14250
14458
|
* Useful for external auto-sizing logic.
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14459
|
+
*
|
|
14460
|
+
* With `availableWidth`, text wraps to the given node width: the returned
|
|
14461
|
+
* width is the longest-word floor (content can never be narrower than its
|
|
14462
|
+
* widest unbreakable word), the height is the wrapped height at that width.
|
|
14463
|
+
*/
|
|
14464
|
+
getContentMinSize(ctx, availableWidth) {
|
|
14465
|
+
if (availableWidth === void 0) return this._content.measure(ctx);
|
|
14466
|
+
const inset = resolveContentInset(this.contentInset, this.contentInsetScale, {
|
|
14467
|
+
width: availableWidth,
|
|
14468
|
+
height: this._height
|
|
14469
|
+
}, this.contentInsetBaseSize);
|
|
14470
|
+
const innerWidth = Math.max(0, availableWidth - inset.left - inset.right);
|
|
14471
|
+
const wrapped = this._content.measure(ctx, innerWidth);
|
|
14472
|
+
return {
|
|
14473
|
+
width: this._content.measure(ctx, 0).width + inset.left + inset.right,
|
|
14474
|
+
height: wrapped.height + inset.top + inset.bottom
|
|
14475
|
+
};
|
|
14254
14476
|
}
|
|
14255
14477
|
};
|
|
14256
14478
|
//#endregion
|
|
@@ -14451,16 +14673,22 @@ var DARK_THEME = {
|
|
|
14451
14673
|
}
|
|
14452
14674
|
}
|
|
14453
14675
|
};
|
|
14676
|
+
function cloneTheme(theme) {
|
|
14677
|
+
return structuredClone(theme);
|
|
14678
|
+
}
|
|
14454
14679
|
/**
|
|
14455
14680
|
* Manages styles and themes
|
|
14456
14681
|
*/
|
|
14457
14682
|
var StyleManager = class {
|
|
14458
14683
|
constructor(theme) {
|
|
14459
14684
|
this._classes = /* @__PURE__ */ new Map();
|
|
14460
|
-
this._builtInThemes = new Map([["default", DEFAULT_THEME], ["dark", DARK_THEME]]);
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14685
|
+
this._builtInThemes = new Map([["default", cloneTheme(DEFAULT_THEME)], ["dark", cloneTheme(DARK_THEME)]]);
|
|
14686
|
+
const initialTheme = theme ?? "default";
|
|
14687
|
+
if (typeof initialTheme === "string") {
|
|
14688
|
+
const registeredTheme = this._builtInThemes.get(initialTheme);
|
|
14689
|
+
if (registeredTheme === void 0) throw new Error(`Unknown theme: ${initialTheme}`);
|
|
14690
|
+
this._theme = cloneTheme(registeredTheme);
|
|
14691
|
+
} else this._theme = cloneTheme(initialTheme);
|
|
14464
14692
|
}
|
|
14465
14693
|
/**
|
|
14466
14694
|
* Current theme
|
|
@@ -14474,14 +14702,15 @@ var StyleManager = class {
|
|
|
14474
14702
|
setTheme(theme) {
|
|
14475
14703
|
if (typeof theme === "string") {
|
|
14476
14704
|
const builtIn = this._builtInThemes.get(theme);
|
|
14477
|
-
if (builtIn
|
|
14478
|
-
|
|
14705
|
+
if (builtIn === void 0) throw new Error(`Unknown theme: ${theme}`);
|
|
14706
|
+
this._theme = cloneTheme(builtIn);
|
|
14707
|
+
} else this._theme = cloneTheme(theme);
|
|
14479
14708
|
}
|
|
14480
14709
|
/**
|
|
14481
14710
|
* Register a custom theme
|
|
14482
14711
|
*/
|
|
14483
14712
|
registerTheme(theme) {
|
|
14484
|
-
this._builtInThemes.set(theme.name, theme);
|
|
14713
|
+
this._builtInThemes.set(theme.name, cloneTheme(theme));
|
|
14485
14714
|
}
|
|
14486
14715
|
/**
|
|
14487
14716
|
* Get all available theme names
|
|
@@ -14596,10 +14825,17 @@ var StyleManager = class {
|
|
|
14596
14825
|
}
|
|
14597
14826
|
}
|
|
14598
14827
|
getBaseEdgeStyle(state) {
|
|
14828
|
+
const defaultStyle = this._theme.edge.default;
|
|
14599
14829
|
switch (state) {
|
|
14600
|
-
case "hover": return
|
|
14601
|
-
|
|
14602
|
-
|
|
14830
|
+
case "hover": return {
|
|
14831
|
+
...defaultStyle,
|
|
14832
|
+
...this._theme.edge.hover
|
|
14833
|
+
};
|
|
14834
|
+
case "selected": return {
|
|
14835
|
+
...defaultStyle,
|
|
14836
|
+
...this._theme.edge.selected
|
|
14837
|
+
};
|
|
14838
|
+
default: return defaultStyle;
|
|
14603
14839
|
}
|
|
14604
14840
|
}
|
|
14605
14841
|
};
|
|
@@ -14771,36 +15007,7 @@ var Serializer = class {
|
|
|
14771
15007
|
position: port.position,
|
|
14772
15008
|
styleClass: port.styleClass
|
|
14773
15009
|
}));
|
|
14774
|
-
|
|
14775
|
-
if (node.label) {
|
|
14776
|
-
const ins = node.label.inset;
|
|
14777
|
-
const defaultInset = 8;
|
|
14778
|
-
const serializedInset = ins.top === defaultInset && ins.right === defaultInset && ins.bottom === defaultInset && ins.left === defaultInset ? void 0 : ins.top === ins.right && ins.top === ins.bottom && ins.top === ins.left ? ins.top : {
|
|
14779
|
-
top: ins.top,
|
|
14780
|
-
right: ins.right,
|
|
14781
|
-
bottom: ins.bottom,
|
|
14782
|
-
left: ins.left
|
|
14783
|
-
};
|
|
14784
|
-
if (hasNonDefaultValues({
|
|
14785
|
-
text: node.label.text,
|
|
14786
|
-
style: node.label.style,
|
|
14787
|
-
maxWidth: node.label.maxWidth,
|
|
14788
|
-
inset: serializedInset ?? void 0,
|
|
14789
|
-
styleClass: node.label.styleClass
|
|
14790
|
-
}, {
|
|
14791
|
-
inset: void 0,
|
|
14792
|
-
style: {},
|
|
14793
|
-
maxWidth: void 0,
|
|
14794
|
-
styleClass: void 0
|
|
14795
|
-
})) label = omitEmptyValues({
|
|
14796
|
-
text: node.label.text,
|
|
14797
|
-
style: Object.keys(node.label.style).length > 0 ? node.label.style : void 0,
|
|
14798
|
-
maxWidth: node.label.maxWidth,
|
|
14799
|
-
inset: serializedInset,
|
|
14800
|
-
styleClass: node.label.styleClass
|
|
14801
|
-
});
|
|
14802
|
-
else label = node.label.text;
|
|
14803
|
-
}
|
|
15010
|
+
const label = node.label ? this.serializeTextLabel(node.label) : void 0;
|
|
14804
15011
|
let icon;
|
|
14805
15012
|
if (node.icon) {
|
|
14806
15013
|
const opts = node.icon.options;
|
|
@@ -14833,6 +15040,8 @@ var Serializer = class {
|
|
|
14833
15040
|
if (hasNonDefaultValues(anchorData, anchorDefaults)) anchorPoints = omitDefaultValues(anchorData, anchorDefaults);
|
|
14834
15041
|
const contentInset = node.contentInset;
|
|
14835
15042
|
const hasContentInset = contentInset.top !== 0 || contentInset.right !== 0 || contentInset.bottom !== 0 || contentInset.left !== 0;
|
|
15043
|
+
const contentInsetScale = node.contentInsetScale;
|
|
15044
|
+
const hasContentInsetScale = Object.keys(contentInsetScale).length > 0;
|
|
14836
15045
|
const base = omitEmptyValues({
|
|
14837
15046
|
id: node.id,
|
|
14838
15047
|
type: node.typeName,
|
|
@@ -14840,14 +15049,16 @@ var Serializer = class {
|
|
|
14840
15049
|
y: node.y,
|
|
14841
15050
|
width: node.width,
|
|
14842
15051
|
height: node.height,
|
|
14843
|
-
style: node.
|
|
15052
|
+
style: { ...node.styleOverrides },
|
|
14844
15053
|
styleClass: node.styleClass,
|
|
14845
15054
|
label,
|
|
14846
|
-
labelStyleClass: typeof label === "string" ? node.label?.styleClass : void 0,
|
|
14847
15055
|
labelPlacement: node.labelPlacement !== "center" ? node.labelPlacement : void 0,
|
|
14848
15056
|
labelGap: node.labelGap !== 4 ? node.labelGap : void 0,
|
|
14849
15057
|
icon,
|
|
14850
15058
|
contentInset: hasContentInset ? contentInset : void 0,
|
|
15059
|
+
contentInsetScale: hasContentInsetScale ? contentInsetScale : void 0,
|
|
15060
|
+
contentInsetBaseSize: node.contentInsetBaseSize,
|
|
15061
|
+
badges: node.badges.length > 0 ? node.badges.map((badge) => ({ ...badge })) : void 0,
|
|
14851
15062
|
anchorPoints,
|
|
14852
15063
|
ports: ports.length > 0 ? ports : void 0,
|
|
14853
15064
|
data: Object.keys(node.data).length > 0 ? node.data : void 0
|
|
@@ -14874,14 +15085,14 @@ var Serializer = class {
|
|
|
14874
15085
|
arrowType: edge.arrowType,
|
|
14875
15086
|
startMarker: edge.startMarker,
|
|
14876
15087
|
endMarker: edge.endMarker,
|
|
14877
|
-
style: edge.
|
|
15088
|
+
style: { ...edge.styleOverrides },
|
|
14878
15089
|
styleClass: edge.styleClass,
|
|
14879
|
-
label: edge.label
|
|
14880
|
-
labelStyleClass: edge.label?.styleClass,
|
|
15090
|
+
label: edge.label ? this.serializeTextLabel(edge.label) : void 0,
|
|
14881
15091
|
labelOffset: edge.labelOffset !== 0 ? edge.labelOffset : void 0,
|
|
14882
15092
|
labelPosition: edge.labelPosition !== .5 ? edge.labelPosition : void 0,
|
|
14883
15093
|
labelFollowPath: edge.labelFollowPath ? true : void 0,
|
|
14884
15094
|
labelBackground: edge.labelBackground,
|
|
15095
|
+
lockAnchors: edge.lockAnchors ? void 0 : false,
|
|
14885
15096
|
labelLineGap: edge.labelLineGap ? true : void 0,
|
|
14886
15097
|
data: Object.keys(edge.data).length > 0 ? edge.data : void 0
|
|
14887
15098
|
};
|
|
@@ -14890,12 +15101,31 @@ var Serializer = class {
|
|
|
14890
15101
|
return {
|
|
14891
15102
|
id: group.id,
|
|
14892
15103
|
childIds: group.children.map((c) => c.id),
|
|
14893
|
-
style: group.
|
|
15104
|
+
style: { ...group.styleOverrides },
|
|
14894
15105
|
styleClass: group.styleClass,
|
|
14895
15106
|
label: group.label,
|
|
15107
|
+
padding: group.padding !== 20 ? group.padding : void 0,
|
|
14896
15108
|
data: Object.keys(group.data).length > 0 ? group.data : void 0
|
|
14897
15109
|
};
|
|
14898
15110
|
}
|
|
15111
|
+
serializeTextLabel(label) {
|
|
15112
|
+
const inset = label.inset;
|
|
15113
|
+
const serializedInset = inset.top === 8 && inset.right === 8 && inset.bottom === 8 && inset.left === 8 ? void 0 : inset.top === inset.right && inset.top === inset.bottom && inset.top === inset.left ? inset.top : {
|
|
15114
|
+
top: inset.top,
|
|
15115
|
+
right: inset.right,
|
|
15116
|
+
bottom: inset.bottom,
|
|
15117
|
+
left: inset.left
|
|
15118
|
+
};
|
|
15119
|
+
const style = label.styleOverrides;
|
|
15120
|
+
if (!(Object.keys(style).length > 0 || label.maxWidth !== void 0 || serializedInset !== void 0 || label.styleClass !== void 0)) return label.text;
|
|
15121
|
+
return omitEmptyValues({
|
|
15122
|
+
text: label.text,
|
|
15123
|
+
style: Object.keys(style).length > 0 ? style : void 0,
|
|
15124
|
+
maxWidth: label.maxWidth,
|
|
15125
|
+
inset: serializedInset,
|
|
15126
|
+
styleClass: label.styleClass
|
|
15127
|
+
});
|
|
15128
|
+
}
|
|
14899
15129
|
};
|
|
14900
15130
|
//#endregion
|
|
14901
15131
|
//#region src/utils/download.ts
|
|
@@ -14977,7 +15207,11 @@ var ImageExporter = class {
|
|
|
14977
15207
|
const padding = options.padding ?? 20;
|
|
14978
15208
|
const format = options.format ?? "png";
|
|
14979
15209
|
const quality = options.quality ?? .92;
|
|
14980
|
-
const bounds = getContentBounds({
|
|
15210
|
+
const bounds = getContentBounds({
|
|
15211
|
+
nodes: this.renderer.nodes.values(),
|
|
15212
|
+
edges: this.renderer.edges.values(),
|
|
15213
|
+
groups: this.renderer.groups.values()
|
|
15214
|
+
});
|
|
14981
15215
|
if (bounds === null) return this.createBlankImage(100, 100, backgroundColor, format, quality);
|
|
14982
15216
|
const width = (bounds.width + padding * 2) * scale;
|
|
14983
15217
|
const height = (bounds.height + padding * 2) * scale;
|
|
@@ -15029,6 +15263,7 @@ var SvgExporter = class {
|
|
|
15029
15263
|
const padding = options.padding ?? 20;
|
|
15030
15264
|
const includeBackground = options.includeBackground ?? true;
|
|
15031
15265
|
const backgroundColor = options.backgroundColor ?? "#ffffff";
|
|
15266
|
+
const escapedBackgroundColor = this.escapeAttribute(backgroundColor);
|
|
15032
15267
|
const edgeLabelOffset = options.edgeLabelOffset;
|
|
15033
15268
|
const bounds = getContentBounds({
|
|
15034
15269
|
nodes: this.renderer.nodes.values(),
|
|
@@ -15045,7 +15280,7 @@ var SvgExporter = class {
|
|
|
15045
15280
|
const renderedEdges = [];
|
|
15046
15281
|
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`);
|
|
15047
15282
|
for (const edge of this.renderer.edges.values()) if (edge.visible) renderedEdges.push(this.renderEdge(edge, edgeLabelOffset));
|
|
15048
|
-
if (includeBackground) parts.push(`<rect width="100%" height="100%" fill="${
|
|
15283
|
+
if (includeBackground) parts.push(`<rect width="100%" height="100%" fill="${escapedBackgroundColor}"/>`);
|
|
15049
15284
|
parts.push(`<g transform="translate(${offsetX}, ${offsetY})">`);
|
|
15050
15285
|
for (const group of this.renderer.groups.values()) if (group.visible) parts.push(this.renderGroup(group));
|
|
15051
15286
|
for (const node of this.renderer.nodes.values()) if (node.visible) parts.push(this.renderNode(node));
|
|
@@ -15064,8 +15299,8 @@ var SvgExporter = class {
|
|
|
15064
15299
|
renderGroup(group) {
|
|
15065
15300
|
const bounds = group.getBounds();
|
|
15066
15301
|
const style = group.style;
|
|
15067
|
-
const fill = style.fillColor ?? "rgba(200, 200, 200, 0.2)";
|
|
15068
|
-
const stroke = style.strokeColor ?? "#999999";
|
|
15302
|
+
const fill = this.escapeAttribute(style.fillColor ?? "rgba(200, 200, 200, 0.2)");
|
|
15303
|
+
const stroke = this.escapeAttribute(style.strokeColor ?? "#999999");
|
|
15069
15304
|
const strokeWidth = style.strokeWidth ?? 1;
|
|
15070
15305
|
const opacity = style.opacity ?? 1;
|
|
15071
15306
|
const label = group.label ? `<text x="${bounds.x + 8}" y="${bounds.y + 14}" fill="#666666" font-size="12">${this.escapeText(group.label)}</text>` : "";
|
|
@@ -15079,8 +15314,8 @@ var SvgExporter = class {
|
|
|
15079
15314
|
renderNode(node) {
|
|
15080
15315
|
const bounds = node.getBounds();
|
|
15081
15316
|
const style = node.style;
|
|
15082
|
-
const fill = style.fillColor ?? "#ffffff";
|
|
15083
|
-
const stroke = style.strokeColor ?? "#333333";
|
|
15317
|
+
const fill = this.escapeAttribute(style.fillColor ?? "#ffffff");
|
|
15318
|
+
const stroke = this.escapeAttribute(style.strokeColor ?? "#333333");
|
|
15084
15319
|
const strokeWidth = style.strokeWidth ?? 2;
|
|
15085
15320
|
const baseOpacity = style.opacity ?? 1;
|
|
15086
15321
|
const fillOpacity = (style.fillOpacity ?? 1) * baseOpacity;
|
|
@@ -15177,14 +15412,15 @@ var SvgExporter = class {
|
|
|
15177
15412
|
if (edge.path.length < 2) return "";
|
|
15178
15413
|
const style = edge.style;
|
|
15179
15414
|
const stroke = style.strokeColor ?? "#666666";
|
|
15415
|
+
const escapedStroke = this.escapeAttribute(stroke);
|
|
15180
15416
|
const strokeWidth = style.strokeWidth ?? 2;
|
|
15181
15417
|
const strokeOpacity = (style.strokeOpacity ?? 1) * (style.opacity ?? 1);
|
|
15182
|
-
const lineCap = style.lineCap ? ` stroke-linecap="${style.lineCap}"` : "";
|
|
15183
|
-
const lineJoin = style.lineJoin ? ` stroke-linejoin="${style.lineJoin}"` : "";
|
|
15418
|
+
const lineCap = style.lineCap ? ` stroke-linecap="${this.escapeAttribute(style.lineCap)}"` : "";
|
|
15419
|
+
const lineJoin = style.lineJoin ? ` stroke-linejoin="${this.escapeAttribute(style.lineJoin)}"` : "";
|
|
15184
15420
|
const dashValues = style.flowDash ?? style.lineDash;
|
|
15185
15421
|
const dash = dashValues ? ` stroke-dasharray="${dashValues.join(" ")}"` : "";
|
|
15186
15422
|
const dashOffset = style.lineDashOffset !== void 0 ? ` stroke-dashoffset="${style.lineDashOffset}"` : "";
|
|
15187
|
-
return `<path d="${this.buildEdgePath(edge, edgeLabelOffset)}" fill="none" stroke="${
|
|
15423
|
+
return `<path d="${this.buildEdgePath(edge, edgeLabelOffset)}" fill="none" stroke="${escapedStroke}" stroke-width="${strokeWidth}" stroke-opacity="${strokeOpacity}" color="${escapedStroke}"${lineCap}${lineJoin}${dash}${dashOffset}/>${this.renderEdgeMarkers(edge, stroke)}${this.renderEdgeLabel(edge, edgeLabelOffset)}`;
|
|
15188
15424
|
}
|
|
15189
15425
|
renderEdgeLabel(edge, edgeLabelOffset) {
|
|
15190
15426
|
if (!edge.label) return "";
|
|
@@ -15196,7 +15432,7 @@ var SvgExporter = class {
|
|
|
15196
15432
|
renderEdgeLabelBackground(edge, point, rotation = 0) {
|
|
15197
15433
|
if (!edge.label) return "";
|
|
15198
15434
|
const metrics = this.measureTextLabel(edge.label.text, edge.label.style, edge.label.inset);
|
|
15199
|
-
const bgColor = edge.labelBackground?.color ?? "#ffffff";
|
|
15435
|
+
const bgColor = this.escapeAttribute(edge.labelBackground?.color ?? "#ffffff");
|
|
15200
15436
|
const bgOpacity = edge.labelBackground?.opacity ?? 1;
|
|
15201
15437
|
const bgRadius = edge.labelBackground?.borderRadius ?? 2;
|
|
15202
15438
|
const x = point.x - metrics.width / 2;
|
|
@@ -15277,7 +15513,11 @@ var SvgExporter = class {
|
|
|
15277
15513
|
return calculateMarkerPoints(edge.path, position, edge.type);
|
|
15278
15514
|
}
|
|
15279
15515
|
renderMarkerShape(marker, from, to, edgeStroke) {
|
|
15280
|
-
return generateSvgMarker(
|
|
15516
|
+
return generateSvgMarker({
|
|
15517
|
+
...marker,
|
|
15518
|
+
strokeColor: marker.strokeColor !== void 0 ? this.escapeAttribute(marker.strokeColor) : void 0,
|
|
15519
|
+
fillColor: marker.fillColor !== void 0 ? this.escapeAttribute(marker.fillColor) : void 0
|
|
15520
|
+
}, from, to, this.escapeAttribute(edgeStroke));
|
|
15281
15521
|
}
|
|
15282
15522
|
buildPath(edge) {
|
|
15283
15523
|
const path = edge.path;
|
|
@@ -15347,11 +15587,11 @@ var SvgExporter = class {
|
|
|
15347
15587
|
};
|
|
15348
15588
|
}
|
|
15349
15589
|
renderTextLabel(text, point, style = {}, rotation = 0) {
|
|
15350
|
-
const fill = style.color ?? "#000000";
|
|
15590
|
+
const fill = this.escapeAttribute(style.color ?? "#000000");
|
|
15351
15591
|
const fontSize = style.fontSize ?? 14;
|
|
15352
|
-
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
15353
|
-
const fontWeight = style.fontWeight ?? "normal";
|
|
15354
|
-
const fontStyle = style.fontStyle ?? "normal";
|
|
15592
|
+
const fontFamily = this.escapeAttribute(style.fontFamily ?? "sans-serif");
|
|
15593
|
+
const fontWeight = this.escapeAttribute(style.fontWeight ?? "normal");
|
|
15594
|
+
const fontStyle = this.escapeAttribute(style.fontStyle ?? "normal");
|
|
15355
15595
|
const fontStyleAttr = fontStyle !== "normal" ? ` font-style="${fontStyle}"` : "";
|
|
15356
15596
|
const opacity = style.opacity ?? 1;
|
|
15357
15597
|
const anchor = style.align === "left" ? "start" : style.align === "right" ? "end" : "middle";
|
|
@@ -15547,9 +15787,10 @@ var SvgExporter = class {
|
|
|
15547
15787
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
15548
15788
|
}
|
|
15549
15789
|
createEmptySvg(width, height, backgroundColor, includeBackground) {
|
|
15790
|
+
const escapedBackgroundColor = this.escapeAttribute(backgroundColor);
|
|
15550
15791
|
return [
|
|
15551
15792
|
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`,
|
|
15552
|
-
includeBackground ? `<rect width="100%" height="100%" fill="${
|
|
15793
|
+
includeBackground ? `<rect width="100%" height="100%" fill="${escapedBackgroundColor}"/>` : "",
|
|
15553
15794
|
"</svg>"
|
|
15554
15795
|
].join("");
|
|
15555
15796
|
}
|