polly-graph 0.1.2 → 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 +331 -129
- package/dist/index.css +236 -0
- package/dist/index.d.cts +41 -1
- package/dist/index.d.ts +41 -1
- package/dist/index.js +321 -129
- package/package.json +5 -4
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
|
|
|
@@ -3809,6 +3822,171 @@ function createGraphSimulation(config) {
|
|
|
3809
3822
|
return { simulation };
|
|
3810
3823
|
}
|
|
3811
3824
|
|
|
3825
|
+
// src/controls/graph-controls.utils.ts
|
|
3826
|
+
function resolveControlsPosition(position) {
|
|
3827
|
+
return position ?? "bottom-left";
|
|
3828
|
+
}
|
|
3829
|
+
function resolveControlsOrientation(orientation) {
|
|
3830
|
+
return orientation ?? "vertical";
|
|
3831
|
+
}
|
|
3832
|
+
function shouldRenderControl(config, key) {
|
|
3833
|
+
const show = config.show;
|
|
3834
|
+
if (!show) {
|
|
3835
|
+
return true;
|
|
3836
|
+
}
|
|
3837
|
+
const value = show[key];
|
|
3838
|
+
if (value === void 0) {
|
|
3839
|
+
return true;
|
|
3840
|
+
}
|
|
3841
|
+
return value;
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
// src/assets/plus.svg?raw
|
|
3845
|
+
var plus_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="M5 12h14m-7-7v14" />\n</svg>';
|
|
3846
|
+
|
|
3847
|
+
// src/assets/minus.svg?raw
|
|
3848
|
+
var minus_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="M19 12H5" />\n</svg>';
|
|
3849
|
+
|
|
3850
|
+
// src/assets/fit.svg?raw
|
|
3851
|
+
var fit_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="M5 9V5H9" />\n <path d="M19 9V5H15" />\n <path d="M5 15V19H9" />\n <path d="M19 15V19H15" />\n</svg>';
|
|
3852
|
+
|
|
3853
|
+
// src/assets/reset.svg?raw
|
|
3854
|
+
var reset_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="M20 12a8 8 0 1 1-2.3-5.7" />\n <path d="M20 4.5v4h-4" />\n</svg>';
|
|
3855
|
+
|
|
3856
|
+
// src/controls/graph-controls.icons.ts
|
|
3857
|
+
var ICON_MAP = {
|
|
3858
|
+
"zoom-in": plus_default,
|
|
3859
|
+
"zoom-out": minus_default,
|
|
3860
|
+
fit: fit_default,
|
|
3861
|
+
reset: reset_default
|
|
3862
|
+
};
|
|
3863
|
+
function getControlIcon(icon) {
|
|
3864
|
+
const raw = ICON_MAP[icon];
|
|
3865
|
+
if (!raw) {
|
|
3866
|
+
throw new Error(`Icon not found: ${icon}`);
|
|
3867
|
+
}
|
|
3868
|
+
return raw.replace("<svg", '<svg class="pg-icon"');
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
// src/controls/create-graph-controls.ts
|
|
3872
|
+
function createGraphControls(overlay, graph, config) {
|
|
3873
|
+
let root2 = null;
|
|
3874
|
+
function mount() {
|
|
3875
|
+
if (!config.enabled) {
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
root2 = document.createElement("div");
|
|
3879
|
+
root2.className = "pg-controls";
|
|
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
|
+
}
|
|
3888
|
+
appendControls(root2, config, graph);
|
|
3889
|
+
overlay.appendChild(root2);
|
|
3890
|
+
}
|
|
3891
|
+
function appendControls(root3, config2, graph2) {
|
|
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
|
+
});
|
|
3903
|
+
}
|
|
3904
|
+
function createButton(type, label, onClick) {
|
|
3905
|
+
const button = document.createElement("button");
|
|
3906
|
+
button.className = "pg-control-btn";
|
|
3907
|
+
button.type = "button";
|
|
3908
|
+
button.setAttribute("aria-label", label);
|
|
3909
|
+
const wrapper = document.createElement("div");
|
|
3910
|
+
wrapper.className = "pg-icon-wrapper";
|
|
3911
|
+
wrapper.innerHTML = getControlIcon(type);
|
|
3912
|
+
const svg = wrapper.querySelector("svg");
|
|
3913
|
+
if (svg) {
|
|
3914
|
+
svg.classList.add("pg-icon");
|
|
3915
|
+
button.appendChild(svg);
|
|
3916
|
+
}
|
|
3917
|
+
button.addEventListener("click", onClick);
|
|
3918
|
+
return button;
|
|
3919
|
+
}
|
|
3920
|
+
function destroy() {
|
|
3921
|
+
if (!root2) {
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
if (root2.parentNode === overlay) {
|
|
3925
|
+
overlay.removeChild(root2);
|
|
3926
|
+
}
|
|
3927
|
+
root2 = null;
|
|
3928
|
+
}
|
|
3929
|
+
return { mount, destroy };
|
|
3930
|
+
}
|
|
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}`);
|
|
3941
|
+
}
|
|
3942
|
+
return raw.replace("<svg", '<svg class="pg-icon"');
|
|
3943
|
+
}
|
|
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
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3812
3990
|
// src/utils/resolve-link-style.ts
|
|
3813
3991
|
var DEFAULT_LINK_STYLE = {
|
|
3814
3992
|
stroke: "#94a3b8",
|
|
@@ -4039,7 +4217,7 @@ function getLinkKey2(link) {
|
|
|
4039
4217
|
}
|
|
4040
4218
|
function renderLinkLabels(params, links) {
|
|
4041
4219
|
const renderableLinks = createRenderableLinks2(params, links);
|
|
4042
|
-
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");
|
|
4043
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);
|
|
4044
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 ?? "");
|
|
4045
4223
|
return labelSelection;
|
|
@@ -4324,10 +4502,11 @@ function observeResize(element, onResize) {
|
|
|
4324
4502
|
if (!entry) {
|
|
4325
4503
|
return;
|
|
4326
4504
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
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
|
+
}
|
|
4331
4510
|
}
|
|
4332
4511
|
);
|
|
4333
4512
|
observer.observe(element);
|
|
@@ -4360,53 +4539,85 @@ function getLinkTargetPoint(link) {
|
|
|
4360
4539
|
};
|
|
4361
4540
|
}
|
|
4362
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
|
+
|
|
4363
4585
|
// src/create-graph.ts
|
|
4364
4586
|
function createGraph(config) {
|
|
4365
4587
|
let cleanupResize = null;
|
|
4366
4588
|
let cleanupZoom = null;
|
|
4367
4589
|
let tooltipBinding = null;
|
|
4590
|
+
let controls = null;
|
|
4591
|
+
let legendCleanup = null;
|
|
4368
4592
|
let dimensions = { width: 0, height: 0 };
|
|
4369
4593
|
let rootGroup = null;
|
|
4594
|
+
let svgElement = null;
|
|
4370
4595
|
let zoomBehavior = null;
|
|
4371
4596
|
let simulation = null;
|
|
4372
4597
|
function render() {
|
|
4373
4598
|
destroy();
|
|
4374
4599
|
const layers = createGraphLayers(config.container);
|
|
4600
|
+
svgElement = layers.svg;
|
|
4375
4601
|
rootGroup = layers.root;
|
|
4376
|
-
cleanupResize = observeResize(
|
|
4377
|
-
|
|
4378
|
-
(width,
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
);
|
|
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
|
+
});
|
|
4387
4612
|
const zoomResult = createZoom({
|
|
4388
|
-
|
|
4389
|
-
* D3 zoom must be attached to SVG
|
|
4390
|
-
* because it requires:
|
|
4391
|
-
*
|
|
4392
|
-
* width.baseVal
|
|
4393
|
-
* height.baseVal
|
|
4394
|
-
*/
|
|
4395
|
-
svg: config.container,
|
|
4396
|
-
/**
|
|
4397
|
-
* Used for pointer semantics /
|
|
4398
|
-
* pan filtering only
|
|
4399
|
-
*/
|
|
4613
|
+
svg: layers.svg,
|
|
4400
4614
|
interactionLayer: layers.interactionLayer,
|
|
4401
|
-
/**
|
|
4402
|
-
* Actual graph transform target
|
|
4403
|
-
*/
|
|
4404
4615
|
root: layers.root
|
|
4405
4616
|
});
|
|
4406
4617
|
zoomBehavior = zoomResult.behavior;
|
|
4407
4618
|
cleanupZoom = zoomResult.cleanup;
|
|
4408
4619
|
const root2 = select_default2(layers.root);
|
|
4409
|
-
const renderContext = { svg:
|
|
4620
|
+
const renderContext = { svg: layers.svg, root: root2, interaction: config.interaction };
|
|
4410
4621
|
const linkSelection = renderLinks(renderContext, config.links);
|
|
4411
4622
|
const linkLabelSelection = renderLinkLabels(renderContext, config.links);
|
|
4412
4623
|
const nodeSelection = renderNodes(renderContext, config.nodes);
|
|
@@ -4419,40 +4630,35 @@ function createGraph(config) {
|
|
|
4419
4630
|
};
|
|
4420
4631
|
const simulationResult = createGraphSimulation(simulationConfig);
|
|
4421
4632
|
simulation = simulationResult.simulation;
|
|
4422
|
-
simulation.on(
|
|
4423
|
-
"
|
|
4424
|
-
() => {
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
nodeSelection.attr("cx", (d) => d.x ?? 0).attr("cy", (d) => d.y ?? 0);
|
|
4448
|
-
labelSelection.attr("x", (d) => d.x ?? 0).attr("y", (d) => d.y ?? 0);
|
|
4449
|
-
tooltipBinding?.reposition();
|
|
4450
|
-
}
|
|
4451
|
-
);
|
|
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
|
+
});
|
|
4452
4658
|
if (config.interaction?.hover?.enabled) {
|
|
4453
4659
|
if (config.interaction?.hover?.tooltip?.enabled) {
|
|
4454
4660
|
tooltipBinding = bindNodeTooltip({
|
|
4455
|
-
container: config.container
|
|
4661
|
+
container: config.container,
|
|
4456
4662
|
selection: nodeSelection,
|
|
4457
4663
|
tooltipConfig: config.interaction.hover.tooltip
|
|
4458
4664
|
});
|
|
@@ -4462,60 +4668,47 @@ function createGraph(config) {
|
|
|
4462
4668
|
if (config.interaction?.drag?.enabled !== false) {
|
|
4463
4669
|
nodeSelection.call(createDragBehavior(simulation));
|
|
4464
4670
|
}
|
|
4465
|
-
if (config.
|
|
4671
|
+
if (config.controls?.enabled) {
|
|
4672
|
+
controls = createGraphControls(
|
|
4673
|
+
layers.overlay,
|
|
4674
|
+
{ zoomIn, zoomOut, resetView, fitView, destroy, render, exportGraph },
|
|
4675
|
+
config.controls
|
|
4676
|
+
);
|
|
4677
|
+
controls.mount();
|
|
4678
|
+
}
|
|
4679
|
+
if (config.legend?.enabled) {
|
|
4680
|
+
legendCleanup = createGraphLegend(layers.overlay, config.legend);
|
|
4466
4681
|
}
|
|
4467
4682
|
}
|
|
4468
4683
|
function resetView() {
|
|
4469
|
-
if (!zoomBehavior)
|
|
4470
|
-
|
|
4471
|
-
}
|
|
4472
|
-
select_default2(config.container).transition().call(
|
|
4473
|
-
zoomBehavior.transform,
|
|
4474
|
-
identity2
|
|
4475
|
-
);
|
|
4684
|
+
if (!zoomBehavior || !svgElement) return;
|
|
4685
|
+
select_default2(svgElement).transition().call(zoomBehavior.transform, identity2);
|
|
4476
4686
|
}
|
|
4477
4687
|
function fitView() {
|
|
4478
|
-
if (!zoomBehavior || !rootGroup || dimensions.width === 0 || dimensions.height === 0)
|
|
4479
|
-
return;
|
|
4480
|
-
}
|
|
4688
|
+
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0) return;
|
|
4481
4689
|
const bounds = rootGroup.getBBox();
|
|
4482
|
-
if (bounds.width === 0 || bounds.height === 0)
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
const
|
|
4486
|
-
const
|
|
4487
|
-
|
|
4488
|
-
width / bounds.width,
|
|
4489
|
-
height / bounds.height
|
|
4490
|
-
) * 0.9;
|
|
4491
|
-
const translateX = (width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4492
|
-
const translateY = (height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
4493
|
-
const transform2 = identity2.translate(
|
|
4494
|
-
translateX,
|
|
4495
|
-
translateY
|
|
4496
|
-
).scale(scale);
|
|
4497
|
-
select_default2(config.container).transition().call(
|
|
4498
|
-
zoomBehavior.transform,
|
|
4499
|
-
transform2
|
|
4500
|
-
);
|
|
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);
|
|
4501
4696
|
}
|
|
4502
4697
|
function zoomIn() {
|
|
4503
|
-
if (!zoomBehavior)
|
|
4504
|
-
|
|
4505
|
-
}
|
|
4506
|
-
select_default2(config.container).transition().call(
|
|
4507
|
-
zoomBehavior.scaleBy,
|
|
4508
|
-
1.2
|
|
4509
|
-
);
|
|
4698
|
+
if (!zoomBehavior || !svgElement) return;
|
|
4699
|
+
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 1.2);
|
|
4510
4700
|
}
|
|
4511
4701
|
function zoomOut() {
|
|
4512
|
-
if (!zoomBehavior)
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
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
|
+
});
|
|
4519
4712
|
}
|
|
4520
4713
|
function destroy() {
|
|
4521
4714
|
if (cleanupResize) {
|
|
@@ -4534,13 +4727,22 @@ function createGraph(config) {
|
|
|
4534
4727
|
simulation.stop();
|
|
4535
4728
|
simulation = null;
|
|
4536
4729
|
}
|
|
4730
|
+
if (controls) {
|
|
4731
|
+
controls.destroy();
|
|
4732
|
+
controls = null;
|
|
4733
|
+
}
|
|
4734
|
+
if (legendCleanup) {
|
|
4735
|
+
legendCleanup();
|
|
4736
|
+
legendCleanup = null;
|
|
4737
|
+
}
|
|
4537
4738
|
rootGroup = null;
|
|
4739
|
+
svgElement = null;
|
|
4538
4740
|
zoomBehavior = null;
|
|
4539
4741
|
while (config.container.firstChild) {
|
|
4540
4742
|
config.container.removeChild(config.container.firstChild);
|
|
4541
4743
|
}
|
|
4542
4744
|
}
|
|
4543
|
-
return { render, zoomIn, zoomOut, resetView, fitView, destroy };
|
|
4745
|
+
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph };
|
|
4544
4746
|
}
|
|
4545
4747
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4546
4748
|
0 && (module.exports = {
|