render = (data, {title, color: colorKey, sequential}) => {
if (sequential) {
data = data.sort((a, b) => d3.ascending(a[colorKey], b[colorKey]));
}
const points = data.map((d) => ({
...d,
points: axes.map((axis) => axis.scale(d[axis.name] / 100))
}));
const color = sequential ? sequentialScale : d3.scaleOrdinal(data.map(d => d[colorKey]), colors);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("font-family", "sans-serif")
.attr("font-size", 10);
const g = svg.append("g")
.attr("transform", `translate(${xOffset}, ${yOffset})`);
const ticks = d3.ticks(0, 1, 5);
const grid = g.append("g")
.attr("stroke", "#ccc")
.attr("stroke-dasharray", "2,2")
.attr("fill", "none")
grid.selectAll("path.gridTriangle")
.data(ticks)
.join("path")
.attr("class", "gridTriangle")
.attr("d", n => `M ${axes.map(axis => axis.scale(n))} Z`);
grid.selectAll("path.gridAxis")
.data(axes)
.join("path")
.attr("class", "gridAxis")
.attr("d", axis => `M ${axis.scale(ticks[1])} ${axis.scale(ticks[ticks.length - 1])}`)
g.selectAll("text.axisLabel")
.data(axes)
.join("text")
.attr("class", "axisLabel")
.attr("transform", axis => {
const [x, y] = axis.scale(1);
const dr = 15;
const dx = dr * Math.cos(axis.angle);
const dy = dr * Math.sin(axis.angle);
return `translate(${x + dx}, ${y + dy})`;
})
.attr("text-anchor", "middle")
.text(d => d.name.toUpperCase())
.attr("dy", "0.32em")
g.selectAll("g.points")
.data(points)
.join("g")
.attr("class", "points")
.call(g => g.append("path")
.attr("d", d => `M ${d.points} Z`)
.attr("stroke", d => color(d[colorKey]))
.attr("stroke-width", 2)
.attr("fill", "none"))
.call(g => g.selectAll("circle")
.data(d => d.points.map(pt => ({...d, pt})))
.join("circle")
.attr("cx", d => d.pt[0])
.attr("cy", d => d.pt[1])
.attr("fill", d => color(d[colorKey]))
.attr("r", 3));
svg.append("text")
.attr("dx", "1em")
.attr("dy", "0.32em")
.attr("x", -5)
.attr("y", 10)
.attr("font-weight", "bold")
.text(title);
svg.selectAll("g.legend")
.data(points)
.join("g")
.attr("class", "legend")
.attr("transform", (d, i) => `translate(10, ${10 + (i + 1) * 15})`)
.call(g => g.append("circle").attr("r", 5).attr("fill", d => color(d[colorKey])))
.call(g => g.append("text").attr("dx", "1em").attr("dy", "0.32em").text(d => d[colorKey]))
return svg.node();
}