Skip to content

Commit b517896

Browse files
committed
Merge pull request #448 from n-riesco/issue-359-add-zoomlayer
Fix issue 359 (zoom overlay drawn beneath shapes)
2 parents 0c741cc + 90700da commit b517896

File tree

6 files changed

+174
-7
lines changed

6 files changed

+174
-7
lines changed

src/plot_api/plot_api.js

+1
Original file line numberDiff line numberDiff line change
@@ -2675,6 +2675,7 @@ function makePlotFramework(gd) {
26752675
// these are in a different svg element normally, but get collapsed into a single
26762676
// svg when exporting (after inserting 3D)
26772677
fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
2678+
fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
26782679
fullLayout._hoverlayer = fullLayout._toppaper.append('g').classed('hoverlayer', true);
26792680

26802681
gd.emit('plotly_framework');

src/plots/cartesian/dragbox.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
139139

140140
dragElement.init(dragOptions);
141141

142-
var x0,
142+
var zoomlayer = gd._fullLayout._zoomlayer,
143+
xs = plotinfo.x()._offset,
144+
ys = plotinfo.y()._offset,
145+
x0,
143146
y0,
144147
box,
145148
lum,
@@ -161,22 +164,24 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
161164
dimmed = false;
162165
zoomMode = 'xy';
163166

164-
zb = plotinfo.plot.append('path')
167+
zb = zoomlayer.append('path')
165168
.attr('class', 'zoombox')
166169
.style({
167170
'fill': lum>0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
168171
'stroke-width': 0
169172
})
173+
.attr('transform','translate(' + xs + ', ' + ys + ')')
170174
.attr('d', path0 + 'Z');
171175

172-
corners = plotinfo.plot.append('path')
176+
corners = zoomlayer.append('path')
173177
.attr('class', 'zoombox-corners')
174178
.style({
175179
fill: Color.background,
176180
stroke: Color.defaultLine,
177181
'stroke-width': 1,
178182
opacity: 0
179183
})
184+
.attr('transform','translate(' + xs + ', ' + ys + ')')
180185
.attr('d','M0,0Z');
181186

182187
clearSelect();
@@ -187,7 +192,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
187192
// until we get around to persistent selections, remove the outline
188193
// here. The selection itself will be removed when the plot redraws
189194
// at the end.
190-
plotinfo.plot.selectAll('.select-outline').remove();
195+
zoomlayer.selectAll('.select-outline').remove();
191196
}
192197

193198
function zoomMove(dx0, dy0) {

src/plots/cartesian/select.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ var MINSELECT = constants.MINSELECT;
2222
function getAxId(ax) { return ax._id; }
2323

2424
module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
25-
var plot = dragOptions.plotinfo.plot,
25+
var plot = dragOptions.gd._fullLayout._zoomlayer,
2626
dragBBox = dragOptions.element.getBoundingClientRect(),
27+
xs = dragOptions.plotinfo.x()._offset,
28+
ys = dragOptions.plotinfo.y()._offset,
2729
x0 = startX - dragBBox.left,
2830
y0 = startY - dragBBox.top,
2931
x1 = x0,
@@ -45,6 +47,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
4547
outlines.enter()
4648
.append('path')
4749
.attr('class', function(d) { return 'select-outline select-outline-' + d; })
50+
.attr('transform','translate(' + xs + ', ' + ys + ')')
4851
.attr('d', path0 + 'Z');
4952

5053
var corners = plot.append('path')
@@ -54,6 +57,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
5457
stroke: color.defaultLine,
5558
'stroke-width': 1
5659
})
60+
.attr('transform','translate(' + xs + ', ' + ys + ')')
5761
.attr('d','M0,0Z');
5862

5963

@@ -176,6 +180,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
176180
};
177181

178182
dragOptions.doneFn = function(dragged, numclicks) {
183+
corners.remove();
179184
if(!dragged && numclicks === 2) {
180185
// clear selection on doubleclick
181186
outlines.remove();
@@ -189,6 +194,5 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
189194
else {
190195
dragOptions.gd.emit('plotly_selected', eventData);
191196
}
192-
corners.remove();
193197
};
194198
};

