Skip to content

Commit 8cc5f55

Browse files
authored
Merge pull request #2241 from plotly/right-click-fix
Right click fix
2 parents 0d99411 + 974cebf commit 8cc5f55

File tree

22 files changed

+654
-378
lines changed

22 files changed

+654
-378
lines changed

src/components/annotations/draw.js

+8-12
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,10 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
593593
xcenter + ',' + ycenter + ')'
594594
});
595595
},
596-
doneFn: function(dragged) {
597-
if(dragged) {
598-
Plotly.relayout(gd, update);
599-
var notesBox = document.querySelector('.js-notes-box-panel');
600-
if(notesBox) notesBox.redraw(notesBox.selectedObj);
601-
}
596+
doneFn: function() {
597+
Plotly.relayout(gd, update);
598+
var notesBox = document.querySelector('.js-notes-box-panel');
599+
if(notesBox) notesBox.redraw(notesBox.selectedObj);
602600
}
603601
});
604602
}
@@ -673,13 +671,11 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
673671

674672
setCursor(annTextGroupInner, csr);
675673
},
676-
doneFn: function(dragged) {
674+
doneFn: function() {
677675
setCursor(annTextGroupInner);
678-
if(dragged) {
679-
Plotly.relayout(gd, update);
680-
var notesBox = document.querySelector('.js-notes-box-panel');
681-
if(notesBox) notesBox.redraw(notesBox.selectedObj);
682-
}
676+
Plotly.relayout(gd, update);
677+
var notesBox = document.querySelector('.js-notes-box-panel');
678+
if(notesBox) notesBox.redraw(notesBox.selectedObj);
683679
}
684680
});
685681
}

