I made a cool timeline graph for my resume page.
import * as d3 from "d3";
import { useEffect, useRef } from "react";
export default function Timeline({
data,
width = 1080,
height = 1080,
}) {
const svgRef = useRef();
const sortedData = [...data].sort((a, b) => a.start_date.getTime() - b.start_date.getTime());
const length_of_data = sortedData.length;
useEffect(() => {
const color = d3.scaleOrdinal(d3.schemeCategory10);
const svg = d3.select(svgRef.current)
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 20px sans-serif;");
svg.selectAll("*").remove();
const xScale = d3.scaleTime().domain([
d3.min(sortedData, d => d.start_date),
d3.max(sortedData, d => d.end_date)
]).range([0, width]);
const yScale = d3.scaleLinear([0, length_of_data], [0, height-100]);
const yScaleDiff = Math.abs(yScale(1) - yScale(0));
const axisbar = svg.append("g")
.attr("transform", `translate(0, 50)`)
.call(d3.axisTop(xScale))
.attr("font-size", '20px');
const timebars = svg.append("g")
.selectAll()
.data(sortedData)
.enter()
.append("g");
timebars
.append("line")
.attr("y1", d => yScale(sortedData.findIndex(r => r.title === d.title))+100)
.attr("y2", d => yScale(sortedData.findIndex(r => r.title === d.title))+100)
.attr("x1", d => xScale(d.start_date))
.attr("x2", d => xScale(d.end_date))
.attr("stroke", d => color(d.group))
.attr("stroke-width", yScaleDiff / 4);
timebars
.append("text")
.text(d => `${d.title}`)
.attr("x", d => xScale(d.start_date)+5 < width - (width/3) ? xScale(d.start_date)+5 : width- 5)
.attr("y", d => yScale(sortedData.findIndex(r => r.title === d.title))+105)
.attr('text-anchor', d => xScale(d.start_date)+5 < width - (width/3) ? 'start' : 'end');
timebars
.append("title")
.text(d => `${d.title}`);
return () => {};
}, [data, width, height]);
return (
<svg ref={svgRef}></svg>
);
}