@@ -38,24 +38,44 @@ dragElement.unhoverRaw = unhover.raw;
38
38
* - Freezes the cursor: whatever mouse cursor the drag element had when the
39
39
* interaction started gets copied to the coverSlip for use until mouseup
40
40
*
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
+ *
41
55
* @param {object } options with keys:
42
56
* element (required) the DOM element to drag
43
57
* prepFn (optional) function(event, startX, startY)
44
58
* executed on mousedown
45
59
* startX and startY are the clientX and clientY pixel position
46
60
* 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)
49
64
* dx and dy are the net pixel offset of the drag,
50
65
* dragged is true/false, has the mouse moved enough to
51
66
* 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)
56
76
* numClicks is how many clicks we've registered within
57
77
* a doubleclick time
58
- * e is the original event
78
+ * e is the original mousedown event
59
79
*/
60
80
dragElement . init = function init ( options ) {
61
81
var gd = options . gd ;
@@ -68,7 +88,9 @@ dragElement.init = function init(options) {
68
88
newMouseDownTime ,
69
89
cursor ,
70
90
dragCover ,
71
- initialTarget ;
91
+ initialEvent ,
92
+ initialTarget ,
93
+ rightClick ;
72
94
73
95
if ( ! gd . _mouseDownTime ) gd . _mouseDownTime = 0 ;
74
96
@@ -78,10 +100,6 @@ dragElement.init = function init(options) {
78
100
element . ontouchstart = onStart ;
79
101
80
102
function onStart ( e ) {
81
- if ( e . buttons && e . buttons === 2 ) { // right click
82
- return ;
83
- }
84
-
85
103
// make dragging and dragged into properties of gd
86
104
// so that others can look at and modify them
87
105
gd . _dragged = false ;
@@ -90,6 +108,8 @@ dragElement.init = function init(options) {
90
108
startX = offset [ 0 ] ;
91
109
startY = offset [ 1 ] ;
92
110
initialTarget = e . target ;
111
+ initialEvent = e ;
112
+ rightClick = ( e . buttons && e . buttons === 2 ) || e . ctrlKey ;
93
113
94
114
newMouseDownTime = ( new Date ( ) ) . getTime ( ) ;
95
115
if ( newMouseDownTime - gd . _mouseDownTime < DBLCLICKDELAY ) {
@@ -104,11 +124,11 @@ dragElement.init = function init(options) {
104
124
105
125
if ( options . prepFn ) options . prepFn ( e , startX , startY ) ;
106
126
107
- if ( hasHover ) {
127
+ if ( hasHover && ! rightClick ) {
108
128
dragCover = coverSlip ( ) ;
109
129
dragCover . style . cursor = window . getComputedStyle ( element ) . cursor ;
110
130
}
111
- else {
131
+ else if ( ! hasHover ) {
112
132
// document acts as a dragcover for mobile, bc we can't create dragcover dynamically
113
133
dragCover = document ;
114
134
cursor = window . getComputedStyle ( document . documentElement ) . cursor ;
@@ -136,7 +156,7 @@ dragElement.init = function init(options) {
136
156
dragElement . unhover ( gd ) ;
137
157
}
138
158
139
- if ( options . moveFn ) options . moveFn ( dx , dy , gd . _dragged ) ;
159
+ if ( gd . _dragged && options . moveFn && ! rightClick ) options . moveFn ( dx , dy ) ;
140
160
141
161
return Lib . pauseEvent ( e ) ;
142
162
}
@@ -167,27 +187,36 @@ dragElement.init = function init(options) {
167
187
numClicks = Math . max ( numClicks - 1 , 1 ) ;
168
188
}
169
189
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 ) ;
188
219
}
189
-
190
- initialTarget . dispatchEvent ( e2 ) ;
191
220
}
192
221
193
222
finishDrag ( gd ) ;
0 commit comments