update = (svg) => {
const root = d3.hierarchy(treeData);
root.x0 = dy / 2;
root.y0 = 0;
root.descendants().forEach((d, i) => {
d.id = d.data.id;
d._children = d.children;
});
const duration = 100
const nodes = root.descendants().reverse();
const links = root.links();
tree(root);
let left = root;
let right = root;
root.eachBefore(node => {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
});
const height = right.x - left.x + margin.top + margin.bottom;
const transition =
svg
.transition()
.duration(duration)
.attr("height", height)
.attr("viewBox", [-margin.left, left.x - margin.top, width, height])
.tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));
// Update the nodes
const existingNodeContainers = svg.select('#nodes').selectAll("g").data(nodes, d => d.id);
// Enter any new nodes at the parent's previous position.
// Create new node containers that each contains a circle and a text label
const newNodeContainers = existingNodeContainers.enter().append("g")
.attr('id', (d,i) => `${d.id}`)
.attr('class', 'node')
.attr("transform", d => `translate(${root.y0},${root.x0})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
newNodeContainers.append("circle")
.attr("r", 2)
.attr("fill", d => d._children ? "#555" : "#999");
newNodeContainers.append('foreignObject')
.attr('x', -50)
.attr('y', -35)
.attr('width', 80)
.attr('height', 40)
.append("xhtml:p")
.text(d => d.data.name)
existingNodeContainers.merge(newNodeContainers)
.on("click", handleClickOnNode)
// Transition nodes to their new position.
// Increase opacity from 0 to 1 during transition
const nodeUpdate = existingNodeContainers.merge(newNodeContainers).transition(transition)
.attr("transform", d => `translate(${d.y},${d.x})`)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);
// Transition exiting nodes to the parent's new position.
// Reduce opacity from 1 to 0 during transition
const nodeExit = existingNodeContainers.exit().transition(transition).remove()
.attr("transform", d => `translate(${root.y},${root.x})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
// Update the links…
const existingLinkPaths = svg.select('#links').selectAll("path").data(links, d => d.target.id);
// Enter any new links at the parent's previous position.
const newLinkPaths = existingLinkPaths.enter().append("path")
.attr("d", d => {
const o = {x: root.x0, y: root.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
existingLinkPaths.merge(newLinkPaths).transition(transition)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
existingLinkPaths.exit().transition(transition).remove()
.attr("d", d => {
const o = {x: root.x, y: root.y};
return diagonal({source: o, target: o});
});
// Stash the old positions for transition.
root.eachBefore(d => {
d.x0 = d.x;
d.y0 = d.y;
});
}