Skip to content

Commit adefca3

Browse files
authored
Merge pull request #2339 from plotly/negative-negative-translate-transform
Negate axis offset and then turn it into a string
2 parents abbb67d + 16b01f0 commit adefca3

File tree

9 files changed

+191
-59
lines changed

9 files changed

+191
-59
lines changed

src/components/fx/hover.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,18 @@ function _hover(gd, evt, subplot, noHoverEvent) {
175175
subplots = subplots.concat(overlayedSubplots);
176176
}
177177

178-
var len = subplots.length,
179-
xaArray = new Array(len),
180-
yaArray = new Array(len);
178+
var len = subplots.length;
179+
var xaArray = new Array(len);
180+
var yaArray = new Array(len);
181+
var supportsCompare = false;
181182

182183
for(var i = 0; i < len; i++) {
183184
var spId = subplots[i];
184185

185186
// 'cartesian' case
186187
var plotObj = plots[spId];
187188
if(plotObj) {
189+
supportsCompare = true;
188190

189191
// TODO make sure that fullLayout_plots axis refs
190192
// get updated properly so that we don't have
@@ -203,6 +205,8 @@ function _hover(gd, evt, subplot, noHoverEvent) {
203205

204206
var hovermode = evt.hovermode || fullLayout.hovermode;
205207

208+
if(hovermode && !supportsCompare) hovermode = 'closest';
209+
206210
if(['x', 'y', 'closest'].indexOf(hovermode) === -1 || !gd.calcdata ||
207211
gd.querySelector('.zoombox') || gd._dragging) {
208212
return dragElement.unhoverRaw(gd, evt);

src/components/modebar/buttons.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ function handleDrag3d(gd, ev) {
302302
layoutUpdate[sceneIds[i] + '.' + parts[1]] = val;
303303
}
304304

305+
// for multi-type subplots
306+
var val2d = (val === 'pan') ? val : 'zoom';
307+
layoutUpdate.dragmode = val2d;
308+
305309
Plotly.relayout(gd, layoutUpdate);
306310
}
307311

src/components/modebar/manage.js

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,13 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) {
8282
var hasTernary = fullLayout._has('ternary');
8383
var hasMapbox = fullLayout._has('mapbox');
8484
var hasPolar = fullLayout._has('polar');
85+
var allAxesFixed = areAllAxesFixed(fullLayout);
8586

8687
var groups = [];
8788

8889
function addGroup(newGroup) {
90+
if(!newGroup.length) return;
91+
8992
var out = [];
9093

9194
for(var i = 0; i < newGroup.length; i++) {
@@ -100,55 +103,71 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) {
100103
// buttons common to all plot types
101104
addGroup(['toImage', 'sendDataToCloud']);
102105

103-
// graphs with more than one plot types get 'union buttons'
104-
// which reset the view or toggle hover labels across all subplots.
105-
if((hasCartesian || hasGL2D || hasPie || hasTernary) + hasGeo + hasGL3D > 1) {
106-
addGroup(['resetViews', 'toggleHover']);
107-
return appendButtonsToGroups(groups, buttonsToAdd);
108-
}
106+
var zoomGroup = [];
107+
var hoverGroup = [];
108+
var resetGroup = [];
109+
var dragModeGroup = [];
109110

110-
if(hasGL3D) {
111-
addGroup(['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation']);
112-
addGroup(['resetCameraDefault3d', 'resetCameraLastSave3d']);
113-
addGroup(['hoverClosest3d']);
111+
if((hasCartesian || hasGL2D || hasPie || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
112+
// graphs with more than one plot types get 'union buttons'
113+
// which reset the view or toggle hover labels across all subplots.
114+
hoverGroup = ['toggleHover'];
115+
resetGroup = ['resetViews'];
116+
}
117+
else if(hasGeo) {
118+
zoomGroup = ['zoomInGeo', 'zoomOutGeo'];
119+
hoverGroup = ['hoverClosestGeo'];
120+
resetGroup = ['resetGeo'];
121+
}
122+
else if(hasGL3D) {
123+
hoverGroup = ['hoverClosest3d'];
124+
resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d'];
125+
}
126+
else if(hasMapbox) {
127+
hoverGroup = ['toggleHover'];
128+
resetGroup = ['resetViewMapbox'];
129+
}
130+
else if(hasGL2D) {
131+
hoverGroup = ['hoverClosestGl2d'];
132+
}
133+
else if(hasPie) {
134+
hoverGroup = ['hoverClosestPie'];
135+
}
136+
else { // hasPolar, hasTernary
137+
// always show at least one hover icon.
138+
hoverGroup = ['toggleHover'];
139+
}
140+
// if we have cartesian, allow switching between closest and compare
141+
// regardless of what other types are on the plot, since they'll all
142+
// just treat any truthy hovermode as 'closest'
143+
if(hasCartesian) {
144+
hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'];
114145
}
115146

116-
var allAxesFixed = areAllAxesFixed(fullLayout),
117-
dragModeGroup = [];
147+
if((hasCartesian || hasGL2D) && !allAxesFixed) {
148+
zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d'];
149+
if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d'];
150+
}
118151

119-
if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
152+
if(hasGL3D) {
153+
dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'];
154+
}
155+
else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
120156
dragModeGroup = ['zoom2d', 'pan2d'];
121157
}
122-
if(hasMapbox || hasGeo) {
158+
else if(hasMapbox || hasGeo) {
123159
dragModeGroup = ['pan2d'];
124160
}
125-
if(hasPolar) {
161+
else if(hasPolar) {
126162
dragModeGroup = ['zoom2d'];
127163
}
128164
if(isSelectable(fullData)) {
129-
dragModeGroup.push('select2d');
130-
dragModeGroup.push('lasso2d');
131-
}
132-
if(dragModeGroup.length) addGroup(dragModeGroup);
133-
134-
if((hasCartesian || hasGL2D) && !allAxesFixed && !hasTernary) {
135-
addGroup(['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d']);
165+
dragModeGroup.push('select2d', 'lasso2d');
136166
}
137167

138-
if(hasCartesian && hasPie) {
139-
addGroup(['toggleHover']);
140-
} else if(hasGL2D) {
141-
addGroup(['hoverClosestGl2d']);
142-
} else if(hasCartesian) {
143-
addGroup(['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']);
144-
} else if(hasPie) {
145-
addGroup(['hoverClosestPie']);
146-
} else if(hasMapbox) {
147-
addGroup(['resetViewMapbox', 'toggleHover']);
148-
} else if(hasGeo) {
149-
addGroup(['zoomInGeo', 'zoomOutGeo', 'resetGeo']);
150-
addGroup(['hoverClosestGeo']);
151-
}
168+
addGroup(dragModeGroup);
169+
addGroup(zoomGroup.concat(resetGroup));
170+
addGroup(hoverGroup);
152171

153172
return appendButtonsToGroups(groups, buttonsToAdd);
154173
}

src/plots/ternary/ternary.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,12 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
328328
_this.layers.bgrid.attr('transform', bTransform);
329329

330330
var aTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
331-
')rotate(30)translate(0,-' + aaxis._offset + ')';
331+
')rotate(30)translate(0,' + -aaxis._offset + ')';
332332
_this.layers.aaxis.attr('transform', aTransform);
333333
_this.layers.agrid.attr('transform', aTransform);
334334

335335
var cTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
336-
')rotate(-30)translate(0,-' + caxis._offset + ')';
336+
')rotate(-30)translate(0,' + -caxis._offset + ')';
337337
_this.layers.caxis.attr('transform', cTransform);
338338
_this.layers.cgrid.attr('transform', cTransform);
339339

test/image/strict-d3.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@ selProto.style = function() {
4646
return originalSelStyle.apply(sel, arguments);
4747
};
4848

49-
function checkAttrVal(sel, key) {
49+
function checkAttrVal(sel, key, val) {
5050
// setting the transform attribute on a <clipPath> does not
5151
// work in Chrome, IE and Edge
5252
if(sel.node().nodeName === 'clipPath' && key === 'transform') {
5353
throw new Error('d3 selection.attr called with key \'transform\' on a clipPath node');
5454
}
55+
56+
// make sure no double-negative string get into the DOM,
57+
// their handling differs from browsers to browsers
58+
if(/--/.test(val) && isNumeric(val.split('--')[1].charAt(0))) {
59+
throw new Error('d3 selection.attr called with value ' + val + ' which includes a double negative');
60+
}
5561
}
5662

5763
function checkStyleVal(sel, key, val) {

test/jasmine/tests/gl3d_plot_interact_test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ describe('@gl Test gl3d modebar handlers', function() {
567567

568568
buttonZoom3d.click();
569569
assertScenes(gd._fullLayout, 'dragmode', 'zoom');
570-
expect(gd.layout.dragmode).toBe(undefined);
570+
expect(gd.layout.dragmode).toBe('zoom'); // for multi-type subplots
571571
expect(gd._fullLayout.dragmode).toBe('zoom');
572572
expect(buttonTurntable.isActive()).toBe(false);
573573
expect(buttonZoom3d.isActive()).toBe(true);
@@ -588,8 +588,8 @@ describe('@gl Test gl3d modebar handlers', function() {
588588

589589
buttonPan3d.click();
590590
assertScenes(gd._fullLayout, 'dragmode', 'pan');
591-
expect(gd.layout.dragmode).toBe(undefined);
592-
expect(gd._fullLayout.dragmode).toBe('zoom');
591+
expect(gd.layout.dragmode).toBe('pan'); // for multi-type subplots
592+
expect(gd._fullLayout.dragmode).toBe('pan');
593593
expect(buttonTurntable.isActive()).toBe(false);
594594
expect(buttonPan3d.isActive()).toBe(true);
595595

@@ -609,7 +609,7 @@ describe('@gl Test gl3d modebar handlers', function() {
609609

610610
buttonOrbit.click();
611611
assertScenes(gd._fullLayout, 'dragmode', 'orbit');
612-
expect(gd.layout.dragmode).toBe(undefined);
612+
expect(gd.layout.dragmode).toBe('zoom'); // fallback for multi-type subplots
613613
expect(gd._fullLayout.dragmode).toBe('zoom');
614614
expect(buttonTurntable.isActive()).toBe(false);
615615
expect(buttonOrbit.isActive()).toBe(true);

test/jasmine/tests/hover_label_test.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,8 @@ describe('hover on fill', function() {
13751375
var gd = createGraphDiv();
13761376

13771377
Plotly.plot(gd, mock.data, mock.layout).then(function() {
1378+
expect(gd._fullLayout.hovermode).toBe('closest');
1379+
13781380
// hover over a point when that's closest, even if you're over
13791381
// a fill, because by default we have hoveron='points+fills'
13801382
return assertLabelsCorrect([237, 150], [240.0, 144],
@@ -1402,7 +1404,35 @@ describe('hover on fill', function() {
14021404
}).then(function() {
14031405
// then make sure we can still select a *different* item afterward
14041406
return assertLabelsCorrect([237, 218], [266.75, 265], 'trace 1');
1405-
}).then(done);
1407+
})
1408+
.catch(fail)
1409+
.then(done);
1410+
});
1411+
1412+
it('should act like closest mode on ternary when cartesian is in compare mode', function(done) {
1413+
var mock = Lib.extendDeep({}, require('@mocks/ternary_fill.json'));
1414+
var gd = createGraphDiv();
1415+
1416+
mock.data.push({y: [7, 8, 9]});
1417+
mock.layout.xaxis = {domain: [0.8, 1], visible: false};
1418+
mock.layout.yaxis = {domain: [0.8, 1], visible: false};
1419+
1420+
Plotly.plot(gd, mock.data, mock.layout).then(function() {
1421+
expect(gd._fullLayout.hovermode).toBe('x');
1422+
1423+
// hover over a point when that's closest, even if you're over
1424+
// a fill, because by default we have hoveron='points+fills'
1425+
return assertLabelsCorrect([237, 150], [240.0, 144],
1426+
'trace 2Component A: 0.8Component B: 0.1Component C: 0.1');
1427+
}).then(function() {
1428+
// hovers over fills
1429+
return assertLabelsCorrect([237, 170], [247.7, 166], 'trace 2');
1430+
}).then(function() {
1431+
// hover on the cartesian trace in the corner
1432+
return assertLabelsCorrect([363, 122], [363, 122], 'trace 38');
1433+
})
1434+
.catch(fail)
1435+
.then(done);
14061436
});
14071437
});
14081438

0 commit comments

Comments
 (0)