@ngroznykh/papirus 0.9.13 → 0.9.14
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/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/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 +332 -140
- 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);
|
|
@@ -1512,9 +1576,23 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1512
1576
|
this.isPanning = false;
|
|
1513
1577
|
this.panStart = null;
|
|
1514
1578
|
this.spacePressed = false;
|
|
1579
|
+
this.boundBlur = () => this.handleWindowBlur();
|
|
1580
|
+
this.boundVisibilityChange = () => {
|
|
1581
|
+
if (document.visibilityState === "hidden") this.handleWindowBlur();
|
|
1582
|
+
};
|
|
1515
1583
|
this.renderer = options.renderer;
|
|
1516
1584
|
this.zoomSensitivity = options.zoomSensitivity ?? .001;
|
|
1517
1585
|
this.panButton = options.panButton ?? 1;
|
|
1586
|
+
window.addEventListener("blur", this.boundBlur);
|
|
1587
|
+
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1588
|
+
}
|
|
1589
|
+
destroy() {
|
|
1590
|
+
window.removeEventListener("blur", this.boundBlur);
|
|
1591
|
+
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1592
|
+
}
|
|
1593
|
+
handleWindowBlur() {
|
|
1594
|
+
this.spacePressed = false;
|
|
1595
|
+
if (this.isPanning) this.endPan();
|
|
1518
1596
|
}
|
|
1519
1597
|
/**
|
|
1520
1598
|
* Check if currently panning
|
|
@@ -1633,7 +1711,7 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1633
1711
|
const actualNewZoom = this.renderer.zoom;
|
|
1634
1712
|
this.renderer.offsetX = oldOffsetX + worldPos.x * (oldZoom - actualNewZoom);
|
|
1635
1713
|
this.renderer.offsetY = oldOffsetY + worldPos.y * (oldZoom - actualNewZoom);
|
|
1636
|
-
this.emit("zoomChange", this.renderer.zoom,
|
|
1714
|
+
this.emit("zoomChange", this.renderer.zoom, worldPos);
|
|
1637
1715
|
}
|
|
1638
1716
|
/**
|
|
1639
1717
|
* Fit all content in view
|
|
@@ -1650,6 +1728,7 @@ var NavigationManager = class extends EventEmitter {
|
|
|
1650
1728
|
* Zoom to fit a specific rectangle
|
|
1651
1729
|
*/
|
|
1652
1730
|
zoomToRect(rect, padding = 50) {
|
|
1731
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
1653
1732
|
const viewWidth = this.renderer.width - padding * 2;
|
|
1654
1733
|
const viewHeight = this.renderer.height - padding * 2;
|
|
1655
1734
|
const scaleX = viewWidth / rect.width;
|
|
@@ -2626,12 +2705,24 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2626
2705
|
* Cancel current connection
|
|
2627
2706
|
*/
|
|
2628
2707
|
cancelConnection() {
|
|
2708
|
+
if (this.isReconnecting) {
|
|
2709
|
+
this.restoreReconnectingEndpoint();
|
|
2710
|
+
this.resetReconnection();
|
|
2711
|
+
this.renderer.markDirty();
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2629
2714
|
if (!this.isConnecting) return;
|
|
2630
2715
|
this.emit("connectionEnd", null);
|
|
2631
2716
|
this.reset();
|
|
2632
2717
|
this.renderer.markDirty();
|
|
2633
2718
|
}
|
|
2634
2719
|
/**
|
|
2720
|
+
* Cancel any in-flight connect or reconnect gesture and restore edge auto-updates.
|
|
2721
|
+
*/
|
|
2722
|
+
cancelAll() {
|
|
2723
|
+
this.cancelConnection();
|
|
2724
|
+
}
|
|
2725
|
+
/**
|
|
2635
2726
|
* Render connection preview
|
|
2636
2727
|
*/
|
|
2637
2728
|
renderPreview(ctx) {
|
|
@@ -2767,6 +2858,11 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
2767
2858
|
this.reconnectPoint = null;
|
|
2768
2859
|
this.setCursor("");
|
|
2769
2860
|
}
|
|
2861
|
+
restoreReconnectingEndpoint() {
|
|
2862
|
+
if (!this.reconnectingEdge || !this.originalEdgeEndpoint || !this.reconnectingEndpoint) return;
|
|
2863
|
+
if (this.reconnectingEndpoint === "start") this.reconnectingEdge.from = this.originalEdgeEndpoint;
|
|
2864
|
+
else this.reconnectingEdge.to = this.originalEdgeEndpoint;
|
|
2865
|
+
}
|
|
2770
2866
|
/**
|
|
2771
2867
|
* Render anchors when hovering a node (not connecting)
|
|
2772
2868
|
*/
|
|
@@ -6058,12 +6154,18 @@ var BezierPathStrategy = class {
|
|
|
6058
6154
|
hitTest(point, path, tolerance) {
|
|
6059
6155
|
if (path.length < 4) return false;
|
|
6060
6156
|
const samples = 20;
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6157
|
+
for (let i = 1; i + 2 < path.length; i += 3) {
|
|
6158
|
+
const start = path[i - 1];
|
|
6159
|
+
const control1 = path[i];
|
|
6160
|
+
const control2 = path[i + 1];
|
|
6161
|
+
const end = path[i + 2];
|
|
6162
|
+
let previous = start;
|
|
6163
|
+
for (let sample = 1; sample <= samples; sample++) {
|
|
6164
|
+
const current = bezierPoint(start, control1, control2, end, sample / samples);
|
|
6165
|
+
if (distanceToSegment(point, previous, current) <= tolerance) return true;
|
|
6166
|
+
previous = current;
|
|
6167
|
+
}
|
|
6065
6168
|
}
|
|
6066
|
-
for (let i = 1; i < points.length; i++) if (distanceToSegment(point, points[i - 1], points[i]) <= tolerance) return true;
|
|
6067
6169
|
return false;
|
|
6068
6170
|
}
|
|
6069
6171
|
};
|
|
@@ -7730,6 +7832,13 @@ var InteractionManager = class {
|
|
|
7730
7832
|
}
|
|
7731
7833
|
destroy() {
|
|
7732
7834
|
this.labelEditor.finish(false);
|
|
7835
|
+
this.propertyChangeBatcher.flush();
|
|
7836
|
+
this.dragManager.cancelDrag();
|
|
7837
|
+
this.resizeManager.cancelResize();
|
|
7838
|
+
this.connectionManager.cancelAll();
|
|
7839
|
+
this.selectionManager.cancelSelectionRect();
|
|
7840
|
+
this.navigationManager.destroy();
|
|
7841
|
+
this.resumeEditHover();
|
|
7733
7842
|
if (this.overlayDragSession) {
|
|
7734
7843
|
this.renderer.endOverlayDrag(this.overlayDragSession);
|
|
7735
7844
|
this.overlayDragSession = null;
|
|
@@ -8471,6 +8580,15 @@ var AnimationManager = class {
|
|
|
8471
8580
|
hasActive() {
|
|
8472
8581
|
return this.enter.size > 0 || this.exit.size > 0 || this.highlight.size > 0;
|
|
8473
8582
|
}
|
|
8583
|
+
/**
|
|
8584
|
+
* Drop all in-flight enter/exit/highlight timers without running exit callbacks.
|
|
8585
|
+
* Used by {@link DiagramRenderer.clear} so removed ids do not keep the render loop dirty.
|
|
8586
|
+
*/
|
|
8587
|
+
clear() {
|
|
8588
|
+
this.enter.clear();
|
|
8589
|
+
this.exit.clear();
|
|
8590
|
+
this.highlight.clear();
|
|
8591
|
+
}
|
|
8474
8592
|
update(now = performance.now()) {
|
|
8475
8593
|
if (!this.options.enabled) return false;
|
|
8476
8594
|
let changed = false;
|
|
@@ -9398,6 +9516,7 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9398
9516
|
this._contentDirty = true;
|
|
9399
9517
|
this._contentRevision = 0;
|
|
9400
9518
|
this._contentBoundsCache = null;
|
|
9519
|
+
this._edgeIndexEndpoints = /* @__PURE__ */ new Map();
|
|
9401
9520
|
this._destroyed = false;
|
|
9402
9521
|
this._contextLost = false;
|
|
9403
9522
|
this._canvasRect = null;
|
|
@@ -9754,10 +9873,12 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9754
9873
|
*/
|
|
9755
9874
|
addEdge(edge) {
|
|
9756
9875
|
edge.setDirtyListener(() => this.markDirty());
|
|
9757
|
-
edge.setBindingListener(() =>
|
|
9876
|
+
edge.setBindingListener(() => {
|
|
9877
|
+
this.syncEdgeNodeIndex(edge);
|
|
9878
|
+
this.markContentDirty();
|
|
9879
|
+
});
|
|
9758
9880
|
this._edges.set(edge.id, edge);
|
|
9759
|
-
|
|
9760
|
-
if (edge.to.nodeId) this._updateEdgeIndex(edge.id, edge.to.nodeId, true);
|
|
9881
|
+
this.syncEdgeNodeIndex(edge);
|
|
9761
9882
|
this.animationManager.registerEnter(edge.id);
|
|
9762
9883
|
this.markContentDirty();
|
|
9763
9884
|
this.emit("edgeAdd", edge);
|
|
@@ -9778,6 +9899,22 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9778
9899
|
}
|
|
9779
9900
|
}
|
|
9780
9901
|
/**
|
|
9902
|
+
* Keep `_nodeEdgeIndex` accurate when an edge is added or its from/to node ids change.
|
|
9903
|
+
*/
|
|
9904
|
+
syncEdgeNodeIndex(edge) {
|
|
9905
|
+
const prev = this._edgeIndexEndpoints.get(edge.id);
|
|
9906
|
+
const nextFrom = edge.from.nodeId;
|
|
9907
|
+
const nextTo = edge.to.nodeId;
|
|
9908
|
+
if (prev?.from !== void 0 && prev.from !== nextFrom) this._updateEdgeIndex(edge.id, prev.from, false);
|
|
9909
|
+
if (prev?.to !== void 0 && prev.to !== nextTo) this._updateEdgeIndex(edge.id, prev.to, false);
|
|
9910
|
+
if (nextFrom !== void 0 && nextFrom !== prev?.from) this._updateEdgeIndex(edge.id, nextFrom, true);
|
|
9911
|
+
if (nextTo !== void 0 && nextTo !== prev?.to) this._updateEdgeIndex(edge.id, nextTo, true);
|
|
9912
|
+
this._edgeIndexEndpoints.set(edge.id, {
|
|
9913
|
+
from: nextFrom,
|
|
9914
|
+
to: nextTo
|
|
9915
|
+
});
|
|
9916
|
+
}
|
|
9917
|
+
/**
|
|
9781
9918
|
* Remove an edge from the diagram
|
|
9782
9919
|
*/
|
|
9783
9920
|
removeEdge(edgeId) {
|
|
@@ -9813,8 +9950,12 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9813
9950
|
if (other.from.edgeId === edgeId || other.to.edgeId === edgeId) dependents.push(other.id);
|
|
9814
9951
|
}
|
|
9815
9952
|
for (const dependentId of dependents) this.removeEdgeImmediate(dependentId);
|
|
9816
|
-
|
|
9817
|
-
if (
|
|
9953
|
+
const indexed = this._edgeIndexEndpoints.get(edgeId);
|
|
9954
|
+
if (indexed?.from !== void 0) this._updateEdgeIndex(edgeId, indexed.from, false);
|
|
9955
|
+
else if (edge.from.nodeId) this._updateEdgeIndex(edgeId, edge.from.nodeId, false);
|
|
9956
|
+
if (indexed?.to !== void 0) this._updateEdgeIndex(edgeId, indexed.to, false);
|
|
9957
|
+
else if (edge.to.nodeId) this._updateEdgeIndex(edgeId, edge.to.nodeId, false);
|
|
9958
|
+
this._edgeIndexEndpoints.delete(edgeId);
|
|
9818
9959
|
this._edges.delete(edgeId);
|
|
9819
9960
|
edge.setDirtyListener(void 0);
|
|
9820
9961
|
edge.setBindingListener(void 0);
|
|
@@ -9984,19 +10125,25 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
9984
10125
|
this._edges.clear();
|
|
9985
10126
|
this._groups.clear();
|
|
9986
10127
|
this._nodeEdgeIndex.clear();
|
|
10128
|
+
this._edgeIndexEndpoints.clear();
|
|
9987
10129
|
}
|
|
9988
10130
|
/**
|
|
9989
10131
|
* Clear all elements from the diagram
|
|
9990
10132
|
*/
|
|
9991
10133
|
clear() {
|
|
9992
10134
|
for (const node of this._nodes.values()) node.setDirtyListener(void 0);
|
|
9993
|
-
for (const edge of this._edges.values())
|
|
10135
|
+
for (const edge of this._edges.values()) {
|
|
10136
|
+
edge.setDirtyListener(void 0);
|
|
10137
|
+
edge.setBindingListener(void 0);
|
|
10138
|
+
}
|
|
9994
10139
|
for (const group of this._groups.values()) group.setDirtyListener(void 0);
|
|
9995
10140
|
this._nodes.clear();
|
|
9996
10141
|
this._edges.clear();
|
|
9997
10142
|
this._groups.clear();
|
|
9998
10143
|
this._nodeEdgeIndex.clear();
|
|
9999
|
-
this.
|
|
10144
|
+
this._edgeIndexEndpoints.clear();
|
|
10145
|
+
this.animationManager.clear();
|
|
10146
|
+
this.markContentDirty();
|
|
10000
10147
|
}
|
|
10001
10148
|
nativePixelRatio() {
|
|
10002
10149
|
return this.options.retina ? window.devicePixelRatio || 1 : 1;
|
|
@@ -10227,6 +10374,7 @@ var SearchManager = class {
|
|
|
10227
10374
|
this.overlayCleanup = this.renderer.addOverlayRenderer((ctx) => this.renderHighlights(ctx));
|
|
10228
10375
|
}
|
|
10229
10376
|
destroy() {
|
|
10377
|
+
if (this.filterActive) this.clearFilter();
|
|
10230
10378
|
this.overlayCleanup?.();
|
|
10231
10379
|
this.overlayCleanup = null;
|
|
10232
10380
|
this.highlightIds.clear();
|
|
@@ -12015,7 +12163,7 @@ var Group = class Group extends Element {
|
|
|
12015
12163
|
}
|
|
12016
12164
|
applyStyleManager(styleManager) {
|
|
12017
12165
|
const mergedStyle = {
|
|
12018
|
-
...styleManager.getGroupStyle(
|
|
12166
|
+
...styleManager.getGroupStyle(this._state === "selected", this._styleClass),
|
|
12019
12167
|
...this._style
|
|
12020
12168
|
};
|
|
12021
12169
|
if (!shallowEqual(this._groupStyle, mergedStyle)) {
|
|
@@ -12123,7 +12271,6 @@ var Group = class Group extends Element {
|
|
|
12123
12271
|
ctx.fillText(this._label, x + 8, y + 4);
|
|
12124
12272
|
}
|
|
12125
12273
|
ctx.globalAlpha = 1;
|
|
12126
|
-
for (const child of this._children) if (child.visible) child.render(ctx);
|
|
12127
12274
|
}
|
|
12128
12275
|
getBounds() {
|
|
12129
12276
|
return {
|
|
@@ -12260,11 +12407,12 @@ var CircleNode = class extends Node {
|
|
|
12260
12407
|
};
|
|
12261
12408
|
}
|
|
12262
12409
|
getLabelContainerBounds(bounds) {
|
|
12263
|
-
const
|
|
12264
|
-
const
|
|
12410
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
12411
|
+
const width = insetBounds.width / Math.SQRT2;
|
|
12412
|
+
const height = insetBounds.height / Math.SQRT2;
|
|
12265
12413
|
return {
|
|
12266
|
-
x:
|
|
12267
|
-
y:
|
|
12414
|
+
x: insetBounds.x + (insetBounds.width - width) / 2,
|
|
12415
|
+
y: insetBounds.y + (insetBounds.height - height) / 2,
|
|
12268
12416
|
width,
|
|
12269
12417
|
height
|
|
12270
12418
|
};
|
|
@@ -12421,11 +12569,12 @@ var DiamondNode = class extends Node {
|
|
|
12421
12569
|
};
|
|
12422
12570
|
}
|
|
12423
12571
|
getLabelContainerBounds(bounds) {
|
|
12424
|
-
const
|
|
12425
|
-
const
|
|
12572
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
12573
|
+
const width = insetBounds.width / 2;
|
|
12574
|
+
const height = insetBounds.height / 2;
|
|
12426
12575
|
return {
|
|
12427
|
-
x:
|
|
12428
|
-
y:
|
|
12576
|
+
x: insetBounds.x + (insetBounds.width - width) / 2,
|
|
12577
|
+
y: insetBounds.y + (insetBounds.height - height) / 2,
|
|
12429
12578
|
width,
|
|
12430
12579
|
height
|
|
12431
12580
|
};
|
|
@@ -13443,16 +13592,33 @@ function flexLayout(container, config, children) {
|
|
|
13443
13592
|
finalSizes[i] = finalSizes[i] + remaining * child.flexGrow / totalGrow;
|
|
13444
13593
|
}
|
|
13445
13594
|
} else if (remaining < 0) {
|
|
13446
|
-
|
|
13447
|
-
for (let i = 0; i < children.length; i++)
|
|
13448
|
-
|
|
13449
|
-
const
|
|
13450
|
-
|
|
13595
|
+
const active = /* @__PURE__ */ new Set();
|
|
13596
|
+
for (let i = 0; i < children.length; i++) {
|
|
13597
|
+
const child = children[i];
|
|
13598
|
+
const minMain = isRow ? child.minSize.width : child.minSize.height;
|
|
13599
|
+
if (child.flexShrink > 0 && finalSizes[i] > minMain) active.add(i);
|
|
13600
|
+
}
|
|
13601
|
+
let deficit = -remaining;
|
|
13602
|
+
const epsilon = 1e-9;
|
|
13603
|
+
while (deficit > epsilon && active.size > 0) {
|
|
13604
|
+
let totalShrink = 0;
|
|
13605
|
+
for (const i of active) totalShrink += children[i].flexShrink * baseSizes[i];
|
|
13606
|
+
if (totalShrink <= 0) break;
|
|
13607
|
+
let distributed = 0;
|
|
13608
|
+
for (const i of active) {
|
|
13451
13609
|
const child = children[i];
|
|
13452
13610
|
const shrinkRatio = child.flexShrink * baseSizes[i] / totalShrink;
|
|
13453
13611
|
const minMain = isRow ? child.minSize.width : child.minSize.height;
|
|
13454
|
-
|
|
13612
|
+
const shrink = Math.min(deficit * shrinkRatio, finalSizes[i] - minMain);
|
|
13613
|
+
finalSizes[i] = finalSizes[i] - shrink;
|
|
13614
|
+
distributed += shrink;
|
|
13615
|
+
if (finalSizes[i] - minMain <= epsilon) {
|
|
13616
|
+
finalSizes[i] = minMain;
|
|
13617
|
+
active.delete(i);
|
|
13618
|
+
}
|
|
13455
13619
|
}
|
|
13620
|
+
if (distributed <= epsilon) break;
|
|
13621
|
+
deficit -= distributed;
|
|
13456
13622
|
}
|
|
13457
13623
|
}
|
|
13458
13624
|
let actualTotal = totalGaps + totalMargins;
|
|
@@ -14141,13 +14307,24 @@ var CompositeNode = class extends Node {
|
|
|
14141
14307
|
const fillOpacity = style.fillOpacity ?? 1;
|
|
14142
14308
|
const strokeOpacity = style.strokeOpacity ?? 1;
|
|
14143
14309
|
this.applyStyle(ctx);
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14310
|
+
if (this._shapeType === "custom" && this._pathFactory) {
|
|
14311
|
+
const path = this._pathFactory(width, height);
|
|
14312
|
+
ctx.save();
|
|
14313
|
+
ctx.translate(x, y);
|
|
14314
|
+
ctx.globalAlpha = baseOpacity * fillOpacity;
|
|
14315
|
+
ctx.fill(path);
|
|
14316
|
+
ctx.globalAlpha = baseOpacity * strokeOpacity;
|
|
14317
|
+
ctx.stroke(path);
|
|
14318
|
+
ctx.restore();
|
|
14319
|
+
} else {
|
|
14320
|
+
ctx.beginPath();
|
|
14321
|
+
this.buildShapePath(ctx, x, y, width, height);
|
|
14322
|
+
ctx.closePath();
|
|
14323
|
+
ctx.globalAlpha = baseOpacity * fillOpacity;
|
|
14324
|
+
ctx.fill();
|
|
14325
|
+
ctx.globalAlpha = baseOpacity * strokeOpacity;
|
|
14326
|
+
ctx.stroke();
|
|
14327
|
+
}
|
|
14151
14328
|
ctx.globalAlpha = 1;
|
|
14152
14329
|
this.renderContents(ctx);
|
|
14153
14330
|
}
|
|
@@ -14169,15 +14346,6 @@ var CompositeNode = class extends Node {
|
|
|
14169
14346
|
break;
|
|
14170
14347
|
}
|
|
14171
14348
|
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
14349
|
this.buildRectPath(ctx, x, y, w, h);
|
|
14182
14350
|
break;
|
|
14183
14351
|
default:
|
|
@@ -14215,35 +14383,40 @@ var CompositeNode = class extends Node {
|
|
|
14215
14383
|
}
|
|
14216
14384
|
applyAutoSize(ctx) {
|
|
14217
14385
|
const contentSize = this._content.measure(ctx);
|
|
14218
|
-
const
|
|
14386
|
+
const bounds = this.getBounds();
|
|
14387
|
+
const inset = resolveContentInset(this.contentInset, this.contentInsetScale, {
|
|
14388
|
+
width: bounds.width,
|
|
14389
|
+
height: bounds.height
|
|
14390
|
+
}, this.contentInsetBaseSize);
|
|
14219
14391
|
const neededWidth = Math.max(this._minWidth, contentSize.width + inset.left + inset.right);
|
|
14220
14392
|
const neededHeight = Math.max(this._minHeight, contentSize.height + inset.top + inset.bottom);
|
|
14221
14393
|
if (neededWidth > this._width) this._width = neededWidth;
|
|
14222
14394
|
if (neededHeight > this._height) this._height = neededHeight;
|
|
14223
14395
|
}
|
|
14224
14396
|
getLabelContainerBounds(bounds) {
|
|
14397
|
+
const insetBounds = super.getLabelContainerBounds(bounds);
|
|
14225
14398
|
if (this._shapeType === "circle") {
|
|
14226
14399
|
const factor = 1 / Math.SQRT2;
|
|
14227
|
-
const w =
|
|
14228
|
-
const h =
|
|
14400
|
+
const w = insetBounds.width * factor;
|
|
14401
|
+
const h = insetBounds.height * factor;
|
|
14229
14402
|
return {
|
|
14230
|
-
x:
|
|
14231
|
-
y:
|
|
14403
|
+
x: insetBounds.x + (insetBounds.width - w) / 2,
|
|
14404
|
+
y: insetBounds.y + (insetBounds.height - h) / 2,
|
|
14232
14405
|
width: w,
|
|
14233
14406
|
height: h
|
|
14234
14407
|
};
|
|
14235
14408
|
}
|
|
14236
14409
|
if (this._shapeType === "diamond") {
|
|
14237
|
-
const w =
|
|
14238
|
-
const h =
|
|
14410
|
+
const w = insetBounds.width / 2;
|
|
14411
|
+
const h = insetBounds.height / 2;
|
|
14239
14412
|
return {
|
|
14240
|
-
x:
|
|
14241
|
-
y:
|
|
14413
|
+
x: insetBounds.x + (insetBounds.width - w) / 2,
|
|
14414
|
+
y: insetBounds.y + (insetBounds.height - h) / 2,
|
|
14242
14415
|
width: w,
|
|
14243
14416
|
height: h
|
|
14244
14417
|
};
|
|
14245
14418
|
}
|
|
14246
|
-
return
|
|
14419
|
+
return insetBounds;
|
|
14247
14420
|
}
|
|
14248
14421
|
/**
|
|
14249
14422
|
* Get the minimum content size needed for the content tree.
|
|
@@ -14451,16 +14624,22 @@ var DARK_THEME = {
|
|
|
14451
14624
|
}
|
|
14452
14625
|
}
|
|
14453
14626
|
};
|
|
14627
|
+
function cloneTheme(theme) {
|
|
14628
|
+
return structuredClone(theme);
|
|
14629
|
+
}
|
|
14454
14630
|
/**
|
|
14455
14631
|
* Manages styles and themes
|
|
14456
14632
|
*/
|
|
14457
14633
|
var StyleManager = class {
|
|
14458
14634
|
constructor(theme) {
|
|
14459
14635
|
this._classes = /* @__PURE__ */ new Map();
|
|
14460
|
-
this._builtInThemes = new Map([["default", DEFAULT_THEME], ["dark", DARK_THEME]]);
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14636
|
+
this._builtInThemes = new Map([["default", cloneTheme(DEFAULT_THEME)], ["dark", cloneTheme(DARK_THEME)]]);
|
|
14637
|
+
const initialTheme = theme ?? "default";
|
|
14638
|
+
if (typeof initialTheme === "string") {
|
|
14639
|
+
const registeredTheme = this._builtInThemes.get(initialTheme);
|
|
14640
|
+
if (registeredTheme === void 0) throw new Error(`Unknown theme: ${initialTheme}`);
|
|
14641
|
+
this._theme = cloneTheme(registeredTheme);
|
|
14642
|
+
} else this._theme = cloneTheme(initialTheme);
|
|
14464
14643
|
}
|
|
14465
14644
|
/**
|
|
14466
14645
|
* Current theme
|
|
@@ -14474,14 +14653,15 @@ var StyleManager = class {
|
|
|
14474
14653
|
setTheme(theme) {
|
|
14475
14654
|
if (typeof theme === "string") {
|
|
14476
14655
|
const builtIn = this._builtInThemes.get(theme);
|
|
14477
|
-
if (builtIn
|
|
14478
|
-
|
|
14656
|
+
if (builtIn === void 0) throw new Error(`Unknown theme: ${theme}`);
|
|
14657
|
+
this._theme = cloneTheme(builtIn);
|
|
14658
|
+
} else this._theme = cloneTheme(theme);
|
|
14479
14659
|
}
|
|
14480
14660
|
/**
|
|
14481
14661
|
* Register a custom theme
|
|
14482
14662
|
*/
|
|
14483
14663
|
registerTheme(theme) {
|
|
14484
|
-
this._builtInThemes.set(theme.name, theme);
|
|
14664
|
+
this._builtInThemes.set(theme.name, cloneTheme(theme));
|
|
14485
14665
|
}
|
|
14486
14666
|
/**
|
|
14487
14667
|
* Get all available theme names
|
|
@@ -14596,10 +14776,17 @@ var StyleManager = class {
|
|
|
14596
14776
|
}
|
|
14597
14777
|
}
|
|
14598
14778
|
getBaseEdgeStyle(state) {
|
|
14779
|
+
const defaultStyle = this._theme.edge.default;
|
|
14599
14780
|
switch (state) {
|
|
14600
|
-
case "hover": return
|
|
14601
|
-
|
|
14602
|
-
|
|
14781
|
+
case "hover": return {
|
|
14782
|
+
...defaultStyle,
|
|
14783
|
+
...this._theme.edge.hover
|
|
14784
|
+
};
|
|
14785
|
+
case "selected": return {
|
|
14786
|
+
...defaultStyle,
|
|
14787
|
+
...this._theme.edge.selected
|
|
14788
|
+
};
|
|
14789
|
+
default: return defaultStyle;
|
|
14603
14790
|
}
|
|
14604
14791
|
}
|
|
14605
14792
|
};
|
|
@@ -14771,36 +14958,7 @@ var Serializer = class {
|
|
|
14771
14958
|
position: port.position,
|
|
14772
14959
|
styleClass: port.styleClass
|
|
14773
14960
|
}));
|
|
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
|
-
}
|
|
14961
|
+
const label = node.label ? this.serializeTextLabel(node.label) : void 0;
|
|
14804
14962
|
let icon;
|
|
14805
14963
|
if (node.icon) {
|
|
14806
14964
|
const opts = node.icon.options;
|
|
@@ -14833,6 +14991,8 @@ var Serializer = class {
|
|
|
14833
14991
|
if (hasNonDefaultValues(anchorData, anchorDefaults)) anchorPoints = omitDefaultValues(anchorData, anchorDefaults);
|
|
14834
14992
|
const contentInset = node.contentInset;
|
|
14835
14993
|
const hasContentInset = contentInset.top !== 0 || contentInset.right !== 0 || contentInset.bottom !== 0 || contentInset.left !== 0;
|
|
14994
|
+
const contentInsetScale = node.contentInsetScale;
|
|
14995
|
+
const hasContentInsetScale = Object.keys(contentInsetScale).length > 0;
|
|
14836
14996
|
const base = omitEmptyValues({
|
|
14837
14997
|
id: node.id,
|
|
14838
14998
|
type: node.typeName,
|
|
@@ -14840,14 +15000,16 @@ var Serializer = class {
|
|
|
14840
15000
|
y: node.y,
|
|
14841
15001
|
width: node.width,
|
|
14842
15002
|
height: node.height,
|
|
14843
|
-
style: node.
|
|
15003
|
+
style: { ...node.styleOverrides },
|
|
14844
15004
|
styleClass: node.styleClass,
|
|
14845
15005
|
label,
|
|
14846
|
-
labelStyleClass: typeof label === "string" ? node.label?.styleClass : void 0,
|
|
14847
15006
|
labelPlacement: node.labelPlacement !== "center" ? node.labelPlacement : void 0,
|
|
14848
15007
|
labelGap: node.labelGap !== 4 ? node.labelGap : void 0,
|
|
14849
15008
|
icon,
|
|
14850
15009
|
contentInset: hasContentInset ? contentInset : void 0,
|
|
15010
|
+
contentInsetScale: hasContentInsetScale ? contentInsetScale : void 0,
|
|
15011
|
+
contentInsetBaseSize: node.contentInsetBaseSize,
|
|
15012
|
+
badges: node.badges.length > 0 ? node.badges.map((badge) => ({ ...badge })) : void 0,
|
|
14851
15013
|
anchorPoints,
|
|
14852
15014
|
ports: ports.length > 0 ? ports : void 0,
|
|
14853
15015
|
data: Object.keys(node.data).length > 0 ? node.data : void 0
|
|
@@ -14874,14 +15036,14 @@ var Serializer = class {
|
|
|
14874
15036
|
arrowType: edge.arrowType,
|
|
14875
15037
|
startMarker: edge.startMarker,
|
|
14876
15038
|
endMarker: edge.endMarker,
|
|
14877
|
-
style: edge.
|
|
15039
|
+
style: { ...edge.styleOverrides },
|
|
14878
15040
|
styleClass: edge.styleClass,
|
|
14879
|
-
label: edge.label
|
|
14880
|
-
labelStyleClass: edge.label?.styleClass,
|
|
15041
|
+
label: edge.label ? this.serializeTextLabel(edge.label) : void 0,
|
|
14881
15042
|
labelOffset: edge.labelOffset !== 0 ? edge.labelOffset : void 0,
|
|
14882
15043
|
labelPosition: edge.labelPosition !== .5 ? edge.labelPosition : void 0,
|
|
14883
15044
|
labelFollowPath: edge.labelFollowPath ? true : void 0,
|
|
14884
15045
|
labelBackground: edge.labelBackground,
|
|
15046
|
+
lockAnchors: edge.lockAnchors ? void 0 : false,
|
|
14885
15047
|
labelLineGap: edge.labelLineGap ? true : void 0,
|
|
14886
15048
|
data: Object.keys(edge.data).length > 0 ? edge.data : void 0
|
|
14887
15049
|
};
|
|
@@ -14890,12 +15052,31 @@ var Serializer = class {
|
|
|
14890
15052
|
return {
|
|
14891
15053
|
id: group.id,
|
|
14892
15054
|
childIds: group.children.map((c) => c.id),
|
|
14893
|
-
style: group.
|
|
15055
|
+
style: { ...group.styleOverrides },
|
|
14894
15056
|
styleClass: group.styleClass,
|
|
14895
15057
|
label: group.label,
|
|
15058
|
+
padding: group.padding !== 20 ? group.padding : void 0,
|
|
14896
15059
|
data: Object.keys(group.data).length > 0 ? group.data : void 0
|
|
14897
15060
|
};
|
|
14898
15061
|
}
|
|
15062
|
+
serializeTextLabel(label) {
|
|
15063
|
+
const inset = label.inset;
|
|
15064
|
+
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 : {
|
|
15065
|
+
top: inset.top,
|
|
15066
|
+
right: inset.right,
|
|
15067
|
+
bottom: inset.bottom,
|
|
15068
|
+
left: inset.left
|
|
15069
|
+
};
|
|
15070
|
+
const style = label.styleOverrides;
|
|
15071
|
+
if (!(Object.keys(style).length > 0 || label.maxWidth !== void 0 || serializedInset !== void 0 || label.styleClass !== void 0)) return label.text;
|
|
15072
|
+
return omitEmptyValues({
|
|
15073
|
+
text: label.text,
|
|
15074
|
+
style: Object.keys(style).length > 0 ? style : void 0,
|
|
15075
|
+
maxWidth: label.maxWidth,
|
|
15076
|
+
inset: serializedInset,
|
|
15077
|
+
styleClass: label.styleClass
|
|
15078
|
+
});
|
|
15079
|
+
}
|
|
14899
15080
|
};
|
|
14900
15081
|
//#endregion
|
|
14901
15082
|
//#region src/utils/download.ts
|
|
@@ -14977,7 +15158,11 @@ var ImageExporter = class {
|
|
|
14977
15158
|
const padding = options.padding ?? 20;
|
|
14978
15159
|
const format = options.format ?? "png";
|
|
14979
15160
|
const quality = options.quality ?? .92;
|
|
14980
|
-
const bounds = getContentBounds({
|
|
15161
|
+
const bounds = getContentBounds({
|
|
15162
|
+
nodes: this.renderer.nodes.values(),
|
|
15163
|
+
edges: this.renderer.edges.values(),
|
|
15164
|
+
groups: this.renderer.groups.values()
|
|
15165
|
+
});
|
|
14981
15166
|
if (bounds === null) return this.createBlankImage(100, 100, backgroundColor, format, quality);
|
|
14982
15167
|
const width = (bounds.width + padding * 2) * scale;
|
|
14983
15168
|
const height = (bounds.height + padding * 2) * scale;
|
|
@@ -15029,6 +15214,7 @@ var SvgExporter = class {
|
|
|
15029
15214
|
const padding = options.padding ?? 20;
|
|
15030
15215
|
const includeBackground = options.includeBackground ?? true;
|
|
15031
15216
|
const backgroundColor = options.backgroundColor ?? "#ffffff";
|
|
15217
|
+
const escapedBackgroundColor = this.escapeAttribute(backgroundColor);
|
|
15032
15218
|
const edgeLabelOffset = options.edgeLabelOffset;
|
|
15033
15219
|
const bounds = getContentBounds({
|
|
15034
15220
|
nodes: this.renderer.nodes.values(),
|
|
@@ -15045,7 +15231,7 @@ var SvgExporter = class {
|
|
|
15045
15231
|
const renderedEdges = [];
|
|
15046
15232
|
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
15233
|
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="${
|
|
15234
|
+
if (includeBackground) parts.push(`<rect width="100%" height="100%" fill="${escapedBackgroundColor}"/>`);
|
|
15049
15235
|
parts.push(`<g transform="translate(${offsetX}, ${offsetY})">`);
|
|
15050
15236
|
for (const group of this.renderer.groups.values()) if (group.visible) parts.push(this.renderGroup(group));
|
|
15051
15237
|
for (const node of this.renderer.nodes.values()) if (node.visible) parts.push(this.renderNode(node));
|
|
@@ -15064,8 +15250,8 @@ var SvgExporter = class {
|
|
|
15064
15250
|
renderGroup(group) {
|
|
15065
15251
|
const bounds = group.getBounds();
|
|
15066
15252
|
const style = group.style;
|
|
15067
|
-
const fill = style.fillColor ?? "rgba(200, 200, 200, 0.2)";
|
|
15068
|
-
const stroke = style.strokeColor ?? "#999999";
|
|
15253
|
+
const fill = this.escapeAttribute(style.fillColor ?? "rgba(200, 200, 200, 0.2)");
|
|
15254
|
+
const stroke = this.escapeAttribute(style.strokeColor ?? "#999999");
|
|
15069
15255
|
const strokeWidth = style.strokeWidth ?? 1;
|
|
15070
15256
|
const opacity = style.opacity ?? 1;
|
|
15071
15257
|
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 +15265,8 @@ var SvgExporter = class {
|
|
|
15079
15265
|
renderNode(node) {
|
|
15080
15266
|
const bounds = node.getBounds();
|
|
15081
15267
|
const style = node.style;
|
|
15082
|
-
const fill = style.fillColor ?? "#ffffff";
|
|
15083
|
-
const stroke = style.strokeColor ?? "#333333";
|
|
15268
|
+
const fill = this.escapeAttribute(style.fillColor ?? "#ffffff");
|
|
15269
|
+
const stroke = this.escapeAttribute(style.strokeColor ?? "#333333");
|
|
15084
15270
|
const strokeWidth = style.strokeWidth ?? 2;
|
|
15085
15271
|
const baseOpacity = style.opacity ?? 1;
|
|
15086
15272
|
const fillOpacity = (style.fillOpacity ?? 1) * baseOpacity;
|
|
@@ -15177,14 +15363,15 @@ var SvgExporter = class {
|
|
|
15177
15363
|
if (edge.path.length < 2) return "";
|
|
15178
15364
|
const style = edge.style;
|
|
15179
15365
|
const stroke = style.strokeColor ?? "#666666";
|
|
15366
|
+
const escapedStroke = this.escapeAttribute(stroke);
|
|
15180
15367
|
const strokeWidth = style.strokeWidth ?? 2;
|
|
15181
15368
|
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}"` : "";
|
|
15369
|
+
const lineCap = style.lineCap ? ` stroke-linecap="${this.escapeAttribute(style.lineCap)}"` : "";
|
|
15370
|
+
const lineJoin = style.lineJoin ? ` stroke-linejoin="${this.escapeAttribute(style.lineJoin)}"` : "";
|
|
15184
15371
|
const dashValues = style.flowDash ?? style.lineDash;
|
|
15185
15372
|
const dash = dashValues ? ` stroke-dasharray="${dashValues.join(" ")}"` : "";
|
|
15186
15373
|
const dashOffset = style.lineDashOffset !== void 0 ? ` stroke-dashoffset="${style.lineDashOffset}"` : "";
|
|
15187
|
-
return `<path d="${this.buildEdgePath(edge, edgeLabelOffset)}" fill="none" stroke="${
|
|
15374
|
+
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
15375
|
}
|
|
15189
15376
|
renderEdgeLabel(edge, edgeLabelOffset) {
|
|
15190
15377
|
if (!edge.label) return "";
|
|
@@ -15196,7 +15383,7 @@ var SvgExporter = class {
|
|
|
15196
15383
|
renderEdgeLabelBackground(edge, point, rotation = 0) {
|
|
15197
15384
|
if (!edge.label) return "";
|
|
15198
15385
|
const metrics = this.measureTextLabel(edge.label.text, edge.label.style, edge.label.inset);
|
|
15199
|
-
const bgColor = edge.labelBackground?.color ?? "#ffffff";
|
|
15386
|
+
const bgColor = this.escapeAttribute(edge.labelBackground?.color ?? "#ffffff");
|
|
15200
15387
|
const bgOpacity = edge.labelBackground?.opacity ?? 1;
|
|
15201
15388
|
const bgRadius = edge.labelBackground?.borderRadius ?? 2;
|
|
15202
15389
|
const x = point.x - metrics.width / 2;
|
|
@@ -15277,7 +15464,11 @@ var SvgExporter = class {
|
|
|
15277
15464
|
return calculateMarkerPoints(edge.path, position, edge.type);
|
|
15278
15465
|
}
|
|
15279
15466
|
renderMarkerShape(marker, from, to, edgeStroke) {
|
|
15280
|
-
return generateSvgMarker(
|
|
15467
|
+
return generateSvgMarker({
|
|
15468
|
+
...marker,
|
|
15469
|
+
strokeColor: marker.strokeColor !== void 0 ? this.escapeAttribute(marker.strokeColor) : void 0,
|
|
15470
|
+
fillColor: marker.fillColor !== void 0 ? this.escapeAttribute(marker.fillColor) : void 0
|
|
15471
|
+
}, from, to, this.escapeAttribute(edgeStroke));
|
|
15281
15472
|
}
|
|
15282
15473
|
buildPath(edge) {
|
|
15283
15474
|
const path = edge.path;
|
|
@@ -15347,11 +15538,11 @@ var SvgExporter = class {
|
|
|
15347
15538
|
};
|
|
15348
15539
|
}
|
|
15349
15540
|
renderTextLabel(text, point, style = {}, rotation = 0) {
|
|
15350
|
-
const fill = style.color ?? "#000000";
|
|
15541
|
+
const fill = this.escapeAttribute(style.color ?? "#000000");
|
|
15351
15542
|
const fontSize = style.fontSize ?? 14;
|
|
15352
|
-
const fontFamily = style.fontFamily ?? "sans-serif";
|
|
15353
|
-
const fontWeight = style.fontWeight ?? "normal";
|
|
15354
|
-
const fontStyle = style.fontStyle ?? "normal";
|
|
15543
|
+
const fontFamily = this.escapeAttribute(style.fontFamily ?? "sans-serif");
|
|
15544
|
+
const fontWeight = this.escapeAttribute(style.fontWeight ?? "normal");
|
|
15545
|
+
const fontStyle = this.escapeAttribute(style.fontStyle ?? "normal");
|
|
15355
15546
|
const fontStyleAttr = fontStyle !== "normal" ? ` font-style="${fontStyle}"` : "";
|
|
15356
15547
|
const opacity = style.opacity ?? 1;
|
|
15357
15548
|
const anchor = style.align === "left" ? "start" : style.align === "right" ? "end" : "middle";
|
|
@@ -15547,9 +15738,10 @@ var SvgExporter = class {
|
|
|
15547
15738
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
15548
15739
|
}
|
|
15549
15740
|
createEmptySvg(width, height, backgroundColor, includeBackground) {
|
|
15741
|
+
const escapedBackgroundColor = this.escapeAttribute(backgroundColor);
|
|
15550
15742
|
return [
|
|
15551
15743
|
`<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="${
|
|
15744
|
+
includeBackground ? `<rect width="100%" height="100%" fill="${escapedBackgroundColor}"/>` : "",
|
|
15553
15745
|
"</svg>"
|
|
15554
15746
|
].join("");
|
|
15555
15747
|
}
|