polly-graph 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +205 -122
- package/dist/index.css +14 -6
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +205 -122
- package/package.json +7 -4
package/dist/index.cjs
CHANGED
|
@@ -2662,15 +2662,15 @@ function defaultWheelDelta(event) {
|
|
|
2662
2662
|
function defaultTouchable2() {
|
|
2663
2663
|
return navigator.maxTouchPoints || "ontouchstart" in this;
|
|
2664
2664
|
}
|
|
2665
|
-
function defaultConstrain(transform2,
|
|
2666
|
-
var dx0 = transform2.invertX(
|
|
2665
|
+
function defaultConstrain(transform2, extent, translateExtent) {
|
|
2666
|
+
var dx0 = transform2.invertX(extent[0][0]) - translateExtent[0][0], dx1 = transform2.invertX(extent[1][0]) - translateExtent[1][0], dy0 = transform2.invertY(extent[0][1]) - translateExtent[0][1], dy1 = transform2.invertY(extent[1][1]) - translateExtent[1][1];
|
|
2667
2667
|
return transform2.translate(
|
|
2668
2668
|
dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
|
|
2669
2669
|
dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
|
|
2670
2670
|
);
|
|
2671
2671
|
}
|
|
2672
2672
|
function zoom_default2() {
|
|
2673
|
-
var filter2 = defaultFilter2,
|
|
2673
|
+
var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default, listeners = dispatch_default2("start", "zoom", "end"), touchstarting, touchfirst, touchending, touchDelay = 500, wheelDelay = 150, clickDistance2 = 0, tapDistance = 10;
|
|
2674
2674
|
function zoom(selection2) {
|
|
2675
2675
|
selection2.property("__zoom", defaultTransform).on("wheel.zoom", wheeled, { passive: false }).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
|
|
2676
2676
|
}
|
|
@@ -2693,7 +2693,7 @@ function zoom_default2() {
|
|
|
2693
2693
|
};
|
|
2694
2694
|
zoom.scaleTo = function(selection2, k, p, event) {
|
|
2695
2695
|
zoom.transform(selection2, function() {
|
|
2696
|
-
var e =
|
|
2696
|
+
var e = extent.apply(this, arguments), t0 = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, p1 = t0.invert(p0), k1 = typeof k === "function" ? k.apply(this, arguments) : k;
|
|
2697
2697
|
return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
|
|
2698
2698
|
}, p, event);
|
|
2699
2699
|
};
|
|
@@ -2702,12 +2702,12 @@ function zoom_default2() {
|
|
|
2702
2702
|
return constrain(this.__zoom.translate(
|
|
2703
2703
|
typeof x3 === "function" ? x3.apply(this, arguments) : x3,
|
|
2704
2704
|
typeof y3 === "function" ? y3.apply(this, arguments) : y3
|
|
2705
|
-
),
|
|
2705
|
+
), extent.apply(this, arguments), translateExtent);
|
|
2706
2706
|
}, null, event);
|
|
2707
2707
|
};
|
|
2708
2708
|
zoom.translateTo = function(selection2, x3, y3, p, event) {
|
|
2709
2709
|
zoom.transform(selection2, function() {
|
|
2710
|
-
var e =
|
|
2710
|
+
var e = extent.apply(this, arguments), t = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
|
|
2711
2711
|
return constrain(identity2.translate(p0[0], p0[1]).scale(t.k).translate(
|
|
2712
2712
|
typeof x3 === "function" ? -x3.apply(this, arguments) : -x3,
|
|
2713
2713
|
typeof y3 === "function" ? -y3.apply(this, arguments) : -y3
|
|
@@ -2722,8 +2722,8 @@ function zoom_default2() {
|
|
|
2722
2722
|
var x3 = p0[0] - p1[0] * transform2.k, y3 = p0[1] - p1[1] * transform2.k;
|
|
2723
2723
|
return x3 === transform2.x && y3 === transform2.y ? transform2 : new Transform(transform2.k, x3, y3);
|
|
2724
2724
|
}
|
|
2725
|
-
function centroid(
|
|
2726
|
-
return [(+
|
|
2725
|
+
function centroid(extent2) {
|
|
2726
|
+
return [(+extent2[0][0] + +extent2[1][0]) / 2, (+extent2[0][1] + +extent2[1][1]) / 2];
|
|
2727
2727
|
}
|
|
2728
2728
|
function schedule(transition2, transform2, point, event) {
|
|
2729
2729
|
transition2.on("start.zoom", function() {
|
|
@@ -2731,7 +2731,7 @@ function zoom_default2() {
|
|
|
2731
2731
|
}).on("interrupt.zoom end.zoom", function() {
|
|
2732
2732
|
gesture(this, arguments).event(event).end();
|
|
2733
2733
|
}).tween("zoom", function() {
|
|
2734
|
-
var that = this, args = arguments, g = gesture(that, args).event(event), e =
|
|
2734
|
+
var that = this, args = arguments, g = gesture(that, args).event(event), e = extent.apply(that, args), p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a2 = that.__zoom, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i = interpolate(a2.invert(p).concat(w / a2.k), b.invert(p).concat(w / b.k));
|
|
2735
2735
|
return function(t) {
|
|
2736
2736
|
if (t === 1) t = b;
|
|
2737
2737
|
else {
|
|
@@ -2750,7 +2750,7 @@ function zoom_default2() {
|
|
|
2750
2750
|
this.args = args;
|
|
2751
2751
|
this.active = 0;
|
|
2752
2752
|
this.sourceEvent = null;
|
|
2753
|
-
this.extent =
|
|
2753
|
+
this.extent = extent.apply(that, args);
|
|
2754
2754
|
this.taps = 0;
|
|
2755
2755
|
}
|
|
2756
2756
|
Gesture.prototype = {
|
|
@@ -2843,7 +2843,7 @@ function zoom_default2() {
|
|
|
2843
2843
|
}
|
|
2844
2844
|
function dblclicked(event, ...args) {
|
|
2845
2845
|
if (!filter2.apply(this, arguments)) return;
|
|
2846
|
-
var t0 = this.__zoom, p0 = pointer_default(event.changedTouches ? event.changedTouches[0] : event, this), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? 0.5 : 2), t1 = constrain(translate(scale(t0, k1), p0, p1),
|
|
2846
|
+
var t0 = this.__zoom, p0 = pointer_default(event.changedTouches ? event.changedTouches[0] : event, this), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? 0.5 : 2), t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
|
|
2847
2847
|
noevent_default2(event);
|
|
2848
2848
|
if (duration > 0) select_default2(this).transition().duration(duration).call(schedule, t1, p0, event);
|
|
2849
2849
|
else select_default2(this).call(zoom.transform, t1, p0, event);
|
|
@@ -2922,7 +2922,7 @@ function zoom_default2() {
|
|
|
2922
2922
|
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant_default4(!!_), zoom) : touchable;
|
|
2923
2923
|
};
|
|
2924
2924
|
zoom.extent = function(_) {
|
|
2925
|
-
return arguments.length ? (
|
|
2925
|
+
return arguments.length ? (extent = typeof _ === "function" ? _ : constant_default4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
|
|
2926
2926
|
};
|
|
2927
2927
|
zoom.scaleExtent = function(_) {
|
|
2928
2928
|
return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
|
|
@@ -2952,9 +2952,6 @@ function zoom_default2() {
|
|
|
2952
2952
|
return zoom;
|
|
2953
2953
|
}
|
|
2954
2954
|
|
|
2955
|
-
// src/create-graph.ts
|
|
2956
|
-
var import_d3 = require("d3");
|
|
2957
|
-
|
|
2958
2955
|
// src/utils/timer-manager.ts
|
|
2959
2956
|
var TimerManager = class {
|
|
2960
2957
|
activeTimers = /* @__PURE__ */ new Map();
|
|
@@ -3341,7 +3338,6 @@ var GraphManager = class {
|
|
|
3341
3338
|
linkMarkerSnapshots = null;
|
|
3342
3339
|
rootSelection = null;
|
|
3343
3340
|
simulationPaused = false;
|
|
3344
|
-
needsImmediateFitView = false;
|
|
3345
3341
|
/**
|
|
3346
3342
|
* Initialize core managers
|
|
3347
3343
|
*/
|
|
@@ -3397,7 +3393,6 @@ var GraphManager = class {
|
|
|
3397
3393
|
this.linkMarkerSnapshots = null;
|
|
3398
3394
|
this.rootSelection = null;
|
|
3399
3395
|
this.simulationPaused = false;
|
|
3400
|
-
this.needsImmediateFitView = false;
|
|
3401
3396
|
this.cleanupFunctions = [];
|
|
3402
3397
|
}
|
|
3403
3398
|
/**
|
|
@@ -4417,6 +4412,24 @@ function createGraphSimulation(config) {
|
|
|
4417
4412
|
const warmupTicks = enhancedConfig.warmup?.ticks ?? (useAdaptive ? Math.min(100, nodeCount * 2) : 50);
|
|
4418
4413
|
warmupSimulation(simulation, warmupTicks);
|
|
4419
4414
|
}
|
|
4415
|
+
if (config.onReady && config.timerManager) {
|
|
4416
|
+
let readyCallbackFired = false;
|
|
4417
|
+
const handleTick = () => {
|
|
4418
|
+
if (!readyCallbackFired && simulation.alpha() < 0.1) {
|
|
4419
|
+
readyCallbackFired = true;
|
|
4420
|
+
simulation.on("tick.ready", null);
|
|
4421
|
+
config.timerManager.setTimeout("simulation-ready", () => {
|
|
4422
|
+
config.onReady?.();
|
|
4423
|
+
}, 100);
|
|
4424
|
+
}
|
|
4425
|
+
};
|
|
4426
|
+
const cleanup = () => {
|
|
4427
|
+
simulation.on("tick.ready", null);
|
|
4428
|
+
config.timerManager.clearTimer("simulation-ready");
|
|
4429
|
+
};
|
|
4430
|
+
simulation.on("tick.ready", handleTick);
|
|
4431
|
+
simulation.on("end.ready", cleanup);
|
|
4432
|
+
}
|
|
4420
4433
|
return { simulation };
|
|
4421
4434
|
}
|
|
4422
4435
|
function seedNodePositions(nodes, containerWidth, containerHeight) {
|
|
@@ -4803,50 +4816,12 @@ function renderLinks(ctx, links) {
|
|
|
4803
4816
|
|
|
4804
4817
|
// src/renderer/nodes.ts
|
|
4805
4818
|
function renderNodes(ctx, nodes) {
|
|
4806
|
-
return ctx.root.select('[data-layer="nodes"]').selectAll("circle").data(
|
|
4807
|
-
nodes,
|
|
4808
|
-
(d) => d.id
|
|
4809
|
-
).join("circle").attr(
|
|
4810
|
-
"r",
|
|
4811
|
-
(node) => node.style?.radius ?? 8
|
|
4812
|
-
).attr(
|
|
4813
|
-
"fill",
|
|
4814
|
-
(node) => node.style?.fill ?? "#6c5ce7"
|
|
4815
|
-
).attr(
|
|
4816
|
-
"stroke",
|
|
4817
|
-
(node) => node.style?.stroke ?? "#ffffff"
|
|
4818
|
-
).attr(
|
|
4819
|
-
"stroke-width",
|
|
4820
|
-
(node) => node.style?.strokeWidth ?? 1.5
|
|
4821
|
-
).attr(
|
|
4822
|
-
"opacity",
|
|
4823
|
-
(node) => node.style?.opacity ?? 1
|
|
4824
|
-
);
|
|
4819
|
+
return ctx.root.select('[data-layer="nodes"]').selectAll("circle").data(nodes, (d) => d.id).join("circle").attr("r", (node) => node.style?.radius ?? 8).attr("fill", (node) => node.style?.fill ?? "#6c5ce7").attr("stroke", (node) => node.style?.stroke ?? "#ffffff").attr("stroke-width", (node) => node.style?.strokeWidth ?? 1.5).attr("opacity", (node) => node.style?.opacity ?? 1).style("cursor", "pointer");
|
|
4825
4820
|
}
|
|
4826
4821
|
|
|
4827
4822
|
// src/renderer/node-labels.ts
|
|
4828
4823
|
function renderNodeLabels(ctx, nodes) {
|
|
4829
|
-
const selection2 = ctx.root.select('[data-layer="node-labels"]').selectAll("text").data(
|
|
4830
|
-
nodes,
|
|
4831
|
-
(d) => d.id
|
|
4832
|
-
).join("text").attr(
|
|
4833
|
-
"text-anchor",
|
|
4834
|
-
"middle"
|
|
4835
|
-
).attr(
|
|
4836
|
-
"dominant-baseline",
|
|
4837
|
-
"middle"
|
|
4838
|
-
).attr(
|
|
4839
|
-
"font-size",
|
|
4840
|
-
9
|
|
4841
|
-
).attr(
|
|
4842
|
-
"fill",
|
|
4843
|
-
(node) => node.style?.textColor ?? "#ffffff"
|
|
4844
|
-
).attr(
|
|
4845
|
-
"pointer-events",
|
|
4846
|
-
"none"
|
|
4847
|
-
).text(
|
|
4848
|
-
(node) => node.label ?? node.id
|
|
4849
|
-
);
|
|
4824
|
+
const selection2 = ctx.root.select('[data-layer="node-labels"]').selectAll("text").data(nodes, (d) => d.id).join("text").attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("font-size", 9).attr("fill", (node) => node.style?.textColor ?? "#ffffff").attr("pointer-events", "none").text((node) => node.label ?? node.id);
|
|
4850
4825
|
selection2.each(function(node) {
|
|
4851
4826
|
const textElement = this;
|
|
4852
4827
|
const fullLabel = node.label ?? node.id;
|
|
@@ -4855,10 +4830,7 @@ function renderNodeLabels(ctx, nodes) {
|
|
|
4855
4830
|
let truncatedLabel = fullLabel;
|
|
4856
4831
|
textElement.textContent = truncatedLabel;
|
|
4857
4832
|
while (truncatedLabel.length > 1 && textElement.getComputedTextLength() > maxWidth) {
|
|
4858
|
-
truncatedLabel = truncatedLabel.slice(
|
|
4859
|
-
0,
|
|
4860
|
-
-1
|
|
4861
|
-
);
|
|
4833
|
+
truncatedLabel = truncatedLabel.slice(0, -1);
|
|
4862
4834
|
textElement.textContent = `${truncatedLabel}\u2026`;
|
|
4863
4835
|
}
|
|
4864
4836
|
});
|
|
@@ -4992,10 +4964,12 @@ var RenderPipeline = class {
|
|
|
4992
4964
|
}
|
|
4993
4965
|
if (this.manager.timerManager && this.manager.fitViewCallback) {
|
|
4994
4966
|
this.manager.timerManager.debounce("fit-view-resize", () => {
|
|
4995
|
-
|
|
4996
|
-
this.manager.fitViewCallback
|
|
4997
|
-
|
|
4998
|
-
|
|
4967
|
+
this.manager.timerManager.setTimeout("fit-view-layout", () => {
|
|
4968
|
+
if (this.manager.fitViewCallback) {
|
|
4969
|
+
this.manager.fitViewCallback();
|
|
4970
|
+
}
|
|
4971
|
+
}, 50);
|
|
4972
|
+
}, 200);
|
|
4999
4973
|
}
|
|
5000
4974
|
});
|
|
5001
4975
|
this.manager.addCleanup(cleanupResize);
|
|
@@ -5065,15 +5039,23 @@ var RenderPipeline = class {
|
|
|
5065
5039
|
config: this.manager.config.simulation
|
|
5066
5040
|
};
|
|
5067
5041
|
try {
|
|
5068
|
-
const
|
|
5042
|
+
const simulationConfigWithCallback = {
|
|
5043
|
+
...simulationConfig,
|
|
5044
|
+
onReady: () => {
|
|
5045
|
+
if (this.manager.fitViewCallback) {
|
|
5046
|
+
this.manager.fitViewCallback();
|
|
5047
|
+
}
|
|
5048
|
+
},
|
|
5049
|
+
timerManager: this.manager.timerManager ?? void 0
|
|
5050
|
+
};
|
|
5051
|
+
const simulationResult = createGraphSimulation(simulationConfigWithCallback);
|
|
5069
5052
|
this.manager.simulation = simulationResult.simulation;
|
|
5070
5053
|
const centerX = simulationConfig.width / 2;
|
|
5071
5054
|
const centerY = simulationConfig.height / 2;
|
|
5072
5055
|
this.manager.simulation.force("center", center_default(centerX, centerY));
|
|
5073
|
-
if (
|
|
5056
|
+
if (simulationConfigWithCallback.width > 0 && simulationConfigWithCallback.height > 0) {
|
|
5074
5057
|
this.manager.reheatSimulation(0.3);
|
|
5075
5058
|
this.manager.simulationPaused = false;
|
|
5076
|
-
this.manager.needsImmediateFitView = true;
|
|
5077
5059
|
} else {
|
|
5078
5060
|
this.manager.simulation.stop();
|
|
5079
5061
|
this.manager.simulationPaused = true;
|
|
@@ -5132,14 +5114,35 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
5132
5114
|
if (hoverStyle) {
|
|
5133
5115
|
nodeSelection.on("mouseenter.hover", function(_event, node) {
|
|
5134
5116
|
const circle = this;
|
|
5117
|
+
if (circle.dataset.selected === "true") {
|
|
5118
|
+
return;
|
|
5119
|
+
}
|
|
5135
5120
|
circle.setAttribute("stroke", hoverStyle.stroke ?? node.style?.stroke ?? "#ffffff");
|
|
5136
5121
|
circle.setAttribute("stroke-width", String(hoverStyle.strokeWidth ?? node.style?.strokeWidth ?? 1.5));
|
|
5137
5122
|
circle.setAttribute("opacity", String(hoverStyle.opacity ?? node.style?.opacity ?? 1));
|
|
5138
5123
|
}).on("mouseleave.hover", function(_event, node) {
|
|
5139
5124
|
const circle = this;
|
|
5140
|
-
circle.
|
|
5141
|
-
|
|
5142
|
-
|
|
5125
|
+
if (circle.dataset.selected === "true") {
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
circle.style.stroke = "";
|
|
5129
|
+
circle.style.strokeWidth = "";
|
|
5130
|
+
circle.style.opacity = "";
|
|
5131
|
+
if (!node.style?.stroke) {
|
|
5132
|
+
circle.setAttribute("stroke", "#ffffff");
|
|
5133
|
+
} else {
|
|
5134
|
+
circle.setAttribute("stroke", node.style.stroke);
|
|
5135
|
+
}
|
|
5136
|
+
if (!node.style?.strokeWidth) {
|
|
5137
|
+
circle.setAttribute("stroke-width", "1.5");
|
|
5138
|
+
} else {
|
|
5139
|
+
circle.setAttribute("stroke-width", String(node.style.strokeWidth));
|
|
5140
|
+
}
|
|
5141
|
+
if (!node.style?.opacity) {
|
|
5142
|
+
circle.setAttribute("opacity", "1");
|
|
5143
|
+
} else {
|
|
5144
|
+
circle.setAttribute("opacity", String(node.style.opacity));
|
|
5145
|
+
}
|
|
5143
5146
|
});
|
|
5144
5147
|
}
|
|
5145
5148
|
const svgElement = firstNode.ownerSVGElement;
|
|
@@ -5536,6 +5539,8 @@ function bindNodeTooltip(params) {
|
|
|
5536
5539
|
destroy: () => {
|
|
5537
5540
|
},
|
|
5538
5541
|
reposition: () => {
|
|
5542
|
+
},
|
|
5543
|
+
hide: () => {
|
|
5539
5544
|
}
|
|
5540
5545
|
};
|
|
5541
5546
|
}
|
|
@@ -5545,6 +5550,9 @@ function bindNodeTooltip(params) {
|
|
|
5545
5550
|
"mouseenter.tooltip",
|
|
5546
5551
|
function(event, node) {
|
|
5547
5552
|
const target = this;
|
|
5553
|
+
if (target.dataset.selected === "true") {
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5548
5556
|
activeTarget = target;
|
|
5549
5557
|
const customContent = params.tooltipConfig?.renderContent?.(node);
|
|
5550
5558
|
const content = customContent ?? getDefaultContent(node);
|
|
@@ -5554,6 +5562,11 @@ function bindNodeTooltip(params) {
|
|
|
5554
5562
|
"mousemove.tooltip",
|
|
5555
5563
|
function() {
|
|
5556
5564
|
const target = this;
|
|
5565
|
+
if (target.dataset.selected === "true") {
|
|
5566
|
+
activeTarget = null;
|
|
5567
|
+
tooltip.hide();
|
|
5568
|
+
return;
|
|
5569
|
+
}
|
|
5557
5570
|
activeTarget = target;
|
|
5558
5571
|
tooltip.move(target);
|
|
5559
5572
|
}
|
|
@@ -5570,12 +5583,16 @@ function bindNodeTooltip(params) {
|
|
|
5570
5583
|
}
|
|
5571
5584
|
tooltip.move(activeTarget);
|
|
5572
5585
|
}
|
|
5586
|
+
function hide() {
|
|
5587
|
+
activeTarget = null;
|
|
5588
|
+
tooltip.hide();
|
|
5589
|
+
}
|
|
5573
5590
|
function destroy() {
|
|
5574
5591
|
activeTarget = null;
|
|
5575
5592
|
params.selection.on(".tooltip", null);
|
|
5576
5593
|
tooltip.destroy();
|
|
5577
5594
|
}
|
|
5578
|
-
return { destroy, reposition };
|
|
5595
|
+
return { destroy, reposition, hide };
|
|
5579
5596
|
}
|
|
5580
5597
|
function getDefaultContent(node) {
|
|
5581
5598
|
return `
|
|
@@ -5805,17 +5822,22 @@ var SelectionManager = class {
|
|
|
5805
5822
|
layers;
|
|
5806
5823
|
linkMarkerSnapshots;
|
|
5807
5824
|
root;
|
|
5808
|
-
|
|
5825
|
+
tooltipBinding;
|
|
5826
|
+
constructor(eventEmitter, config, layers, linkMarkerSnapshots, root2, tooltipBinding) {
|
|
5809
5827
|
this.eventEmitter = eventEmitter;
|
|
5810
5828
|
this.config = config;
|
|
5811
5829
|
this.layers = layers;
|
|
5812
5830
|
this.linkMarkerSnapshots = linkMarkerSnapshots;
|
|
5813
5831
|
this.root = root2;
|
|
5832
|
+
this.tooltipBinding = tooltipBinding;
|
|
5814
5833
|
}
|
|
5815
5834
|
/**
|
|
5816
5835
|
* Select a node, automatically deselecting any current selection
|
|
5817
5836
|
*/
|
|
5818
5837
|
selectNode(nodeElement, nodeData) {
|
|
5838
|
+
if (this.tooltipBinding) {
|
|
5839
|
+
this.tooltipBinding.hide();
|
|
5840
|
+
}
|
|
5819
5841
|
this.clearHoverState();
|
|
5820
5842
|
this.clearSelection();
|
|
5821
5843
|
this.bringNodeToFront(nodeElement, nodeData);
|
|
@@ -6099,6 +6121,27 @@ var SelectionManager = class {
|
|
|
6099
6121
|
}
|
|
6100
6122
|
};
|
|
6101
6123
|
|
|
6124
|
+
// src/utils/resolve-node-style.ts
|
|
6125
|
+
var DEFAULT_NODE_HOVER_STYLE = {
|
|
6126
|
+
stroke: `color-mix(in srgb, ${"#8E42EE" /* PURPLE */}, ${"#000000" /* BLACK */} 20%)`,
|
|
6127
|
+
strokeWidth: 3
|
|
6128
|
+
};
|
|
6129
|
+
function resolveNodeStyle(params) {
|
|
6130
|
+
if (params.isSelected) {
|
|
6131
|
+
return mergeNodeStyle(DEFAULT_NODE_HOVER_STYLE, params.interaction?.selection?.nodeStyle);
|
|
6132
|
+
}
|
|
6133
|
+
if (params.isHovered) {
|
|
6134
|
+
return mergeNodeStyle(DEFAULT_NODE_HOVER_STYLE, params.interaction?.hover?.nodeStyle);
|
|
6135
|
+
}
|
|
6136
|
+
return void 0;
|
|
6137
|
+
}
|
|
6138
|
+
function mergeNodeStyle(base, override) {
|
|
6139
|
+
return {
|
|
6140
|
+
...base,
|
|
6141
|
+
...override
|
|
6142
|
+
};
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6102
6145
|
// src/core/interaction-manager.ts
|
|
6103
6146
|
var InteractionManager = class {
|
|
6104
6147
|
constructor(manager) {
|
|
@@ -6150,7 +6193,13 @@ var InteractionManager = class {
|
|
|
6150
6193
|
tooltipConfig: this.manager.config.interaction.hover.tooltip
|
|
6151
6194
|
});
|
|
6152
6195
|
}
|
|
6153
|
-
|
|
6196
|
+
const defaultNodeHoverStyle = resolveNodeStyle({
|
|
6197
|
+
node: {},
|
|
6198
|
+
// We don't need the actual node for defaults
|
|
6199
|
+
interaction: this.manager.config.interaction,
|
|
6200
|
+
isHovered: true
|
|
6201
|
+
});
|
|
6202
|
+
createNodeHover(selections.nodeSelection, defaultNodeHoverStyle);
|
|
6154
6203
|
createLinkHover(selections.linkSelection, this.manager.config.interaction.hover.linkStyle);
|
|
6155
6204
|
}
|
|
6156
6205
|
}
|
|
@@ -6190,7 +6239,8 @@ var InteractionManager = class {
|
|
|
6190
6239
|
this.manager.config.interaction.selection,
|
|
6191
6240
|
this.manager.layers,
|
|
6192
6241
|
this.manager.linkMarkerSnapshots,
|
|
6193
|
-
this.manager.rootSelection
|
|
6242
|
+
this.manager.rootSelection,
|
|
6243
|
+
this.manager.tooltipBinding || void 0
|
|
6194
6244
|
);
|
|
6195
6245
|
this.setupSelectionHandlers(selections);
|
|
6196
6246
|
this.setupBackgroundClickHandler();
|
|
@@ -6470,6 +6520,7 @@ async function captureAndDownloadGraph(container, options = {}) {
|
|
|
6470
6520
|
}
|
|
6471
6521
|
svgClone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
6472
6522
|
svgClone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
6523
|
+
fixTextStyling(svgClone);
|
|
6473
6524
|
const svgString = new XMLSerializer().serializeToString(svgClone);
|
|
6474
6525
|
const svgBlob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" });
|
|
6475
6526
|
const svgUrl = URL.createObjectURL(svgBlob);
|
|
@@ -6616,6 +6667,64 @@ function createLegendSVGElement(legendEntries, dimensions) {
|
|
|
6616
6667
|
});
|
|
6617
6668
|
return legendGroup;
|
|
6618
6669
|
}
|
|
6670
|
+
function fixTextStyling(svgElement) {
|
|
6671
|
+
const nodeLabelsLayer = svgElement.querySelector('[data-layer="node-labels"]');
|
|
6672
|
+
if (nodeLabelsLayer) {
|
|
6673
|
+
const nodeTexts = nodeLabelsLayer.querySelectorAll("text");
|
|
6674
|
+
nodeTexts.forEach((text) => {
|
|
6675
|
+
const textElement = text;
|
|
6676
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6677
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6678
|
+
textElement.setAttribute("font-size", "9");
|
|
6679
|
+
}
|
|
6680
|
+
textElement.setAttribute("font-weight", "500");
|
|
6681
|
+
textElement.setAttribute("text-anchor", "middle");
|
|
6682
|
+
textElement.setAttribute("dominant-baseline", "central");
|
|
6683
|
+
if (!textElement.getAttribute("fill")) {
|
|
6684
|
+
textElement.setAttribute("fill", "#374151");
|
|
6685
|
+
}
|
|
6686
|
+
});
|
|
6687
|
+
}
|
|
6688
|
+
const linkLabels = svgElement.querySelectorAll("g.link-label");
|
|
6689
|
+
linkLabels.forEach((labelGroup) => {
|
|
6690
|
+
const textElement = labelGroup.querySelector("text");
|
|
6691
|
+
if (textElement) {
|
|
6692
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6693
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6694
|
+
textElement.setAttribute("font-size", "10");
|
|
6695
|
+
}
|
|
6696
|
+
textElement.setAttribute("font-weight", "400");
|
|
6697
|
+
textElement.setAttribute("text-anchor", "middle");
|
|
6698
|
+
textElement.setAttribute("dominant-baseline", "central");
|
|
6699
|
+
if (!textElement.getAttribute("fill")) {
|
|
6700
|
+
textElement.setAttribute("fill", "#6b7280");
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
const rectElement = labelGroup.querySelector("rect");
|
|
6704
|
+
if (rectElement) {
|
|
6705
|
+
if (!rectElement.getAttribute("fill")) {
|
|
6706
|
+
rectElement.setAttribute("fill", "#ffffff");
|
|
6707
|
+
}
|
|
6708
|
+
if (!rectElement.getAttribute("stroke")) {
|
|
6709
|
+
rectElement.setAttribute("stroke", "#e5e7eb");
|
|
6710
|
+
}
|
|
6711
|
+
if (!rectElement.getAttribute("stroke-width")) {
|
|
6712
|
+
rectElement.setAttribute("stroke-width", "1");
|
|
6713
|
+
}
|
|
6714
|
+
rectElement.setAttribute("rx", "4");
|
|
6715
|
+
}
|
|
6716
|
+
});
|
|
6717
|
+
const allTexts = svgElement.querySelectorAll("text");
|
|
6718
|
+
allTexts.forEach((text) => {
|
|
6719
|
+
const textElement = text;
|
|
6720
|
+
if (!textElement.getAttribute("font-family")) {
|
|
6721
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6722
|
+
}
|
|
6723
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6724
|
+
textElement.setAttribute("font-size", "10");
|
|
6725
|
+
}
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6619
6728
|
function normalizeColor(color2) {
|
|
6620
6729
|
const rgbMatch = color2.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
6621
6730
|
if (rgbMatch) {
|
|
@@ -6948,10 +7057,6 @@ function createGraph(config) {
|
|
|
6948
7057
|
renderPipeline.execute().then((selections) => {
|
|
6949
7058
|
interactionManager.setupInteractions(selections);
|
|
6950
7059
|
setupAdditionalComponents();
|
|
6951
|
-
if (graphManager.needsImmediateFitView) {
|
|
6952
|
-
graphManager.needsImmediateFitView = false;
|
|
6953
|
-
fitViewWithInitialPositions();
|
|
6954
|
-
}
|
|
6955
7060
|
}).catch((error) => {
|
|
6956
7061
|
console.error("[Polly Graph] Render failed:", error);
|
|
6957
7062
|
});
|
|
@@ -7022,56 +7127,34 @@ function createGraph(config) {
|
|
|
7022
7127
|
const svg = graphManager.svgElement;
|
|
7023
7128
|
const nodes = config.nodes;
|
|
7024
7129
|
if (nodes.length === 0) return;
|
|
7025
|
-
const
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
const xExtent = (0, import_d3.extent)(positions, (d) => d.x);
|
|
7030
|
-
const yExtent = (0, import_d3.extent)(positions, (d) => d.y);
|
|
7031
|
-
const padding = 50;
|
|
7032
|
-
const width = graphManager.dimensions.width - padding * 2;
|
|
7033
|
-
const height = graphManager.dimensions.height - padding * 2;
|
|
7034
|
-
const nodeWidth = xExtent[1] - xExtent[0];
|
|
7035
|
-
const nodeHeight = yExtent[1] - yExtent[0];
|
|
7036
|
-
if (nodeWidth === 0 || nodeHeight === 0) {
|
|
7130
|
+
const graphRoot = select_default2(svg).select('[data-layer="viewport"]');
|
|
7131
|
+
const graphRootNode = graphRoot.node();
|
|
7132
|
+
if (!graphRootNode) {
|
|
7133
|
+
console.warn("[Polly Graph] Cannot fit view: graph root not found");
|
|
7037
7134
|
return;
|
|
7038
7135
|
}
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
select_default2(svg).transition().duration(400).call(graphManager.zoomBehavior.transform, transform2);
|
|
7045
|
-
}
|
|
7046
|
-
}
|
|
7047
|
-
function fitViewWithInitialPositions() {
|
|
7048
|
-
if (!graphManager.simulation || !graphManager.svgElement) {
|
|
7049
|
-
console.warn("[Polly Graph] Cannot fit view: simulation or SVG not available");
|
|
7136
|
+
let bounds;
|
|
7137
|
+
try {
|
|
7138
|
+
bounds = graphRootNode.getBBox();
|
|
7139
|
+
} catch {
|
|
7140
|
+
console.warn("[Polly Graph] Cannot get bounds, falling back to default view");
|
|
7050
7141
|
return;
|
|
7051
7142
|
}
|
|
7052
|
-
|
|
7053
|
-
const nodes = config.nodes;
|
|
7054
|
-
if (nodes.length === 0) return;
|
|
7055
|
-
const positions = nodes.map((node) => ({
|
|
7056
|
-
x: node.initialX ?? node.x ?? 0,
|
|
7057
|
-
y: node.initialY ?? node.y ?? 0
|
|
7058
|
-
}));
|
|
7059
|
-
const xExtent = (0, import_d3.extent)(positions, (d) => d.x);
|
|
7060
|
-
const yExtent = (0, import_d3.extent)(positions, (d) => d.y);
|
|
7061
|
-
const padding = 50;
|
|
7062
|
-
const width = graphManager.dimensions.width - padding * 2;
|
|
7063
|
-
const height = graphManager.dimensions.height - padding * 2;
|
|
7064
|
-
const nodeWidth = xExtent[1] - xExtent[0];
|
|
7065
|
-
const nodeHeight = yExtent[1] - yExtent[0];
|
|
7066
|
-
if (nodeWidth === 0 || nodeHeight === 0) {
|
|
7143
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
7067
7144
|
return;
|
|
7068
7145
|
}
|
|
7069
|
-
const
|
|
7070
|
-
const
|
|
7071
|
-
const
|
|
7072
|
-
const
|
|
7146
|
+
const svgRect = svg.getBoundingClientRect();
|
|
7147
|
+
const padding = 40;
|
|
7148
|
+
const availableWidth = svgRect.width - padding * 2;
|
|
7149
|
+
const availableHeight = svgRect.height - padding * 2;
|
|
7150
|
+
const scaleX = availableWidth / bounds.width;
|
|
7151
|
+
const scaleY = availableHeight / bounds.height;
|
|
7152
|
+
const scale = Math.min(scaleX, scaleY, 4);
|
|
7153
|
+
const centerX = bounds.x + bounds.width / 2;
|
|
7154
|
+
const centerY = bounds.y + bounds.height / 2;
|
|
7155
|
+
const transform2 = identity2.translate(svgRect.width / 2, svgRect.height / 2).scale(scale).translate(-centerX, -centerY);
|
|
7073
7156
|
if (graphManager.zoomBehavior) {
|
|
7074
|
-
select_default2(svg).transition().duration(
|
|
7157
|
+
select_default2(svg).transition().duration(750).call(graphManager.zoomBehavior.transform, transform2);
|
|
7075
7158
|
}
|
|
7076
7159
|
}
|
|
7077
7160
|
function exportGraph(fileName) {
|
package/dist/index.css
CHANGED
|
@@ -200,14 +200,17 @@
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
/* src/styles/graph-tooltip.css */
|
|
203
|
+
:root {
|
|
204
|
+
--dark-bg: color-mix(in srgb, #808080, #000000 20%);
|
|
205
|
+
}
|
|
203
206
|
.graph-tooltip {
|
|
204
207
|
position: absolute;
|
|
205
208
|
pointer-events: none;
|
|
206
209
|
z-index: 1000;
|
|
207
210
|
width: fit-content;
|
|
208
211
|
max-width: 280px;
|
|
209
|
-
border-radius:
|
|
210
|
-
background:
|
|
212
|
+
border-radius: 0.375rem;
|
|
213
|
+
background: var(--dark-bg);
|
|
211
214
|
color: #f8fafc;
|
|
212
215
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
213
216
|
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
|
|
@@ -230,15 +233,15 @@
|
|
|
230
233
|
.graph-tooltip__content {
|
|
231
234
|
position: relative;
|
|
232
235
|
z-index: 2;
|
|
233
|
-
padding:
|
|
236
|
+
padding: 0.375rem 0.5rem;
|
|
234
237
|
word-wrap: break-word;
|
|
235
238
|
overflow-wrap: break-word;
|
|
236
239
|
}
|
|
237
240
|
.graph-tooltip__arrow {
|
|
238
241
|
position: absolute;
|
|
239
|
-
width:
|
|
240
|
-
height:
|
|
241
|
-
background:
|
|
242
|
+
width: 0.625rem;
|
|
243
|
+
height: 0.625rem;
|
|
244
|
+
background: var(--dark-bg);
|
|
242
245
|
transform: rotate(45deg);
|
|
243
246
|
z-index: 1;
|
|
244
247
|
}
|
|
@@ -298,6 +301,10 @@
|
|
|
298
301
|
display: block;
|
|
299
302
|
width: 100%;
|
|
300
303
|
height: 100%;
|
|
304
|
+
cursor: grab;
|
|
305
|
+
}
|
|
306
|
+
.pg-canvas:active {
|
|
307
|
+
cursor: grabbing;
|
|
301
308
|
}
|
|
302
309
|
.pg-overlay {
|
|
303
310
|
position: absolute;
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
transition: r 0.2s ease, stroke-width 0.2s ease;
|
|
311
318
|
}
|
|
312
319
|
.pg-layer-links line {
|
|
320
|
+
cursor: pointer;
|
|
313
321
|
transition: stroke-opacity 0.2s ease;
|
|
314
322
|
}
|
|
315
323
|
|
package/dist/index.d.cts
CHANGED
|
@@ -537,6 +537,12 @@ interface RenderableGraphLink {
|
|
|
537
537
|
readonly markerEnd: string;
|
|
538
538
|
}
|
|
539
539
|
|
|
540
|
+
interface NodeTooltipBinding {
|
|
541
|
+
destroy(): void;
|
|
542
|
+
reposition(): void;
|
|
543
|
+
hide(): void;
|
|
544
|
+
}
|
|
545
|
+
|
|
540
546
|
/**
|
|
541
547
|
* Centralized selection management for graph nodes and links.
|
|
542
548
|
* Simplifies selection logic and ensures consistent behavior.
|
|
@@ -560,7 +566,8 @@ declare class SelectionManager {
|
|
|
560
566
|
private readonly layers;
|
|
561
567
|
private readonly linkMarkerSnapshots;
|
|
562
568
|
private readonly root;
|
|
563
|
-
|
|
569
|
+
private readonly tooltipBinding?;
|
|
570
|
+
constructor(eventEmitter: TypedGraphEventEmitter, config: SelectionInteractionConfig, layers: GraphLayers, linkMarkerSnapshots: Map<SVGLineElement, string | null>, root: Selection<SVGGElement, unknown, null, undefined>, tooltipBinding?: NodeTooltipBinding);
|
|
564
571
|
/**
|
|
565
572
|
* Select a node, automatically deselecting any current selection
|
|
566
573
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -537,6 +537,12 @@ interface RenderableGraphLink {
|
|
|
537
537
|
readonly markerEnd: string;
|
|
538
538
|
}
|
|
539
539
|
|
|
540
|
+
interface NodeTooltipBinding {
|
|
541
|
+
destroy(): void;
|
|
542
|
+
reposition(): void;
|
|
543
|
+
hide(): void;
|
|
544
|
+
}
|
|
545
|
+
|
|
540
546
|
/**
|
|
541
547
|
* Centralized selection management for graph nodes and links.
|
|
542
548
|
* Simplifies selection logic and ensures consistent behavior.
|
|
@@ -560,7 +566,8 @@ declare class SelectionManager {
|
|
|
560
566
|
private readonly layers;
|
|
561
567
|
private readonly linkMarkerSnapshots;
|
|
562
568
|
private readonly root;
|
|
563
|
-
|
|
569
|
+
private readonly tooltipBinding?;
|
|
570
|
+
constructor(eventEmitter: TypedGraphEventEmitter, config: SelectionInteractionConfig, layers: GraphLayers, linkMarkerSnapshots: Map<SVGLineElement, string | null>, root: Selection<SVGGElement, unknown, null, undefined>, tooltipBinding?: NodeTooltipBinding);
|
|
564
571
|
/**
|
|
565
572
|
* Select a node, automatically deselecting any current selection
|
|
566
573
|
*/
|
package/dist/index.js
CHANGED
|
@@ -2630,15 +2630,15 @@ function defaultWheelDelta(event) {
|
|
|
2630
2630
|
function defaultTouchable2() {
|
|
2631
2631
|
return navigator.maxTouchPoints || "ontouchstart" in this;
|
|
2632
2632
|
}
|
|
2633
|
-
function defaultConstrain(transform2,
|
|
2634
|
-
var dx0 = transform2.invertX(
|
|
2633
|
+
function defaultConstrain(transform2, extent, translateExtent) {
|
|
2634
|
+
var dx0 = transform2.invertX(extent[0][0]) - translateExtent[0][0], dx1 = transform2.invertX(extent[1][0]) - translateExtent[1][0], dy0 = transform2.invertY(extent[0][1]) - translateExtent[0][1], dy1 = transform2.invertY(extent[1][1]) - translateExtent[1][1];
|
|
2635
2635
|
return transform2.translate(
|
|
2636
2636
|
dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
|
|
2637
2637
|
dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
|
|
2638
2638
|
);
|
|
2639
2639
|
}
|
|
2640
2640
|
function zoom_default2() {
|
|
2641
|
-
var filter2 = defaultFilter2,
|
|
2641
|
+
var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default, listeners = dispatch_default2("start", "zoom", "end"), touchstarting, touchfirst, touchending, touchDelay = 500, wheelDelay = 150, clickDistance2 = 0, tapDistance = 10;
|
|
2642
2642
|
function zoom(selection2) {
|
|
2643
2643
|
selection2.property("__zoom", defaultTransform).on("wheel.zoom", wheeled, { passive: false }).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
|
|
2644
2644
|
}
|
|
@@ -2661,7 +2661,7 @@ function zoom_default2() {
|
|
|
2661
2661
|
};
|
|
2662
2662
|
zoom.scaleTo = function(selection2, k, p, event) {
|
|
2663
2663
|
zoom.transform(selection2, function() {
|
|
2664
|
-
var e =
|
|
2664
|
+
var e = extent.apply(this, arguments), t0 = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, p1 = t0.invert(p0), k1 = typeof k === "function" ? k.apply(this, arguments) : k;
|
|
2665
2665
|
return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
|
|
2666
2666
|
}, p, event);
|
|
2667
2667
|
};
|
|
@@ -2670,12 +2670,12 @@ function zoom_default2() {
|
|
|
2670
2670
|
return constrain(this.__zoom.translate(
|
|
2671
2671
|
typeof x3 === "function" ? x3.apply(this, arguments) : x3,
|
|
2672
2672
|
typeof y3 === "function" ? y3.apply(this, arguments) : y3
|
|
2673
|
-
),
|
|
2673
|
+
), extent.apply(this, arguments), translateExtent);
|
|
2674
2674
|
}, null, event);
|
|
2675
2675
|
};
|
|
2676
2676
|
zoom.translateTo = function(selection2, x3, y3, p, event) {
|
|
2677
2677
|
zoom.transform(selection2, function() {
|
|
2678
|
-
var e =
|
|
2678
|
+
var e = extent.apply(this, arguments), t = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
|
|
2679
2679
|
return constrain(identity2.translate(p0[0], p0[1]).scale(t.k).translate(
|
|
2680
2680
|
typeof x3 === "function" ? -x3.apply(this, arguments) : -x3,
|
|
2681
2681
|
typeof y3 === "function" ? -y3.apply(this, arguments) : -y3
|
|
@@ -2690,8 +2690,8 @@ function zoom_default2() {
|
|
|
2690
2690
|
var x3 = p0[0] - p1[0] * transform2.k, y3 = p0[1] - p1[1] * transform2.k;
|
|
2691
2691
|
return x3 === transform2.x && y3 === transform2.y ? transform2 : new Transform(transform2.k, x3, y3);
|
|
2692
2692
|
}
|
|
2693
|
-
function centroid(
|
|
2694
|
-
return [(+
|
|
2693
|
+
function centroid(extent2) {
|
|
2694
|
+
return [(+extent2[0][0] + +extent2[1][0]) / 2, (+extent2[0][1] + +extent2[1][1]) / 2];
|
|
2695
2695
|
}
|
|
2696
2696
|
function schedule(transition2, transform2, point, event) {
|
|
2697
2697
|
transition2.on("start.zoom", function() {
|
|
@@ -2699,7 +2699,7 @@ function zoom_default2() {
|
|
|
2699
2699
|
}).on("interrupt.zoom end.zoom", function() {
|
|
2700
2700
|
gesture(this, arguments).event(event).end();
|
|
2701
2701
|
}).tween("zoom", function() {
|
|
2702
|
-
var that = this, args = arguments, g = gesture(that, args).event(event), e =
|
|
2702
|
+
var that = this, args = arguments, g = gesture(that, args).event(event), e = extent.apply(that, args), p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a2 = that.__zoom, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i = interpolate(a2.invert(p).concat(w / a2.k), b.invert(p).concat(w / b.k));
|
|
2703
2703
|
return function(t) {
|
|
2704
2704
|
if (t === 1) t = b;
|
|
2705
2705
|
else {
|
|
@@ -2718,7 +2718,7 @@ function zoom_default2() {
|
|
|
2718
2718
|
this.args = args;
|
|
2719
2719
|
this.active = 0;
|
|
2720
2720
|
this.sourceEvent = null;
|
|
2721
|
-
this.extent =
|
|
2721
|
+
this.extent = extent.apply(that, args);
|
|
2722
2722
|
this.taps = 0;
|
|
2723
2723
|
}
|
|
2724
2724
|
Gesture.prototype = {
|
|
@@ -2811,7 +2811,7 @@ function zoom_default2() {
|
|
|
2811
2811
|
}
|
|
2812
2812
|
function dblclicked(event, ...args) {
|
|
2813
2813
|
if (!filter2.apply(this, arguments)) return;
|
|
2814
|
-
var t0 = this.__zoom, p0 = pointer_default(event.changedTouches ? event.changedTouches[0] : event, this), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? 0.5 : 2), t1 = constrain(translate(scale(t0, k1), p0, p1),
|
|
2814
|
+
var t0 = this.__zoom, p0 = pointer_default(event.changedTouches ? event.changedTouches[0] : event, this), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? 0.5 : 2), t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
|
|
2815
2815
|
noevent_default2(event);
|
|
2816
2816
|
if (duration > 0) select_default2(this).transition().duration(duration).call(schedule, t1, p0, event);
|
|
2817
2817
|
else select_default2(this).call(zoom.transform, t1, p0, event);
|
|
@@ -2890,7 +2890,7 @@ function zoom_default2() {
|
|
|
2890
2890
|
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant_default4(!!_), zoom) : touchable;
|
|
2891
2891
|
};
|
|
2892
2892
|
zoom.extent = function(_) {
|
|
2893
|
-
return arguments.length ? (
|
|
2893
|
+
return arguments.length ? (extent = typeof _ === "function" ? _ : constant_default4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
|
|
2894
2894
|
};
|
|
2895
2895
|
zoom.scaleExtent = function(_) {
|
|
2896
2896
|
return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
|
|
@@ -2920,9 +2920,6 @@ function zoom_default2() {
|
|
|
2920
2920
|
return zoom;
|
|
2921
2921
|
}
|
|
2922
2922
|
|
|
2923
|
-
// src/create-graph.ts
|
|
2924
|
-
import { extent } from "d3";
|
|
2925
|
-
|
|
2926
2923
|
// src/utils/timer-manager.ts
|
|
2927
2924
|
var TimerManager = class {
|
|
2928
2925
|
activeTimers = /* @__PURE__ */ new Map();
|
|
@@ -3309,7 +3306,6 @@ var GraphManager = class {
|
|
|
3309
3306
|
linkMarkerSnapshots = null;
|
|
3310
3307
|
rootSelection = null;
|
|
3311
3308
|
simulationPaused = false;
|
|
3312
|
-
needsImmediateFitView = false;
|
|
3313
3309
|
/**
|
|
3314
3310
|
* Initialize core managers
|
|
3315
3311
|
*/
|
|
@@ -3365,7 +3361,6 @@ var GraphManager = class {
|
|
|
3365
3361
|
this.linkMarkerSnapshots = null;
|
|
3366
3362
|
this.rootSelection = null;
|
|
3367
3363
|
this.simulationPaused = false;
|
|
3368
|
-
this.needsImmediateFitView = false;
|
|
3369
3364
|
this.cleanupFunctions = [];
|
|
3370
3365
|
}
|
|
3371
3366
|
/**
|
|
@@ -4385,6 +4380,24 @@ function createGraphSimulation(config) {
|
|
|
4385
4380
|
const warmupTicks = enhancedConfig.warmup?.ticks ?? (useAdaptive ? Math.min(100, nodeCount * 2) : 50);
|
|
4386
4381
|
warmupSimulation(simulation, warmupTicks);
|
|
4387
4382
|
}
|
|
4383
|
+
if (config.onReady && config.timerManager) {
|
|
4384
|
+
let readyCallbackFired = false;
|
|
4385
|
+
const handleTick = () => {
|
|
4386
|
+
if (!readyCallbackFired && simulation.alpha() < 0.1) {
|
|
4387
|
+
readyCallbackFired = true;
|
|
4388
|
+
simulation.on("tick.ready", null);
|
|
4389
|
+
config.timerManager.setTimeout("simulation-ready", () => {
|
|
4390
|
+
config.onReady?.();
|
|
4391
|
+
}, 100);
|
|
4392
|
+
}
|
|
4393
|
+
};
|
|
4394
|
+
const cleanup = () => {
|
|
4395
|
+
simulation.on("tick.ready", null);
|
|
4396
|
+
config.timerManager.clearTimer("simulation-ready");
|
|
4397
|
+
};
|
|
4398
|
+
simulation.on("tick.ready", handleTick);
|
|
4399
|
+
simulation.on("end.ready", cleanup);
|
|
4400
|
+
}
|
|
4388
4401
|
return { simulation };
|
|
4389
4402
|
}
|
|
4390
4403
|
function seedNodePositions(nodes, containerWidth, containerHeight) {
|
|
@@ -4771,50 +4784,12 @@ function renderLinks(ctx, links) {
|
|
|
4771
4784
|
|
|
4772
4785
|
// src/renderer/nodes.ts
|
|
4773
4786
|
function renderNodes(ctx, nodes) {
|
|
4774
|
-
return ctx.root.select('[data-layer="nodes"]').selectAll("circle").data(
|
|
4775
|
-
nodes,
|
|
4776
|
-
(d) => d.id
|
|
4777
|
-
).join("circle").attr(
|
|
4778
|
-
"r",
|
|
4779
|
-
(node) => node.style?.radius ?? 8
|
|
4780
|
-
).attr(
|
|
4781
|
-
"fill",
|
|
4782
|
-
(node) => node.style?.fill ?? "#6c5ce7"
|
|
4783
|
-
).attr(
|
|
4784
|
-
"stroke",
|
|
4785
|
-
(node) => node.style?.stroke ?? "#ffffff"
|
|
4786
|
-
).attr(
|
|
4787
|
-
"stroke-width",
|
|
4788
|
-
(node) => node.style?.strokeWidth ?? 1.5
|
|
4789
|
-
).attr(
|
|
4790
|
-
"opacity",
|
|
4791
|
-
(node) => node.style?.opacity ?? 1
|
|
4792
|
-
);
|
|
4787
|
+
return ctx.root.select('[data-layer="nodes"]').selectAll("circle").data(nodes, (d) => d.id).join("circle").attr("r", (node) => node.style?.radius ?? 8).attr("fill", (node) => node.style?.fill ?? "#6c5ce7").attr("stroke", (node) => node.style?.stroke ?? "#ffffff").attr("stroke-width", (node) => node.style?.strokeWidth ?? 1.5).attr("opacity", (node) => node.style?.opacity ?? 1).style("cursor", "pointer");
|
|
4793
4788
|
}
|
|
4794
4789
|
|
|
4795
4790
|
// src/renderer/node-labels.ts
|
|
4796
4791
|
function renderNodeLabels(ctx, nodes) {
|
|
4797
|
-
const selection2 = ctx.root.select('[data-layer="node-labels"]').selectAll("text").data(
|
|
4798
|
-
nodes,
|
|
4799
|
-
(d) => d.id
|
|
4800
|
-
).join("text").attr(
|
|
4801
|
-
"text-anchor",
|
|
4802
|
-
"middle"
|
|
4803
|
-
).attr(
|
|
4804
|
-
"dominant-baseline",
|
|
4805
|
-
"middle"
|
|
4806
|
-
).attr(
|
|
4807
|
-
"font-size",
|
|
4808
|
-
9
|
|
4809
|
-
).attr(
|
|
4810
|
-
"fill",
|
|
4811
|
-
(node) => node.style?.textColor ?? "#ffffff"
|
|
4812
|
-
).attr(
|
|
4813
|
-
"pointer-events",
|
|
4814
|
-
"none"
|
|
4815
|
-
).text(
|
|
4816
|
-
(node) => node.label ?? node.id
|
|
4817
|
-
);
|
|
4792
|
+
const selection2 = ctx.root.select('[data-layer="node-labels"]').selectAll("text").data(nodes, (d) => d.id).join("text").attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("font-size", 9).attr("fill", (node) => node.style?.textColor ?? "#ffffff").attr("pointer-events", "none").text((node) => node.label ?? node.id);
|
|
4818
4793
|
selection2.each(function(node) {
|
|
4819
4794
|
const textElement = this;
|
|
4820
4795
|
const fullLabel = node.label ?? node.id;
|
|
@@ -4823,10 +4798,7 @@ function renderNodeLabels(ctx, nodes) {
|
|
|
4823
4798
|
let truncatedLabel = fullLabel;
|
|
4824
4799
|
textElement.textContent = truncatedLabel;
|
|
4825
4800
|
while (truncatedLabel.length > 1 && textElement.getComputedTextLength() > maxWidth) {
|
|
4826
|
-
truncatedLabel = truncatedLabel.slice(
|
|
4827
|
-
0,
|
|
4828
|
-
-1
|
|
4829
|
-
);
|
|
4801
|
+
truncatedLabel = truncatedLabel.slice(0, -1);
|
|
4830
4802
|
textElement.textContent = `${truncatedLabel}\u2026`;
|
|
4831
4803
|
}
|
|
4832
4804
|
});
|
|
@@ -4960,10 +4932,12 @@ var RenderPipeline = class {
|
|
|
4960
4932
|
}
|
|
4961
4933
|
if (this.manager.timerManager && this.manager.fitViewCallback) {
|
|
4962
4934
|
this.manager.timerManager.debounce("fit-view-resize", () => {
|
|
4963
|
-
|
|
4964
|
-
this.manager.fitViewCallback
|
|
4965
|
-
|
|
4966
|
-
|
|
4935
|
+
this.manager.timerManager.setTimeout("fit-view-layout", () => {
|
|
4936
|
+
if (this.manager.fitViewCallback) {
|
|
4937
|
+
this.manager.fitViewCallback();
|
|
4938
|
+
}
|
|
4939
|
+
}, 50);
|
|
4940
|
+
}, 200);
|
|
4967
4941
|
}
|
|
4968
4942
|
});
|
|
4969
4943
|
this.manager.addCleanup(cleanupResize);
|
|
@@ -5033,15 +5007,23 @@ var RenderPipeline = class {
|
|
|
5033
5007
|
config: this.manager.config.simulation
|
|
5034
5008
|
};
|
|
5035
5009
|
try {
|
|
5036
|
-
const
|
|
5010
|
+
const simulationConfigWithCallback = {
|
|
5011
|
+
...simulationConfig,
|
|
5012
|
+
onReady: () => {
|
|
5013
|
+
if (this.manager.fitViewCallback) {
|
|
5014
|
+
this.manager.fitViewCallback();
|
|
5015
|
+
}
|
|
5016
|
+
},
|
|
5017
|
+
timerManager: this.manager.timerManager ?? void 0
|
|
5018
|
+
};
|
|
5019
|
+
const simulationResult = createGraphSimulation(simulationConfigWithCallback);
|
|
5037
5020
|
this.manager.simulation = simulationResult.simulation;
|
|
5038
5021
|
const centerX = simulationConfig.width / 2;
|
|
5039
5022
|
const centerY = simulationConfig.height / 2;
|
|
5040
5023
|
this.manager.simulation.force("center", center_default(centerX, centerY));
|
|
5041
|
-
if (
|
|
5024
|
+
if (simulationConfigWithCallback.width > 0 && simulationConfigWithCallback.height > 0) {
|
|
5042
5025
|
this.manager.reheatSimulation(0.3);
|
|
5043
5026
|
this.manager.simulationPaused = false;
|
|
5044
|
-
this.manager.needsImmediateFitView = true;
|
|
5045
5027
|
} else {
|
|
5046
5028
|
this.manager.simulation.stop();
|
|
5047
5029
|
this.manager.simulationPaused = true;
|
|
@@ -5100,14 +5082,35 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
5100
5082
|
if (hoverStyle) {
|
|
5101
5083
|
nodeSelection.on("mouseenter.hover", function(_event, node) {
|
|
5102
5084
|
const circle = this;
|
|
5085
|
+
if (circle.dataset.selected === "true") {
|
|
5086
|
+
return;
|
|
5087
|
+
}
|
|
5103
5088
|
circle.setAttribute("stroke", hoverStyle.stroke ?? node.style?.stroke ?? "#ffffff");
|
|
5104
5089
|
circle.setAttribute("stroke-width", String(hoverStyle.strokeWidth ?? node.style?.strokeWidth ?? 1.5));
|
|
5105
5090
|
circle.setAttribute("opacity", String(hoverStyle.opacity ?? node.style?.opacity ?? 1));
|
|
5106
5091
|
}).on("mouseleave.hover", function(_event, node) {
|
|
5107
5092
|
const circle = this;
|
|
5108
|
-
circle.
|
|
5109
|
-
|
|
5110
|
-
|
|
5093
|
+
if (circle.dataset.selected === "true") {
|
|
5094
|
+
return;
|
|
5095
|
+
}
|
|
5096
|
+
circle.style.stroke = "";
|
|
5097
|
+
circle.style.strokeWidth = "";
|
|
5098
|
+
circle.style.opacity = "";
|
|
5099
|
+
if (!node.style?.stroke) {
|
|
5100
|
+
circle.setAttribute("stroke", "#ffffff");
|
|
5101
|
+
} else {
|
|
5102
|
+
circle.setAttribute("stroke", node.style.stroke);
|
|
5103
|
+
}
|
|
5104
|
+
if (!node.style?.strokeWidth) {
|
|
5105
|
+
circle.setAttribute("stroke-width", "1.5");
|
|
5106
|
+
} else {
|
|
5107
|
+
circle.setAttribute("stroke-width", String(node.style.strokeWidth));
|
|
5108
|
+
}
|
|
5109
|
+
if (!node.style?.opacity) {
|
|
5110
|
+
circle.setAttribute("opacity", "1");
|
|
5111
|
+
} else {
|
|
5112
|
+
circle.setAttribute("opacity", String(node.style.opacity));
|
|
5113
|
+
}
|
|
5111
5114
|
});
|
|
5112
5115
|
}
|
|
5113
5116
|
const svgElement = firstNode.ownerSVGElement;
|
|
@@ -5504,6 +5507,8 @@ function bindNodeTooltip(params) {
|
|
|
5504
5507
|
destroy: () => {
|
|
5505
5508
|
},
|
|
5506
5509
|
reposition: () => {
|
|
5510
|
+
},
|
|
5511
|
+
hide: () => {
|
|
5507
5512
|
}
|
|
5508
5513
|
};
|
|
5509
5514
|
}
|
|
@@ -5513,6 +5518,9 @@ function bindNodeTooltip(params) {
|
|
|
5513
5518
|
"mouseenter.tooltip",
|
|
5514
5519
|
function(event, node) {
|
|
5515
5520
|
const target = this;
|
|
5521
|
+
if (target.dataset.selected === "true") {
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5516
5524
|
activeTarget = target;
|
|
5517
5525
|
const customContent = params.tooltipConfig?.renderContent?.(node);
|
|
5518
5526
|
const content = customContent ?? getDefaultContent(node);
|
|
@@ -5522,6 +5530,11 @@ function bindNodeTooltip(params) {
|
|
|
5522
5530
|
"mousemove.tooltip",
|
|
5523
5531
|
function() {
|
|
5524
5532
|
const target = this;
|
|
5533
|
+
if (target.dataset.selected === "true") {
|
|
5534
|
+
activeTarget = null;
|
|
5535
|
+
tooltip.hide();
|
|
5536
|
+
return;
|
|
5537
|
+
}
|
|
5525
5538
|
activeTarget = target;
|
|
5526
5539
|
tooltip.move(target);
|
|
5527
5540
|
}
|
|
@@ -5538,12 +5551,16 @@ function bindNodeTooltip(params) {
|
|
|
5538
5551
|
}
|
|
5539
5552
|
tooltip.move(activeTarget);
|
|
5540
5553
|
}
|
|
5554
|
+
function hide() {
|
|
5555
|
+
activeTarget = null;
|
|
5556
|
+
tooltip.hide();
|
|
5557
|
+
}
|
|
5541
5558
|
function destroy() {
|
|
5542
5559
|
activeTarget = null;
|
|
5543
5560
|
params.selection.on(".tooltip", null);
|
|
5544
5561
|
tooltip.destroy();
|
|
5545
5562
|
}
|
|
5546
|
-
return { destroy, reposition };
|
|
5563
|
+
return { destroy, reposition, hide };
|
|
5547
5564
|
}
|
|
5548
5565
|
function getDefaultContent(node) {
|
|
5549
5566
|
return `
|
|
@@ -5773,17 +5790,22 @@ var SelectionManager = class {
|
|
|
5773
5790
|
layers;
|
|
5774
5791
|
linkMarkerSnapshots;
|
|
5775
5792
|
root;
|
|
5776
|
-
|
|
5793
|
+
tooltipBinding;
|
|
5794
|
+
constructor(eventEmitter, config, layers, linkMarkerSnapshots, root2, tooltipBinding) {
|
|
5777
5795
|
this.eventEmitter = eventEmitter;
|
|
5778
5796
|
this.config = config;
|
|
5779
5797
|
this.layers = layers;
|
|
5780
5798
|
this.linkMarkerSnapshots = linkMarkerSnapshots;
|
|
5781
5799
|
this.root = root2;
|
|
5800
|
+
this.tooltipBinding = tooltipBinding;
|
|
5782
5801
|
}
|
|
5783
5802
|
/**
|
|
5784
5803
|
* Select a node, automatically deselecting any current selection
|
|
5785
5804
|
*/
|
|
5786
5805
|
selectNode(nodeElement, nodeData) {
|
|
5806
|
+
if (this.tooltipBinding) {
|
|
5807
|
+
this.tooltipBinding.hide();
|
|
5808
|
+
}
|
|
5787
5809
|
this.clearHoverState();
|
|
5788
5810
|
this.clearSelection();
|
|
5789
5811
|
this.bringNodeToFront(nodeElement, nodeData);
|
|
@@ -6067,6 +6089,27 @@ var SelectionManager = class {
|
|
|
6067
6089
|
}
|
|
6068
6090
|
};
|
|
6069
6091
|
|
|
6092
|
+
// src/utils/resolve-node-style.ts
|
|
6093
|
+
var DEFAULT_NODE_HOVER_STYLE = {
|
|
6094
|
+
stroke: `color-mix(in srgb, ${"#8E42EE" /* PURPLE */}, ${"#000000" /* BLACK */} 20%)`,
|
|
6095
|
+
strokeWidth: 3
|
|
6096
|
+
};
|
|
6097
|
+
function resolveNodeStyle(params) {
|
|
6098
|
+
if (params.isSelected) {
|
|
6099
|
+
return mergeNodeStyle(DEFAULT_NODE_HOVER_STYLE, params.interaction?.selection?.nodeStyle);
|
|
6100
|
+
}
|
|
6101
|
+
if (params.isHovered) {
|
|
6102
|
+
return mergeNodeStyle(DEFAULT_NODE_HOVER_STYLE, params.interaction?.hover?.nodeStyle);
|
|
6103
|
+
}
|
|
6104
|
+
return void 0;
|
|
6105
|
+
}
|
|
6106
|
+
function mergeNodeStyle(base, override) {
|
|
6107
|
+
return {
|
|
6108
|
+
...base,
|
|
6109
|
+
...override
|
|
6110
|
+
};
|
|
6111
|
+
}
|
|
6112
|
+
|
|
6070
6113
|
// src/core/interaction-manager.ts
|
|
6071
6114
|
var InteractionManager = class {
|
|
6072
6115
|
constructor(manager) {
|
|
@@ -6118,7 +6161,13 @@ var InteractionManager = class {
|
|
|
6118
6161
|
tooltipConfig: this.manager.config.interaction.hover.tooltip
|
|
6119
6162
|
});
|
|
6120
6163
|
}
|
|
6121
|
-
|
|
6164
|
+
const defaultNodeHoverStyle = resolveNodeStyle({
|
|
6165
|
+
node: {},
|
|
6166
|
+
// We don't need the actual node for defaults
|
|
6167
|
+
interaction: this.manager.config.interaction,
|
|
6168
|
+
isHovered: true
|
|
6169
|
+
});
|
|
6170
|
+
createNodeHover(selections.nodeSelection, defaultNodeHoverStyle);
|
|
6122
6171
|
createLinkHover(selections.linkSelection, this.manager.config.interaction.hover.linkStyle);
|
|
6123
6172
|
}
|
|
6124
6173
|
}
|
|
@@ -6158,7 +6207,8 @@ var InteractionManager = class {
|
|
|
6158
6207
|
this.manager.config.interaction.selection,
|
|
6159
6208
|
this.manager.layers,
|
|
6160
6209
|
this.manager.linkMarkerSnapshots,
|
|
6161
|
-
this.manager.rootSelection
|
|
6210
|
+
this.manager.rootSelection,
|
|
6211
|
+
this.manager.tooltipBinding || void 0
|
|
6162
6212
|
);
|
|
6163
6213
|
this.setupSelectionHandlers(selections);
|
|
6164
6214
|
this.setupBackgroundClickHandler();
|
|
@@ -6438,6 +6488,7 @@ async function captureAndDownloadGraph(container, options = {}) {
|
|
|
6438
6488
|
}
|
|
6439
6489
|
svgClone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
6440
6490
|
svgClone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
6491
|
+
fixTextStyling(svgClone);
|
|
6441
6492
|
const svgString = new XMLSerializer().serializeToString(svgClone);
|
|
6442
6493
|
const svgBlob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" });
|
|
6443
6494
|
const svgUrl = URL.createObjectURL(svgBlob);
|
|
@@ -6584,6 +6635,64 @@ function createLegendSVGElement(legendEntries, dimensions) {
|
|
|
6584
6635
|
});
|
|
6585
6636
|
return legendGroup;
|
|
6586
6637
|
}
|
|
6638
|
+
function fixTextStyling(svgElement) {
|
|
6639
|
+
const nodeLabelsLayer = svgElement.querySelector('[data-layer="node-labels"]');
|
|
6640
|
+
if (nodeLabelsLayer) {
|
|
6641
|
+
const nodeTexts = nodeLabelsLayer.querySelectorAll("text");
|
|
6642
|
+
nodeTexts.forEach((text) => {
|
|
6643
|
+
const textElement = text;
|
|
6644
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6645
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6646
|
+
textElement.setAttribute("font-size", "9");
|
|
6647
|
+
}
|
|
6648
|
+
textElement.setAttribute("font-weight", "500");
|
|
6649
|
+
textElement.setAttribute("text-anchor", "middle");
|
|
6650
|
+
textElement.setAttribute("dominant-baseline", "central");
|
|
6651
|
+
if (!textElement.getAttribute("fill")) {
|
|
6652
|
+
textElement.setAttribute("fill", "#374151");
|
|
6653
|
+
}
|
|
6654
|
+
});
|
|
6655
|
+
}
|
|
6656
|
+
const linkLabels = svgElement.querySelectorAll("g.link-label");
|
|
6657
|
+
linkLabels.forEach((labelGroup) => {
|
|
6658
|
+
const textElement = labelGroup.querySelector("text");
|
|
6659
|
+
if (textElement) {
|
|
6660
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6661
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6662
|
+
textElement.setAttribute("font-size", "10");
|
|
6663
|
+
}
|
|
6664
|
+
textElement.setAttribute("font-weight", "400");
|
|
6665
|
+
textElement.setAttribute("text-anchor", "middle");
|
|
6666
|
+
textElement.setAttribute("dominant-baseline", "central");
|
|
6667
|
+
if (!textElement.getAttribute("fill")) {
|
|
6668
|
+
textElement.setAttribute("fill", "#6b7280");
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
6671
|
+
const rectElement = labelGroup.querySelector("rect");
|
|
6672
|
+
if (rectElement) {
|
|
6673
|
+
if (!rectElement.getAttribute("fill")) {
|
|
6674
|
+
rectElement.setAttribute("fill", "#ffffff");
|
|
6675
|
+
}
|
|
6676
|
+
if (!rectElement.getAttribute("stroke")) {
|
|
6677
|
+
rectElement.setAttribute("stroke", "#e5e7eb");
|
|
6678
|
+
}
|
|
6679
|
+
if (!rectElement.getAttribute("stroke-width")) {
|
|
6680
|
+
rectElement.setAttribute("stroke-width", "1");
|
|
6681
|
+
}
|
|
6682
|
+
rectElement.setAttribute("rx", "4");
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
const allTexts = svgElement.querySelectorAll("text");
|
|
6686
|
+
allTexts.forEach((text) => {
|
|
6687
|
+
const textElement = text;
|
|
6688
|
+
if (!textElement.getAttribute("font-family")) {
|
|
6689
|
+
textElement.setAttribute("font-family", 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif');
|
|
6690
|
+
}
|
|
6691
|
+
if (!textElement.getAttribute("font-size")) {
|
|
6692
|
+
textElement.setAttribute("font-size", "10");
|
|
6693
|
+
}
|
|
6694
|
+
});
|
|
6695
|
+
}
|
|
6587
6696
|
function normalizeColor(color2) {
|
|
6588
6697
|
const rgbMatch = color2.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
6589
6698
|
if (rgbMatch) {
|
|
@@ -6916,10 +7025,6 @@ function createGraph(config) {
|
|
|
6916
7025
|
renderPipeline.execute().then((selections) => {
|
|
6917
7026
|
interactionManager.setupInteractions(selections);
|
|
6918
7027
|
setupAdditionalComponents();
|
|
6919
|
-
if (graphManager.needsImmediateFitView) {
|
|
6920
|
-
graphManager.needsImmediateFitView = false;
|
|
6921
|
-
fitViewWithInitialPositions();
|
|
6922
|
-
}
|
|
6923
7028
|
}).catch((error) => {
|
|
6924
7029
|
console.error("[Polly Graph] Render failed:", error);
|
|
6925
7030
|
});
|
|
@@ -6990,56 +7095,34 @@ function createGraph(config) {
|
|
|
6990
7095
|
const svg = graphManager.svgElement;
|
|
6991
7096
|
const nodes = config.nodes;
|
|
6992
7097
|
if (nodes.length === 0) return;
|
|
6993
|
-
const
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
const xExtent = extent(positions, (d) => d.x);
|
|
6998
|
-
const yExtent = extent(positions, (d) => d.y);
|
|
6999
|
-
const padding = 50;
|
|
7000
|
-
const width = graphManager.dimensions.width - padding * 2;
|
|
7001
|
-
const height = graphManager.dimensions.height - padding * 2;
|
|
7002
|
-
const nodeWidth = xExtent[1] - xExtent[0];
|
|
7003
|
-
const nodeHeight = yExtent[1] - yExtent[0];
|
|
7004
|
-
if (nodeWidth === 0 || nodeHeight === 0) {
|
|
7098
|
+
const graphRoot = select_default2(svg).select('[data-layer="viewport"]');
|
|
7099
|
+
const graphRootNode = graphRoot.node();
|
|
7100
|
+
if (!graphRootNode) {
|
|
7101
|
+
console.warn("[Polly Graph] Cannot fit view: graph root not found");
|
|
7005
7102
|
return;
|
|
7006
7103
|
}
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
select_default2(svg).transition().duration(400).call(graphManager.zoomBehavior.transform, transform2);
|
|
7013
|
-
}
|
|
7014
|
-
}
|
|
7015
|
-
function fitViewWithInitialPositions() {
|
|
7016
|
-
if (!graphManager.simulation || !graphManager.svgElement) {
|
|
7017
|
-
console.warn("[Polly Graph] Cannot fit view: simulation or SVG not available");
|
|
7104
|
+
let bounds;
|
|
7105
|
+
try {
|
|
7106
|
+
bounds = graphRootNode.getBBox();
|
|
7107
|
+
} catch {
|
|
7108
|
+
console.warn("[Polly Graph] Cannot get bounds, falling back to default view");
|
|
7018
7109
|
return;
|
|
7019
7110
|
}
|
|
7020
|
-
|
|
7021
|
-
const nodes = config.nodes;
|
|
7022
|
-
if (nodes.length === 0) return;
|
|
7023
|
-
const positions = nodes.map((node) => ({
|
|
7024
|
-
x: node.initialX ?? node.x ?? 0,
|
|
7025
|
-
y: node.initialY ?? node.y ?? 0
|
|
7026
|
-
}));
|
|
7027
|
-
const xExtent = extent(positions, (d) => d.x);
|
|
7028
|
-
const yExtent = extent(positions, (d) => d.y);
|
|
7029
|
-
const padding = 50;
|
|
7030
|
-
const width = graphManager.dimensions.width - padding * 2;
|
|
7031
|
-
const height = graphManager.dimensions.height - padding * 2;
|
|
7032
|
-
const nodeWidth = xExtent[1] - xExtent[0];
|
|
7033
|
-
const nodeHeight = yExtent[1] - yExtent[0];
|
|
7034
|
-
if (nodeWidth === 0 || nodeHeight === 0) {
|
|
7111
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
7035
7112
|
return;
|
|
7036
7113
|
}
|
|
7037
|
-
const
|
|
7038
|
-
const
|
|
7039
|
-
const
|
|
7040
|
-
const
|
|
7114
|
+
const svgRect = svg.getBoundingClientRect();
|
|
7115
|
+
const padding = 40;
|
|
7116
|
+
const availableWidth = svgRect.width - padding * 2;
|
|
7117
|
+
const availableHeight = svgRect.height - padding * 2;
|
|
7118
|
+
const scaleX = availableWidth / bounds.width;
|
|
7119
|
+
const scaleY = availableHeight / bounds.height;
|
|
7120
|
+
const scale = Math.min(scaleX, scaleY, 4);
|
|
7121
|
+
const centerX = bounds.x + bounds.width / 2;
|
|
7122
|
+
const centerY = bounds.y + bounds.height / 2;
|
|
7123
|
+
const transform2 = identity2.translate(svgRect.width / 2, svgRect.height / 2).scale(scale).translate(-centerX, -centerY);
|
|
7041
7124
|
if (graphManager.zoomBehavior) {
|
|
7042
|
-
select_default2(svg).transition().duration(
|
|
7125
|
+
select_default2(svg).transition().duration(750).call(graphManager.zoomBehavior.transform, transform2);
|
|
7043
7126
|
}
|
|
7044
7127
|
}
|
|
7045
7128
|
function exportGraph(fileName) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polly-graph",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Reusable D3-based graph visualization SDK with configurable nodes, links, labels, interactions, and layout behaviors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Badal",
|
|
@@ -52,11 +52,14 @@
|
|
|
52
52
|
"lint": "eslint \"src/**/*.ts\"",
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
54
|
"test": "vitest run --passWithNoTests",
|
|
55
|
-
"prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build"
|
|
55
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build",
|
|
56
|
+
"version:patch": "npm version patch",
|
|
57
|
+
"version:minor": "npm version minor",
|
|
58
|
+
"version:major": "npm version major",
|
|
59
|
+
"version:prerelease": "npm version prerelease"
|
|
56
60
|
},
|
|
57
61
|
"dependencies": {
|
|
58
|
-
"d3": "7.9.0"
|
|
59
|
-
"html2canvas": "^1.4.1"
|
|
62
|
+
"d3": "7.9.0"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"@eslint/js": "^9.39.4",
|