Public
Edited
Mar 31, 2023
Paused
Insert cell
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
filteredData.length
? html`<p style="font-family:sans-serif;">${filteredData.length} event${
filteredData.length > 1 ? "s" : ""
} occured at ${mouseoverDate}</p>`
: ""
Insert cell
chart = {
const tooltipDiv = d3.select(tooltipContainer);
const container = d3.create("div").style("min-height", "150px");
container.append(() => tooltipDiv.node());
const svg = container.append("svg").attr("viewBox", [0, 0, width, height]);

const bg = svg
.append("rect")
.classed("background", true)
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "white");

const labelCollapsed = svg
.append("text")
.attr("x", 0)
.attr("y", 12)
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("opacity", 0)
.text("All Events");

const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

const xAxisGroup = g.append("g").call(xAxis);

const xAxis2Group = g.append("g").call(xAxis2);

const yAxisGroup = g.append("g").call(yAxis);

const eventsGroup = g.append("g").classed("events", true);

eventsGroup
.selectAll("rect")
.data(parsed)
.join("rect")
.attr("x", d => xScale(d.startDate))
.attr("y", d => yScale(d.id))
.attr("width", d => {
const w = Math.round(xScale(d.endDate) - xScale(d.startDate));
return w < rectProps.minWidth || isNaN(w) ? rectProps.minWidth : w;
})
.attr("height", 16)
.attr("fill", "cornflowerblue")
.attr("opacity", 0.7)
.style("mix-blend-mode", "multiply")
.each(function() {
d3.select(this).call(tooltip, tooltipDiv);
});

function collapse() {
eventsGroup
.selectAll("rect")
.transition()
.call(transition)
.attr("y", 0);
xAxisGroup
.transition()
.call(transition)
.attr("transform", `translate(0, ${rectProps.height})`);
xAxis2Group
.transition()
.call(transition)
.attr("opacity", 0);
yAxisGroup
.transition()
.call(transition)
.attr("opacity", 0);
svg
.transition()
.call(transition)
.attr("viewBox", [
0,
0,
width,
rectProps.height + margin.top + margin.bottom
]);
labelCollapsed
.transition()
.call(transition)
.attr("opacity", 1);
}

function expand() {
labelCollapsed.attr("opacity", 0);
eventsGroup
.selectAll("rect")
.transition()
.call(transition)
.attr("y", d => yScale(d.id));
xAxis2Group
.transition()
.call(transition)
.attr("opacity", 1);
xAxisGroup
.transition()
.call(transition)
.attr("transform", `translate(0, ${height - margin.bottom})`);
yAxisGroup
.transition()
.call(transition)
.attr("opacity", 1);
svg
.transition()
.call(transition)
.attr("viewBox", [0, 0, width, height]);
}

function toggle(event) {
if (event.target.value) {
collapse();
} else {
expand();
}
}

// thanks to Steven Tan (@chienleng) for the suggestion of adding an event listener to a view
viewof collapsed.addEventListener("input", toggle);

return container.node();
}
Insert cell
gantt = {
const data = await FileAttachment("gantt.csv").csv({typed: true});
data.forEach(d => {
d.Start = d3.timeParse("%m/%d/%Y")(d.Start);
d.End = new Date(d.Start);
d.End.setDate(d.End.getDate() + d.Duration);
d.Current = new Date(d.Start);
d.Current.setDate(d.Current.getDate() + d.Duration * d.Progress);
});
return data;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more