Published
Edited
Jun 15, 2020
1 fork
Insert cell
Insert cell
chart = {
const svg = d3.select(DOM.svg(width, height));
const margin = {top: 40, right: 10, bottom: 40, left: 30}
// const color = d3.scaleOrdinal().range(["#28677D", "#12A6AD"])
const x0 = d3
.scaleBand()
.domain(expData.map((d,i) => d.month + d.id))
.rangeRound([margin.left, width - margin.right])
.paddingInner(0.1)

const x1 = d3
.scaleBand()
.domain(keys)
.rangeRound([0, x0.bandwidth()])
.padding(0.05)

const y = d3
.scaleLinear()
.domain([0, d3.max(expData, d => Math.ceil(d3.max(keys, key => d[key]) / 5) * 5)])
.rangeRound([height - margin.bottom, margin.top])

const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x0)).attr("font-size", "0.75rem")
.call(g => g.select(".domain").remove())
.selectAll('text')
.attr("transform", "translate(-8,11), rotate(-45)")
.text(d => d.slice(0,3))


const yAxis = g => g
.attr("transform", `translate(${margin.right},0)`)
.call(d3.axisRight(y)
.ticks(7, "s")
.tickSize(width - margin.right))
.call(g => g.select(".domain")
.remove())
.call(g => g.selectAll(".tick:not(:first-of-type) line")
.attr("stroke-opacity", 0.5)
.attr("stroke-dasharray", "2,2"))
.call(g => g.selectAll(".tick text")
.attr("x", 0)
.attr("dy", -5.5))
.attr("font-size", "0.9rem")
.attr("opacity", 0.8)

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

svg.append("g")
// .attr("width", width)
// .attr("height", height + 110)
.selectAll("g") // searches within parent g for child g's -> returns empty []
.data(expData) // fill empty [] with expData

// ✅ logic below runs on each item in expData array
.join("g") // appends g element
.attr('class', 'bar')
.attr("transform", d => `translate(${x0(d.month + d.id)},0)`)

.selectAll("rect") // evaluates to empty []
.data(d => keys.map(key => ({key, value: d[key]}))) // attach data for each month (2 rects)
.join("rect") // appends a rect for each key value pair
.attr("x", d => x1(d.key)) // each rect element has associated data bound to it
.attr("width", x1.bandwidth())
.attr("fill", d => color(d.key))
.attr("height", d => height - y(0) - margin.bottom)
.attr("y", d => y(0))

svg.selectAll("rect")
.transition()
.duration(600)
.attr("height", d => height - y(d.value) - margin.bottom)
.attr("y", d => y(d.value))
svg.selectAll('rect').on('mouseover', function(d, i) {
svg.append("text")
.attr('class', 'count-display')
.attr('x', () => {
// match first value of parent node's translate then add conditional space
let x = /^[^\d]*(\d+)/.exec(d3.select(this.parentNode).attr("transform"))[1]
let xPlus = d.key == "Work Begins" ? x1.bandwidth() / 2 : x1.bandwidth() * 1.5
return Number(x) + xPlus + 2
})
.attr("y", (d2) => y(d.value) - 7)
.attr('text-anchor', 'middle')
.text((d2) => d.value);
svg.selectAll('rect')
.transition().duration(10)
.attr('opacity', (d2) => d2 != d ? 0.6 : 1)
})
.on('mouseout', (d) => {
d3.select('.count-display').remove()
svg.selectAll('rect')
.transition().duration(500)
.attr('opacity', 1)
})


return svg.node();
}
Insert cell
color = d3.scaleOrdinal().range(["#28677D", "#12A6AD"])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
keys = ["Work Begins", "Launched"]
Insert cell
margin = ({top: 10, right: 10, bottom: 20, left: 40})
Insert cell
width = document.body.clientWidth
Insert cell
height = 500
Insert cell
d3 = require("d3@5")
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