chart = {
let initialLoad = false;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, size.width, size.height])
.attr("width", size.width)
.attr("height", size.height)
.attr("style", "max-width: 100%; height: auto;");
svg.append('g')
.attr('class', 'axis axis-x')
.attr('transform', 'translate(0, ' + margin.top + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'axis axis-y')
.attr('transform', 'translate(' + xScale(0) + ', 0)')
.call(yAxis);
const rows = svg.selectAll('.rows')
.data(data)
.enter()
.append('g')
.attr('class', 'rows')
.attr('transform', (d) => `translate(0, ${yScale(d.name)})`);
const elements = rows.selectAll(".elements")
.data((d) => d.values)
.enter()
.append('g')
.attr('class', 'elements')
.attr('transform', `translate(${margin.left}, 0)`)
.style('display', 'none');
d3.select(window)
.on('click touchstart mouseover focus', (event, d) => {
if (initialLoad) return;
elements.transition()
.delay((_, i) => 500 + i * 250)
.duration(500)
.style('display', 'inline')
.attr('transform', (v) => `translate(${xScale(v)}, 0)`);
d3.select(event.currentTarget)
.on('touch mouseover focusin', null);
initialLoad = true;
});
elements.append('circle')
.attr('class', 'circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', 6)
.style("fill", "rgba(61, 212, 161, 0.8)")
.style("mix-blend-mode", "multiply");
const guide = svg.append('text')
.attr('class', 'guide')
.attr('x', size.width - margin.right)
.attr('y', 0)
.attr('dy', '.5em')
.attr('text-anchor', 'end')
.attr('dominant-baseline', 'text-before-edge');
function createLine(event, d) {
const line = d3.select(event.currentTarget.parentNode)
.append('g')
.attr('class', 'line');
line.append('line')
.attr('x1', margin.left)
.attr('x2', margin.left)
.transition()
.delay(250)
.duration(250)
.attr('x2', xScale(d) - 6)
.style("stroke", "#ff0045")
.style("stroke-width", "4px");
line.append('text')
.attr('x', xScale(d) - 6)
.attr('dx', '-.2em')
.attr('dy', '-.2em')
.attr('text-anchor', 'end')
.text(d.toString());
}
function createGuide(event, d) {
const text = `${d3.select(event.currentTarget.parentNode).datum().name}: ${d}min.`;
guide.style('display', 'inline')
.text(text);
}
elements.on('click', (event, d) => {
const element = d3.select(event.currentTarget);
const isSelected = element.classed('selected');
const hasSelected = !d3.selectAll('.selected').empty();
element.classed('selected', !isSelected);
d3.selectAll('.active').classed('active', false);
if (isSelected) {
d3.selectAll('.line').remove();
} else if (hasSelected) {
d3.selectAll('.selected').classed('selected', false);
d3.selectAll('.line').remove();
createLine(event, d);
createGuide(event, d);
}
element.classed('selected', !isSelected);
});
elements.on('mouseover touchstart', (event, d) => {
if (d3.selectAll('.selected').empty()) {
createLine(event, d);
createGuide(event, d);
}
d3.select(event.currentTarget).classed('active', true);
});
elements.on('mouseout touchend', (event, d) => {
const isSelected = d3.select(event.currentTarget).classed('selected');
d3.select(event.currentTarget).classed('active', false);
if (d3.selectAll('.selected').empty()) {
d3.selectAll('.line').remove();
guide.style('display', 'none');
}
});
return svg.node();
}