chart = {
const nodes = layoutedData.nodes;
const links = layoutedData.links;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", d => Math.sqrt(d.value));
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", 5)
.attr("fill", color)
.attr("cursor", "pointer");
node.append("title")
.text(d => d.id);
let hoveredNode = undefined;
let clickedNode = undefined;
const update = () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.attr("stroke-dasharray", d => {
const activeNode = clickedNode || hoveredNode;
if (activeNode && (d.__proto__.source === activeNode.id || d.__proto__.target === activeNode.id)) {
return "";
}
const lineLengthCoveredByNode = 5;
const lineLength = Math.sqrt((d.source.x - d.target.x) ** 2 + (d.source.y - d.target.y) ** 2) - lineLengthCoveredByNode * 2;
const lineStubLength = config.stubDrawingType === 'relative' ? lineLength * config.relativeStubLength : config.absoluteStubLength;
if (lineLength < lineStubLength * 2) {
return "";
}
return `${lineLengthCoveredByNode + lineStubLength} ${lineLength - lineStubLength * 2}`;
});
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
};
node
.on("mouseover", d => {
hoveredNode = d;
update();
})
.on("mouseout", d => {
if (hoveredNode === d) {
hoveredNode = undefined;
update();
}
})
.on("mousedown", d => {
clickedNode = d;
update();
})
.on("mouseup", d => {
clickedNode = undefined;
update();
});
document.addEventListener("mousemove", event => {
if (event.buttons === 0) {
clickedNode = undefined;
update();
}
});
node.call(d3.drag().on("drag", d => {
d.x = d3.event.x;
d.y = d3.event.y;
update();
}));
update();
return svg.node();
}