spytial-core 1.4.20 → 1.4.22
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.
|
@@ -91338,9 +91338,6 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91338
91338
|
const bbox2 = hasgetBBox(element2) ? element2.getBBox() : { x: 0, y: 0, width: 0, height: 0 };
|
|
91339
91339
|
return !(bbox2.x > bbox1.x + bbox1.width || bbox2.x + bbox2.width < bbox1.x || bbox2.y > bbox1.y + bbox1.height || bbox2.y + bbox2.height < bbox1.y);
|
|
91340
91340
|
}
|
|
91341
|
-
function hasInnerBounds(target) {
|
|
91342
|
-
return target && typeof target === "object" && "innerBounds" in target;
|
|
91343
|
-
}
|
|
91344
91341
|
var d32, cola, DEFAULT_SCALE_FACTOR, _WebColaCnDGraph, WebColaCnDGraph;
|
|
91345
91342
|
var init_webcola_cnd_graph = __esm({
|
|
91346
91343
|
"src/translators/webcola/webcola-cnd-graph.ts"() {
|
|
@@ -91350,7 +91347,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91350
91347
|
cola = window.cola;
|
|
91351
91348
|
DEFAULT_SCALE_FACTOR = 5;
|
|
91352
91349
|
_WebColaCnDGraph = class _WebColaCnDGraph extends HTMLElement {
|
|
91353
|
-
constructor() {
|
|
91350
|
+
constructor(isInputAllowed = false) {
|
|
91354
91351
|
super();
|
|
91355
91352
|
// Reduced from 5 for performance, but kept at 1 for alignment
|
|
91356
91353
|
/**
|
|
@@ -91364,6 +91361,16 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91364
91361
|
edgesBetweenNodes: /* @__PURE__ */ new Map(),
|
|
91365
91362
|
alignmentEdges: /* @__PURE__ */ new Set()
|
|
91366
91363
|
};
|
|
91364
|
+
/**
|
|
91365
|
+
* Tracks whether the user has manually interacted with zoom/pan.
|
|
91366
|
+
* When true, we don't auto-fit the viewport to preserve user's view.
|
|
91367
|
+
*/
|
|
91368
|
+
this.userHasManuallyZoomed = false;
|
|
91369
|
+
/**
|
|
91370
|
+
* Tracks whether this is the initial render (first layout).
|
|
91371
|
+
* We always fit viewport on initial render.
|
|
91372
|
+
*/
|
|
91373
|
+
this.isInitialRender = true;
|
|
91367
91374
|
/**
|
|
91368
91375
|
* Stores the starting coordinates when a node begins dragging so
|
|
91369
91376
|
* drag end events can report both the previous and new positions.
|
|
@@ -91373,6 +91380,23 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91373
91380
|
* Input mode state management for edge creation and modification
|
|
91374
91381
|
*/
|
|
91375
91382
|
this.isInputModeActive = false;
|
|
91383
|
+
this.inputModeEnabled = true;
|
|
91384
|
+
this.inputModeListenersAttached = false;
|
|
91385
|
+
this.handleInputModeKeydown = (event) => {
|
|
91386
|
+
if ((event.metaKey || event.ctrlKey) && !this.isInputModeActive) {
|
|
91387
|
+
this.activateInputMode();
|
|
91388
|
+
}
|
|
91389
|
+
};
|
|
91390
|
+
this.handleInputModeKeyup = (event) => {
|
|
91391
|
+
if (!event.metaKey && !event.ctrlKey && this.isInputModeActive) {
|
|
91392
|
+
this.deactivateInputMode();
|
|
91393
|
+
}
|
|
91394
|
+
};
|
|
91395
|
+
this.handleInputModeBlur = () => {
|
|
91396
|
+
if (this.isInputModeActive) {
|
|
91397
|
+
this.deactivateInputMode();
|
|
91398
|
+
}
|
|
91399
|
+
};
|
|
91376
91400
|
this.edgeCreationState = {
|
|
91377
91401
|
isCreating: false,
|
|
91378
91402
|
sourceNode: null,
|
|
@@ -91395,6 +91419,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91395
91419
|
this.initializeDOM();
|
|
91396
91420
|
this.initializeD3();
|
|
91397
91421
|
this.lineFunction = d32.line().x((d) => d.x).y((d) => d.y).curve(d32.curveBasis);
|
|
91422
|
+
this.inputModeEnabled = isInputAllowed;
|
|
91398
91423
|
this.initializeInputModeHandlers();
|
|
91399
91424
|
}
|
|
91400
91425
|
/**
|
|
@@ -91689,6 +91714,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91689
91714
|
<div id="zoom-controls">
|
|
91690
91715
|
<button id="zoom-in" title="Zoom In" aria-label="Zoom in">+</button>
|
|
91691
91716
|
<button id="zoom-out" title="Zoom Out" aria-label="Zoom out">\u2212</button>
|
|
91717
|
+
<button id="zoom-fit" title="Fit to View" aria-label="Fit graph to view">\u2922</button>
|
|
91692
91718
|
</div>
|
|
91693
91719
|
</div>
|
|
91694
91720
|
<div id="svg-container">
|
|
@@ -91724,7 +91750,11 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91724
91750
|
this.svg = d32.select(this.shadowRoot.querySelector("#svg"));
|
|
91725
91751
|
this.container = this.svg.select(".zoomable");
|
|
91726
91752
|
if (d32.zoom) {
|
|
91727
|
-
this.zoomBehavior = d32.zoom().scaleExtent([0.01, 20]).on("
|
|
91753
|
+
this.zoomBehavior = d32.zoom().scaleExtent([0.01, 20]).on("start", () => {
|
|
91754
|
+
if (d32.event.sourceEvent) {
|
|
91755
|
+
this.userHasManuallyZoomed = true;
|
|
91756
|
+
}
|
|
91757
|
+
}).on("zoom", () => {
|
|
91728
91758
|
this.container.attr("transform", d32.event.transform);
|
|
91729
91759
|
this.updateZoomControlStates();
|
|
91730
91760
|
this.updateSmallNodeClasses();
|
|
@@ -91741,37 +91771,51 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91741
91771
|
initializeZoomControls() {
|
|
91742
91772
|
const zoomInButton = this.shadowRoot.querySelector("#zoom-in");
|
|
91743
91773
|
const zoomOutButton = this.shadowRoot.querySelector("#zoom-out");
|
|
91774
|
+
const zoomFitButton = this.shadowRoot.querySelector("#zoom-fit");
|
|
91744
91775
|
if (zoomInButton) {
|
|
91745
91776
|
zoomInButton.addEventListener("click", () => {
|
|
91777
|
+
this.userHasManuallyZoomed = true;
|
|
91746
91778
|
this.zoomIn();
|
|
91747
91779
|
});
|
|
91748
91780
|
}
|
|
91749
91781
|
if (zoomOutButton) {
|
|
91750
91782
|
zoomOutButton.addEventListener("click", () => {
|
|
91783
|
+
this.userHasManuallyZoomed = true;
|
|
91751
91784
|
this.zoomOut();
|
|
91752
91785
|
});
|
|
91753
91786
|
}
|
|
91787
|
+
if (zoomFitButton) {
|
|
91788
|
+
zoomFitButton.addEventListener("click", () => {
|
|
91789
|
+
this.resetViewToFitContent();
|
|
91790
|
+
});
|
|
91791
|
+
}
|
|
91754
91792
|
this.updateZoomControlStates();
|
|
91755
91793
|
}
|
|
91756
91794
|
/**
|
|
91757
91795
|
* Initialize keyboard event handlers for input mode activation
|
|
91758
91796
|
*/
|
|
91759
91797
|
initializeInputModeHandlers() {
|
|
91760
|
-
|
|
91761
|
-
|
|
91762
|
-
|
|
91763
|
-
|
|
91764
|
-
|
|
91765
|
-
|
|
91766
|
-
|
|
91767
|
-
|
|
91768
|
-
|
|
91769
|
-
|
|
91770
|
-
window.addEventListener("blur",
|
|
91771
|
-
|
|
91772
|
-
|
|
91773
|
-
|
|
91774
|
-
|
|
91798
|
+
if (this.inputModeEnabled) {
|
|
91799
|
+
this.attachInputModeListeners();
|
|
91800
|
+
}
|
|
91801
|
+
}
|
|
91802
|
+
attachInputModeListeners() {
|
|
91803
|
+
if (this.inputModeListenersAttached) {
|
|
91804
|
+
return;
|
|
91805
|
+
}
|
|
91806
|
+
document.addEventListener("keydown", this.handleInputModeKeydown);
|
|
91807
|
+
document.addEventListener("keyup", this.handleInputModeKeyup);
|
|
91808
|
+
window.addEventListener("blur", this.handleInputModeBlur);
|
|
91809
|
+
this.inputModeListenersAttached = true;
|
|
91810
|
+
}
|
|
91811
|
+
detachInputModeListeners() {
|
|
91812
|
+
if (!this.inputModeListenersAttached) {
|
|
91813
|
+
return;
|
|
91814
|
+
}
|
|
91815
|
+
document.removeEventListener("keydown", this.handleInputModeKeydown);
|
|
91816
|
+
document.removeEventListener("keyup", this.handleInputModeKeyup);
|
|
91817
|
+
window.removeEventListener("blur", this.handleInputModeBlur);
|
|
91818
|
+
this.inputModeListenersAttached = false;
|
|
91775
91819
|
}
|
|
91776
91820
|
/**
|
|
91777
91821
|
* Activate input mode for edge creation and modification
|
|
@@ -91899,6 +91943,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
91899
91943
|
*/
|
|
91900
91944
|
setupNodeDragHandlers(nodeDrag) {
|
|
91901
91945
|
nodeDrag.on("start.cnd", (d) => {
|
|
91946
|
+
this.userHasManuallyZoomed = true;
|
|
91902
91947
|
const start = { x: d.x, y: d.y };
|
|
91903
91948
|
this.dragStartPositions.set(d.id, start);
|
|
91904
91949
|
this.dispatchEvent(
|
|
@@ -92130,6 +92175,10 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
92130
92175
|
if (!isInstanceLayout(instanceLayout)) {
|
|
92131
92176
|
throw new Error("Invalid instance layout provided. Expected an InstanceLayout instance.");
|
|
92132
92177
|
}
|
|
92178
|
+
if (!options?.priorPositions) {
|
|
92179
|
+
this.isInitialRender = true;
|
|
92180
|
+
this.userHasManuallyZoomed = false;
|
|
92181
|
+
}
|
|
92133
92182
|
if (this.svg && this.zoomBehavior && d32) {
|
|
92134
92183
|
try {
|
|
92135
92184
|
const identity = d32.zoomIdentity;
|
|
@@ -92968,35 +93017,17 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
92968
93017
|
const targetGroup = potentialGroups.find((group) => group.keyNode === this.getNodeIndex(source));
|
|
92969
93018
|
if (targetGroup) {
|
|
92970
93019
|
target = targetGroup;
|
|
92971
|
-
if (hasInnerBounds(target)) {
|
|
92972
|
-
target.innerBounds = targetGroup.bounds?.inflate(-1 * (targetGroup.padding || 10));
|
|
92973
|
-
}
|
|
92974
|
-
} else {
|
|
92975
|
-
console.log("Target group not found", potentialGroups, this.getNodeIndex(target));
|
|
92976
93020
|
}
|
|
92977
93021
|
} else if (addSourceToGroup) {
|
|
92978
93022
|
const potentialGroups = this.getContainingGroups(this.currentLayout?.groups || [], source);
|
|
92979
93023
|
const sourceGroup = potentialGroups.find((group) => group.keyNode === this.getNodeIndex(target));
|
|
92980
93024
|
if (sourceGroup) {
|
|
92981
93025
|
source = sourceGroup;
|
|
92982
|
-
if (hasInnerBounds(source)) {
|
|
92983
|
-
source.innerBounds = sourceGroup.bounds?.inflate(-1 * (sourceGroup.padding || 10));
|
|
92984
|
-
}
|
|
92985
|
-
} else {
|
|
92986
|
-
console.log("Source group not found", potentialGroups, this.getNodeIndex(source));
|
|
92987
93026
|
}
|
|
92988
|
-
} else {
|
|
92989
|
-
console.log("This is a group edge (on tick), but neither source nor target is a group.", d);
|
|
92990
93027
|
}
|
|
92991
93028
|
}
|
|
92992
|
-
|
|
92993
|
-
|
|
92994
|
-
return this.lineFunction([route.sourceIntersection, route.arrowStart]);
|
|
92995
|
-
}
|
|
92996
|
-
return this.lineFunction([
|
|
92997
|
-
{ x: source.x || 0, y: source.y || 0 },
|
|
92998
|
-
{ x: target.x || 0, y: target.y || 0 }
|
|
92999
|
-
]);
|
|
93029
|
+
const route = this.getStableEdgePath(source, target);
|
|
93030
|
+
return this.lineFunction(route);
|
|
93000
93031
|
}).attr("marker-end", (d) => {
|
|
93001
93032
|
if (this.isAlignmentEdge(d)) return "none";
|
|
93002
93033
|
return "url(#end-arrow)";
|
|
@@ -93632,6 +93663,83 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
93632
93663
|
const closestY = Math.max(y, Math.min(point.y, Y));
|
|
93633
93664
|
return { x: closestX, y: closestY };
|
|
93634
93665
|
}
|
|
93666
|
+
/**
|
|
93667
|
+
* Calculates a stable anchor point on a rectangle's perimeter for edge drawing.
|
|
93668
|
+
* This method produces consistent, jitter-free anchor points by using the
|
|
93669
|
+
* center of the rectangle edge that faces the target point.
|
|
93670
|
+
*
|
|
93671
|
+
* Unlike intersection-based approaches that can jump erratically as rectangles
|
|
93672
|
+
* move, this method selects one of four edge centers (top, bottom, left, right)
|
|
93673
|
+
* based on the dominant direction to the target, producing smooth transitions.
|
|
93674
|
+
*
|
|
93675
|
+
* @param bounds - Rectangle bounds with x, y, X, Y properties (or cx(), cy(), width(), height() methods)
|
|
93676
|
+
* @param targetPoint - The point the edge is connecting to
|
|
93677
|
+
* @returns Stable anchor point on the rectangle's perimeter
|
|
93678
|
+
*/
|
|
93679
|
+
getStableEdgeAnchor(bounds, targetPoint) {
|
|
93680
|
+
if (!bounds) return targetPoint;
|
|
93681
|
+
let cx, cy, halfWidth, halfHeight;
|
|
93682
|
+
if (typeof bounds.cx === "function") {
|
|
93683
|
+
cx = bounds.cx();
|
|
93684
|
+
cy = bounds.cy();
|
|
93685
|
+
halfWidth = bounds.width() / 2;
|
|
93686
|
+
halfHeight = bounds.height() / 2;
|
|
93687
|
+
} else if (bounds.x !== void 0 && bounds.X !== void 0) {
|
|
93688
|
+
cx = (bounds.x + bounds.X) / 2;
|
|
93689
|
+
cy = (bounds.y + bounds.Y) / 2;
|
|
93690
|
+
halfWidth = (bounds.X - bounds.x) / 2;
|
|
93691
|
+
halfHeight = (bounds.Y - bounds.y) / 2;
|
|
93692
|
+
} else {
|
|
93693
|
+
return targetPoint;
|
|
93694
|
+
}
|
|
93695
|
+
const dx = targetPoint.x - cx;
|
|
93696
|
+
const dy = targetPoint.y - cy;
|
|
93697
|
+
const normalizedDx = Math.abs(dx) / halfWidth;
|
|
93698
|
+
const normalizedDy = Math.abs(dy) / halfHeight;
|
|
93699
|
+
if (normalizedDx > normalizedDy) {
|
|
93700
|
+
if (dx > 0) {
|
|
93701
|
+
return { x: cx + halfWidth, y: cy };
|
|
93702
|
+
} else {
|
|
93703
|
+
return { x: cx - halfWidth, y: cy };
|
|
93704
|
+
}
|
|
93705
|
+
} else {
|
|
93706
|
+
if (dy > 0) {
|
|
93707
|
+
return { x: cx, y: cy + halfHeight };
|
|
93708
|
+
} else {
|
|
93709
|
+
return { x: cx, y: cy - halfHeight };
|
|
93710
|
+
}
|
|
93711
|
+
}
|
|
93712
|
+
}
|
|
93713
|
+
/**
|
|
93714
|
+
* Calculates stable edge path points for drawing during tick/drag operations.
|
|
93715
|
+
* This method avoids jitter by using stable anchor points instead of
|
|
93716
|
+
* dynamic intersection calculations.
|
|
93717
|
+
*
|
|
93718
|
+
* @param source - Source node or group with bounds
|
|
93719
|
+
* @param target - Target node or group with bounds
|
|
93720
|
+
* @returns Array of two points for a simple line path
|
|
93721
|
+
*/
|
|
93722
|
+
getStableEdgePath(source, target) {
|
|
93723
|
+
let targetCenter;
|
|
93724
|
+
if (target.bounds && typeof target.bounds.cx === "function") {
|
|
93725
|
+
targetCenter = { x: target.bounds.cx(), y: target.bounds.cy() };
|
|
93726
|
+
} else if (target.bounds) {
|
|
93727
|
+
targetCenter = { x: (target.bounds.x + target.bounds.X) / 2, y: (target.bounds.y + target.bounds.Y) / 2 };
|
|
93728
|
+
} else {
|
|
93729
|
+
targetCenter = { x: target.x || 0, y: target.y || 0 };
|
|
93730
|
+
}
|
|
93731
|
+
let sourceCenter;
|
|
93732
|
+
if (source.bounds && typeof source.bounds.cx === "function") {
|
|
93733
|
+
sourceCenter = { x: source.bounds.cx(), y: source.bounds.cy() };
|
|
93734
|
+
} else if (source.bounds) {
|
|
93735
|
+
sourceCenter = { x: (source.bounds.x + source.bounds.X) / 2, y: (source.bounds.y + source.bounds.Y) / 2 };
|
|
93736
|
+
} else {
|
|
93737
|
+
sourceCenter = { x: source.x || 0, y: source.y || 0 };
|
|
93738
|
+
}
|
|
93739
|
+
const sourceAnchor = source.bounds || source.innerBounds ? this.getStableEdgeAnchor(source.bounds || source.innerBounds, targetCenter) : sourceCenter;
|
|
93740
|
+
const targetAnchor = target.bounds || target.innerBounds ? this.getStableEdgeAnchor(target.bounds || target.innerBounds, sourceCenter) : targetCenter;
|
|
93741
|
+
return [sourceAnchor, targetAnchor];
|
|
93742
|
+
}
|
|
93635
93743
|
/**
|
|
93636
93744
|
* Adjusts a point to lie on the perimeter of a rectangle.
|
|
93637
93745
|
*
|
|
@@ -93688,36 +93796,51 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
93688
93796
|
minimizeOverlap(currentLabel, overlappingLabels) {
|
|
93689
93797
|
}
|
|
93690
93798
|
/**
|
|
93691
|
-
* Fits the viewport to show all content with
|
|
93692
|
-
* Uses
|
|
93799
|
+
* Fits the viewport to show all content with appropriate zoom and pan.
|
|
93800
|
+
* Uses D3 zoom transform for smooth, consistent behavior.
|
|
93801
|
+
* Only performs fit if:
|
|
93802
|
+
* - This is the initial render, OR
|
|
93803
|
+
* - User has not manually zoomed/panned, OR
|
|
93804
|
+
* - Force parameter is true (e.g., from reset button)
|
|
93805
|
+
*
|
|
93806
|
+
* @param force - If true, fit regardless of user interaction state
|
|
93807
|
+
*/
|
|
93808
|
+
fitViewportToContent(force = false) {
|
|
93809
|
+
const svgElement = this.svg?.node();
|
|
93810
|
+
if (!svgElement || !this.zoomBehavior) return;
|
|
93811
|
+
if (this.userHasManuallyZoomed && !this.isInitialRender && !force) {
|
|
93812
|
+
return;
|
|
93813
|
+
}
|
|
93814
|
+
const bounds = this.calculateContentBounds();
|
|
93815
|
+
if (!bounds) return;
|
|
93816
|
+
const containerWidth = svgElement.clientWidth || svgElement.parentElement?.clientWidth || 800;
|
|
93817
|
+
const containerHeight = svgElement.clientHeight || svgElement.parentElement?.clientHeight || 600;
|
|
93818
|
+
const padding = _WebColaCnDGraph.VIEWBOX_PADDING * 4;
|
|
93819
|
+
const scaleX = (containerWidth - padding * 2) / bounds.width;
|
|
93820
|
+
const scaleY = (containerHeight - padding * 2) / bounds.height;
|
|
93821
|
+
const scale = Math.min(scaleX, scaleY, 1);
|
|
93822
|
+
const [minScale, maxScale] = this.zoomBehavior.scaleExtent();
|
|
93823
|
+
const clampedScale = Math.max(minScale, Math.min(maxScale, scale));
|
|
93824
|
+
const contentCenterX = bounds.x + bounds.width / 2;
|
|
93825
|
+
const contentCenterY = bounds.y + bounds.height / 2;
|
|
93826
|
+
const translateX = containerWidth / 2 - contentCenterX * clampedScale;
|
|
93827
|
+
const translateY = containerHeight / 2 - contentCenterY * clampedScale;
|
|
93828
|
+
const transform = d32.zoomIdentity.translate(translateX, translateY).scale(clampedScale);
|
|
93829
|
+
if (this.isInitialRender) {
|
|
93830
|
+
this.svg.call(this.zoomBehavior.transform, transform);
|
|
93831
|
+
this.isInitialRender = false;
|
|
93832
|
+
} else {
|
|
93833
|
+
this.svg.transition().duration(300).ease(d32.easeCubicOut).call(this.zoomBehavior.transform, transform);
|
|
93834
|
+
}
|
|
93835
|
+
this.updateZoomControlStates();
|
|
93836
|
+
}
|
|
93837
|
+
/**
|
|
93838
|
+
* Resets the view to fit all content, clearing user zoom state.
|
|
93839
|
+
* Called when user clicks the reset/fit button.
|
|
93693
93840
|
*/
|
|
93694
|
-
|
|
93695
|
-
|
|
93696
|
-
|
|
93697
|
-
const manualBounds = this.calculateContentBounds();
|
|
93698
|
-
const bbox = manualBounds || svgElement.getBBox();
|
|
93699
|
-
const padding = _WebColaCnDGraph.VIEWBOX_PADDING;
|
|
93700
|
-
let extraBottomPadding = 50;
|
|
93701
|
-
if (this.currentLayout?.nodes) {
|
|
93702
|
-
const bottomNode = this.currentLayout.nodes.reduce((bottom, node) => {
|
|
93703
|
-
if (typeof node.x === "number" && typeof node.y === "number") {
|
|
93704
|
-
const nodeBottom = node.y + (node.height || 0);
|
|
93705
|
-
const currentBottom = bottom ? bottom.y + (bottom.height || 0) : -Infinity;
|
|
93706
|
-
return nodeBottom > currentBottom ? node : bottom;
|
|
93707
|
-
}
|
|
93708
|
-
return bottom;
|
|
93709
|
-
}, null);
|
|
93710
|
-
if (bottomNode && bottomNode.height) {
|
|
93711
|
-
extraBottomPadding = Math.min(50, bottomNode.height / 1.5);
|
|
93712
|
-
}
|
|
93713
|
-
}
|
|
93714
|
-
const viewBox = [
|
|
93715
|
-
bbox.x - padding,
|
|
93716
|
-
bbox.y - padding,
|
|
93717
|
-
bbox.width + 2 * padding,
|
|
93718
|
-
bbox.height + 2 * padding + extraBottomPadding
|
|
93719
|
-
].join(" ");
|
|
93720
|
-
this.svg.attr("viewBox", viewBox);
|
|
93841
|
+
resetViewToFitContent() {
|
|
93842
|
+
this.userHasManuallyZoomed = false;
|
|
93843
|
+
this.fitViewportToContent(true);
|
|
93721
93844
|
}
|
|
93722
93845
|
/**
|
|
93723
93846
|
* Manually calculates the bounding box of all content to ensure accurate viewport fitting.
|
|
@@ -94273,7 +94396,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
94273
94396
|
#zoom-controls {
|
|
94274
94397
|
display: flex;
|
|
94275
94398
|
flex-direction: row;
|
|
94276
|
-
gap:
|
|
94399
|
+
gap: 8px;
|
|
94277
94400
|
align-items: center;
|
|
94278
94401
|
}
|
|
94279
94402
|
|
|
@@ -94677,6 +94800,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
94677
94800
|
* - Temporary UI elements (modals, overlays)
|
|
94678
94801
|
*/
|
|
94679
94802
|
dispose() {
|
|
94803
|
+
this.detachInputModeListeners();
|
|
94680
94804
|
this.deactivateInputMode();
|
|
94681
94805
|
if (this.svg) {
|
|
94682
94806
|
this.svg.on(".zoom", null);
|
|
@@ -95491,7 +95615,7 @@ VVbdfjptxz|~\x80\x82\x84\xA6\xA8W\b
|
|
|
95491
95615
|
init_layoutspec();
|
|
95492
95616
|
exports.StructuredInputGraph = class extends WebColaCnDGraph {
|
|
95493
95617
|
constructor(dataInstance) {
|
|
95494
|
-
super();
|
|
95618
|
+
super(true);
|
|
95495
95619
|
this.evaluator = null;
|
|
95496
95620
|
this.layoutInstance = null;
|
|
95497
95621
|
this.cndSpecString = "";
|