{
const svg = d3.create('svg');
svg.attr('width', width).attr('height', height);
const stage = svg.append('g');
stage.attr('transform', `translate(${marginX},${marginY})`);
const keys = timeline.map(t => t.dateKey);
const values = timeline.map(t => t.value);
const xMax = Math.max(...keys);
const xMin = Math.min(...keys);
const yMax = Math.max(...values);
const yMin = Math.min(...values);
const xScaler = d3
.scaleLinear()
.domain([xMin, xMax])
.range([0, width - marginX * 2]);
const yScaler = d3
.scaleLinear()
.domain([yMin, yMax])
.range([height - marginY * 2, 0]);
const xAxis = stage
.append('g')
.attr('transform', `translate(0,${height - marginY * 2})`)
.classed('x-axis', true)
.call(
d3
.axisBottom(xScaler)
.tickValues(keys)
.tickFormat(d3.timeFormat('%b %d'))
);
const yAxis = stage
.append('g')
.classed('y-axis', true)
.call(
d3
.axisLeft(yScaler)
.tickValues(values)
.tickFormat(d3.format('~s'))
);
const lineDraw = d3
.line()
.x(d => xScaler(d.dateKey))
.y(d => yScaler(d.value));
const line = stage
.append('path')
.datum(timeline)
.classed('line-path', true)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.5)
.attr('d', lineDraw);
const points = stage
.selectAll('.point')
.data(timeline)
.join('circle')
.attr('class', d => `point-${d.index}`)
.classed('point', true)
.attr('fill', 'black')
.attr('cx', 4)
.attr('cy', 4)
.attr('r', 4)
.attr(
'transform',
d => `translate(${xScaler(d.dateKey) - 4},${yScaler(d.value) - 4})`
);
let infoHover = null;
const onMouseOver = (d, i, e) => {
console.log('mouseover', d);
svg.selectAll(`.point-${d.index}`).classed('hover', true);
if (!infoHover) infoHover = stage.append('text');
infoHover
.text(`${d3.timeFormat('%b %d')(d.dateKey)}-${d3.format('~s')(d.value)}`)
.attr(
'transform',
`translate(${xScaler(d.dateKey) - 4},${yScaler(d.value) - 4})`
);
};
const onMouseOut = d => {
// console.log('mouseout',d)
svg.selectAll(`.point-${d.index}`).classed('hover', false);
if (infoHover) {
infoHover.remove();
infoHover = null;
}
};
const delaunay = D3Delaunay.Delaunay.from(
timeline,
d => xScaler(d.dateKey),
d => yScaler(d.value)
); // data, fx, fy
const voronoi = delaunay.voronoi([
0,
0,
width - marginX * 2,
height - marginY * 2
]);
// console.log(voronoi.render())
const voronoiPath = stage
.append('g')
.selectAll('voronoi-path')
.data(timeline)
.join('path')
.attr('d', (d, i) => voronoi.renderCell(i))
.classed('voronoi-path', true)
.attr('stroke-width', 1.0)
.attr('stroke', voronoiVisible ? 'pink' : 'transparent')
.attr('fill', 'transparent')
.on('mouseover', onMouseOver);
return svg.node();
}