1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var disposable_1 = require("@phosphor/disposable");
|
4 | /**
|
5 | * An object which manages a drag-drop operation.
|
6 | *
|
7 | * A drag object dispatches four different events to drop targets:
|
8 | *
|
9 | * - `'p-dragenter'` - Dispatched when the mouse enters the target
|
10 | * element. This event must be canceled in order to receive any
|
11 | * of the other events.
|
12 | *
|
13 | * - `'p-dragover'` - Dispatched when the mouse moves over the drop
|
14 | * target. It must cancel the event and set the `dropAction` to one
|
15 | * of the supported actions in order to receive drop events.
|
16 | *
|
17 | * - `'p-dragleave'` - Dispatched when the mouse leaves the target
|
18 | * element. This includes moving the mouse into child elements.
|
19 | *
|
20 | * - `'p-drop'`- Dispatched when the mouse is released over the target
|
21 | * element when the target indicates an appropriate drop action. If
|
22 | * the event is canceled, the indicated drop action is returned to
|
23 | * the initiator through the resolved promise.
|
24 | *
|
25 | * A drag operation can be terminated at any time by pressing `Escape`
|
26 | * or by disposing the drag object.
|
27 | *
|
28 | * A drag object has the ability to automatically scroll a scrollable
|
29 | * element when the mouse is hovered near one of its edges. To enable
|
30 | * this, add the `data-p-dragscroll` attribute to any element which
|
31 | * the drag object should consider for scrolling.
|
32 | *
|
33 | * #### Notes
|
34 | * This class is designed to be used when dragging and dropping custom
|
35 | * data *within* a single application. It is *not* a replacement for
|
36 | * the native drag-drop API. Instead, it provides an API which allows
|
37 | * drag operations to be initiated programmatically and enables the
|
38 | * transfer of arbitrary non-string objects; features which are not
|
39 | * possible with the native drag-drop API.
|
40 | */
|
41 | var Drag = /** @class */ (function () {
|
42 | /**
|
43 | * Construct a new drag object.
|
44 | *
|
45 | * @param options - The options for initializing the drag.
|
46 | */
|
47 | function Drag(options) {
|
48 | var _this = this;
|
49 | /**
|
50 | * The scroll loop handler function.
|
51 | */
|
52 | this._onScrollFrame = function () {
|
53 | // Bail early if there is no scroll target.
|
54 | if (!_this._scrollTarget) {
|
55 | return;
|
56 | }
|
57 | // Unpack the scroll target.
|
58 | var _a = _this._scrollTarget, element = _a.element, edge = _a.edge, distance = _a.distance;
|
59 | // Calculate the scroll delta using nonlinear acceleration.
|
60 | var d = Private.SCROLL_EDGE_SIZE - distance;
|
61 | var f = Math.pow(d / Private.SCROLL_EDGE_SIZE, 2);
|
62 | var s = Math.max(1, Math.round(f * Private.SCROLL_EDGE_SIZE));
|
63 | // Scroll the element in the specified direction.
|
64 | switch (edge) {
|
65 | case 'top':
|
66 | element.scrollTop -= s;
|
67 | break;
|
68 | case 'left':
|
69 | element.scrollLeft -= s;
|
70 | break;
|
71 | case 'right':
|
72 | element.scrollLeft += s;
|
73 | break;
|
74 | case 'bottom':
|
75 | element.scrollTop += s;
|
76 | break;
|
77 | }
|
78 | // Request the next cycle of the scroll loop.
|
79 | requestAnimationFrame(_this._onScrollFrame);
|
80 | };
|
81 | this._disposed = false;
|
82 | this._dropAction = 'none';
|
83 | this._override = null;
|
84 | this._currentTarget = null;
|
85 | this._currentElement = null;
|
86 | this._promise = null;
|
87 | this._scrollTarget = null;
|
88 | this._resolve = null;
|
89 | this.mimeData = options.mimeData;
|
90 | this.dragImage = options.dragImage || null;
|
91 | this.proposedAction = options.proposedAction || 'copy';
|
92 | this.supportedActions = options.supportedActions || 'all';
|
93 | this.source = options.source || null;
|
94 | }
|
95 | /**
|
96 | * Dispose of the resources held by the drag object.
|
97 | *
|
98 | * #### Notes
|
99 | * This will cancel the drag operation if it is active.
|
100 | */
|
101 | Drag.prototype.dispose = function () {
|
102 | // Do nothing if the drag object is already disposed.
|
103 | if (this._disposed) {
|
104 | return;
|
105 | }
|
106 | this._disposed = true;
|
107 | // If there is a current target, dispatch a drag leave event.
|
108 | if (this._currentTarget) {
|
109 | var event_1 = Private.createMouseEvent('mouseup', -1, -1);
|
110 | Private.dispatchDragLeave(this, this._currentTarget, null, event_1);
|
111 | }
|
112 | // Finalize the drag object with `'none'`.
|
113 | this._finalize('none');
|
114 | };
|
115 | Object.defineProperty(Drag.prototype, "isDisposed", {
|
116 | /**
|
117 | * Test whether the drag object is disposed.
|
118 | */
|
119 | get: function () {
|
120 | return this._disposed;
|
121 | },
|
122 | enumerable: true,
|
123 | configurable: true
|
124 | });
|
125 | /**
|
126 | * Start the drag operation at the specified client position.
|
127 | *
|
128 | * @param clientX - The client X position for the drag start.
|
129 | *
|
130 | * @param clientY - The client Y position for the drag start.
|
131 | *
|
132 | * @returns A promise which resolves to the result of the drag.
|
133 | *
|
134 | * #### Notes
|
135 | * If the drag has already been started, the promise created by the
|
136 | * first call to `start` is returned.
|
137 | *
|
138 | * If the drag operation has ended, or if the drag object has been
|
139 | * disposed, the returned promise will resolve to `'none'`.
|
140 | *
|
141 | * The drag object will be automatically disposed when drag operation
|
142 | * completes. This means `Drag` objects are for single-use only.
|
143 | *
|
144 | * This method assumes the left mouse button is already held down.
|
145 | */
|
146 | Drag.prototype.start = function (clientX, clientY) {
|
147 | var _this = this;
|
148 | // If the drag object is already disposed, resolve to `None`.
|
149 | if (this._disposed) {
|
150 | return Promise.resolve('none');
|
151 | }
|
152 | // If the drag has already been started, return the promise.
|
153 | if (this._promise) {
|
154 | return this._promise;
|
155 | }
|
156 | // Install the document listeners for the drag object.
|
157 | this._addListeners();
|
158 | // Attach the drag image at the specified client position.
|
159 | this._attachDragImage(clientX, clientY);
|
160 | // Create the promise which will be resolved on completion.
|
161 | this._promise = new Promise(function (resolve, reject) {
|
162 | _this._resolve = resolve;
|
163 | });
|
164 | // Trigger a fake move event to kick off the drag operation.
|
165 | var event = Private.createMouseEvent('mousemove', clientX, clientY);
|
166 | document.dispatchEvent(event);
|
167 | // Return the pending promise for the drag operation.
|
168 | return this._promise;
|
169 | };
|
170 | /**
|
171 | * Handle the DOM events for the drag operation.
|
172 | *
|
173 | * @param event - The DOM event sent to the drag object.
|
174 | *
|
175 | * #### Notes
|
176 | * This method implements the DOM `EventListener` interface and is
|
177 | * called in response to events on the document. It should not be
|
178 | * called directly by user code.
|
179 | */
|
180 | Drag.prototype.handleEvent = function (event) {
|
181 | switch (event.type) {
|
182 | case 'mousemove':
|
183 | this._evtMouseMove(event);
|
184 | break;
|
185 | case 'mouseup':
|
186 | this._evtMouseUp(event);
|
187 | break;
|
188 | case 'keydown':
|
189 | this._evtKeyDown(event);
|
190 | break;
|
191 | default:
|
192 | // Stop all other events during drag-drop.
|
193 | event.preventDefault();
|
194 | event.stopPropagation();
|
195 | break;
|
196 | }
|
197 | };
|
198 | /**
|
199 | * Handle the `'mousemove'` event for the drag object.
|
200 | */
|
201 | Drag.prototype._evtMouseMove = function (event) {
|
202 | // Stop all input events during drag-drop.
|
203 | event.preventDefault();
|
204 | event.stopPropagation();
|
205 | // Update the current target node and dispatch enter/leave events.
|
206 | this._updateCurrentTarget(event);
|
207 | // Update the drag scroll element.
|
208 | this._updateDragScroll(event);
|
209 | // Move the drag image to the specified client position. This is
|
210 | // performed *after* dispatching to prevent unnecessary reflows.
|
211 | this._moveDragImage(event.clientX, event.clientY);
|
212 | };
|
213 | /**
|
214 | * Handle the `'mouseup'` event for the drag object.
|
215 | */
|
216 | Drag.prototype._evtMouseUp = function (event) {
|
217 | // Stop all input events during drag-drop.
|
218 | event.preventDefault();
|
219 | event.stopPropagation();
|
220 | // Do nothing if the left button is not released.
|
221 | if (event.button !== 0) {
|
222 | return;
|
223 | }
|
224 | // Update the current target node and dispatch enter/leave events.
|
225 | // This prevents a subtle issue where the DOM mutates under the
|
226 | // cursor after the last move event but before the drop event.
|
227 | this._updateCurrentTarget(event);
|
228 | // If there is no current target, finalize with `'none'`.
|
229 | if (!this._currentTarget) {
|
230 | this._finalize('none');
|
231 | return;
|
232 | }
|
233 | // If the last drop action was `'none'`, dispatch a leave event
|
234 | // to the current target and finalize the drag with `'none'`.
|
235 | if (this._dropAction === 'none') {
|
236 | Private.dispatchDragLeave(this, this._currentTarget, null, event);
|
237 | this._finalize('none');
|
238 | return;
|
239 | }
|
240 | // Dispatch the drop event at the current target and finalize
|
241 | // with the resulting drop action.
|
242 | var action = Private.dispatchDrop(this, this._currentTarget, event);
|
243 | this._finalize(action);
|
244 | };
|
245 | /**
|
246 | * Handle the `'keydown'` event for the drag object.
|
247 | */
|
248 | Drag.prototype._evtKeyDown = function (event) {
|
249 | // Stop all input events during drag-drop.
|
250 | event.preventDefault();
|
251 | event.stopPropagation();
|
252 | // Cancel the drag if `Escape` is pressed.
|
253 | if (event.keyCode === 27) {
|
254 | this.dispose();
|
255 | }
|
256 | };
|
257 | /**
|
258 | * Add the document event listeners for the drag object.
|
259 | */
|
260 | Drag.prototype._addListeners = function () {
|
261 | document.addEventListener('mousedown', this, true);
|
262 | document.addEventListener('mousemove', this, true);
|
263 | document.addEventListener('mouseup', this, true);
|
264 | document.addEventListener('mouseenter', this, true);
|
265 | document.addEventListener('mouseleave', this, true);
|
266 | document.addEventListener('mouseover', this, true);
|
267 | document.addEventListener('mouseout', this, true);
|
268 | document.addEventListener('keydown', this, true);
|
269 | document.addEventListener('keyup', this, true);
|
270 | document.addEventListener('keypress', this, true);
|
271 | document.addEventListener('contextmenu', this, true);
|
272 | };
|
273 | /**
|
274 | * Remove the document event listeners for the drag object.
|
275 | */
|
276 | Drag.prototype._removeListeners = function () {
|
277 | document.removeEventListener('mousedown', this, true);
|
278 | document.removeEventListener('mousemove', this, true);
|
279 | document.removeEventListener('mouseup', this, true);
|
280 | document.removeEventListener('mouseenter', this, true);
|
281 | document.removeEventListener('mouseleave', this, true);
|
282 | document.removeEventListener('mouseover', this, true);
|
283 | document.removeEventListener('mouseout', this, true);
|
284 | document.removeEventListener('keydown', this, true);
|
285 | document.removeEventListener('keyup', this, true);
|
286 | document.removeEventListener('keypress', this, true);
|
287 | document.removeEventListener('contextmenu', this, true);
|
288 | };
|
289 | /**
|
290 | * Update the drag scroll element under the mouse.
|
291 | */
|
292 | Drag.prototype._updateDragScroll = function (event) {
|
293 | // Find the scroll target under the mouse.
|
294 | var target = Private.findScrollTarget(event);
|
295 | // Bail if there is nothing to scroll.
|
296 | if (!this._scrollTarget && !target) {
|
297 | return;
|
298 | }
|
299 | // Start the scroll loop if needed.
|
300 | if (!this._scrollTarget) {
|
301 | setTimeout(this._onScrollFrame, 500);
|
302 | }
|
303 | // Update the scroll target.
|
304 | this._scrollTarget = target;
|
305 | };
|
306 | /**
|
307 | * Update the current target node using the given mouse event.
|
308 | */
|
309 | Drag.prototype._updateCurrentTarget = function (event) {
|
310 | // Fetch common local state.
|
311 | var prevTarget = this._currentTarget;
|
312 | var currTarget = this._currentTarget;
|
313 | var prevElem = this._currentElement;
|
314 | // Find the current indicated element at the given position.
|
315 | var currElem = document.elementFromPoint(event.clientX, event.clientY);
|
316 | // Update the current element reference.
|
317 | this._currentElement = currElem;
|
318 | // If the indicated element changes from the previous iteration,
|
319 | // and is different from the current target, dispatch the exit
|
320 | // event to the target.
|
321 | if (currElem !== prevElem && currElem !== currTarget) {
|
322 | Private.dispatchDragExit(this, currTarget, currElem, event);
|
323 | }
|
324 | // If the indicated element changes from the previous iteration,
|
325 | // and is different from the current target, dispatch the enter
|
326 | // event and compute the new target element.
|
327 | if (currElem !== prevElem && currElem !== currTarget) {
|
328 | currTarget = Private.dispatchDragEnter(this, currElem, currTarget, event);
|
329 | }
|
330 | // If the current target element has changed, update the current
|
331 | // target reference and dispatch the leave event to the old target.
|
332 | if (currTarget !== prevTarget) {
|
333 | this._currentTarget = currTarget;
|
334 | Private.dispatchDragLeave(this, prevTarget, currTarget, event);
|
335 | }
|
336 | // Dispatch the drag over event and update the drop action.
|
337 | var action = Private.dispatchDragOver(this, currTarget, event);
|
338 | this._setDropAction(action);
|
339 | };
|
340 | /**
|
341 | * Attach the drag image element at the specified location.
|
342 | *
|
343 | * This is a no-op if there is no drag image element.
|
344 | */
|
345 | Drag.prototype._attachDragImage = function (clientX, clientY) {
|
346 | if (!this.dragImage) {
|
347 | return;
|
348 | }
|
349 | this.dragImage.classList.add('p-mod-drag-image');
|
350 | var style = this.dragImage.style;
|
351 | style.pointerEvents = 'none';
|
352 | style.position = 'fixed';
|
353 | style.top = clientY + "px";
|
354 | style.left = clientX + "px";
|
355 | document.body.appendChild(this.dragImage);
|
356 | };
|
357 | /**
|
358 | * Move the drag image element to the specified location.
|
359 | *
|
360 | * This is a no-op if there is no drag image element.
|
361 | */
|
362 | Drag.prototype._moveDragImage = function (clientX, clientY) {
|
363 | if (!this.dragImage) {
|
364 | return;
|
365 | }
|
366 | var style = this.dragImage.style;
|
367 | style.top = clientY + "px";
|
368 | style.left = clientX + "px";
|
369 | };
|
370 | /**
|
371 | * Detach the drag image element from the DOM.
|
372 | *
|
373 | * This is a no-op if there is no drag image element.
|
374 | */
|
375 | Drag.prototype._detachDragImage = function () {
|
376 | if (!this.dragImage) {
|
377 | return;
|
378 | }
|
379 | var parent = this.dragImage.parentNode;
|
380 | if (!parent) {
|
381 | return;
|
382 | }
|
383 | parent.removeChild(this.dragImage);
|
384 | };
|
385 | /**
|
386 | * Set the internal drop action state and update the drag cursor.
|
387 | */
|
388 | Drag.prototype._setDropAction = function (action) {
|
389 | action = Private.validateAction(action, this.supportedActions);
|
390 | if (this._override && this._dropAction === action) {
|
391 | return;
|
392 | }
|
393 | switch (action) {
|
394 | case 'none':
|
395 | this._dropAction = action;
|
396 | this._override = Drag.overrideCursor('no-drop');
|
397 | break;
|
398 | case 'copy':
|
399 | this._dropAction = action;
|
400 | this._override = Drag.overrideCursor('copy');
|
401 | break;
|
402 | case 'link':
|
403 | this._dropAction = action;
|
404 | this._override = Drag.overrideCursor('alias');
|
405 | break;
|
406 | case 'move':
|
407 | this._dropAction = action;
|
408 | this._override = Drag.overrideCursor('move');
|
409 | break;
|
410 | }
|
411 | };
|
412 | /**
|
413 | * Finalize the drag operation and resolve the drag promise.
|
414 | */
|
415 | Drag.prototype._finalize = function (action) {
|
416 | // Store the resolve function as a temp variable.
|
417 | var resolve = this._resolve;
|
418 | // Remove the document event listeners.
|
419 | this._removeListeners();
|
420 | // Detach the drag image.
|
421 | this._detachDragImage();
|
422 | // Dispose of the cursor override.
|
423 | if (this._override) {
|
424 | this._override.dispose();
|
425 | this._override = null;
|
426 | }
|
427 | // Clear the mime data.
|
428 | this.mimeData.clear();
|
429 | // Clear the rest of the internal drag state.
|
430 | this._disposed = true;
|
431 | this._dropAction = 'none';
|
432 | this._currentTarget = null;
|
433 | this._currentElement = null;
|
434 | this._scrollTarget = null;
|
435 | this._promise = null;
|
436 | this._resolve = null;
|
437 | // Finally, resolve the promise to the given drop action.
|
438 | if (resolve) {
|
439 | resolve(action);
|
440 | }
|
441 | };
|
442 | return Drag;
|
443 | }());
|
444 | exports.Drag = Drag;
|
445 | /**
|
446 | * The namespace for the `Drag` class statics.
|
447 | */
|
448 | (function (Drag) {
|
449 | /**
|
450 | * Override the cursor icon for the entire document.
|
451 | *
|
452 | * @param cursor - The string representing the cursor style.
|
453 | *
|
454 | * @returns A disposable which will clear the override when disposed.
|
455 | *
|
456 | * #### Notes
|
457 | * The most recent call to `overrideCursor` takes precedence.
|
458 | * Disposing an old override has no effect on the current override.
|
459 | *
|
460 | * This utility function is used by the `Drag` class to override the
|
461 | * mouse cursor during a drag-drop operation, but it can also be used
|
462 | * by other classes to fix the cursor icon during normal mouse drags.
|
463 | *
|
464 | * #### Example
|
465 | * ```typescript
|
466 | * import { Drag } from '@phosphor/dragdrop';
|
467 | *
|
468 | * // Force the cursor to be 'wait' for the entire document.
|
469 | * let override = Drag.overrideCursor('wait');
|
470 | *
|
471 | * // Clear the override by disposing the return value.
|
472 | * override.dispose();
|
473 | * ```
|
474 | */
|
475 | function overrideCursor(cursor) {
|
476 | var id = ++overrideCursorID;
|
477 | document.body.style.cursor = cursor;
|
478 | document.body.classList.add('p-mod-override-cursor');
|
479 | return new disposable_1.DisposableDelegate(function () {
|
480 | if (id === overrideCursorID) {
|
481 | document.body.style.cursor = '';
|
482 | document.body.classList.remove('p-mod-override-cursor');
|
483 | }
|
484 | });
|
485 | }
|
486 | Drag.overrideCursor = overrideCursor;
|
487 | /**
|
488 | * The internal id for the active cursor override.
|
489 | */
|
490 | var overrideCursorID = 0;
|
491 | })(Drag = exports.Drag || (exports.Drag = {}));
|
492 | exports.Drag = Drag;
|
493 | /**
|
494 | * The namespace for the module implementation details.
|
495 | */
|
496 | var Private;
|
497 | (function (Private) {
|
498 | /**
|
499 | * The size of a drag scroll edge, in pixels.
|
500 | */
|
501 | Private.SCROLL_EDGE_SIZE = 20;
|
502 | /**
|
503 | * Validate the given action is one of the supported actions.
|
504 | *
|
505 | * Returns the given action or `'none'` if the action is unsupported.
|
506 | */
|
507 | function validateAction(action, supported) {
|
508 | return (actionTable[action] & supportedTable[supported]) ? action : 'none';
|
509 | }
|
510 | Private.validateAction = validateAction;
|
511 | /**
|
512 | * Create a left mouse event at the given position.
|
513 | *
|
514 | * @param type - The event type for the mouse event.
|
515 | *
|
516 | * @param clientX - The client X position.
|
517 | *
|
518 | * @param clientY - The client Y position.
|
519 | *
|
520 | * @returns A newly created and initialized mouse event.
|
521 | */
|
522 | function createMouseEvent(type, clientX, clientY) {
|
523 | var event = document.createEvent('MouseEvent');
|
524 | event.initMouseEvent(type, true, true, window, 0, 0, 0, clientX, clientY, false, false, false, false, 0, null);
|
525 | return event;
|
526 | }
|
527 | Private.createMouseEvent = createMouseEvent;
|
528 | /**
|
529 | * Find the drag scroll target under the mouse, if any.
|
530 | */
|
531 | function findScrollTarget(event) {
|
532 | // Look up the client mouse position.
|
533 | var x = event.clientX;
|
534 | var y = event.clientY;
|
535 | // Get the element under the mouse.
|
536 | var element = document.elementFromPoint(x, y);
|
537 | // Search for a scrollable target based on the mouse position.
|
538 | // The null assert in third clause of for-loop is required due to:
|
539 | // https://github.com/Microsoft/TypeScript/issues/14143
|
540 | for (; element; element = element.parentElement) {
|
541 | // Ignore elements which are not marked as scrollable.
|
542 | if (!element.hasAttribute('data-p-dragscroll')) {
|
543 | continue;
|
544 | }
|
545 | // Set up the coordinate offsets for the element.
|
546 | var offsetX = 0;
|
547 | var offsetY = 0;
|
548 | if (element === document.body) {
|
549 | offsetX = window.pageXOffset;
|
550 | offsetY = window.pageYOffset;
|
551 | }
|
552 | // Get the element bounds in viewport coordinates.
|
553 | var r = element.getBoundingClientRect();
|
554 | var top_1 = r.top + offsetY;
|
555 | var left = r.left + offsetX;
|
556 | var right = left + r.width;
|
557 | var bottom = top_1 + r.height;
|
558 | // Skip the element if it's not under the mouse.
|
559 | if (x < left || x >= right || y < top_1 || y >= bottom) {
|
560 | continue;
|
561 | }
|
562 | // Compute the distance to each edge.
|
563 | var dl = x - left + 1;
|
564 | var dt = y - top_1 + 1;
|
565 | var dr = right - x;
|
566 | var db = bottom - y;
|
567 | // Find the smallest of the edge distances.
|
568 | var distance = Math.min(dl, dt, dr, db);
|
569 | // Skip the element if the mouse is not within a scroll edge.
|
570 | if (distance > Private.SCROLL_EDGE_SIZE) {
|
571 | continue;
|
572 | }
|
573 | // Set up the edge result variable.
|
574 | var edge = void 0;
|
575 | // Find the edge for the computed distance.
|
576 | switch (distance) {
|
577 | case db:
|
578 | edge = 'bottom';
|
579 | break;
|
580 | case dt:
|
581 | edge = 'top';
|
582 | break;
|
583 | case dr:
|
584 | edge = 'right';
|
585 | break;
|
586 | case dl:
|
587 | edge = 'left';
|
588 | break;
|
589 | default:
|
590 | throw 'unreachable';
|
591 | }
|
592 | // Compute how much the element can scroll in width and height.
|
593 | var dsw = element.scrollWidth - element.clientWidth;
|
594 | var dsh = element.scrollHeight - element.clientHeight;
|
595 | // Determine if the element should be scrolled for the edge.
|
596 | var shouldScroll = void 0;
|
597 | switch (edge) {
|
598 | case 'top':
|
599 | shouldScroll = dsh > 0 && element.scrollTop > 0;
|
600 | break;
|
601 | case 'left':
|
602 | shouldScroll = dsw > 0 && element.scrollLeft > 0;
|
603 | break;
|
604 | case 'right':
|
605 | shouldScroll = dsw > 0 && element.scrollLeft < dsw;
|
606 | break;
|
607 | case 'bottom':
|
608 | shouldScroll = dsh > 0 && element.scrollTop < dsh;
|
609 | break;
|
610 | default:
|
611 | throw 'unreachable';
|
612 | }
|
613 | // Skip the element if it should not be scrolled.
|
614 | if (!shouldScroll) {
|
615 | continue;
|
616 | }
|
617 | // Return the drag scroll target.
|
618 | return { element: element, edge: edge, distance: distance };
|
619 | }
|
620 | // No drag scroll target was found.
|
621 | return null;
|
622 | }
|
623 | Private.findScrollTarget = findScrollTarget;
|
624 | /**
|
625 | * Dispatch a drag enter event to the indicated element.
|
626 | *
|
627 | * @param drag - The drag object associated with the action.
|
628 | *
|
629 | * @param currElem - The currently indicated element, or `null`. This
|
630 | * is the "immediate user selection" from the whatwg spec.
|
631 | *
|
632 | * @param currTarget - The current drag target element, or `null`. This
|
633 | * is the "current target element" from the whatwg spec.
|
634 | *
|
635 | * @param event - The mouse event related to the action.
|
636 | *
|
637 | * @returns The element to use as the current drag target. This is the
|
638 | * "current target element" from the whatwg spec, and may be `null`.
|
639 | *
|
640 | * #### Notes
|
641 | * This largely implements the drag enter portion of the whatwg spec:
|
642 | * https://html.spec.whatwg.org/multipage/interaction.html#drag-and-drop-processing-model
|
643 | */
|
644 | function dispatchDragEnter(drag, currElem, currTarget, event) {
|
645 | // If the current element is null, return null as the new target.
|
646 | if (!currElem) {
|
647 | return null;
|
648 | }
|
649 | // Dispatch a drag enter event to the current element.
|
650 | var dragEvent = createDragEvent('p-dragenter', drag, event, currTarget);
|
651 | var canceled = !currElem.dispatchEvent(dragEvent);
|
652 | // If the event was canceled, use the current element as the new target.
|
653 | if (canceled) {
|
654 | return currElem;
|
655 | }
|
656 | // If the current element is the document body, keep the original target.
|
657 | if (currElem === document.body) {
|
658 | return currTarget;
|
659 | }
|
660 | // Dispatch a drag enter event on the document body.
|
661 | dragEvent = createDragEvent('p-dragenter', drag, event, currTarget);
|
662 | document.body.dispatchEvent(dragEvent);
|
663 | // Ignore the event cancellation, and use the body as the new target.
|
664 | return document.body;
|
665 | }
|
666 | Private.dispatchDragEnter = dispatchDragEnter;
|
667 | /**
|
668 | * Dispatch a drag exit event to the indicated element.
|
669 | *
|
670 | * @param drag - The drag object associated with the action.
|
671 | *
|
672 | * @param prevTarget - The previous target element, or `null`. This
|
673 | * is the previous "current target element" from the whatwg spec.
|
674 | *
|
675 | * @param currTarget - The current drag target element, or `null`. This
|
676 | * is the "current target element" from the whatwg spec.
|
677 | *
|
678 | * @param event - The mouse event related to the action.
|
679 | *
|
680 | * #### Notes
|
681 | * This largely implements the drag exit portion of the whatwg spec:
|
682 | * https://html.spec.whatwg.org/multipage/interaction.html#drag-and-drop-processing-model
|
683 | */
|
684 | function dispatchDragExit(drag, prevTarget, currTarget, event) {
|
685 | // If the previous target is null, do nothing.
|
686 | if (!prevTarget) {
|
687 | return;
|
688 | }
|
689 | // Dispatch the drag exit event to the previous target.
|
690 | var dragEvent = createDragEvent('p-dragexit', drag, event, currTarget);
|
691 | prevTarget.dispatchEvent(dragEvent);
|
692 | }
|
693 | Private.dispatchDragExit = dispatchDragExit;
|
694 | /**
|
695 | * Dispatch a drag leave event to the indicated element.
|
696 | *
|
697 | * @param drag - The drag object associated with the action.
|
698 | *
|
699 | * @param prevTarget - The previous target element, or `null`. This
|
700 | * is the previous "current target element" from the whatwg spec.
|
701 | *
|
702 | * @param currTarget - The current drag target element, or `null`. This
|
703 | * is the "current target element" from the whatwg spec.
|
704 | *
|
705 | * @param event - The mouse event related to the action.
|
706 | *
|
707 | * #### Notes
|
708 | * This largely implements the drag leave portion of the whatwg spec:
|
709 | * https://html.spec.whatwg.org/multipage/interaction.html#drag-and-drop-processing-model
|
710 | */
|
711 | function dispatchDragLeave(drag, prevTarget, currTarget, event) {
|
712 | // If the previous target is null, do nothing.
|
713 | if (!prevTarget) {
|
714 | return;
|
715 | }
|
716 | // Dispatch the drag leave event to the previous target.
|
717 | var dragEvent = createDragEvent('p-dragleave', drag, event, currTarget);
|
718 | prevTarget.dispatchEvent(dragEvent);
|
719 | }
|
720 | Private.dispatchDragLeave = dispatchDragLeave;
|
721 | /**
|
722 | * Dispatch a drag over event to the indicated element.
|
723 | *
|
724 | * @param drag - The drag object associated with the action.
|
725 | *
|
726 | * @param currTarget - The current drag target element, or `null`. This
|
727 | * is the "current target element" from the whatwg spec.
|
728 | *
|
729 | * @param event - The mouse event related to the action.
|
730 | *
|
731 | * @returns The `DropAction` result of the drag over event.
|
732 | *
|
733 | * #### Notes
|
734 | * This largely implements the drag over portion of the whatwg spec:
|
735 | * https://html.spec.whatwg.org/multipage/interaction.html#drag-and-drop-processing-model
|
736 | */
|
737 | function dispatchDragOver(drag, currTarget, event) {
|
738 | // If there is no current target, the drop action is none.
|
739 | if (!currTarget) {
|
740 | return 'none';
|
741 | }
|
742 | // Dispatch the drag over event to the current target.
|
743 | var dragEvent = createDragEvent('p-dragover', drag, event, null);
|
744 | var canceled = !currTarget.dispatchEvent(dragEvent);
|
745 | // If the event was canceled, return the drop action result.
|
746 | if (canceled) {
|
747 | return dragEvent.dropAction;
|
748 | }
|
749 | // Otherwise, the effective drop action is none.
|
750 | return 'none';
|
751 | }
|
752 | Private.dispatchDragOver = dispatchDragOver;
|
753 | /**
|
754 | * Dispatch a drop event to the indicated element.
|
755 | *
|
756 | * @param drag - The drag object associated with the action.
|
757 | *
|
758 | * @param currTarget - The current drag target element, or `null`. This
|
759 | * is the "current target element" from the whatwg spec.
|
760 | *
|
761 | * @param event - The mouse event related to the action.
|
762 | *
|
763 | * @returns The `DropAction` result of the drop event.
|
764 | *
|
765 | * #### Notes
|
766 | * This largely implements the drag over portion of the whatwg spec:
|
767 | * https://html.spec.whatwg.org/multipage/interaction.html#drag-and-drop-processing-model
|
768 | */
|
769 | function dispatchDrop(drag, currTarget, event) {
|
770 | // If there is no current target, the drop action is none.
|
771 | if (!currTarget) {
|
772 | return 'none';
|
773 | }
|
774 | // Dispatch the drop event to the current target.
|
775 | var dragEvent = createDragEvent('p-drop', drag, event, null);
|
776 | var canceled = !currTarget.dispatchEvent(dragEvent);
|
777 | // If the event was canceled, return the drop action result.
|
778 | if (canceled) {
|
779 | return dragEvent.dropAction;
|
780 | }
|
781 | // Otherwise, the effective drop action is none.
|
782 | return 'none';
|
783 | }
|
784 | Private.dispatchDrop = dispatchDrop;
|
785 | /**
|
786 | * A lookup table from drop action to bit value.
|
787 | */
|
788 | var actionTable = {
|
789 | 'none': 0x0,
|
790 | 'copy': 0x1,
|
791 | 'link': 0x2,
|
792 | 'move': 0x4
|
793 | };
|
794 | /**
|
795 | * A lookup table from supported action to drop action bit mask.
|
796 | */
|
797 | var supportedTable = {
|
798 | 'none': actionTable['none'],
|
799 | 'copy': actionTable['copy'],
|
800 | 'link': actionTable['link'],
|
801 | 'move': actionTable['move'],
|
802 | 'copy-link': actionTable['copy'] | actionTable['link'],
|
803 | 'copy-move': actionTable['copy'] | actionTable['move'],
|
804 | 'link-move': actionTable['link'] | actionTable['move'],
|
805 | 'all': actionTable['copy'] | actionTable['link'] | actionTable['move']
|
806 | };
|
807 | /**
|
808 | * Create a new initialized `IDragEvent` from the given data.
|
809 | *
|
810 | * @param type - The event type for the drag event.
|
811 | *
|
812 | * @param drag - The drag object to use for seeding the drag data.
|
813 | *
|
814 | * @param event - The mouse event to use for seeding the mouse data.
|
815 | *
|
816 | * @param related - The related target for the event, or `null`.
|
817 | *
|
818 | * @returns A new object which implements `IDragEvent`.
|
819 | */
|
820 | function createDragEvent(type, drag, event, related) {
|
821 | // Create a new mouse event to use as the drag event. Currently,
|
822 | // JS engines do now allow user-defined Event subclasses.
|
823 | var dragEvent = document.createEvent('MouseEvent');
|
824 | // Initialize the mouse event data.
|
825 | dragEvent.initMouseEvent(type, true, true, window, 0, event.screenX, event.screenY, event.clientX, event.clientY, event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, event.button, related);
|
826 | // Forcefully add the custom drag event properties.
|
827 | dragEvent.dropAction = 'none';
|
828 | dragEvent.mimeData = drag.mimeData;
|
829 | dragEvent.proposedAction = drag.proposedAction;
|
830 | dragEvent.supportedActions = drag.supportedActions;
|
831 | dragEvent.source = drag.source;
|
832 | // Return the fully initialized drag event.
|
833 | return dragEvent;
|
834 | }
|
835 | })(Private || (Private = {}));
|