Skip to content

Commit fdae65b

Browse files
authored
Merge pull request #2944 from plotly/1852-persistent-click-via-selectPoints
1852 persistent click
2 parents 178feb5 + b6bd813 commit fdae65b

File tree

24 files changed

+1479
-288
lines changed

24 files changed

+1479
-288
lines changed

src/components/fx/layout_attributes.js

+33-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ fontAttrs.family.dflt = constants.HOVERFONT;
1818
fontAttrs.size.dflt = constants.HOVERFONTSIZE;
1919

2020
module.exports = {
21+
clickmode: {
22+
valType: 'flaglist',
23+
role: 'info',
24+
flags: ['event', 'select'],
25+
dflt: 'event',
26+
editType: 'plot',
27+
extras: ['none'],
28+
description: [
29+
'Determines the mode of single click interactions.',
30+
'*event* is the default value and emits the `plotly_click`',
31+
'event. In addition this mode emits the `plotly_selected` event',
32+
'in drag modes *lasso* and *select*, but with no event data attached',
33+
'(kept for compatibility reasons).',
34+
'The *select* flag enables selecting single',
35+
'data points via click. This mode also supports persistent selections,',
36+
'meaning that pressing Shift while clicking, adds to / subtracts from an',
37+
'existing selection. *select* with `hovermode`: *x* can be confusing, consider',
38+
'explicitly setting `hovermode`: *closest* when using this feature.',
39+
'Selection events are sent accordingly as long as *event* flag is set as well.',
40+
'When the *event* flag is missing, `plotly_click` and `plotly_selected`',
41+
'events are not fired.'
42+
].join(' ')
43+
},
2144
dragmode: {
2245
valType: 'enumerated',
2346
role: 'info',
@@ -36,7 +59,16 @@ module.exports = {
3659
role: 'info',
3760
values: ['x', 'y', 'closest', false],
3861
editType: 'modebar',
39-
description: 'Determines the mode of hover interactions.'
62+
description: [
63+
'Determines the mode of hover interactions.',
64+
'If `clickmode` includes the *select* flag,',
65+
'`hovermode` defaults to *closest*.',
66+
'If `clickmode` lacks the *select* flag,',
67+
'it defaults to *x* or *y* (depending on the trace\'s',
68+
'`orientation` value) for plots based on',
69+
'cartesian coordinates. For anything else the default',
70+
'value is *closest*.',
71+
].join(' ')
4072
},
4173
hoverdistance: {
4274
valType: 'integer',

src/components/fx/layout_defaults.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
1616
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
1717
}
1818

19+
var clickmode = coerce('clickmode');
20+
1921
var dragMode = coerce('dragmode');
2022
if(dragMode === 'select') coerce('selectdirection');
2123

2224
var hovermodeDflt;
2325
if(layoutOut._has('cartesian')) {
24-
// flag for 'horizontal' plots:
25-
// determines the state of the mode bar 'compare' hovermode button
26-
layoutOut._isHoriz = isHoriz(fullData);
27-
hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x';
26+
if(clickmode.indexOf('select') > -1) {
27+
hovermodeDflt = 'closest';
28+
} else {
29+
// flag for 'horizontal' plots:
30+
// determines the state of the mode bar 'compare' hovermode button
31+
layoutOut._isHoriz = isHoriz(fullData);
32+
hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x';
33+
}
2834
}
2935
else hovermodeDflt = 'closest';
3036

src/lib/polygon.js

-46
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ var polygon = module.exports = {};
3131
* returns boolean: is pt inside the polygon (including on its edges)
3232
*/
3333
polygon.tester = function tester(ptsIn) {
34-
if(Array.isArray(ptsIn[0][0])) return polygon.multitester(ptsIn);
35-
3634
var pts = ptsIn.slice(),
3735
xmin = pts[0][0],
3836
xmax = xmin,
@@ -174,50 +172,6 @@ polygon.tester = function tester(ptsIn) {
174172
};
175173
};
176174

177-
/**
178-
* Test multiple polygons
179-
*/
180-
polygon.multitester = function multitester(list) {
181-
var testers = [],
182-
xmin = list[0][0][0],
183-
xmax = xmin,
184-
ymin = list[0][0][1],
185-
ymax = ymin;
186-
187-
for(var i = 0; i < list.length; i++) {
188-
var tester = polygon.tester(list[i]);
189-
tester.subtract = list[i].subtract;
190-
testers.push(tester);
191-
xmin = Math.min(xmin, tester.xmin);
192-
xmax = Math.max(xmax, tester.xmax);
193-
ymin = Math.min(ymin, tester.ymin);
194-
ymax = Math.max(ymax, tester.ymax);
195-
}
196-
197-
function contains(pt, arg) {
198-
var yes = false;
199-
for(var i = 0; i < testers.length; i++) {
200-
if(testers[i].contains(pt, arg)) {
201-
// if contained by subtract polygon - exclude the point
202-
yes = testers[i].subtract === false;
203-
}
204-
}
205-
206-
return yes;
207-
}
208-
209-
return {
210-
xmin: xmin,
211-
xmax: xmax,
212-
ymin: ymin,
213-
ymax: ymax,
214-
pts: [],
215-
contains: contains,
216-
isRect: false,
217-
degenerate: false
218-
};
219-
};
220-
221175
/**
222176
* Test if a segment of a points array is bent or straight
223177
*

src/plots/cartesian/dragbox.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var doTicksSingle = require('./axes').doTicksSingle;
3030
var getFromId = require('./axis_ids').getFromId;
3131
var prepSelect = require('./select').prepSelect;
3232
var clearSelect = require('./select').clearSelect;
33+
var selectOnClick = require('./select').selectOnClick;
3334
var scaleZoom = require('./scale_zoom');
3435

3536
var constants = require('./constants');
@@ -148,7 +149,11 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
148149
};
149150

150151
dragOptions.prepFn = function(e, startX, startY) {
152+
var dragModePrev = dragOptions.dragmode;
151153
var dragModeNow = gd._fullLayout.dragmode;
154+
if(dragModeNow !== dragModePrev) {
155+
dragOptions.dragmode = dragModeNow;
156+
}
152157

153158
recomputeAxisLists();
154159

@@ -178,7 +183,19 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
178183
prepSelect(e, startX, startY, dragOptions, dragModeNow);
179184
} else {
180185
dragOptions.clickFn = clickFn;
181-
clearAndResetSelect();
186+
if(isSelectOrLasso(dragModePrev)) {
187+
// TODO Fix potential bug
188+
// Note: clearing / resetting selection state only happens, when user
189+
// triggers at least one interaction in pan/zoom mode. Otherwise, the
190+
// select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection
191+
// cache isn't cleared. So when the user switches back to select/lasso and
192+
// 'adds to a selection' with Shift, the "old", seemingly removed outlines
193+
// are redrawn again because the selection cache still holds their coordinates.
194+
// However, this isn't easily solved, since plots.js would need
195+
// to have a reference to the dragOptions object (which holds the
196+
// selection cache).
197+
clearAndResetSelect();
198+
}
182199

183200
if(!allFixedRanges) {
184201
if(dragModeNow === 'zoom') {
@@ -207,12 +224,20 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
207224
}
208225

209226
function clickFn(numClicks, evt) {
227+
var clickmode = gd._fullLayout.clickmode;
228+
210229
removeZoombox(gd);
211230

212231
if(numClicks === 2 && !singleEnd) doubleClick();
213232

214233
if(isMainDrag) {
215-
Fx.click(gd, evt, plotinfo.id);
234+
if(clickmode.indexOf('select') > -1) {
235+
selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions);
236+
}
237+
238+
if(clickmode.indexOf('event') > -1) {
239+
Fx.click(gd, evt, plotinfo.id);
240+
}
216241
}
217242
else if(numClicks === 1 && singleEnd) {
218243
var ax = ns ? ya0 : xa0,

0 commit comments

Comments
 (0)