polly-graph 0.1.4 → 0.1.6
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 +82 -159
- package/dist/index.cjs +293 -181
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +293 -181
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3608,68 +3608,6 @@ function manyBody_default() {
|
|
|
3608
3608
|
return force;
|
|
3609
3609
|
}
|
|
3610
3610
|
|
|
3611
|
-
// node_modules/d3-force/src/x.js
|
|
3612
|
-
function x_default2(x3) {
|
|
3613
|
-
var strength = constant_default5(0.1), nodes, strengths, xz;
|
|
3614
|
-
if (typeof x3 !== "function") x3 = constant_default5(x3 == null ? 0 : +x3);
|
|
3615
|
-
function force(alpha) {
|
|
3616
|
-
for (var i = 0, n = nodes.length, node; i < n; ++i) {
|
|
3617
|
-
node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
function initialize() {
|
|
3621
|
-
if (!nodes) return;
|
|
3622
|
-
var i, n = nodes.length;
|
|
3623
|
-
strengths = new Array(n);
|
|
3624
|
-
xz = new Array(n);
|
|
3625
|
-
for (i = 0; i < n; ++i) {
|
|
3626
|
-
strengths[i] = isNaN(xz[i] = +x3(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
|
|
3627
|
-
}
|
|
3628
|
-
}
|
|
3629
|
-
force.initialize = function(_) {
|
|
3630
|
-
nodes = _;
|
|
3631
|
-
initialize();
|
|
3632
|
-
};
|
|
3633
|
-
force.strength = function(_) {
|
|
3634
|
-
return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default5(+_), initialize(), force) : strength;
|
|
3635
|
-
};
|
|
3636
|
-
force.x = function(_) {
|
|
3637
|
-
return arguments.length ? (x3 = typeof _ === "function" ? _ : constant_default5(+_), initialize(), force) : x3;
|
|
3638
|
-
};
|
|
3639
|
-
return force;
|
|
3640
|
-
}
|
|
3641
|
-
|
|
3642
|
-
// node_modules/d3-force/src/y.js
|
|
3643
|
-
function y_default2(y3) {
|
|
3644
|
-
var strength = constant_default5(0.1), nodes, strengths, yz;
|
|
3645
|
-
if (typeof y3 !== "function") y3 = constant_default5(y3 == null ? 0 : +y3);
|
|
3646
|
-
function force(alpha) {
|
|
3647
|
-
for (var i = 0, n = nodes.length, node; i < n; ++i) {
|
|
3648
|
-
node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
|
|
3649
|
-
}
|
|
3650
|
-
}
|
|
3651
|
-
function initialize() {
|
|
3652
|
-
if (!nodes) return;
|
|
3653
|
-
var i, n = nodes.length;
|
|
3654
|
-
strengths = new Array(n);
|
|
3655
|
-
yz = new Array(n);
|
|
3656
|
-
for (i = 0; i < n; ++i) {
|
|
3657
|
-
strengths[i] = isNaN(yz[i] = +y3(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
force.initialize = function(_) {
|
|
3661
|
-
nodes = _;
|
|
3662
|
-
initialize();
|
|
3663
|
-
};
|
|
3664
|
-
force.strength = function(_) {
|
|
3665
|
-
return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default5(+_), initialize(), force) : strength;
|
|
3666
|
-
};
|
|
3667
|
-
force.y = function(_) {
|
|
3668
|
-
return arguments.length ? (y3 = typeof _ === "function" ? _ : constant_default5(+_), initialize(), force) : y3;
|
|
3669
|
-
};
|
|
3670
|
-
return force;
|
|
3671
|
-
}
|
|
3672
|
-
|
|
3673
3611
|
// src/core/create-graph-layers.ts
|
|
3674
3612
|
function createGraphLayers(host) {
|
|
3675
3613
|
host.innerHTML = "";
|
|
@@ -3778,14 +3716,71 @@ function createGraphSimulation(config) {
|
|
|
3778
3716
|
});
|
|
3779
3717
|
const simulation = simulation_default(config.nodes).alpha(0.9).alphaDecay(0.12).alphaMin(0.03).velocityDecay(0.5).force(
|
|
3780
3718
|
"link",
|
|
3781
|
-
link_default(config.links).id((d) => d.id).distance(
|
|
3719
|
+
link_default(config.links).id((d) => d.id).distance((d) => {
|
|
3720
|
+
const source = d.source;
|
|
3721
|
+
const target = d.target;
|
|
3722
|
+
const sourceR = source.style?.radius || 20;
|
|
3723
|
+
const targetR = target.style?.radius || 20;
|
|
3724
|
+
const labelBuffer = d.style?.label?.height || 40;
|
|
3725
|
+
return (sourceR + targetR + labelBuffer) * 2;
|
|
3726
|
+
}).strength(0.8)
|
|
3782
3727
|
).force("charge", manyBody_default().strength(-220)).force(
|
|
3783
3728
|
"collide",
|
|
3784
|
-
collide_default().radius((node) => (node.style?.radius ?? 12) + 10).
|
|
3729
|
+
collide_default().radius((node) => (node.style?.radius ?? 12) + 10).iterations(2)
|
|
3785
3730
|
).force("center", center_default(centerX, centerY).strength(0.08));
|
|
3786
3731
|
return { simulation };
|
|
3787
3732
|
}
|
|
3788
3733
|
|
|
3734
|
+
// src/utils/get-link-marker-id.ts
|
|
3735
|
+
function getLinkMarkerId(style) {
|
|
3736
|
+
const markerStyle = {
|
|
3737
|
+
stroke: style.stroke ?? "#94a3b8",
|
|
3738
|
+
strokeWidth: style.strokeWidth ?? 2,
|
|
3739
|
+
arrowFill: style.arrow?.fill ?? style.stroke ?? "#94a3b8",
|
|
3740
|
+
arrowSize: style.arrow?.size ?? 6
|
|
3741
|
+
};
|
|
3742
|
+
const serializedStyle = JSON.stringify(markerStyle);
|
|
3743
|
+
const hash = createHash(serializedStyle);
|
|
3744
|
+
return `graph-arrow-${hash}`;
|
|
3745
|
+
}
|
|
3746
|
+
function createHash(value) {
|
|
3747
|
+
let hash = 0;
|
|
3748
|
+
for (let index2 = 0; index2 < value.length; index2 += 1) {
|
|
3749
|
+
const charCode = value.charCodeAt(index2);
|
|
3750
|
+
hash = (hash << 5) - hash + charCode;
|
|
3751
|
+
hash |= 0;
|
|
3752
|
+
}
|
|
3753
|
+
return Math.abs(hash).toString(36);
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
// src/core/create-arrow-marker.ts
|
|
3757
|
+
function createArrowMarker(params) {
|
|
3758
|
+
const markerId = getLinkMarkerId(params.style);
|
|
3759
|
+
const existingMarker = params.svg.querySelector(`#${markerId}`);
|
|
3760
|
+
if (existingMarker) {
|
|
3761
|
+
return markerId;
|
|
3762
|
+
}
|
|
3763
|
+
const arrowSize = params.style.arrow?.size ?? 6;
|
|
3764
|
+
const fill = params.style.arrow?.fill ?? params.style.stroke ?? "#94a3b8";
|
|
3765
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
3766
|
+
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
3767
|
+
marker.setAttribute("id", markerId);
|
|
3768
|
+
marker.setAttribute("viewBox", "0 0 20 20");
|
|
3769
|
+
marker.setAttribute("refX", "0");
|
|
3770
|
+
marker.setAttribute("refY", "10");
|
|
3771
|
+
marker.setAttribute("markerWidth", String(arrowSize * 2));
|
|
3772
|
+
marker.setAttribute("markerHeight", String(arrowSize * 2));
|
|
3773
|
+
marker.setAttribute("orient", "auto");
|
|
3774
|
+
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
3775
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
3776
|
+
path.setAttribute("d", "M 0 0 L 20 10 L 0 20 z");
|
|
3777
|
+
path.setAttribute("fill", fill);
|
|
3778
|
+
marker.appendChild(path);
|
|
3779
|
+
defs.appendChild(marker);
|
|
3780
|
+
params.svg.insertBefore(defs, params.svg.firstChild);
|
|
3781
|
+
return markerId;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3789
3784
|
// src/controls/graph-controls.utils.ts
|
|
3790
3785
|
function resolveControlsPosition(position) {
|
|
3791
3786
|
return position ?? "bottom-left";
|
|
@@ -3964,6 +3959,7 @@ var DEFAULT_LINK_STYLE = {
|
|
|
3964
3959
|
},
|
|
3965
3960
|
label: {
|
|
3966
3961
|
enabled: true,
|
|
3962
|
+
visibility: "always",
|
|
3967
3963
|
backgroundFill: "color-mix(in srgb, #8E42EE, #FFFFFF 90%)",
|
|
3968
3964
|
borderColor: "color-mix(in srgb, #8E42EE, #FFFFFF 10%)",
|
|
3969
3965
|
borderWidth: 1.5,
|
|
@@ -3999,6 +3995,7 @@ function mergeLinkStyle(base, override) {
|
|
|
3999
3995
|
},
|
|
4000
3996
|
label: {
|
|
4001
3997
|
enabled: override?.label?.enabled ?? base.label.enabled,
|
|
3998
|
+
visibility: override?.label?.visibility ?? base.label.visibility,
|
|
4002
3999
|
backgroundFill: override?.label?.backgroundFill ?? base.label.backgroundFill,
|
|
4003
4000
|
borderColor: override?.label?.borderColor ?? base.label.borderColor,
|
|
4004
4001
|
borderWidth: override?.label?.borderWidth ?? base.label.borderWidth,
|
|
@@ -4012,56 +4009,6 @@ function mergeLinkStyle(base, override) {
|
|
|
4012
4009
|
};
|
|
4013
4010
|
}
|
|
4014
4011
|
|
|
4015
|
-
// src/utils/get-link-marker-id.ts
|
|
4016
|
-
function getLinkMarkerId(style) {
|
|
4017
|
-
const markerStyle = {
|
|
4018
|
-
stroke: style.stroke ?? "#94a3b8",
|
|
4019
|
-
strokeWidth: style.strokeWidth ?? 2,
|
|
4020
|
-
arrowFill: style.arrow?.fill ?? style.stroke ?? "#94a3b8",
|
|
4021
|
-
arrowSize: style.arrow?.size ?? 6
|
|
4022
|
-
};
|
|
4023
|
-
const serializedStyle = JSON.stringify(markerStyle);
|
|
4024
|
-
const hash = createHash(serializedStyle);
|
|
4025
|
-
return `graph-arrow-${hash}`;
|
|
4026
|
-
}
|
|
4027
|
-
function createHash(value) {
|
|
4028
|
-
let hash = 0;
|
|
4029
|
-
for (let index2 = 0; index2 < value.length; index2 += 1) {
|
|
4030
|
-
const charCode = value.charCodeAt(index2);
|
|
4031
|
-
hash = (hash << 5) - hash + charCode;
|
|
4032
|
-
hash |= 0;
|
|
4033
|
-
}
|
|
4034
|
-
return Math.abs(hash).toString(36);
|
|
4035
|
-
}
|
|
4036
|
-
|
|
4037
|
-
// src/core/create-arrow-marker.ts
|
|
4038
|
-
function createArrowMarker(params) {
|
|
4039
|
-
const markerId = getLinkMarkerId(params.style);
|
|
4040
|
-
const existingMarker = params.svg.querySelector(`#${markerId}`);
|
|
4041
|
-
if (existingMarker) {
|
|
4042
|
-
return markerId;
|
|
4043
|
-
}
|
|
4044
|
-
const arrowSize = params.style.arrow?.size ?? 6;
|
|
4045
|
-
const fill = params.style.arrow?.fill ?? params.style.stroke ?? "#94a3b8";
|
|
4046
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
4047
|
-
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
4048
|
-
marker.setAttribute("id", markerId);
|
|
4049
|
-
marker.setAttribute("viewBox", "0 0 20 20");
|
|
4050
|
-
marker.setAttribute("refX", "0");
|
|
4051
|
-
marker.setAttribute("refY", "10");
|
|
4052
|
-
marker.setAttribute("markerWidth", String(arrowSize * 2));
|
|
4053
|
-
marker.setAttribute("markerHeight", String(arrowSize * 2));
|
|
4054
|
-
marker.setAttribute("orient", "auto");
|
|
4055
|
-
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
4056
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
4057
|
-
path.setAttribute("d", "M 0 0 L 20 10 L 0 20 z");
|
|
4058
|
-
path.setAttribute("fill", fill);
|
|
4059
|
-
marker.appendChild(path);
|
|
4060
|
-
defs.appendChild(marker);
|
|
4061
|
-
params.svg.insertBefore(defs, params.svg.firstChild);
|
|
4062
|
-
return markerId;
|
|
4063
|
-
}
|
|
4064
|
-
|
|
4065
4012
|
// src/renderer/links.ts
|
|
4066
4013
|
function getShortenedTargetPoint(link, style) {
|
|
4067
4014
|
const source = link.source;
|
|
@@ -4099,7 +4046,14 @@ function getLinkKey(link) {
|
|
|
4099
4046
|
}
|
|
4100
4047
|
function renderLinks(ctx, links) {
|
|
4101
4048
|
const renderableLinks = createRenderableLinks(ctx, links);
|
|
4102
|
-
|
|
4049
|
+
const linkSelection = ctx.root.select('[data-layer="links"]').selectAll("line").data(renderableLinks, (item) => getLinkKey(item.link)).join("line").attr("class", "graph-link").attr("stroke", (item) => item.style.stroke).attr("stroke-width", (item) => item.style.strokeWidth).attr("opacity", (item) => item.style.opacity).attr("marker-end", (item) => item.markerEnd).style("pointer-events", "stroke").style("cursor", "pointer");
|
|
4050
|
+
const labelSelection = ctx.root.selectAll(".link-label");
|
|
4051
|
+
linkSelection.on("mouseenter.label-hover", (_event, d) => {
|
|
4052
|
+
labelSelection.filter((labelItem) => labelItem.link === d.link && labelItem.style.label.visibility === "hover").interrupt().transition().duration(200).style("opacity", 1);
|
|
4053
|
+
}).on("mouseleave.label-hover", (_event, d) => {
|
|
4054
|
+
labelSelection.filter((labelItem) => labelItem.link === d.link && labelItem.style.label.visibility === "hover").interrupt().transition().duration(200).style("opacity", 0);
|
|
4055
|
+
});
|
|
4056
|
+
return linkSelection;
|
|
4103
4057
|
}
|
|
4104
4058
|
|
|
4105
4059
|
// src/renderer/nodes.ts
|
|
@@ -4169,7 +4123,10 @@ function renderNodeLabels(ctx, nodes) {
|
|
|
4169
4123
|
// src/renderer/link-labels.ts
|
|
4170
4124
|
function createRenderableLinks2(params, links) {
|
|
4171
4125
|
return links.map(
|
|
4172
|
-
(link) => ({
|
|
4126
|
+
(link) => ({
|
|
4127
|
+
link,
|
|
4128
|
+
style: resolveLinkStyle({ link, interaction: params.interaction })
|
|
4129
|
+
})
|
|
4173
4130
|
).filter(
|
|
4174
4131
|
(item) => item.style.label.enabled && Boolean(item.link.label)
|
|
4175
4132
|
);
|
|
@@ -4181,7 +4138,13 @@ function getLinkKey2(link) {
|
|
|
4181
4138
|
}
|
|
4182
4139
|
function renderLinkLabels(params, links) {
|
|
4183
4140
|
const renderableLinks = createRenderableLinks2(params, links);
|
|
4184
|
-
const labelSelection = params.root.select('[data-layer="link-labels"]').selectAll(".link-label").data(renderableLinks, (item) => getLinkKey2(item.link)).join("g").attr("class", "link-label").
|
|
4141
|
+
const labelSelection = params.root.select('[data-layer="link-labels"]').selectAll(".link-label").data(renderableLinks, (item) => getLinkKey2(item.link)).join("g").attr("class", "link-label").style("opacity", (item) => {
|
|
4142
|
+
const visibility = item.style.label.visibility ?? "always";
|
|
4143
|
+
return visibility === "always" ? 1 : 0;
|
|
4144
|
+
}).style("pointer-events", (item) => {
|
|
4145
|
+
const visibility = item.style.label.visibility ?? "always";
|
|
4146
|
+
return visibility === "always" ? "auto" : "none";
|
|
4147
|
+
}).style("cursor", "pointer");
|
|
4185
4148
|
labelSelection.selectAll("rect").data((item) => [item]).join("rect").attr("rx", (item) => item.style.label.borderRadius).attr("ry", (item) => item.style.label.borderRadius).attr("height", (item) => item.style.label.height).attr("fill", (item) => item.style.label.backgroundFill).attr("stroke", (item) => item.style.label.borderColor).attr("stroke-width", (item) => item.style.label.borderWidth);
|
|
4186
4149
|
labelSelection.selectAll("text").data((item) => [item]).join("text").attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("font-size", (item) => item.style.label.fontSize).attr("fill", (item) => item.style.label.textColor).text((item) => item.link.label ?? "");
|
|
4187
4150
|
return labelSelection;
|
|
@@ -4210,58 +4173,37 @@ function createDragBehavior(simulation) {
|
|
|
4210
4173
|
|
|
4211
4174
|
// src/interactions/create-node-hover.ts
|
|
4212
4175
|
function createNodeHover(nodeSelection, hoverStyle) {
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
"mouseenter.hover",
|
|
4218
|
-
function(_event, node) {
|
|
4176
|
+
const firstNode = nodeSelection.node();
|
|
4177
|
+
if (!firstNode) return;
|
|
4178
|
+
if (hoverStyle) {
|
|
4179
|
+
nodeSelection.on("mouseenter.hover", function(_event, node) {
|
|
4219
4180
|
const circle = this;
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
"stroke",
|
|
4225
|
-
hoverStroke
|
|
4226
|
-
);
|
|
4227
|
-
circle.setAttribute(
|
|
4228
|
-
"stroke-width",
|
|
4229
|
-
String(
|
|
4230
|
-
hoverStrokeWidth
|
|
4231
|
-
)
|
|
4232
|
-
);
|
|
4233
|
-
circle.setAttribute(
|
|
4234
|
-
"opacity",
|
|
4235
|
-
String(
|
|
4236
|
-
hoverOpacity
|
|
4237
|
-
)
|
|
4238
|
-
);
|
|
4239
|
-
}
|
|
4240
|
-
).on(
|
|
4241
|
-
"mouseleave.hover",
|
|
4242
|
-
function(_event, node) {
|
|
4181
|
+
circle.setAttribute("stroke", hoverStyle.stroke ?? node.style?.stroke ?? "#ffffff");
|
|
4182
|
+
circle.setAttribute("stroke-width", String(hoverStyle.strokeWidth ?? node.style?.strokeWidth ?? 1.5));
|
|
4183
|
+
circle.setAttribute("opacity", String(hoverStyle.opacity ?? node.style?.opacity ?? 1));
|
|
4184
|
+
}).on("mouseleave.hover", function(_event, node) {
|
|
4243
4185
|
const circle = this;
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
);
|
|
4263
|
-
}
|
|
4264
|
-
);
|
|
4186
|
+
circle.setAttribute("stroke", node.style?.stroke ?? "#ffffff");
|
|
4187
|
+
circle.setAttribute("stroke-width", String(node.style?.strokeWidth ?? 1.5));
|
|
4188
|
+
circle.setAttribute("opacity", String(node.style?.opacity ?? 1));
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
const svgElement = firstNode.ownerSVGElement;
|
|
4192
|
+
if (!svgElement) return;
|
|
4193
|
+
const root2 = select_default2(svgElement);
|
|
4194
|
+
const labelSelection = root2.selectAll(".link-label");
|
|
4195
|
+
nodeSelection.on("mouseenter.labels", (_event, d) => {
|
|
4196
|
+
labelSelection.filter((item) => {
|
|
4197
|
+
if (item.style.label.visibility !== "hover") return false;
|
|
4198
|
+
const s = item.link.source;
|
|
4199
|
+
const t = item.link.target;
|
|
4200
|
+
return s.id === d.id || t.id === d.id;
|
|
4201
|
+
}).interrupt().transition().duration(200).style("opacity", 1).style("pointer-events", "auto");
|
|
4202
|
+
}).on("mouseleave.labels", (_event) => {
|
|
4203
|
+
labelSelection.filter(function(item) {
|
|
4204
|
+
return item.style.label.visibility === "hover" && !this.classList.contains("label-selection-pinned");
|
|
4205
|
+
}).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
4206
|
+
});
|
|
4265
4207
|
}
|
|
4266
4208
|
|
|
4267
4209
|
// src/utils/resolve-tooltip-position.ts
|
|
@@ -4553,11 +4495,33 @@ function createGraph(config) {
|
|
|
4553
4495
|
let tooltipBinding = null;
|
|
4554
4496
|
let controls = null;
|
|
4555
4497
|
let legendCleanup = null;
|
|
4498
|
+
let fitViewTimer = null;
|
|
4556
4499
|
let dimensions = { width: 0, height: 0 };
|
|
4557
4500
|
let rootGroup = null;
|
|
4558
4501
|
let svgElement = null;
|
|
4559
4502
|
let zoomBehavior = null;
|
|
4560
4503
|
let simulation = null;
|
|
4504
|
+
const nodeSelectHandlers = /* @__PURE__ */ new Set();
|
|
4505
|
+
const linkSelectHandlers = /* @__PURE__ */ new Set();
|
|
4506
|
+
function on(event, handler) {
|
|
4507
|
+
if (event === "nodeSelect") {
|
|
4508
|
+
nodeSelectHandlers.add(handler);
|
|
4509
|
+
return () => {
|
|
4510
|
+
nodeSelectHandlers.delete(handler);
|
|
4511
|
+
};
|
|
4512
|
+
}
|
|
4513
|
+
linkSelectHandlers.add(handler);
|
|
4514
|
+
return () => {
|
|
4515
|
+
linkSelectHandlers.delete(handler);
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
function off(event, handler) {
|
|
4519
|
+
if (event === "nodeSelect") {
|
|
4520
|
+
nodeSelectHandlers.delete(handler);
|
|
4521
|
+
} else {
|
|
4522
|
+
linkSelectHandlers.delete(handler);
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4561
4525
|
function render() {
|
|
4562
4526
|
destroy();
|
|
4563
4527
|
const layers = createGraphLayers(config.container);
|
|
@@ -4569,9 +4533,17 @@ function createGraph(config) {
|
|
|
4569
4533
|
layers.svg.setAttribute("height", String(height));
|
|
4570
4534
|
layers.interactionRect.setAttribute("width", String(width));
|
|
4571
4535
|
layers.interactionRect.setAttribute("height", String(height));
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4536
|
+
if (simulation) {
|
|
4537
|
+
simulation.force("center", center_default(width / 2, height / 2));
|
|
4538
|
+
simulation.alpha(0.3).restart();
|
|
4539
|
+
}
|
|
4540
|
+
if (fitViewTimer) {
|
|
4541
|
+
clearTimeout(fitViewTimer);
|
|
4542
|
+
}
|
|
4543
|
+
fitViewTimer = setTimeout(() => {
|
|
4544
|
+
fitView();
|
|
4545
|
+
fitViewTimer = null;
|
|
4546
|
+
}, 150);
|
|
4575
4547
|
});
|
|
4576
4548
|
const zoomResult = createZoom({
|
|
4577
4549
|
svg: layers.svg,
|
|
@@ -4589,8 +4561,9 @@ function createGraph(config) {
|
|
|
4589
4561
|
const simulationConfig = {
|
|
4590
4562
|
nodes: config.nodes,
|
|
4591
4563
|
links: config.links,
|
|
4592
|
-
|
|
4593
|
-
|
|
4564
|
+
// Uses the observed dimensions to ensure physics are calculated on actual container size
|
|
4565
|
+
width: dimensions.width || config.container.clientWidth,
|
|
4566
|
+
height: dimensions.height || config.container.clientHeight
|
|
4594
4567
|
};
|
|
4595
4568
|
const simulationResult = createGraphSimulation(simulationConfig);
|
|
4596
4569
|
simulation = simulationResult.simulation;
|
|
@@ -4632,10 +4605,135 @@ function createGraph(config) {
|
|
|
4632
4605
|
if (config.interaction?.drag?.enabled !== false) {
|
|
4633
4606
|
nodeSelection.call(createDragBehavior(simulation));
|
|
4634
4607
|
}
|
|
4608
|
+
const selectionConfig = config.interaction?.selection;
|
|
4609
|
+
if (selectionConfig?.enabled) {
|
|
4610
|
+
let selectedNodeElement = null;
|
|
4611
|
+
let selectedLinkElement = null;
|
|
4612
|
+
const linkMarkerSnapshots = /* @__PURE__ */ new Map();
|
|
4613
|
+
linkSelection.each(function() {
|
|
4614
|
+
const linkElement = this;
|
|
4615
|
+
linkMarkerSnapshots.set(linkElement, linkElement.getAttribute("marker-end"));
|
|
4616
|
+
});
|
|
4617
|
+
const deselectNode = () => {
|
|
4618
|
+
if (!selectedNodeElement) {
|
|
4619
|
+
return;
|
|
4620
|
+
}
|
|
4621
|
+
const nodeElement = selectedNodeElement;
|
|
4622
|
+
nodeElement.style.fill = "";
|
|
4623
|
+
nodeElement.style.stroke = "";
|
|
4624
|
+
nodeElement.style.strokeWidth = "";
|
|
4625
|
+
nodeElement.style.opacity = "";
|
|
4626
|
+
nodeElement.style.removeProperty("r");
|
|
4627
|
+
root2.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
4628
|
+
selectedNodeElement = null;
|
|
4629
|
+
};
|
|
4630
|
+
const deselectLink = () => {
|
|
4631
|
+
if (!selectedLinkElement) {
|
|
4632
|
+
return;
|
|
4633
|
+
}
|
|
4634
|
+
const linkElement = selectedLinkElement;
|
|
4635
|
+
linkElement.style.stroke = "";
|
|
4636
|
+
linkElement.style.strokeWidth = "";
|
|
4637
|
+
linkElement.style.opacity = "";
|
|
4638
|
+
const originalMarkerEnd = linkMarkerSnapshots.get(linkElement);
|
|
4639
|
+
if (originalMarkerEnd) {
|
|
4640
|
+
linkElement.setAttribute("marker-end", originalMarkerEnd);
|
|
4641
|
+
} else {
|
|
4642
|
+
linkElement.removeAttribute("marker-end");
|
|
4643
|
+
}
|
|
4644
|
+
selectedLinkElement = null;
|
|
4645
|
+
};
|
|
4646
|
+
nodeSelection.on("click.select", function(event, node) {
|
|
4647
|
+
event.stopPropagation();
|
|
4648
|
+
const nodeElement = this;
|
|
4649
|
+
if (selectedNodeElement === nodeElement) {
|
|
4650
|
+
deselectNode();
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
deselectNode();
|
|
4654
|
+
deselectLink();
|
|
4655
|
+
selectedNodeElement = nodeElement;
|
|
4656
|
+
const nodeStyle = selectionConfig.nodeStyle;
|
|
4657
|
+
if (nodeStyle) {
|
|
4658
|
+
if (nodeStyle.fill !== void 0) {
|
|
4659
|
+
nodeElement.style.fill = nodeStyle.fill;
|
|
4660
|
+
}
|
|
4661
|
+
if (nodeStyle.stroke !== void 0) {
|
|
4662
|
+
nodeElement.style.stroke = nodeStyle.stroke;
|
|
4663
|
+
}
|
|
4664
|
+
if (nodeStyle.strokeWidth !== void 0) {
|
|
4665
|
+
nodeElement.style.strokeWidth = String(nodeStyle.strokeWidth);
|
|
4666
|
+
}
|
|
4667
|
+
if (nodeStyle.opacity !== void 0) {
|
|
4668
|
+
nodeElement.style.opacity = String(nodeStyle.opacity);
|
|
4669
|
+
}
|
|
4670
|
+
if (nodeStyle.radius !== void 0) {
|
|
4671
|
+
nodeElement.style.setProperty("r", String(nodeStyle.radius));
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
root2.selectAll(".link-label").filter((item) => {
|
|
4675
|
+
if (item.style.label.visibility !== "hover") {
|
|
4676
|
+
return false;
|
|
4677
|
+
}
|
|
4678
|
+
const source = item.link.source;
|
|
4679
|
+
const target = item.link.target;
|
|
4680
|
+
return source.id === node.id || target.id === node.id;
|
|
4681
|
+
}).classed("label-selection-pinned", true).interrupt().transition().duration(200).style("opacity", 1).style("pointer-events", "auto");
|
|
4682
|
+
nodeSelectHandlers.forEach((handler) => handler(node, nodeElement));
|
|
4683
|
+
});
|
|
4684
|
+
const selectLink = (event, renderableLink, linkElement) => {
|
|
4685
|
+
event.stopPropagation();
|
|
4686
|
+
if (selectedLinkElement === linkElement) {
|
|
4687
|
+
deselectLink();
|
|
4688
|
+
return;
|
|
4689
|
+
}
|
|
4690
|
+
deselectLink();
|
|
4691
|
+
deselectNode();
|
|
4692
|
+
selectedLinkElement = linkElement;
|
|
4693
|
+
const linkStyle = selectionConfig.linkStyle;
|
|
4694
|
+
if (linkStyle) {
|
|
4695
|
+
if (linkStyle.stroke !== void 0) {
|
|
4696
|
+
linkElement.style.stroke = linkStyle.stroke;
|
|
4697
|
+
}
|
|
4698
|
+
if (linkStyle.strokeWidth !== void 0) {
|
|
4699
|
+
linkElement.style.strokeWidth = String(linkStyle.strokeWidth);
|
|
4700
|
+
}
|
|
4701
|
+
if (linkStyle.opacity !== void 0) {
|
|
4702
|
+
linkElement.style.opacity = String(linkStyle.opacity);
|
|
4703
|
+
}
|
|
4704
|
+
if (linkStyle.stroke !== void 0 && renderableLink.style.arrow.enabled) {
|
|
4705
|
+
const selectionMarkerStyle = {
|
|
4706
|
+
stroke: linkStyle.stroke,
|
|
4707
|
+
arrow: { fill: linkStyle.stroke, size: renderableLink.style.arrow.size }
|
|
4708
|
+
};
|
|
4709
|
+
const selectionMarkerId = createArrowMarker({ svg: layers.svg, style: selectionMarkerStyle });
|
|
4710
|
+
select_default2(linkElement).attr("marker-end", `url(#${selectionMarkerId})`);
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
linkSelectHandlers.forEach((handler) => handler(renderableLink.link, linkElement));
|
|
4714
|
+
};
|
|
4715
|
+
linkSelection.on("click.select", function(event, renderableLink) {
|
|
4716
|
+
selectLink(event, renderableLink, this);
|
|
4717
|
+
});
|
|
4718
|
+
const linkHitAreaSelection = root2.select('[data-layer="links"]').selectAll("line.link-hit-area").data(linkSelection.data()).join("line").attr("class", "link-hit-area").attr("stroke", "rgba(0,0,0,0)").attr("stroke-width", (item) => item.style.arrow.size * 4).style("pointer-events", "stroke").style("cursor", "pointer").attr("opacity", 0);
|
|
4719
|
+
simulation.on("tick.hitarea", () => {
|
|
4720
|
+
linkHitAreaSelection.attr("x1", (item) => item.link.source.x ?? 0).attr("y1", (item) => item.link.source.y ?? 0).attr("x2", (item) => item.link.target.x ?? 0).attr("y2", (item) => item.link.target.y ?? 0);
|
|
4721
|
+
});
|
|
4722
|
+
linkHitAreaSelection.on("click.select", function(event, renderableLink) {
|
|
4723
|
+
const visibleLinkNode = linkSelection.filter((d) => d === renderableLink).node();
|
|
4724
|
+
if (visibleLinkNode) {
|
|
4725
|
+
selectLink(event, renderableLink, visibleLinkNode);
|
|
4726
|
+
}
|
|
4727
|
+
});
|
|
4728
|
+
select_default2(layers.svg).on("click.deselect", () => {
|
|
4729
|
+
deselectNode();
|
|
4730
|
+
deselectLink();
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4635
4733
|
if (config.controls?.enabled) {
|
|
4636
4734
|
controls = createGraphControls(
|
|
4637
4735
|
layers.overlay,
|
|
4638
|
-
{ zoomIn, zoomOut, resetView, fitView
|
|
4736
|
+
{ zoomIn, zoomOut, resetView, fitView },
|
|
4639
4737
|
config.controls
|
|
4640
4738
|
);
|
|
4641
4739
|
controls.mount();
|
|
@@ -4645,25 +4743,35 @@ function createGraph(config) {
|
|
|
4645
4743
|
}
|
|
4646
4744
|
}
|
|
4647
4745
|
function resetView() {
|
|
4648
|
-
if (!zoomBehavior || !svgElement)
|
|
4649
|
-
|
|
4746
|
+
if (!zoomBehavior || !svgElement) {
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, identity2);
|
|
4650
4750
|
}
|
|
4651
4751
|
function fitView() {
|
|
4652
|
-
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0)
|
|
4752
|
+
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0) {
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4653
4755
|
const bounds = rootGroup.getBBox();
|
|
4654
|
-
if (bounds.width === 0 || bounds.height === 0)
|
|
4756
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4655
4759
|
const scale = Math.min(dimensions.width / bounds.width, dimensions.height / bounds.height) * 0.9;
|
|
4656
4760
|
const translateX = (dimensions.width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4657
4761
|
const translateY = (dimensions.height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
4658
4762
|
const transform2 = identity2.translate(translateX, translateY).scale(scale);
|
|
4659
|
-
select_default2(svgElement).transition().call(zoomBehavior.transform, transform2);
|
|
4763
|
+
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, transform2);
|
|
4660
4764
|
}
|
|
4661
4765
|
function zoomIn() {
|
|
4662
|
-
if (!zoomBehavior || !svgElement)
|
|
4766
|
+
if (!zoomBehavior || !svgElement) {
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4663
4769
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 1.2);
|
|
4664
4770
|
}
|
|
4665
4771
|
function zoomOut() {
|
|
4666
|
-
if (!zoomBehavior || !svgElement)
|
|
4772
|
+
if (!zoomBehavior || !svgElement) {
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4667
4775
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 0.8);
|
|
4668
4776
|
}
|
|
4669
4777
|
async function exportGraph(fileName) {
|
|
@@ -4675,6 +4783,10 @@ function createGraph(config) {
|
|
|
4675
4783
|
});
|
|
4676
4784
|
}
|
|
4677
4785
|
function destroy() {
|
|
4786
|
+
if (fitViewTimer) {
|
|
4787
|
+
clearTimeout(fitViewTimer);
|
|
4788
|
+
fitViewTimer = null;
|
|
4789
|
+
}
|
|
4678
4790
|
if (cleanupResize) {
|
|
4679
4791
|
cleanupResize();
|
|
4680
4792
|
cleanupResize = null;
|
|
@@ -4706,7 +4818,7 @@ function createGraph(config) {
|
|
|
4706
4818
|
config.container.removeChild(config.container.firstChild);
|
|
4707
4819
|
}
|
|
4708
4820
|
}
|
|
4709
|
-
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph };
|
|
4821
|
+
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph, on, off };
|
|
4710
4822
|
}
|
|
4711
4823
|
export {
|
|
4712
4824
|
createGraph
|
package/package.json
CHANGED