I'm creating 2 nested brushes using d3. The inner brush is bound by the outer brush. I also limit the behavior of the brushes using filter function. Clicking on the outer brush selection creates the inner brush, and double clicking the selection of the inner brush removes it.
I see that the brushes behave perfectly in all but one instance.
If I squish the outer brush into the inner brush, and then try to alter the height of the inner brush, d3 is not respecting the extent of the inner brush. It seems like d3 is multiplying the height by 10, which seems to be causing this.
<!DOCTYPE html>
<style>
.handle--w, .selection, .overlay {
cursor: auto;
}
.brush2>.handle--n {
cursor: auto;
}
.brush2>.selection {
fill: red;
}
</style>
<svg width=1200 height=500></svg>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script type="text/javascript">
var rectHeight = 80, rectWidth = 180,
svg = d3.select("svg"),
mainGroup = svg.append("g");
var xScale = d3.scaleLinear()
.domain([0,1])
.range([0,rectWidth]);
// function for outer brush
var brush1 = d3.brushX()
.extent([[0, 0], [rectWidth-10, rectHeight]])
.handleSize([4])
.filter(function(){
// keep only the right side expansion/contraction
return !d3.event.button
&& !d3.select(d3.event.target).classed("handle--w")
&& !d3.select(d3.event.target).classed("overlay")
&& !d3.select(d3.event.target).classed("selection")
;
})
.on("brush", brushed1)
;
// when brush1 is "move"d
function brushed1() {
var w = d3.select(this).select(".selection").attr("width")-3;
//console.log("in brushed1 -- "+w);
if(!d3.select(this.nextSibling).empty()){
// handle the inner brush, if present
//console.log("found inner brush");
var innerBrush = d3.select(this.nextSibling);
brush2.extent([[0,0],[w,rectHeight]]);
innerBrush.call(brush2);
if (w < innerBrush.select(".selection").attr("width")){
var innerHeight = innerBrush.select(".selection").attr("height");
innerBrush.call(brush2.move, [[0,0],[w,innerHeight]]);
}
}
}
// function for inner brush
var brush2 = d3.brush()
.handleSize([4])
.filter(function(){
return !d3.event.button
&& !d3.select(d3.event.target).classed("overlay")
&& !d3.select(d3.event.target).classed("selection")
;
});
// create outer brush
mainGroup.append("g")
.attr("class", "brush1")
.call(brush1)
.call(brush1.move, [0,0.65].map(xScale));
// add function for outer brush selection click
mainGroup.selectAll("rect.selection")
.on("mousedown touchstart", function() {
var w = d3.select(this).attr("width")-3;
//console.log("Selection clicked -- " + w);
var p = d3.select(this.parentNode.parentNode);
// Create inner brush if one doesn't exist
if (d3.select(this.parentNode.nextSibling).empty()) {
//console.log("creating element");
//console.log(`w ${w} rectHeight ${rectHeight}`);
brush2.extent([[0,0], [w,rectHeight]]);
var innerBrush = p.append("g")
.attr("class", "brush2")
.call(brush2)
.call(brush2.move, [[0,0],[w/2,rectHeight/2]])
;
// remove inner brush if double clicked
innerBrush.select("rect.selection")
.on("dblclick touchstart", function(){
//console.log("removing element");
d3.select(this.parentNode).remove();
})
}
});
</script>