chart = {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
function isCurrentBlock(element) {
if(element.id === currentID) {
return true;
}
}
const currentBlock = nodes.find(isCurrentBlock);
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(120))
.force("charge", d3.forceManyBody().strength(-1300))
.force("x", d3.forceX())
.force("y", d3.forceY());
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);
svg.append("defs").selectAll("marker")
.data(links)
.join("marker")
.attr("id", d => `arrow-${d.relation_type}`)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -0.5)
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("orient", "auto")
.append("path")
.attr("fill", linkColor)
.attr("d", "M0,-5L10,0L0,5");
const link = svg.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.selectAll("path")
.data(links)
.join("path")
.attr("stroke", linkColor)
.attr("marker-end", d => `url(${new URL(`#arrow-${d.relation_type}`, location)})`);
const node = svg.append("g")
.attr("fill", "currentColor")
.attr("stroke-linecap", "round")
.attr("stroke-linejoin", "round")
.selectAll("g")
.data(nodes)
.join("g")
.call(drag(simulation));
node.append("circle")
.attr("class",function(d){if(d.id===currentID)return "currentNode"
else return"node"})
.attr("stroke", function(d){if(d.id===currentID)return "yellow"
else return"white"})
.attr("stroke-width", 3)
.attr("fill", nodeColor)
.attr("r", 9);
const tooltip = svg.append("g");
const currentBlockBox = svg.append("g");
svg
.selectAll(".node")
.on("mouseover", function(event, d) {
tooltip.call(
callout,
`Block ${d.id}
${d.contents}`
);
tooltip.attr("transform", "translate(200,250)");
})
.on("mouseout", function() {
tooltip.call(callout, null);
});
svg
.on("touchmove mousemove", function(event, d) {
currentBlockBox.call(
callout,
`Block ${currentBlock.id}
${currentBlock.contents}`
);
currentBlockBox.attr("transform", "translate(-200,-250)");
});
node.append("text")
.attr("x", 0)
.attr("y", "-10")
.text(d => d.block_type)
.attr("font-weight", "600")
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill",nodeColor)
.attr("text-anchor", "middle")
.clone(true).lower()
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 2);
node.append("title")
.text(d => d.contents);
const linkText = svg.selectAll("linkText")
.data(links)
.enter()
.append("text")
.text(function(d) {
return d.relation_type;
})
.attr("font-family", "sans-serif")
.attr("font-size", "8px")
.attr("fill", linkColor)
.attr("text-anchor", "middle");
simulation.on("tick", () => {
link.attr("d", linkArc);
node.attr("transform", d => `translate(${d.x},${d.y})`);
linkText
.attr("x", d => (d.source.x*2+d.target.x)/3)
.attr("y", d => (d.source.y*2+d.target.y)/3);
});
invalidation.then(() => simulation.stop());
return svg.node();
}