getMultiLineGraph = (data, options) => {
const { height, margin, width, yRange, xRange, xLabel } = options;
let T, X, Y;
const xAxis = d3.scaleLinear().domain(xRange).range([margin.left, width - margin.right]);
const yAxis = d3.scaleLinear().domain(yRange).range([height - margin.bottom, margin.top]);
const svg = d3.select(DOM.svg(width, height));
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xAxis))
.call(g => g.select(".tick:first-of-type text").text(xLabel));
svg.append("g")
.attr("transform", `translate(${margin.left - 12},0)`)
.call(d3.axisLeft(yAxis).ticks(8))
.call(g => g.select(".domain").remove());
const legendData = data.filter(d => d.label !== undefined)
const legend = svg.selectAll('.legend').data(legendData)
.enter().append("g")
.attr("class", "legend")
.attr("transform", "translate(" + (width - 300) + "," + -80 + ")");
legend
.append("circle")
.attr("cx", 100)
.attr("cy", (_d,i) => 100 + i*25 )
.attr("r", 7)
.style("fill", d => d.color )
legend
.append("text")
.attr("x", 120)
.attr("y", (_d,i) => 100 + i*25 )
.style("fill", d => d.color )
.text(d => d.label)
.attr("text-anchor", "left")
.style("alignment-baseline", "middle")
for (let i=0; i<data.length; i++) {
const { x: xData, y: yData, size, color } = data[i];
for (let j=0; j<size; j++) {
const path = svg.append("path")
.attr("fill", "none")
.attr("stroke", color)
.attr("stroke-width", 1.55);
path.attr("d", d3.line().x(xAxis).y((_, i) => yAxis(yData[i][j]))(xData));
}
}
return svg.node();
}