{
const margins = { l: 30, r: 100, b: 20, t: 10 };
const drawWidth = width;
const drawHeight = 500;
const svg = d3
.create("svg")
.attr("width", drawWidth)
.attr("height", drawHeight)
.attr("viewBox", `0 0 ${drawWidth} ${drawHeight}`);
const g = svg.append("g");
if (zoomable) {
svg.call(
d3
.zoom()
.extent([
[0, 0],
[drawWidth, drawHeight]
])
.scaleExtent([1, 4])
.on("zoom", zoomed)
);
}
function zoomed({ transform }) {
timeline.selectAll("g").attr("transform", transform);
}
const timeline = g.append("g").attr("id", "timeline");
const legend = g.append("g");
legend
.append("text")
.attr("id", "legend")
.attr("x", margins["l"])
.attr("y", 40)
.attr("font-size", 45)
.text("");
legend
.append("text")
.attr("class", "legend-adv")
.attr("x", margins["l"])
.attr("y", 70)
.attr("font-size", 20)
.attr("fill", "#333")
.text("");
legend
.append("text")
.attr("class", "legend-detail")
.attr("x", margins["l"])
.attr("y", 100)
.attr("font-size", 20)
.attr("fill", "#333")
.text("");
const scaleCol = d3.scaleBand().domain([0, 1]).range([0, 100]);
const scaleRow = d3
.scaleBand()
.domain(d3.range(10))
.range([margins["t"], drawHeight - margins["b"]]);
const clubs = g.append("g").attr("id", "clubs");
clubs
.selectAll("rect")
.data(sorted_teams)
.join("rect")
.attr("x", (d, i) => drawWidth - margins["r"] + scaleCol(i % 2) + 20)
.attr("y", (d, i) => margins["t"] + scaleRow(parseInt(i / 2)))
.attr("width", scaleCol.bandwidth() / 2)
.attr("height", scaleRow.bandwidth() / 2)
.attr("fill", (d) => sc(d))
.attr("opacity", 1)
.attr("stroke", "#333")
.on("mouseover", function (event, d) {
d3.selectAll("circle").attr("opacity", "0.2");
d3.selectAll(`.${d.split(" ").join("")}`)
.transition()
.duration(400)
.attr("opacity", 1)
.attr("r", emphasisR);
d3.select("#legend").attr("fill", sc(d)).text(d);
d3.select(".legend-adv").text("");
d3.select(".legend-detail").text("");
})
.on("mouseleave", function (event, d) {
d3.selectAll("circle").attr("opacity", "1");
d3.selectAll(`.${d.split(" ").join("")}`)
.transition()
.duration(400)
.attr("opacity", 1)
.attr("r", defaultR);
});
clubs
.selectAll("text")
.data(sorted_teams)
.join("text")
.attr("x", (d, i) => drawWidth - margins["r"] + scaleCol(i % 2) + 20)
.attr("y", (d, i) => margins["t"] + scaleRow(parseInt(i / 2)))
.text((d) => d[0]);
const st = d3
.scaleBand()
.domain(d3.range(38))
.range([margins["l"], drawWidth - margins["r"]]);
const taxis = d3.axisBottom(st).tickFormat((d) => d + 1);
g.append("g")
.attr("transform", `translate(0, ${drawHeight - margins["b"]})`)
.call(taxis);
const sy = d3
.scaleLinear()
.domain([0, 95])
.range([drawHeight - margins["b"], margins["t"]])
.nice();
const yaxis = d3.axisRight(sy).ticks(10);
g.append("g").attr("transform", `translate(0, ${-margins["b"]})`).call(yaxis);
const defaultR = 4;
const emphasisR = 8;
for (const t of teams) {
timeline
.append("g")
.selectAll("circle")
.data(getSorted(t))
.join(
(enter) => {
enter
.append("circle")
.attr("class", t.split(" ").join(""))
.attr("cx", (d, i) => st(i) + st.bandwidth() / 2)
.attr("cy", (d, i) => {
return sy(getLatestPoints(d.team, i)) - margins["b"];
})
.attr("ith", (d, i) => i)
.attr("stroke", "#444")
.attr("fill", (d) => sc(d.team))
.attr("r", defaultR)
.on("mouseover", function (event, d) {
const ith = d3.select(this).attr("ith");
d3.select("#timeline").selectAll("circle").attr("opacity", 0.2);
d3.selectAll(`.${d.team.split(" ").join("")}`).attr("opacity", 1);
d3.select("#legend").call(drawLegend, d);
d3.select(".legend-adv").call(drawLegendAdv, d, ith);
d3.select(".legend-detail").call(drawLegendDetail, d, ith);
d3.select(this).transition().duration(500).attr("r", emphasisR);
})
.on("mouseleave", function (event, d) {
d3.select(this).transition().duration(500).attr("r", defaultR);
});
},
(update) => update,
(exit) => exit.remove()
);
}
return svg.node();
}