test/jasmine/tests/cartesian_test.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
var d3 = require('d3');
2+
3+
var Plotly = require('@lib/index');
4+
var Lib = require('@src/lib');
5+
6+
var createGraphDiv = require('../assets/create_graph_div');
7+
var destroyGraphDiv = require('../assets/destroy_graph_div');
8+
var mouseEvent = require('../assets/mouse_event');
9+
10+
11+
describe('zoom box element', function() {
12+
var mock = require('@mocks/14.json');
13+
14+
var gd;
15+
beforeEach(function(done) {
16+
gd = createGraphDiv();
17+
18+
var mockCopy = Lib.extendDeep({}, mock);
19+
mockCopy.layout.dragmode = 'zoom';
20+
21+
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
22+
});
23+
24+
afterEach(destroyGraphDiv);
25+
26+
it('should be appended to the zoom layer', function() {
27+
var x0 = 100;
28+
var y0 = 200;
29+
var x1 = 150;
30+
var y1 = 200;
31+
32+
mouseEvent('mousemove', x0, y0);
33+
expect(d3.selectAll('.zoomlayer > .zoombox').size())
34+
.toEqual(0);
35+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
36+
.toEqual(0);
37+
38+
mouseEvent('mousedown', x0, y0);
39+
mouseEvent('mousemove', x1, y1);
40+
expect(d3.selectAll('.zoomlayer > .zoombox').size())
41+
.toEqual(1);
42+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
43+
.toEqual(1);
44+
45+
mouseEvent('mouseup', x1, y1);
46+
expect(d3.selectAll('.zoomlayer > .zoombox').size())
47+
.toEqual(0);
48+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
49+
.toEqual(0);
50+
});
51+
});

test/jasmine/tests/click_test.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,13 @@ describe('Test click interactions:', function() {
180180

181181
describe('drag interactions', function() {
182182
beforeEach(function(done) {
183-
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
183+
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
184+
// Do not let the notifier hide the drag elements
185+
var tooltip = document.querySelector('.notifier-note');
186+
if(tooltip) tooltip.style.display = 'None';
187+
188+
done();
189+
});
184190
});
185191

186192
it('on nw dragbox should update the axis ranges', function(done) {

test/jasmine/tests/select_test.js

+100
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
var d3 = require('d3');
2+
13
var Plotly = require('@lib/index');
24
var Lib = require('@src/lib');
35
var DBLCLICKDELAY = require('@src/plots/cartesian/constants').DBLCLICKDELAY;
@@ -54,6 +56,104 @@ describe('select box and lasso', function() {
5456
expect(actual.y).toBeCloseToArray(expected.y, PRECISION);
5557
}
5658

59+
describe('select elements', function() {
60+
var mockCopy = Lib.extendDeep({}, mock);
61+
mockCopy.layout.dragmode = 'select';
62+
63+
var gd;
64+
beforeEach(function(done) {
65+
gd = createGraphDiv();
66+
67+
Plotly.plot(gd, mockCopy.data, mockCopy.layout)
68+
.then(done);
69+
});
70+
71+
it('should be appended to the zoom layer', function(done) {
72+
var x0 = 100,
73+
y0 = 200,
74+
x1 = 150,
75+
y1 = 250,
76+
x2 = 50,
77+
y2 = 50;
78+
79+
gd.once('plotly_selecting', function() {
80+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
81+
.toEqual(1);
82+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
83+
.toEqual(2);
84+
});
85+
86+
gd.once('plotly_selected', function() {
87+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
88+
.toEqual(0);
89+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
90+
.toEqual(2);
91+
});
92+
93+
gd.once('plotly_deselect', function() {
94+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
95+
.toEqual(0);
96+
});
97+
98+
mouseEvent('mousemove', x0, y0);
99+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
100+
.toEqual(0);
101+
102+
drag([[x0, y0], [x1, y1]]);
103+
104+
doubleClick(x2, y2, done);
105+
});
106+
});
107+
108+
describe('lasso elements', function() {
109+
var mockCopy = Lib.extendDeep({}, mock);
110+
mockCopy.layout.dragmode = 'lasso';
111+
112+
var gd;
113+
beforeEach(function(done) {
114+
gd = createGraphDiv();
115+
116+
Plotly.plot(gd, mockCopy.data, mockCopy.layout)
117+
.then(done);
118+
});
119+
120+
it('should be appended to the zoom layer', function(done) {
121+
var x0 = 100,
122+
y0 = 200,
123+
x1 = 150,
124+
y1 = 250,
125+
x2 = 50,
126+
y2 = 50;
127+
128+
gd.once('plotly_selecting', function() {
129+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
130+
.toEqual(1);
131+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
132+
.toEqual(2);
133+
});
134+
135+
gd.once('plotly_selected', function() {
136+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
137+
.toEqual(0);
138+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
139+
.toEqual(2);
140+
});
141+
142+
gd.once('plotly_deselect', function() {
143+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
144+
.toEqual(0);
145+
});
146+
147+
mouseEvent('mousemove', x0, y0);
148+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
149+
.toEqual(0);
150+
151+
drag([[x0, y0], [x1, y1]]);
152+
153+
doubleClick(x2, y2, done);
154+
});
155+
});
156+
57157
describe('select events', function() {
58158
var mockCopy = Lib.extendDeep({}, mock);
59159
mockCopy.layout.dragmode = 'select';

0 commit comments

Comments
 (0)