src/components/colorbar/draw.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -584,10 +584,10 @@ module.exports = function draw(gd, id) {
584584
opts.xanchor, opts.yanchor);
585585
setCursor(container, csr);
586586
},
587-
doneFn: function(dragged) {
587+
doneFn: function() {
588588
setCursor(container);
589589

590-
if(dragged && xf !== undefined && yf !== undefined) {
590+
if(xf !== undefined && yf !== undefined) {
591591
Plotly.restyle(gd,
592592
{'colorbar.x': xf, 'colorbar.y': yf},
593593
getTrace().index);

src/components/dragelement/index.js

+64-35
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,44 @@ dragElement.unhoverRaw = unhover.raw;
3838
* - Freezes the cursor: whatever mouse cursor the drag element had when the
3939
* interaction started gets copied to the coverSlip for use until mouseup
4040
*
41+
* If the user executes a drag bigger than MINDRAG, callbacks will fire as:
42+
* prepFn, moveFn (1 or more times), doneFn
43+
* If the user does not drag enough, prepFn and clickFn will fire.
44+
*
45+
* Note: If you cancel contextmenu, clickFn will fire even with a right click
46+
* (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
47+
* gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
48+
* TODO: we should probably turn this into a `config` parameter, so we can fix it
49+
* such that if you *don't* cancel contextmenu, we can prevent partial drags, which
50+
* put you in a weird state.
51+
*
52+
* If the user clicks multiple times quickly, clickFn will fire each time
53+
* but numClicks will increase to help you recognize doubleclicks.
54+
*
4155
* @param {object} options with keys:
4256
* element (required) the DOM element to drag
4357
* prepFn (optional) function(event, startX, startY)
4458
* executed on mousedown
4559
* startX and startY are the clientX and clientY pixel position
4660
* of the mousedown event
47-
* moveFn (optional) function(dx, dy, dragged)
48-
* executed on move
61+
* moveFn (optional) function(dx, dy)
62+
* executed on move, ONLY after we've exceeded MINDRAG
63+
* (we keep executing moveFn if you move back to where you started)
4964
* dx and dy are the net pixel offset of the drag,
5065
* dragged is true/false, has the mouse moved enough to
5166
* constitute a drag
52-
* doneFn (optional) function(dragged, numClicks, e)
53-
* executed on mouseup, or mouseout of window since
54-
* we don't get events after that
55-
* dragged is as in moveFn
67+
* doneFn (optional) function(e)
68+
* executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
69+
* sure that moveFn has been called at least once)
70+
* numClicks is how many clicks we've registered within
71+
* a doubleclick time
72+
* e is the original mouseup event
73+
* clickFn (optional) function(numClicks, e)
74+
* executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
75+
* has not been called at all)
5676
* numClicks is how many clicks we've registered within
5777
* a doubleclick time
58-
* e is the original event
78+
* e is the original mousedown event
5979
*/
6080
dragElement.init = function init(options) {
6181
var gd = options.gd;
@@ -68,7 +88,9 @@ dragElement.init = function init(options) {
6888
newMouseDownTime,
6989
cursor,
7090
dragCover,
71-
initialTarget;
91+
initialEvent,
92+
initialTarget,
93+
rightClick;
7294

7395
if(!gd._mouseDownTime) gd._mouseDownTime = 0;
7496

@@ -78,10 +100,6 @@ dragElement.init = function init(options) {
78100
element.ontouchstart = onStart;
79101

80102
function onStart(e) {
81-
if(e.buttons && e.buttons === 2) { // right click
82-
return;
83-
}
84-
85103
// make dragging and dragged into properties of gd
86104
// so that others can look at and modify them
87105
gd._dragged = false;
@@ -90,6 +108,8 @@ dragElement.init = function init(options) {
90108
startX = offset[0];
91109
startY = offset[1];
92110
initialTarget = e.target;
111+
initialEvent = e;
112+
rightClick = (e.buttons && e.buttons === 2) || e.ctrlKey;
93113

94114
newMouseDownTime = (new Date()).getTime();
95115
if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) {
@@ -104,11 +124,11 @@ dragElement.init = function init(options) {
104124

105125
if(options.prepFn) options.prepFn(e, startX, startY);
106126

107-
if(hasHover) {
127+
if(hasHover && !rightClick) {
108128
dragCover = coverSlip();
109129
dragCover.style.cursor = window.getComputedStyle(element).cursor;
110130
}
111-
else {
131+
else if(!hasHover) {
112132
// document acts as a dragcover for mobile, bc we can't create dragcover dynamically
113133
dragCover = document;
114134
cursor = window.getComputedStyle(document.documentElement).cursor;
@@ -136,7 +156,7 @@ dragElement.init = function init(options) {
136156
dragElement.unhover(gd);
137157
}
138158

139-
if(options.moveFn) options.moveFn(dx, dy, gd._dragged);
159+
if(gd._dragged && options.moveFn && !rightClick) options.moveFn(dx, dy);
140160

141161
return Lib.pauseEvent(e);
142162
}
@@ -167,27 +187,36 @@ dragElement.init = function init(options) {
167187
numClicks = Math.max(numClicks - 1, 1);
168188
}
169189

170-
if(options.doneFn) options.doneFn(gd._dragged, numClicks, e);
171-
172-
if(!gd._dragged) {
173-
var e2;
174-
175-
try {
176-
e2 = new MouseEvent('click', e);
177-
}
178-
catch(err) {
179-
var offset = pointerOffset(e);
180-
e2 = document.createEvent('MouseEvents');
181-
e2.initMouseEvent('click',
182-
e.bubbles, e.cancelable,
183-
e.view, e.detail,
184-
e.screenX, e.screenY,
185-
offset[0], offset[1],
186-
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
187-
e.button, e.relatedTarget);
190+
if(gd._dragged) {
191+
if(options.doneFn) options.doneFn(e);
192+
}
193+
else {
194+
if(options.clickFn) options.clickFn(numClicks, initialEvent);
195+
196+
// If we haven't dragged, this should be a click. But because of the
197+
// coverSlip changing the element, the natural system might not generate one,
198+
// so we need to make our own. But right clicks don't normally generate
199+
// click events, only contextmenu events, which happen on mousedown.
200+
if(!rightClick) {
201+
var e2;
202+
203+
try {
204+
e2 = new MouseEvent('click', e);
205+
}
206+
catch(err) {
207+
var offset = pointerOffset(e);
208+
e2 = document.createEvent('MouseEvents');
209+
e2.initMouseEvent('click',
210+
e.bubbles, e.cancelable,
211+
e.view, e.detail,
212+
e.screenX, e.screenY,
213+
offset[0], offset[1],
214+
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
215+
e.button, e.relatedTarget);
216+
}
217+
218+
initialTarget.dispatchEvent(e2);
188219
}
189-
190-
initialTarget.dispatchEvent(e2);
191220
}
192221

193222
finishDrag(gd);

src/components/legend/draw.js

+19-16
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,28 @@ module.exports = function draw(gd) {
334334
xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
335335
yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
336336
},
337-
doneFn: function(dragged, numClicks, e) {
338-
if(dragged && xf !== undefined && yf !== undefined) {
337+
doneFn: function() {
338+
if(xf !== undefined && yf !== undefined) {
339339
Plotly.relayout(gd, {'legend.x': xf, 'legend.y': yf});
340-
} else {
341-
var clickedTrace =
342-
fullLayout._infolayer.selectAll('g.traces').filter(function() {
343-
var bbox = this.getBoundingClientRect();
344-
return (e.clientX >= bbox.left && e.clientX <= bbox.right &&
345-
e.clientY >= bbox.top && e.clientY <= bbox.bottom);
346-
});
347-
if(clickedTrace.size() > 0) {
348-
if(numClicks === 1) {
349-
legend._clickTimeout = setTimeout(function() { handleClick(clickedTrace, gd, numClicks); }, DBLCLICKDELAY);
350-
} else if(numClicks === 2) {
351-
if(legend._clickTimeout) {
352-
clearTimeout(legend._clickTimeout);
353-
}
340+
}
341+
},
342+
clickFn: function(numClicks, e) {
343+
var clickedTrace =
344+
fullLayout._infolayer.selectAll('g.traces').filter(function() {
345+
var bbox = this.getBoundingClientRect();
346+
return (e.clientX >= bbox.left && e.clientX <= bbox.right &&
347+
e.clientY >= bbox.top && e.clientY <= bbox.bottom);
348+
});
349+
if(clickedTrace.size() > 0) {
350+
if(numClicks === 1) {
351+
legend._clickTimeout = setTimeout(function() {
354352
handleClick(clickedTrace, gd, numClicks);
353+
}, DBLCLICKDELAY);
354+
} else if(numClicks === 2) {
355+
if(legend._clickTimeout) {
356+
clearTimeout(legend._clickTimeout);
355357
}
358+
handleClick(clickedTrace, gd, numClicks);
356359
}
357360
}
358361
}

src/components/shapes/draw.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,9 @@ function setupDragElement(gd, shapePath, shapeOptions, index) {
211211
dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
212212
}
213213

214-
function endDrag(dragged) {
214+
function endDrag() {
215215
setCursor(shapePath);
216-
if(dragged) {
217-
Plotly.relayout(gd, update);
218-
}
216+
Plotly.relayout(gd, update);
219217
}
220218

221219
function moveShape(dx, dy) {

0 commit comments

Comments
 (0)