chart ={
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("class", "svgBackground");
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
const path = svg.append("g")
.selectAll("path")
.data(data)
.join("path")
.attr("class", "time")
.attr("id", d => `country${d.ISO}`)
.attr("d", d => line(d.Timeseries))
.attr("fill", "none")
.attr("stroke", d => d.Fontclr)
.attr("stroke-width", 2)
.attr("stroke-dasharray", d => d.Linedsh)
.attr("visibility", "hidden");
const microtext = svg.append("g")
.selectAll("textPath")
.data(data)
.join("text")
.attr("font-size", d => d.fontSize)
.attr("font-family", "Roboto")
.attr("dy", 3)
.attr("font-style", d => d.Fontita)
.attr("font-weight", d => d.Fontwgt)
.append("textPath")
.attr("href", d => `#country${d.ISO}`)
.attr("fill", d => d.Fontclr)
.text(d => d.microstring);
// text labels - force is used on these
const labels = svg.append("g")
.selectAll("text.label")
.data(data)
.join("text")
.attr("class", "label")
.attr("x", width - margin.right + 30)
.attr("y", d => unempScale(d.Timevalue.slice(-1)[0]))
.attr("font-size", 16)
.attr("font-family", "Roboto")
.attr("font-weight", "bold")
.attr("fill", d => d.Fontclr)
.text(d => `${d3.format(".1f")(d.Timevalue.slice(-1)[0])} ${d.Country}`);
// leader lines to text labels
const links = svg.append("g")
.selectAll("line")
.data(data)
.join("line")
.attr("x1", width - margin.right + 10)
.attr("x2", width - margin.right + 25)
.attr("y1", d => unempScale(d.Timevalue.slice(-1)[0]))
.attr("y2", d => unempScale(d.Timevalue.slice(-1)[0]))
.attr("stroke-width", 2 )
.attr("stroke-dasharray", function(d) { return d.Linedsh; })
.attr("stroke", function(d,i) { return d.Fontclr; });
// mousover to highlight dataseries
microtext.on('mouseover', mouseover)
microtext.on('mouseout', mouseout)
labels.on('mouseover', mouseover)
labels.on('mouseout', mouseout)
path.on('mouseover', mouseover)
path.on('mouseout', mouseout)
yield svg.node();
// need this code to run AFTER YIELDING. Collision detection of text depends on the element width and height; and getBBox() only works if the text elements have been returned as a DOM element. So YIELD first to ensure getBBox() will grab the width and height THEN run collision on the elements.
await d3.selectAll("text.label").each(function(d){
const box = this.getBBox()
d.width = box.width
d.height = box.height
})
// force simulation for text lables and leader lines
const simulation = d3.forceSimulation(data)
.force("center", d3.forceY(d => unempScale(d.Timevalue.slice(-1)[0])))
.force("collide", customCollide());
simulation.on('tick', () => {
links.attr("y2", d => d.y - d.height / 4)
labels.attr("y", d => d.y);
});
invalidation.then(() => simulation.stop());
}