chart = {
const svg = d3.create('svg')
.attr('height', height)
.attr('width', width)
.attr('font-family', 'sans-serif')
.attr('font-size', 12)
const brush = d3.brushY().extent([
[-(brushWidth / 2), margin.top],
[brushWidth / 2, height - margin.bottom]
])
.on("start brush end", brushed);
const axisSpacing = width / axisNames.length;
const x = d3.scalePoint()
.domain(axisNames)
.range([margin.left + axisSpacing / 2, width - margin.right - axisSpacing / 2]);
const y = new Map(Array.from(axisNames, axisName => [axisName, d3.scaleLinear(d3.extent(data, d => d[axisName]), [margin.top, height - margin.bottom])]));
const line = d3.line()
.defined(([, value]) => value != null)
.x(([axisName]) => x(axisName))
.y(([axisName, value]) => y.get(axisName)(value));
const path = svg.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.attr("stroke-opacity", 0.4)
.selectAll("path")
.data(data)
.join("path")
.attr("stroke", selectedColor)
.attr("d", d => line(d3.cross(axisNames, [d], (axisName, d) => [axisName, d[axisName]])));
svg.append("g")
.selectAll("g")
.data(axisNames)
.join("g")
.attr("transform", d => `translate(${x(d)}, 0)`)
.each(function(d) {
d3.select(this).call(d3.axisLeft(y.get(d)));
})
.call(g => g.append("text")
.attr("y", height - 6)
.attr("text-anchor", 'middle')
.attr("font-weight", "bold")
.attr("fill", '#000')
.text(d => d))
.call(g => g.selectAll("text")
.clone(true).lower()
.attr("fill", "none")
.attr("stroke-width", 5)
.attr("stroke-linejoin", "round")
.attr("stroke", "white"))
.call(brush);
const selections = new Map();
function brushed({selection}, axisName) {
if (selection === null) {
selections.delete(axisName);
} else {
selections.set(axisName, selection.map(y.get(axisName).invert));
}
const selected = [];
path.each(function(d) {
const active = Array.from(selections).every(([axisName, [min, max]]) => d[axisName] >= min && d[axisName] <= max);
d3.select(this).style("stroke", active ? selectedColor : unselectedColor);
if (active) {
d3.select(this).raise();
selected.push(d);
}
});
svg.property("value", selected).dispatch("input");
}
return svg.node();
}