polly-graph 0.1.3 → 0.1.4
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 +243 -182
- package/dist/index.css +221 -32
- package/dist/index.d.cts +22 -3
- package/dist/index.d.ts +22 -3
- package/dist/index.js +233 -182
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -3697,41 +3707,44 @@ function y_default2(y3) {
|
|
|
3697
3707
|
}
|
|
3698
3708
|
|
|
3699
3709
|
// src/core/create-graph-layers.ts
|
|
3700
|
-
function createGraphLayers(
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
const
|
|
3710
|
+
function createGraphLayers(host) {
|
|
3711
|
+
host.innerHTML = "";
|
|
3712
|
+
const rootContainer = document.createElement("div");
|
|
3713
|
+
rootContainer.className = "pg-root";
|
|
3714
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
3715
|
+
svg.setAttribute("class", "pg-canvas");
|
|
3716
|
+
const overlay = document.createElement("div");
|
|
3717
|
+
overlay.className = "pg-overlay";
|
|
3718
|
+
rootContainer.appendChild(svg);
|
|
3719
|
+
rootContainer.appendChild(overlay);
|
|
3720
|
+
host.appendChild(rootContainer);
|
|
3721
|
+
const createGroup = (layerName) => {
|
|
3705
3722
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
3706
|
-
group.setAttribute("class",
|
|
3707
|
-
group.setAttribute("data-layer",
|
|
3723
|
+
group.setAttribute("class", `pg-layer-${layerName}`);
|
|
3724
|
+
group.setAttribute("data-layer", layerName);
|
|
3708
3725
|
return group;
|
|
3709
3726
|
};
|
|
3710
3727
|
const interactionLayer = createGroup("interaction-layer");
|
|
3711
3728
|
const interactionRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
height: "100%",
|
|
3716
|
-
fill: "transparent",
|
|
3717
|
-
"pointer-events": "all"
|
|
3718
|
-
};
|
|
3719
|
-
Object.entries(interactionAttributes).forEach(([key, value]) => {
|
|
3720
|
-
interactionRect.setAttribute(key, value);
|
|
3721
|
-
});
|
|
3729
|
+
interactionRect.setAttribute("class", "pg-interaction-surface");
|
|
3730
|
+
interactionRect.setAttribute("fill", "transparent");
|
|
3731
|
+
interactionRect.setAttribute("pointer-events", "all");
|
|
3722
3732
|
interactionLayer.appendChild(interactionRect);
|
|
3723
|
-
const
|
|
3733
|
+
const graphRoot = createGroup("viewport");
|
|
3724
3734
|
const layers = {
|
|
3735
|
+
svg,
|
|
3736
|
+
overlay,
|
|
3725
3737
|
interactionLayer,
|
|
3726
3738
|
interactionRect,
|
|
3727
|
-
root:
|
|
3739
|
+
root: graphRoot,
|
|
3740
|
+
// These keys now match your ctx.root.select('[data-layer="..."]') calls
|
|
3728
3741
|
links: createGroup("links"),
|
|
3729
3742
|
linkLabels: createGroup("link-labels"),
|
|
3730
3743
|
nodeRings: createGroup("node-rings"),
|
|
3731
3744
|
nodes: createGroup("nodes"),
|
|
3732
3745
|
nodeLabels: createGroup("node-labels")
|
|
3733
3746
|
};
|
|
3734
|
-
|
|
3747
|
+
graphRoot.append(
|
|
3735
3748
|
layers.links,
|
|
3736
3749
|
layers.linkLabels,
|
|
3737
3750
|
layers.nodeRings,
|
|
@@ -3739,7 +3752,7 @@ function createGraphLayers(svg) {
|
|
|
3739
3752
|
layers.nodeLabels
|
|
3740
3753
|
);
|
|
3741
3754
|
svg.appendChild(interactionLayer);
|
|
3742
|
-
svg.appendChild(
|
|
3755
|
+
svg.appendChild(graphRoot);
|
|
3743
3756
|
return layers;
|
|
3744
3757
|
}
|
|
3745
3758
|
|
|
@@ -3856,49 +3869,51 @@ function getControlIcon(icon) {
|
|
|
3856
3869
|
}
|
|
3857
3870
|
|
|
3858
3871
|
// src/controls/create-graph-controls.ts
|
|
3859
|
-
function createGraphControls(
|
|
3872
|
+
function createGraphControls(overlay, graph, config) {
|
|
3860
3873
|
let root2 = null;
|
|
3861
3874
|
function mount() {
|
|
3862
3875
|
if (!config.enabled) {
|
|
3863
3876
|
return;
|
|
3864
3877
|
}
|
|
3865
|
-
const parent = container.parentElement;
|
|
3866
|
-
if (!parent) {
|
|
3867
|
-
return;
|
|
3868
|
-
}
|
|
3869
3878
|
root2 = document.createElement("div");
|
|
3870
3879
|
root2.className = "pg-controls";
|
|
3871
|
-
|
|
3872
|
-
|
|
3880
|
+
const position = resolveControlsPosition(config.position);
|
|
3881
|
+
root2.classList.add(`pg-pos-${position}`);
|
|
3882
|
+
const orientation = resolveControlsOrientation(config.orientation);
|
|
3883
|
+
root2.classList.add(`pg-orient-${orientation}`);
|
|
3884
|
+
if (config.offset) {
|
|
3885
|
+
root2.style.setProperty("--pg-controls-offset-x", `${config.offset.x}px`);
|
|
3886
|
+
root2.style.setProperty("--pg-controls-offset-y", `${config.offset.y}px`);
|
|
3887
|
+
}
|
|
3873
3888
|
appendControls(root2, config, graph);
|
|
3874
|
-
|
|
3889
|
+
overlay.appendChild(root2);
|
|
3875
3890
|
}
|
|
3876
3891
|
function appendControls(root3, config2, graph2) {
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
}
|
|
3892
|
+
const actions = [
|
|
3893
|
+
{ key: "zoomIn", icon: "zoom-in", label: "Zoom in", fn: graph2.zoomIn.bind(graph2) },
|
|
3894
|
+
{ key: "zoomOut", icon: "zoom-out", label: "Zoom out", fn: graph2.zoomOut.bind(graph2) },
|
|
3895
|
+
{ key: "fit", icon: "fit", label: "Fit view", fn: graph2.fitView.bind(graph2) },
|
|
3896
|
+
{ key: "reset", icon: "reset", label: "Reset view", fn: graph2.resetView.bind(graph2) }
|
|
3897
|
+
];
|
|
3898
|
+
actions.forEach((action) => {
|
|
3899
|
+
if (shouldRenderControl(config2, action.key)) {
|
|
3900
|
+
root3.appendChild(createButton(action.icon, action.label, action.fn));
|
|
3901
|
+
}
|
|
3902
|
+
});
|
|
3889
3903
|
}
|
|
3890
3904
|
function createButton(type, label, onClick) {
|
|
3891
3905
|
const button = document.createElement("button");
|
|
3906
|
+
button.className = "pg-control-btn";
|
|
3892
3907
|
button.type = "button";
|
|
3893
3908
|
button.setAttribute("aria-label", label);
|
|
3894
3909
|
const wrapper = document.createElement("div");
|
|
3910
|
+
wrapper.className = "pg-icon-wrapper";
|
|
3895
3911
|
wrapper.innerHTML = getControlIcon(type);
|
|
3896
3912
|
const svg = wrapper.querySelector("svg");
|
|
3897
|
-
if (
|
|
3898
|
-
|
|
3913
|
+
if (svg) {
|
|
3914
|
+
svg.classList.add("pg-icon");
|
|
3915
|
+
button.appendChild(svg);
|
|
3899
3916
|
}
|
|
3900
|
-
svg.classList.add("pg-icon");
|
|
3901
|
-
button.appendChild(svg);
|
|
3902
3917
|
button.addEventListener("click", onClick);
|
|
3903
3918
|
return button;
|
|
3904
3919
|
}
|
|
@@ -3906,39 +3921,70 @@ function createGraphControls(container, graph, config) {
|
|
|
3906
3921
|
if (!root2) {
|
|
3907
3922
|
return;
|
|
3908
3923
|
}
|
|
3909
|
-
|
|
3910
|
-
|
|
3924
|
+
if (root2.parentNode === overlay) {
|
|
3925
|
+
overlay.removeChild(root2);
|
|
3926
|
+
}
|
|
3911
3927
|
root2 = null;
|
|
3912
3928
|
}
|
|
3913
3929
|
return { mount, destroy };
|
|
3914
3930
|
}
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
el.style.right = `${offset.x}px`;
|
|
3926
|
-
el.style.bottom = `${offset.y}px`;
|
|
3927
|
-
break;
|
|
3928
|
-
case "top-left":
|
|
3929
|
-
el.style.left = `${offset.x}px`;
|
|
3930
|
-
el.style.top = `${offset.y}px`;
|
|
3931
|
-
break;
|
|
3932
|
-
case "top-right":
|
|
3933
|
-
el.style.right = `${offset.x}px`;
|
|
3934
|
-
el.style.top = `${offset.y}px`;
|
|
3935
|
-
break;
|
|
3931
|
+
|
|
3932
|
+
// src/assets/caret.svg?raw
|
|
3933
|
+
var caret_default = '<svg\n xmlns="http://www.w3.org/2000/svg"\n viewBox="0 0 24 24"\n fill="none"\n stroke="currentColor"\n stroke-width="2"\n stroke-linecap="round"\n stroke-linejoin="round"\n>\n <path d="M9 20L16.5 12L9 4" />\n</svg>';
|
|
3934
|
+
|
|
3935
|
+
// src/legends/graph-legend-icon.ts
|
|
3936
|
+
var LEGEND_ICON_MAP = { caret: caret_default };
|
|
3937
|
+
function getLegendIcon(icon) {
|
|
3938
|
+
const raw = LEGEND_ICON_MAP[icon];
|
|
3939
|
+
if (!raw) {
|
|
3940
|
+
throw new Error(`Legend icon not found: ${icon}`);
|
|
3936
3941
|
}
|
|
3942
|
+
return raw.replace("<svg", '<svg class="pg-icon"');
|
|
3937
3943
|
}
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3944
|
+
|
|
3945
|
+
// src/legends/create-graph-legends.ts
|
|
3946
|
+
function createGraphLegend(overlay, config) {
|
|
3947
|
+
const legendWrapper = document.createElement("div");
|
|
3948
|
+
legendWrapper.className = "pg-legend";
|
|
3949
|
+
const position = config.position || "bottom-right";
|
|
3950
|
+
legendWrapper.classList.add(`pg-pos-${position}`);
|
|
3951
|
+
if (config.defaultExpanded === false) {
|
|
3952
|
+
legendWrapper.classList.add("pg-is-collapsed");
|
|
3953
|
+
}
|
|
3954
|
+
if (config.collapsible) {
|
|
3955
|
+
const toggleBtn = document.createElement("button");
|
|
3956
|
+
toggleBtn.className = "pg-legend-toggle";
|
|
3957
|
+
toggleBtn.type = "button";
|
|
3958
|
+
toggleBtn.innerHTML = getLegendIcon("caret");
|
|
3959
|
+
toggleBtn.onclick = (e) => {
|
|
3960
|
+
e.stopPropagation();
|
|
3961
|
+
legendWrapper.classList.toggle("pg-is-collapsed");
|
|
3962
|
+
};
|
|
3963
|
+
legendWrapper.appendChild(toggleBtn);
|
|
3964
|
+
}
|
|
3965
|
+
const body = document.createElement("div");
|
|
3966
|
+
body.className = "pg-legend-body";
|
|
3967
|
+
const list = document.createElement("ul");
|
|
3968
|
+
list.className = "pg-legend-list";
|
|
3969
|
+
config.items.forEach((item) => {
|
|
3970
|
+
const listItem = document.createElement("li");
|
|
3971
|
+
listItem.className = "pg-legend-item";
|
|
3972
|
+
const swatch = document.createElement("span");
|
|
3973
|
+
swatch.className = `pg-legend-swatch is-${item.shape || "circle"}`;
|
|
3974
|
+
swatch.style.backgroundColor = item.color;
|
|
3975
|
+
const label = document.createElement("span");
|
|
3976
|
+
label.className = "pg-legend-label";
|
|
3977
|
+
label.innerText = item.label;
|
|
3978
|
+
listItem.appendChild(swatch);
|
|
3979
|
+
listItem.appendChild(label);
|
|
3980
|
+
list.appendChild(listItem);
|
|
3981
|
+
});
|
|
3982
|
+
body.appendChild(list);
|
|
3983
|
+
legendWrapper.appendChild(body);
|
|
3984
|
+
overlay.appendChild(legendWrapper);
|
|
3985
|
+
return () => {
|
|
3986
|
+
if (legendWrapper.parentNode === overlay) overlay.removeChild(legendWrapper);
|
|
3987
|
+
};
|
|
3942
3988
|
}
|
|
3943
3989
|
|
|
3944
3990
|
// src/utils/resolve-link-style.ts
|
|
@@ -4171,7 +4217,7 @@ function getLinkKey2(link) {
|
|
|
4171
4217
|
}
|
|
4172
4218
|
function renderLinkLabels(params, links) {
|
|
4173
4219
|
const renderableLinks = createRenderableLinks2(params, links);
|
|
4174
|
-
const labelSelection = params.root.select("
|
|
4220
|
+
const labelSelection = params.root.select('[data-layer="link-labels"]').selectAll(".link-label").data(renderableLinks, (item) => getLinkKey2(item.link)).join("g").attr("class", "link-label").attr("pointer-events", "auto").attr("cursor", "pointer");
|
|
4175
4221
|
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);
|
|
4176
4222
|
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 ?? "");
|
|
4177
4223
|
return labelSelection;
|
|
@@ -4456,10 +4502,11 @@ function observeResize(element, onResize) {
|
|
|
4456
4502
|
if (!entry) {
|
|
4457
4503
|
return;
|
|
4458
4504
|
}
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4505
|
+
const width = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
|
|
4506
|
+
const height = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;
|
|
4507
|
+
if (width > 0 && height > 0) {
|
|
4508
|
+
onResize(width, height);
|
|
4509
|
+
}
|
|
4463
4510
|
}
|
|
4464
4511
|
);
|
|
4465
4512
|
observer.observe(element);
|
|
@@ -4492,54 +4539,85 @@ function getLinkTargetPoint(link) {
|
|
|
4492
4539
|
};
|
|
4493
4540
|
}
|
|
4494
4541
|
|
|
4542
|
+
// src/utils/export-graph.ts
|
|
4543
|
+
var import_html2canvas = __toESM(require("html2canvas"), 1);
|
|
4544
|
+
async function captureAndDownloadGraph(container, options = {}) {
|
|
4545
|
+
const {
|
|
4546
|
+
fileName = `graph-export-${Date.now()}.png`,
|
|
4547
|
+
backgroundColor = "#ffffff",
|
|
4548
|
+
pixelRatio = 2
|
|
4549
|
+
} = options;
|
|
4550
|
+
const root2 = container.querySelector(".pg-root");
|
|
4551
|
+
if (!root2) return;
|
|
4552
|
+
const controls = root2.querySelector(".pg-controls");
|
|
4553
|
+
const legendToggle = root2.querySelector(".pg-legend-toggle");
|
|
4554
|
+
const interactionLayer = root2.querySelector(".pg-interaction-layer");
|
|
4555
|
+
const legend = root2.querySelector(".pg-legend");
|
|
4556
|
+
const wasCollapsed = legend?.classList.contains("pg-is-collapsed");
|
|
4557
|
+
if (controls) controls.style.display = "none";
|
|
4558
|
+
if (legendToggle) legendToggle.style.display = "none";
|
|
4559
|
+
if (interactionLayer) interactionLayer.style.display = "none";
|
|
4560
|
+
if (legend && wasCollapsed) {
|
|
4561
|
+
legend.classList.remove("pg-is-collapsed");
|
|
4562
|
+
}
|
|
4563
|
+
try {
|
|
4564
|
+
const canvas = await (0, import_html2canvas.default)(root2, {
|
|
4565
|
+
scale: pixelRatio,
|
|
4566
|
+
backgroundColor,
|
|
4567
|
+
useCORS: true,
|
|
4568
|
+
logging: false
|
|
4569
|
+
});
|
|
4570
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
4571
|
+
const link = document.createElement("a");
|
|
4572
|
+
link.download = fileName;
|
|
4573
|
+
link.href = dataUrl;
|
|
4574
|
+
link.click();
|
|
4575
|
+
} finally {
|
|
4576
|
+
if (controls) controls.style.display = "flex";
|
|
4577
|
+
if (legendToggle) legendToggle.style.display = "flex";
|
|
4578
|
+
if (interactionLayer) interactionLayer.style.display = "block";
|
|
4579
|
+
if (legend && wasCollapsed) {
|
|
4580
|
+
legend.classList.add("pg-is-collapsed");
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4495
4585
|
// src/create-graph.ts
|
|
4496
4586
|
function createGraph(config) {
|
|
4497
4587
|
let cleanupResize = null;
|
|
4498
4588
|
let cleanupZoom = null;
|
|
4499
4589
|
let tooltipBinding = null;
|
|
4500
4590
|
let controls = null;
|
|
4591
|
+
let legendCleanup = null;
|
|
4501
4592
|
let dimensions = { width: 0, height: 0 };
|
|
4502
4593
|
let rootGroup = null;
|
|
4594
|
+
let svgElement = null;
|
|
4503
4595
|
let zoomBehavior = null;
|
|
4504
4596
|
let simulation = null;
|
|
4505
4597
|
function render() {
|
|
4506
4598
|
destroy();
|
|
4507
4599
|
const layers = createGraphLayers(config.container);
|
|
4600
|
+
svgElement = layers.svg;
|
|
4508
4601
|
rootGroup = layers.root;
|
|
4509
|
-
cleanupResize = observeResize(
|
|
4510
|
-
|
|
4511
|
-
(width,
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
);
|
|
4602
|
+
cleanupResize = observeResize(config.container, (width, height) => {
|
|
4603
|
+
dimensions = { width, height };
|
|
4604
|
+
layers.svg.setAttribute("width", String(width));
|
|
4605
|
+
layers.svg.setAttribute("height", String(height));
|
|
4606
|
+
layers.interactionRect.setAttribute("width", String(width));
|
|
4607
|
+
layers.interactionRect.setAttribute("height", String(height));
|
|
4608
|
+
simulation?.force("x", x_default2(width / 2).strength(0.03));
|
|
4609
|
+
simulation?.force("y", y_default2(height / 2).strength(0.03));
|
|
4610
|
+
simulation?.alpha(0.25).restart();
|
|
4611
|
+
});
|
|
4520
4612
|
const zoomResult = createZoom({
|
|
4521
|
-
|
|
4522
|
-
* D3 zoom must be attached to SVG
|
|
4523
|
-
* because it requires:
|
|
4524
|
-
*
|
|
4525
|
-
* width.baseVal
|
|
4526
|
-
* height.baseVal
|
|
4527
|
-
*/
|
|
4528
|
-
svg: config.container,
|
|
4529
|
-
/**
|
|
4530
|
-
* Used for pointer semantics /
|
|
4531
|
-
* pan filtering only
|
|
4532
|
-
*/
|
|
4613
|
+
svg: layers.svg,
|
|
4533
4614
|
interactionLayer: layers.interactionLayer,
|
|
4534
|
-
/**
|
|
4535
|
-
* Actual graph transform target
|
|
4536
|
-
*/
|
|
4537
4615
|
root: layers.root
|
|
4538
4616
|
});
|
|
4539
4617
|
zoomBehavior = zoomResult.behavior;
|
|
4540
4618
|
cleanupZoom = zoomResult.cleanup;
|
|
4541
4619
|
const root2 = select_default2(layers.root);
|
|
4542
|
-
const renderContext = { svg:
|
|
4620
|
+
const renderContext = { svg: layers.svg, root: root2, interaction: config.interaction };
|
|
4543
4621
|
const linkSelection = renderLinks(renderContext, config.links);
|
|
4544
4622
|
const linkLabelSelection = renderLinkLabels(renderContext, config.links);
|
|
4545
4623
|
const nodeSelection = renderNodes(renderContext, config.nodes);
|
|
@@ -4552,40 +4630,35 @@ function createGraph(config) {
|
|
|
4552
4630
|
};
|
|
4553
4631
|
const simulationResult = createGraphSimulation(simulationConfig);
|
|
4554
4632
|
simulation = simulationResult.simulation;
|
|
4555
|
-
simulation.on(
|
|
4556
|
-
"
|
|
4557
|
-
() => {
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
nodeSelection.attr("cx", (d) => d.x ?? 0).attr("cy", (d) => d.y ?? 0);
|
|
4581
|
-
labelSelection.attr("x", (d) => d.x ?? 0).attr("y", (d) => d.y ?? 0);
|
|
4582
|
-
tooltipBinding?.reposition();
|
|
4583
|
-
}
|
|
4584
|
-
);
|
|
4633
|
+
simulation.on("tick", () => {
|
|
4634
|
+
linkSelection.attr("x1", (item) => item.link.source.x ?? 0).attr("y1", (item) => item.link.source.y ?? 0).attr("x2", (item) => getShortenedTargetPoint(item.link, item.style).x).attr("y2", (item) => getShortenedTargetPoint(item.link, item.style).y);
|
|
4635
|
+
linkLabelSelection.attr("transform", (item) => {
|
|
4636
|
+
const link = item.link;
|
|
4637
|
+
const source = link.source;
|
|
4638
|
+
const targetPoint = getLinkTargetPoint(link);
|
|
4639
|
+
const x3 = ((source.x ?? 0) + targetPoint.x) / 2;
|
|
4640
|
+
const y3 = ((source.y ?? 0) + targetPoint.y) / 2;
|
|
4641
|
+
return `translate(${x3}, ${y3})`;
|
|
4642
|
+
}).each(function() {
|
|
4643
|
+
const group = this;
|
|
4644
|
+
const text = group.querySelector("text");
|
|
4645
|
+
const rect = group.querySelector("rect");
|
|
4646
|
+
if (!text || !rect) return;
|
|
4647
|
+
const bBox = text.getBBox();
|
|
4648
|
+
const padding = 6;
|
|
4649
|
+
rect.setAttribute("x", String(bBox.x - padding));
|
|
4650
|
+
rect.setAttribute("y", String(bBox.y - padding));
|
|
4651
|
+
rect.setAttribute("width", String(bBox.width + padding * 2));
|
|
4652
|
+
rect.setAttribute("height", String(bBox.height + padding * 2));
|
|
4653
|
+
});
|
|
4654
|
+
nodeSelection.attr("cx", (d) => d.x ?? 0).attr("cy", (d) => d.y ?? 0);
|
|
4655
|
+
labelSelection.attr("x", (d) => d.x ?? 0).attr("y", (d) => d.y ?? 0);
|
|
4656
|
+
tooltipBinding?.reposition();
|
|
4657
|
+
});
|
|
4585
4658
|
if (config.interaction?.hover?.enabled) {
|
|
4586
4659
|
if (config.interaction?.hover?.tooltip?.enabled) {
|
|
4587
4660
|
tooltipBinding = bindNodeTooltip({
|
|
4588
|
-
container: config.container
|
|
4661
|
+
container: config.container,
|
|
4589
4662
|
selection: nodeSelection,
|
|
4590
4663
|
tooltipConfig: config.interaction.hover.tooltip
|
|
4591
4664
|
});
|
|
@@ -4595,64 +4668,47 @@ function createGraph(config) {
|
|
|
4595
4668
|
if (config.interaction?.drag?.enabled !== false) {
|
|
4596
4669
|
nodeSelection.call(createDragBehavior(simulation));
|
|
4597
4670
|
}
|
|
4598
|
-
if (config.interaction?.selection?.enabled) {
|
|
4599
|
-
}
|
|
4600
4671
|
if (config.controls?.enabled) {
|
|
4601
|
-
controls = createGraphControls(
|
|
4672
|
+
controls = createGraphControls(
|
|
4673
|
+
layers.overlay,
|
|
4674
|
+
{ zoomIn, zoomOut, resetView, fitView, destroy, render, exportGraph },
|
|
4675
|
+
config.controls
|
|
4676
|
+
);
|
|
4602
4677
|
controls.mount();
|
|
4603
4678
|
}
|
|
4679
|
+
if (config.legend?.enabled) {
|
|
4680
|
+
legendCleanup = createGraphLegend(layers.overlay, config.legend);
|
|
4681
|
+
}
|
|
4604
4682
|
}
|
|
4605
4683
|
function resetView() {
|
|
4606
|
-
if (!zoomBehavior)
|
|
4607
|
-
|
|
4608
|
-
}
|
|
4609
|
-
select_default2(config.container).transition().call(
|
|
4610
|
-
zoomBehavior.transform,
|
|
4611
|
-
identity2
|
|
4612
|
-
);
|
|
4684
|
+
if (!zoomBehavior || !svgElement) return;
|
|
4685
|
+
select_default2(svgElement).transition().call(zoomBehavior.transform, identity2);
|
|
4613
4686
|
}
|
|
4614
4687
|
function fitView() {
|
|
4615
|
-
if (!zoomBehavior || !rootGroup || dimensions.width === 0 || dimensions.height === 0)
|
|
4616
|
-
return;
|
|
4617
|
-
}
|
|
4688
|
+
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0) return;
|
|
4618
4689
|
const bounds = rootGroup.getBBox();
|
|
4619
|
-
if (bounds.width === 0 || bounds.height === 0)
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
const
|
|
4623
|
-
const
|
|
4624
|
-
|
|
4625
|
-
width / bounds.width,
|
|
4626
|
-
height / bounds.height
|
|
4627
|
-
) * 0.9;
|
|
4628
|
-
const translateX = (width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4629
|
-
const translateY = (height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
4630
|
-
const transform2 = identity2.translate(
|
|
4631
|
-
translateX,
|
|
4632
|
-
translateY
|
|
4633
|
-
).scale(scale);
|
|
4634
|
-
select_default2(config.container).transition().call(
|
|
4635
|
-
zoomBehavior.transform,
|
|
4636
|
-
transform2
|
|
4637
|
-
);
|
|
4690
|
+
if (bounds.width === 0 || bounds.height === 0) return;
|
|
4691
|
+
const scale = Math.min(dimensions.width / bounds.width, dimensions.height / bounds.height) * 0.9;
|
|
4692
|
+
const translateX = (dimensions.width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4693
|
+
const translateY = (dimensions.height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
4694
|
+
const transform2 = identity2.translate(translateX, translateY).scale(scale);
|
|
4695
|
+
select_default2(svgElement).transition().call(zoomBehavior.transform, transform2);
|
|
4638
4696
|
}
|
|
4639
4697
|
function zoomIn() {
|
|
4640
|
-
if (!zoomBehavior)
|
|
4641
|
-
|
|
4642
|
-
}
|
|
4643
|
-
select_default2(config.container).transition().call(
|
|
4644
|
-
zoomBehavior.scaleBy,
|
|
4645
|
-
1.2
|
|
4646
|
-
);
|
|
4698
|
+
if (!zoomBehavior || !svgElement) return;
|
|
4699
|
+
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 1.2);
|
|
4647
4700
|
}
|
|
4648
4701
|
function zoomOut() {
|
|
4649
|
-
if (!zoomBehavior)
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4702
|
+
if (!zoomBehavior || !svgElement) return;
|
|
4703
|
+
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 0.8);
|
|
4704
|
+
}
|
|
4705
|
+
async function exportGraph(fileName) {
|
|
4706
|
+
fitView();
|
|
4707
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
4708
|
+
await captureAndDownloadGraph(config.container, {
|
|
4709
|
+
fileName,
|
|
4710
|
+
pixelRatio: 2
|
|
4711
|
+
});
|
|
4656
4712
|
}
|
|
4657
4713
|
function destroy() {
|
|
4658
4714
|
if (cleanupResize) {
|
|
@@ -4675,13 +4731,18 @@ function createGraph(config) {
|
|
|
4675
4731
|
controls.destroy();
|
|
4676
4732
|
controls = null;
|
|
4677
4733
|
}
|
|
4734
|
+
if (legendCleanup) {
|
|
4735
|
+
legendCleanup();
|
|
4736
|
+
legendCleanup = null;
|
|
4737
|
+
}
|
|
4678
4738
|
rootGroup = null;
|
|
4739
|
+
svgElement = null;
|
|
4679
4740
|
zoomBehavior = null;
|
|
4680
4741
|
while (config.container.firstChild) {
|
|
4681
4742
|
config.container.removeChild(config.container.firstChild);
|
|
4682
4743
|
}
|
|
4683
4744
|
}
|
|
4684
|
-
return { render, zoomIn, zoomOut, resetView, fitView, destroy };
|
|
4745
|
+
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph };
|
|
4685
4746
|
}
|
|
4686
4747
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4687
4748
|
0 && (module.exports = {
|