polly-graph 0.1.5 → 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/dist/index.cjs +217 -59
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +217 -59
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3767,6 +3767,56 @@ function createGraphSimulation(config) {
|
|
|
3767
3767
|
return { simulation };
|
|
3768
3768
|
}
|
|
3769
3769
|
|
|
3770
|
+
// src/utils/get-link-marker-id.ts
|
|
3771
|
+
function getLinkMarkerId(style) {
|
|
3772
|
+
const markerStyle = {
|
|
3773
|
+
stroke: style.stroke ?? "#94a3b8",
|
|
3774
|
+
strokeWidth: style.strokeWidth ?? 2,
|
|
3775
|
+
arrowFill: style.arrow?.fill ?? style.stroke ?? "#94a3b8",
|
|
3776
|
+
arrowSize: style.arrow?.size ?? 6
|
|
3777
|
+
};
|
|
3778
|
+
const serializedStyle = JSON.stringify(markerStyle);
|
|
3779
|
+
const hash = createHash(serializedStyle);
|
|
3780
|
+
return `graph-arrow-${hash}`;
|
|
3781
|
+
}
|
|
3782
|
+
function createHash(value) {
|
|
3783
|
+
let hash = 0;
|
|
3784
|
+
for (let index2 = 0; index2 < value.length; index2 += 1) {
|
|
3785
|
+
const charCode = value.charCodeAt(index2);
|
|
3786
|
+
hash = (hash << 5) - hash + charCode;
|
|
3787
|
+
hash |= 0;
|
|
3788
|
+
}
|
|
3789
|
+
return Math.abs(hash).toString(36);
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
// src/core/create-arrow-marker.ts
|
|
3793
|
+
function createArrowMarker(params) {
|
|
3794
|
+
const markerId = getLinkMarkerId(params.style);
|
|
3795
|
+
const existingMarker = params.svg.querySelector(`#${markerId}`);
|
|
3796
|
+
if (existingMarker) {
|
|
3797
|
+
return markerId;
|
|
3798
|
+
}
|
|
3799
|
+
const arrowSize = params.style.arrow?.size ?? 6;
|
|
3800
|
+
const fill = params.style.arrow?.fill ?? params.style.stroke ?? "#94a3b8";
|
|
3801
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
3802
|
+
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
3803
|
+
marker.setAttribute("id", markerId);
|
|
3804
|
+
marker.setAttribute("viewBox", "0 0 20 20");
|
|
3805
|
+
marker.setAttribute("refX", "0");
|
|
3806
|
+
marker.setAttribute("refY", "10");
|
|
3807
|
+
marker.setAttribute("markerWidth", String(arrowSize * 2));
|
|
3808
|
+
marker.setAttribute("markerHeight", String(arrowSize * 2));
|
|
3809
|
+
marker.setAttribute("orient", "auto");
|
|
3810
|
+
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
3811
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
3812
|
+
path.setAttribute("d", "M 0 0 L 20 10 L 0 20 z");
|
|
3813
|
+
path.setAttribute("fill", fill);
|
|
3814
|
+
marker.appendChild(path);
|
|
3815
|
+
defs.appendChild(marker);
|
|
3816
|
+
params.svg.insertBefore(defs, params.svg.firstChild);
|
|
3817
|
+
return markerId;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3770
3820
|
// src/controls/graph-controls.utils.ts
|
|
3771
3821
|
function resolveControlsPosition(position) {
|
|
3772
3822
|
return position ?? "bottom-left";
|
|
@@ -3995,56 +4045,6 @@ function mergeLinkStyle(base, override) {
|
|
|
3995
4045
|
};
|
|
3996
4046
|
}
|
|
3997
4047
|
|
|
3998
|
-
// src/utils/get-link-marker-id.ts
|
|
3999
|
-
function getLinkMarkerId(style) {
|
|
4000
|
-
const markerStyle = {
|
|
4001
|
-
stroke: style.stroke ?? "#94a3b8",
|
|
4002
|
-
strokeWidth: style.strokeWidth ?? 2,
|
|
4003
|
-
arrowFill: style.arrow?.fill ?? style.stroke ?? "#94a3b8",
|
|
4004
|
-
arrowSize: style.arrow?.size ?? 6
|
|
4005
|
-
};
|
|
4006
|
-
const serializedStyle = JSON.stringify(markerStyle);
|
|
4007
|
-
const hash = createHash(serializedStyle);
|
|
4008
|
-
return `graph-arrow-${hash}`;
|
|
4009
|
-
}
|
|
4010
|
-
function createHash(value) {
|
|
4011
|
-
let hash = 0;
|
|
4012
|
-
for (let index2 = 0; index2 < value.length; index2 += 1) {
|
|
4013
|
-
const charCode = value.charCodeAt(index2);
|
|
4014
|
-
hash = (hash << 5) - hash + charCode;
|
|
4015
|
-
hash |= 0;
|
|
4016
|
-
}
|
|
4017
|
-
return Math.abs(hash).toString(36);
|
|
4018
|
-
}
|
|
4019
|
-
|
|
4020
|
-
// src/core/create-arrow-marker.ts
|
|
4021
|
-
function createArrowMarker(params) {
|
|
4022
|
-
const markerId = getLinkMarkerId(params.style);
|
|
4023
|
-
const existingMarker = params.svg.querySelector(`#${markerId}`);
|
|
4024
|
-
if (existingMarker) {
|
|
4025
|
-
return markerId;
|
|
4026
|
-
}
|
|
4027
|
-
const arrowSize = params.style.arrow?.size ?? 6;
|
|
4028
|
-
const fill = params.style.arrow?.fill ?? params.style.stroke ?? "#94a3b8";
|
|
4029
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
4030
|
-
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
4031
|
-
marker.setAttribute("id", markerId);
|
|
4032
|
-
marker.setAttribute("viewBox", "0 0 20 20");
|
|
4033
|
-
marker.setAttribute("refX", "0");
|
|
4034
|
-
marker.setAttribute("refY", "10");
|
|
4035
|
-
marker.setAttribute("markerWidth", String(arrowSize * 2));
|
|
4036
|
-
marker.setAttribute("markerHeight", String(arrowSize * 2));
|
|
4037
|
-
marker.setAttribute("orient", "auto");
|
|
4038
|
-
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
4039
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
4040
|
-
path.setAttribute("d", "M 0 0 L 20 10 L 0 20 z");
|
|
4041
|
-
path.setAttribute("fill", fill);
|
|
4042
|
-
marker.appendChild(path);
|
|
4043
|
-
defs.appendChild(marker);
|
|
4044
|
-
params.svg.insertBefore(defs, params.svg.firstChild);
|
|
4045
|
-
return markerId;
|
|
4046
|
-
}
|
|
4047
|
-
|
|
4048
4048
|
// src/renderer/links.ts
|
|
4049
4049
|
function getShortenedTargetPoint(link, style) {
|
|
4050
4050
|
const source = link.source;
|
|
@@ -4082,7 +4082,7 @@ function getLinkKey(link) {
|
|
|
4082
4082
|
}
|
|
4083
4083
|
function renderLinks(ctx, links) {
|
|
4084
4084
|
const renderableLinks = createRenderableLinks(ctx, links);
|
|
4085
|
-
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");
|
|
4085
|
+
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");
|
|
4086
4086
|
const labelSelection = ctx.root.selectAll(".link-label");
|
|
4087
4087
|
linkSelection.on("mouseenter.label-hover", (_event, d) => {
|
|
4088
4088
|
labelSelection.filter((labelItem) => labelItem.link === d.link && labelItem.style.label.visibility === "hover").interrupt().transition().duration(200).style("opacity", 1);
|
|
@@ -4236,7 +4236,9 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
4236
4236
|
return s.id === d.id || t.id === d.id;
|
|
4237
4237
|
}).interrupt().transition().duration(200).style("opacity", 1).style("pointer-events", "auto");
|
|
4238
4238
|
}).on("mouseleave.labels", (_event) => {
|
|
4239
|
-
labelSelection.filter((item)
|
|
4239
|
+
labelSelection.filter(function(item) {
|
|
4240
|
+
return item.style.label.visibility === "hover" && !this.classList.contains("label-selection-pinned");
|
|
4241
|
+
}).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
4240
4242
|
});
|
|
4241
4243
|
}
|
|
4242
4244
|
|
|
@@ -4535,6 +4537,27 @@ function createGraph(config) {
|
|
|
4535
4537
|
let svgElement = null;
|
|
4536
4538
|
let zoomBehavior = null;
|
|
4537
4539
|
let simulation = null;
|
|
4540
|
+
const nodeSelectHandlers = /* @__PURE__ */ new Set();
|
|
4541
|
+
const linkSelectHandlers = /* @__PURE__ */ new Set();
|
|
4542
|
+
function on(event, handler) {
|
|
4543
|
+
if (event === "nodeSelect") {
|
|
4544
|
+
nodeSelectHandlers.add(handler);
|
|
4545
|
+
return () => {
|
|
4546
|
+
nodeSelectHandlers.delete(handler);
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4549
|
+
linkSelectHandlers.add(handler);
|
|
4550
|
+
return () => {
|
|
4551
|
+
linkSelectHandlers.delete(handler);
|
|
4552
|
+
};
|
|
4553
|
+
}
|
|
4554
|
+
function off(event, handler) {
|
|
4555
|
+
if (event === "nodeSelect") {
|
|
4556
|
+
nodeSelectHandlers.delete(handler);
|
|
4557
|
+
} else {
|
|
4558
|
+
linkSelectHandlers.delete(handler);
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4538
4561
|
function render() {
|
|
4539
4562
|
destroy();
|
|
4540
4563
|
const layers = createGraphLayers(config.container);
|
|
@@ -4618,10 +4641,135 @@ function createGraph(config) {
|
|
|
4618
4641
|
if (config.interaction?.drag?.enabled !== false) {
|
|
4619
4642
|
nodeSelection.call(createDragBehavior(simulation));
|
|
4620
4643
|
}
|
|
4644
|
+
const selectionConfig = config.interaction?.selection;
|
|
4645
|
+
if (selectionConfig?.enabled) {
|
|
4646
|
+
let selectedNodeElement = null;
|
|
4647
|
+
let selectedLinkElement = null;
|
|
4648
|
+
const linkMarkerSnapshots = /* @__PURE__ */ new Map();
|
|
4649
|
+
linkSelection.each(function() {
|
|
4650
|
+
const linkElement = this;
|
|
4651
|
+
linkMarkerSnapshots.set(linkElement, linkElement.getAttribute("marker-end"));
|
|
4652
|
+
});
|
|
4653
|
+
const deselectNode = () => {
|
|
4654
|
+
if (!selectedNodeElement) {
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4657
|
+
const nodeElement = selectedNodeElement;
|
|
4658
|
+
nodeElement.style.fill = "";
|
|
4659
|
+
nodeElement.style.stroke = "";
|
|
4660
|
+
nodeElement.style.strokeWidth = "";
|
|
4661
|
+
nodeElement.style.opacity = "";
|
|
4662
|
+
nodeElement.style.removeProperty("r");
|
|
4663
|
+
root2.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
4664
|
+
selectedNodeElement = null;
|
|
4665
|
+
};
|
|
4666
|
+
const deselectLink = () => {
|
|
4667
|
+
if (!selectedLinkElement) {
|
|
4668
|
+
return;
|
|
4669
|
+
}
|
|
4670
|
+
const linkElement = selectedLinkElement;
|
|
4671
|
+
linkElement.style.stroke = "";
|
|
4672
|
+
linkElement.style.strokeWidth = "";
|
|
4673
|
+
linkElement.style.opacity = "";
|
|
4674
|
+
const originalMarkerEnd = linkMarkerSnapshots.get(linkElement);
|
|
4675
|
+
if (originalMarkerEnd) {
|
|
4676
|
+
linkElement.setAttribute("marker-end", originalMarkerEnd);
|
|
4677
|
+
} else {
|
|
4678
|
+
linkElement.removeAttribute("marker-end");
|
|
4679
|
+
}
|
|
4680
|
+
selectedLinkElement = null;
|
|
4681
|
+
};
|
|
4682
|
+
nodeSelection.on("click.select", function(event, node) {
|
|
4683
|
+
event.stopPropagation();
|
|
4684
|
+
const nodeElement = this;
|
|
4685
|
+
if (selectedNodeElement === nodeElement) {
|
|
4686
|
+
deselectNode();
|
|
4687
|
+
return;
|
|
4688
|
+
}
|
|
4689
|
+
deselectNode();
|
|
4690
|
+
deselectLink();
|
|
4691
|
+
selectedNodeElement = nodeElement;
|
|
4692
|
+
const nodeStyle = selectionConfig.nodeStyle;
|
|
4693
|
+
if (nodeStyle) {
|
|
4694
|
+
if (nodeStyle.fill !== void 0) {
|
|
4695
|
+
nodeElement.style.fill = nodeStyle.fill;
|
|
4696
|
+
}
|
|
4697
|
+
if (nodeStyle.stroke !== void 0) {
|
|
4698
|
+
nodeElement.style.stroke = nodeStyle.stroke;
|
|
4699
|
+
}
|
|
4700
|
+
if (nodeStyle.strokeWidth !== void 0) {
|
|
4701
|
+
nodeElement.style.strokeWidth = String(nodeStyle.strokeWidth);
|
|
4702
|
+
}
|
|
4703
|
+
if (nodeStyle.opacity !== void 0) {
|
|
4704
|
+
nodeElement.style.opacity = String(nodeStyle.opacity);
|
|
4705
|
+
}
|
|
4706
|
+
if (nodeStyle.radius !== void 0) {
|
|
4707
|
+
nodeElement.style.setProperty("r", String(nodeStyle.radius));
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
root2.selectAll(".link-label").filter((item) => {
|
|
4711
|
+
if (item.style.label.visibility !== "hover") {
|
|
4712
|
+
return false;
|
|
4713
|
+
}
|
|
4714
|
+
const source = item.link.source;
|
|
4715
|
+
const target = item.link.target;
|
|
4716
|
+
return source.id === node.id || target.id === node.id;
|
|
4717
|
+
}).classed("label-selection-pinned", true).interrupt().transition().duration(200).style("opacity", 1).style("pointer-events", "auto");
|
|
4718
|
+
nodeSelectHandlers.forEach((handler) => handler(node, nodeElement));
|
|
4719
|
+
});
|
|
4720
|
+
const selectLink = (event, renderableLink, linkElement) => {
|
|
4721
|
+
event.stopPropagation();
|
|
4722
|
+
if (selectedLinkElement === linkElement) {
|
|
4723
|
+
deselectLink();
|
|
4724
|
+
return;
|
|
4725
|
+
}
|
|
4726
|
+
deselectLink();
|
|
4727
|
+
deselectNode();
|
|
4728
|
+
selectedLinkElement = linkElement;
|
|
4729
|
+
const linkStyle = selectionConfig.linkStyle;
|
|
4730
|
+
if (linkStyle) {
|
|
4731
|
+
if (linkStyle.stroke !== void 0) {
|
|
4732
|
+
linkElement.style.stroke = linkStyle.stroke;
|
|
4733
|
+
}
|
|
4734
|
+
if (linkStyle.strokeWidth !== void 0) {
|
|
4735
|
+
linkElement.style.strokeWidth = String(linkStyle.strokeWidth);
|
|
4736
|
+
}
|
|
4737
|
+
if (linkStyle.opacity !== void 0) {
|
|
4738
|
+
linkElement.style.opacity = String(linkStyle.opacity);
|
|
4739
|
+
}
|
|
4740
|
+
if (linkStyle.stroke !== void 0 && renderableLink.style.arrow.enabled) {
|
|
4741
|
+
const selectionMarkerStyle = {
|
|
4742
|
+
stroke: linkStyle.stroke,
|
|
4743
|
+
arrow: { fill: linkStyle.stroke, size: renderableLink.style.arrow.size }
|
|
4744
|
+
};
|
|
4745
|
+
const selectionMarkerId = createArrowMarker({ svg: layers.svg, style: selectionMarkerStyle });
|
|
4746
|
+
select_default2(linkElement).attr("marker-end", `url(#${selectionMarkerId})`);
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
linkSelectHandlers.forEach((handler) => handler(renderableLink.link, linkElement));
|
|
4750
|
+
};
|
|
4751
|
+
linkSelection.on("click.select", function(event, renderableLink) {
|
|
4752
|
+
selectLink(event, renderableLink, this);
|
|
4753
|
+
});
|
|
4754
|
+
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);
|
|
4755
|
+
simulation.on("tick.hitarea", () => {
|
|
4756
|
+
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);
|
|
4757
|
+
});
|
|
4758
|
+
linkHitAreaSelection.on("click.select", function(event, renderableLink) {
|
|
4759
|
+
const visibleLinkNode = linkSelection.filter((d) => d === renderableLink).node();
|
|
4760
|
+
if (visibleLinkNode) {
|
|
4761
|
+
selectLink(event, renderableLink, visibleLinkNode);
|
|
4762
|
+
}
|
|
4763
|
+
});
|
|
4764
|
+
select_default2(layers.svg).on("click.deselect", () => {
|
|
4765
|
+
deselectNode();
|
|
4766
|
+
deselectLink();
|
|
4767
|
+
});
|
|
4768
|
+
}
|
|
4621
4769
|
if (config.controls?.enabled) {
|
|
4622
4770
|
controls = createGraphControls(
|
|
4623
4771
|
layers.overlay,
|
|
4624
|
-
{ zoomIn, zoomOut, resetView, fitView
|
|
4772
|
+
{ zoomIn, zoomOut, resetView, fitView },
|
|
4625
4773
|
config.controls
|
|
4626
4774
|
);
|
|
4627
4775
|
controls.mount();
|
|
@@ -4631,13 +4779,19 @@ function createGraph(config) {
|
|
|
4631
4779
|
}
|
|
4632
4780
|
}
|
|
4633
4781
|
function resetView() {
|
|
4634
|
-
if (!zoomBehavior || !svgElement)
|
|
4782
|
+
if (!zoomBehavior || !svgElement) {
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4635
4785
|
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, identity2);
|
|
4636
4786
|
}
|
|
4637
4787
|
function fitView() {
|
|
4638
|
-
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0)
|
|
4788
|
+
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0) {
|
|
4789
|
+
return;
|
|
4790
|
+
}
|
|
4639
4791
|
const bounds = rootGroup.getBBox();
|
|
4640
|
-
if (bounds.width === 0 || bounds.height === 0)
|
|
4792
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4641
4795
|
const scale = Math.min(dimensions.width / bounds.width, dimensions.height / bounds.height) * 0.9;
|
|
4642
4796
|
const translateX = (dimensions.width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4643
4797
|
const translateY = (dimensions.height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
@@ -4645,11 +4799,15 @@ function createGraph(config) {
|
|
|
4645
4799
|
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, transform2);
|
|
4646
4800
|
}
|
|
4647
4801
|
function zoomIn() {
|
|
4648
|
-
if (!zoomBehavior || !svgElement)
|
|
4802
|
+
if (!zoomBehavior || !svgElement) {
|
|
4803
|
+
return;
|
|
4804
|
+
}
|
|
4649
4805
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 1.2);
|
|
4650
4806
|
}
|
|
4651
4807
|
function zoomOut() {
|
|
4652
|
-
if (!zoomBehavior || !svgElement)
|
|
4808
|
+
if (!zoomBehavior || !svgElement) {
|
|
4809
|
+
return;
|
|
4810
|
+
}
|
|
4653
4811
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 0.8);
|
|
4654
4812
|
}
|
|
4655
4813
|
async function exportGraph(fileName) {
|
|
@@ -4696,7 +4854,7 @@ function createGraph(config) {
|
|
|
4696
4854
|
config.container.removeChild(config.container.firstChild);
|
|
4697
4855
|
}
|
|
4698
4856
|
}
|
|
4699
|
-
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph };
|
|
4857
|
+
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph, on, off };
|
|
4700
4858
|
}
|
|
4701
4859
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4702
4860
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -138,6 +138,10 @@ interface GraphInstance {
|
|
|
138
138
|
fitView(): void;
|
|
139
139
|
destroy(): void;
|
|
140
140
|
exportGraph(fileName?: string): void;
|
|
141
|
+
on(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): () => void;
|
|
142
|
+
on(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): () => void;
|
|
143
|
+
off(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): void;
|
|
144
|
+
off(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): void;
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
declare function createGraph(config: GraphConfig): GraphInstance;
|
package/dist/index.d.ts
CHANGED
|
@@ -138,6 +138,10 @@ interface GraphInstance {
|
|
|
138
138
|
fitView(): void;
|
|
139
139
|
destroy(): void;
|
|
140
140
|
exportGraph(fileName?: string): void;
|
|
141
|
+
on(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): () => void;
|
|
142
|
+
on(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): () => void;
|
|
143
|
+
off(event: 'nodeSelect', handler: (node: GraphNode, element: SVGCircleElement) => void): void;
|
|
144
|
+
off(event: 'linkSelect', handler: (link: GraphLink, element: SVGLineElement) => void): void;
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
declare function createGraph(config: GraphConfig): GraphInstance;
|
package/dist/index.js
CHANGED
|
@@ -3731,6 +3731,56 @@ function createGraphSimulation(config) {
|
|
|
3731
3731
|
return { simulation };
|
|
3732
3732
|
}
|
|
3733
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
|
+
|
|
3734
3784
|
// src/controls/graph-controls.utils.ts
|
|
3735
3785
|
function resolveControlsPosition(position) {
|
|
3736
3786
|
return position ?? "bottom-left";
|
|
@@ -3959,56 +4009,6 @@ function mergeLinkStyle(base, override) {
|
|
|
3959
4009
|
};
|
|
3960
4010
|
}
|
|
3961
4011
|
|
|
3962
|
-
// src/utils/get-link-marker-id.ts
|
|
3963
|
-
function getLinkMarkerId(style) {
|
|
3964
|
-
const markerStyle = {
|
|
3965
|
-
stroke: style.stroke ?? "#94a3b8",
|
|
3966
|
-
strokeWidth: style.strokeWidth ?? 2,
|
|
3967
|
-
arrowFill: style.arrow?.fill ?? style.stroke ?? "#94a3b8",
|
|
3968
|
-
arrowSize: style.arrow?.size ?? 6
|
|
3969
|
-
};
|
|
3970
|
-
const serializedStyle = JSON.stringify(markerStyle);
|
|
3971
|
-
const hash = createHash(serializedStyle);
|
|
3972
|
-
return `graph-arrow-${hash}`;
|
|
3973
|
-
}
|
|
3974
|
-
function createHash(value) {
|
|
3975
|
-
let hash = 0;
|
|
3976
|
-
for (let index2 = 0; index2 < value.length; index2 += 1) {
|
|
3977
|
-
const charCode = value.charCodeAt(index2);
|
|
3978
|
-
hash = (hash << 5) - hash + charCode;
|
|
3979
|
-
hash |= 0;
|
|
3980
|
-
}
|
|
3981
|
-
return Math.abs(hash).toString(36);
|
|
3982
|
-
}
|
|
3983
|
-
|
|
3984
|
-
// src/core/create-arrow-marker.ts
|
|
3985
|
-
function createArrowMarker(params) {
|
|
3986
|
-
const markerId = getLinkMarkerId(params.style);
|
|
3987
|
-
const existingMarker = params.svg.querySelector(`#${markerId}`);
|
|
3988
|
-
if (existingMarker) {
|
|
3989
|
-
return markerId;
|
|
3990
|
-
}
|
|
3991
|
-
const arrowSize = params.style.arrow?.size ?? 6;
|
|
3992
|
-
const fill = params.style.arrow?.fill ?? params.style.stroke ?? "#94a3b8";
|
|
3993
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
3994
|
-
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
3995
|
-
marker.setAttribute("id", markerId);
|
|
3996
|
-
marker.setAttribute("viewBox", "0 0 20 20");
|
|
3997
|
-
marker.setAttribute("refX", "0");
|
|
3998
|
-
marker.setAttribute("refY", "10");
|
|
3999
|
-
marker.setAttribute("markerWidth", String(arrowSize * 2));
|
|
4000
|
-
marker.setAttribute("markerHeight", String(arrowSize * 2));
|
|
4001
|
-
marker.setAttribute("orient", "auto");
|
|
4002
|
-
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
4003
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
4004
|
-
path.setAttribute("d", "M 0 0 L 20 10 L 0 20 z");
|
|
4005
|
-
path.setAttribute("fill", fill);
|
|
4006
|
-
marker.appendChild(path);
|
|
4007
|
-
defs.appendChild(marker);
|
|
4008
|
-
params.svg.insertBefore(defs, params.svg.firstChild);
|
|
4009
|
-
return markerId;
|
|
4010
|
-
}
|
|
4011
|
-
|
|
4012
4012
|
// src/renderer/links.ts
|
|
4013
4013
|
function getShortenedTargetPoint(link, style) {
|
|
4014
4014
|
const source = link.source;
|
|
@@ -4046,7 +4046,7 @@ function getLinkKey(link) {
|
|
|
4046
4046
|
}
|
|
4047
4047
|
function renderLinks(ctx, links) {
|
|
4048
4048
|
const renderableLinks = createRenderableLinks(ctx, links);
|
|
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");
|
|
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
4050
|
const labelSelection = ctx.root.selectAll(".link-label");
|
|
4051
4051
|
linkSelection.on("mouseenter.label-hover", (_event, d) => {
|
|
4052
4052
|
labelSelection.filter((labelItem) => labelItem.link === d.link && labelItem.style.label.visibility === "hover").interrupt().transition().duration(200).style("opacity", 1);
|
|
@@ -4200,7 +4200,9 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
4200
4200
|
return s.id === d.id || t.id === d.id;
|
|
4201
4201
|
}).interrupt().transition().duration(200).style("opacity", 1).style("pointer-events", "auto");
|
|
4202
4202
|
}).on("mouseleave.labels", (_event) => {
|
|
4203
|
-
labelSelection.filter((item)
|
|
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");
|
|
4204
4206
|
});
|
|
4205
4207
|
}
|
|
4206
4208
|
|
|
@@ -4499,6 +4501,27 @@ function createGraph(config) {
|
|
|
4499
4501
|
let svgElement = null;
|
|
4500
4502
|
let zoomBehavior = null;
|
|
4501
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
|
+
}
|
|
4502
4525
|
function render() {
|
|
4503
4526
|
destroy();
|
|
4504
4527
|
const layers = createGraphLayers(config.container);
|
|
@@ -4582,10 +4605,135 @@ function createGraph(config) {
|
|
|
4582
4605
|
if (config.interaction?.drag?.enabled !== false) {
|
|
4583
4606
|
nodeSelection.call(createDragBehavior(simulation));
|
|
4584
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
|
+
}
|
|
4585
4733
|
if (config.controls?.enabled) {
|
|
4586
4734
|
controls = createGraphControls(
|
|
4587
4735
|
layers.overlay,
|
|
4588
|
-
{ zoomIn, zoomOut, resetView, fitView
|
|
4736
|
+
{ zoomIn, zoomOut, resetView, fitView },
|
|
4589
4737
|
config.controls
|
|
4590
4738
|
);
|
|
4591
4739
|
controls.mount();
|
|
@@ -4595,13 +4743,19 @@ function createGraph(config) {
|
|
|
4595
4743
|
}
|
|
4596
4744
|
}
|
|
4597
4745
|
function resetView() {
|
|
4598
|
-
if (!zoomBehavior || !svgElement)
|
|
4746
|
+
if (!zoomBehavior || !svgElement) {
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4599
4749
|
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, identity2);
|
|
4600
4750
|
}
|
|
4601
4751
|
function fitView() {
|
|
4602
|
-
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0)
|
|
4752
|
+
if (!zoomBehavior || !rootGroup || !svgElement || dimensions.width === 0 || dimensions.height === 0) {
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4603
4755
|
const bounds = rootGroup.getBBox();
|
|
4604
|
-
if (bounds.width === 0 || bounds.height === 0)
|
|
4756
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4605
4759
|
const scale = Math.min(dimensions.width / bounds.width, dimensions.height / bounds.height) * 0.9;
|
|
4606
4760
|
const translateX = (dimensions.width - bounds.width * scale) / 2 - bounds.x * scale;
|
|
4607
4761
|
const translateY = (dimensions.height - bounds.height * scale) / 2 - bounds.y * scale;
|
|
@@ -4609,11 +4763,15 @@ function createGraph(config) {
|
|
|
4609
4763
|
select_default2(svgElement).transition().duration(400).call(zoomBehavior.transform, transform2);
|
|
4610
4764
|
}
|
|
4611
4765
|
function zoomIn() {
|
|
4612
|
-
if (!zoomBehavior || !svgElement)
|
|
4766
|
+
if (!zoomBehavior || !svgElement) {
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4613
4769
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 1.2);
|
|
4614
4770
|
}
|
|
4615
4771
|
function zoomOut() {
|
|
4616
|
-
if (!zoomBehavior || !svgElement)
|
|
4772
|
+
if (!zoomBehavior || !svgElement) {
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4617
4775
|
select_default2(svgElement).transition().call(zoomBehavior.scaleBy, 0.8);
|
|
4618
4776
|
}
|
|
4619
4777
|
async function exportGraph(fileName) {
|
|
@@ -4660,7 +4818,7 @@ function createGraph(config) {
|
|
|
4660
4818
|
config.container.removeChild(config.container.firstChild);
|
|
4661
4819
|
}
|
|
4662
4820
|
}
|
|
4663
|
-
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph };
|
|
4821
|
+
return { render, zoomIn, zoomOut, resetView, fitView, destroy, exportGraph, on, off };
|
|
4664
4822
|
}
|
|
4665
4823
|
export {
|
|
4666
4824
|
createGraph
|
package/package.json
CHANGED