@@ -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)
56
70
* numClicks is how many clicks we've registered within
57
71
* a doubleclick time
58
- * e is the original event
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)
76
+ * numClicks is how many clicks we've registered within
77
+ * a doubleclick time
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
@@ -86,7 +108,8 @@ dragElement.init = function init(options) {
86
108
startX = offset [ 0 ] ;
87
109
startY = offset [ 1 ] ;
88
110
initialTarget = e . target ;
89
- var rightClick = e . buttons && e . buttons === 2 ;
111
+ initialEvent = e ;
112
+ rightClick = ( e . buttons && e . buttons === 2 ) || e . ctrlKey ;
90
113
91
114
newMouseDownTime = ( new Date ( ) ) . getTime ( ) ;
92
115
if ( newMouseDownTime - gd . _mouseDownTime < DBLCLICKDELAY ) {
@@ -133,7 +156,7 @@ dragElement.init = function init(options) {
133
156
dragElement . unhover ( gd ) ;
134
157
}
135
158
136
- if ( options . moveFn ) options . moveFn ( dx , dy , gd . _dragged ) ;
159
+ if ( gd . _dragged && options . moveFn && ! rightClick ) options . moveFn ( dx , dy ) ;
137
160
138
161
return Lib . pauseEvent ( e ) ;
139
162
}
@@ -164,27 +187,36 @@ dragElement.init = function init(options) {
164
187
numClicks = Math . max ( numClicks - 1 , 1 ) ;
165
188
}
166
189
167
- if ( options . doneFn ) options . doneFn ( gd . _dragged , numClicks , e ) ;
168
-
169
- if ( ! gd . _dragged ) {
170
- var e2 ;
171
-
172
- try {
173
- e2 = new MouseEvent ( 'click' , e ) ;
174
- }
175
- catch ( err ) {
176
- var offset = pointerOffset ( e ) ;
177
- e2 = document . createEvent ( 'MouseEvents' ) ;
178
- e2 . initMouseEvent ( 'click' ,
179
- e . bubbles , e . cancelable ,
180
- e . view , e . detail ,
181
- e . screenX , e . screenY ,
182
- offset [ 0 ] , offset [ 1 ] ,
183
- e . ctrlKey , e . altKey , e . shiftKey , e . metaKey ,
184
- 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 ) ;
185
219
}
186
-
187
- initialTarget . dispatchEvent ( e2 ) ;
188
220
}
189
221
190
222
finishDrag ( gd ) ;
0 commit comments