function BrushableBarChart() {
const margin = { left: 120, top: 20, bottom: 20, right: 20 };
let x = d3.scaleLinear().nice(),
y = d3.scaleBand().padding(0.2),
xAttr = "x",
yAttr = "y",
id = null,
width = 600,
height = 500,
onBrush = () => {};
const brush = d3.brushY();
const updateBrush = (sel, data) => {
function brushed(event) {
const selection = event.selection;
if (!event.sourceEvent) return;
if (selection) {
const [y0, y1] = selection;
const isSelected = d =>
y(d[yAttr]) + y.bandwidth() >= y0 && y(d[yAttr]) <= y1;
const filteredData = sel
.selectAll(".item")
.style("fill", "#ccc")
.filter(isSelected)
.style("fill", "steelblue")
.data();
console.log("barchart", y1, y0, filteredData);
onBrush(filteredData);
} else {
sel.selectAll(".item").style("fill", "steelblue");
onBrush(null);
}
}
brush
.extent([
[0, 0],
[
width - margin.left - margin.right,
height - margin.top - margin.bottom
]
])
.on("start brush end", brushed);
sel.select(".brush").call(brush);
};
const onEnter = enter => {
const svgEnter = enter.append("svg");
const g = svgEnter.append("g").attr("class", "gDrawing");
g.append("g")
.attr("class", "x-axis")
.append("text")
.attr("class", "axisLabel");
g.append("g")
.attr("class", "y-axis")
.append("text")
.attr("class", "axisLabel");
g.append("g").attr("class", "marks");
g.append("g").attr("class", "brush");
return svgEnter;
};
const onUpdate = update => update;
const onExit = exit => exit.remove();
function chart(sel) {
sel.each(data => {
const iwidth = width - margin.left - margin.right,
iheight = height - margin.top - margin.bottom;
const svg = sel
.selectAll("svg")
.data([data])
.join(onEnter, onUpdate, onExit)
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height);
const g = svg
.select("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
x.domain([0, d3.max(data, d => d[xAttr])]).range([0, iwidth]);
y.domain(data.map(d => d[yAttr])).range([0, iheight]);
updateBrush(sel, data);
const t = g.transition().duration(750);
invalidation.then(() => svg.interrupt(t));
g.select(".brush").call(brush);
g.select(".x-axis")
.attr("transform", `translate(0, ${iheight})`)
.transition(t)
.call(d3.axisBottom(x).ticks(3));
g.select(".x-axis")
.select(".axisLabel")
.style("fill", "black")
.attr("transform", `translate(${iwidth}, -10)`)
.style("text-anchor", "end")
.text(xAttr);
g.select(".y-axis")
.transition(t)
.call(d3.axisLeft(y).ticks(3));
g.select(".y-axis")
.select(".axisLabel")
.style("fill", "black")
.style("text-anchor", "middle")
.attr("transform", `translate(0, -10)`)
.text(yAttr);
const moveItems = item =>
item
.attr("x", 0)
.attr("width", d => x(d[xAttr]))
.attr("y", d => y(d[yAttr]));
g.select(".marks")
.selectAll(".item")
.data(data, (d, i) => (id !== null ? d[id] : i))
.join(
enter =>
enter
.append("rect")
.attr("class", "item")
.call(moveItems),
update => update.call(update => update.transition(t).call(moveItems))
)
.attr("height", y.bandwidth())
.style("fill", "steelblue");
});
}
chart.x = function(_) {
if (!arguments.length) return xAttr;
xAttr = _;
return chart;
};
chart.y = function(_) {
if (!arguments.length) return yAttr;
yAttr = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.onBrush = function(_) {
if (!arguments.length) return onBrush;
onBrush = _;
return chart;
};
chart.id = function(_) {
if (!arguments.length) return id;
id = _;
return chart;
};
return chart;
}