UNPKG

27.1 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width,initial-scale=1">
6 <title>packages/@interactjs/actions/drop/plugin.ts - Documentation</title>
7
8 <script src="scripts/prettify/prettify.js"></script>
9 <script src="scripts/prettify/lang-css.js"></script>
10 <!--[if lt IE 9]>
11 <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
12 <![endif]-->
13 <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
14 <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15 <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
16</head>
17<body>
18
19<input type="checkbox" id="nav-trigger" class="nav-trigger" />
20<label for="nav-trigger" class="navicon-button x">
21 <div class="navicon"></div>
22</label>
23
24<label for="nav-trigger" class="overlay"></label>
25
26<nav>
27 <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="DropEvent_DropEvent.html">DropEvent</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Interactable.html">Interactable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#context">context</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#deltaSource">deltaSource</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#draggable">draggable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#dropCheck">dropCheck</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#dropzone">dropzone</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#fire">fire</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#gesturable">gesturable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#getRect">getRect</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#off">off</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#on">on</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#origin">origin</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#rectChecker">rectChecker</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#reflow">reflow</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#resizable">resizable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#set">set</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Interactable.html#unset">unset</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="InteractEvent_InteractEvent.html">InteractEvent</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Interaction_Interaction.html">Interaction</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="module.exports_module.exports.html">exports</a></span></li><li class="nav-heading">Modules</li><li class="nav-heading"><span class="nav-item-type type-module">M</span><span class="nav-item-name"><a href="module-interact.html">interact</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.isSet">isSet</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.maxInteractions">maxInteractions</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.pointerMoveTolerance">pointerMoveTolerance</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.stop">stop</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.supportsPointerEvent">supportsPointerEvent</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.supportsTouch">supportsTouch</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="module-interact.html#.use">use</a></span></li><li class="nav-heading"><span class="nav-item-type type-module">M</span><span class="nav-item-name"><a href="module-modifiers_aspectRatio.html">modifiers/aspectRatio</a></span></li><li class="nav-heading"><span class="nav-item-type type-module">M</span><span class="nav-item-name"><a href="module-modifiers_snapEdges.html">modifiers/snapEdges</a></span></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#interact">interact</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#preventDefault">preventDefault</a></span></li>
28</nav>
29
30<div class="code-col-bg"></div>
31
32<div id="main">
33
34 <h1 class="page-title">packages/@interactjs/actions/drop/plugin.ts</h1>
35
36
37
38
39
40
41
42 <section>
43 <article>
44 <pre class="prettyprint source linenums"><code>import type { EventPhase, InteractEvent } from '@interactjs/core/InteractEvent'
45import type { Interactable } from '@interactjs/core/Interactable'
46import type { Interaction, DoPhaseArg } from '@interactjs/core/Interaction'
47import type { Scope, Plugin } from '@interactjs/core/scope'
48import type { DropzoneOptions, Element, PointerEventType, Rect } from '@interactjs/types/index'
49import * as domUtils from '@interactjs/utils/domUtils'
50import extend from '@interactjs/utils/extend'
51import getOriginXY from '@interactjs/utils/getOriginXY'
52import is from '@interactjs/utils/is'
53import normalizeListeners from '@interactjs/utils/normalizeListeners'
54import * as pointerUtils from '@interactjs/utils/pointerUtils'
55
56import type { DragEvent } from '../drag/plugin'
57import drag from '../drag/plugin'
58
59import { DropEvent } from './DropEvent'
60
61export interface DropzoneMethod {
62 (this: Interactable, options: DropzoneOptions | boolean): Interactable
63 (): DropzoneOptions
64}
65
66declare module '@interactjs/core/Interactable' {
67 interface Interactable {
68 dropzone: DropzoneMethod
69 dropCheck: (
70 dragEvent: InteractEvent,
71 event: PointerEventType,
72 draggable: Interactable,
73 draggableElement: Element,
74 dropElemen: Element,
75 rect: any,
76 ) => boolean
77 }
78}
79
80declare module '@interactjs/core/Interaction' {
81 interface Interaction {
82 dropState?: DropState
83 }
84}
85
86declare module '@interactjs/core/InteractEvent' {
87 interface InteractEvent {
88 prevDropzone?: Interactable
89 dropzone?: Interactable
90 dragEnter?: Element
91 dragLeave?: Element
92 }
93}
94
95declare module '@interactjs/core/options' {
96 interface ActionDefaults {
97 drop: DropzoneOptions
98 }
99}
100
101declare module '@interactjs/core/scope' {
102 interface ActionMap {
103 drop?: typeof drop
104 }
105
106 interface Scope {
107 dynamicDrop?: boolean
108 }
109
110 interface SignalArgs {
111 'actions/drop:start': DropSignalArg
112 'actions/drop:move': DropSignalArg
113 'actions/drop:end': DropSignalArg
114 }
115}
116
117declare module '@interactjs/core/InteractStatic' {
118 interface InteractStatic {
119 dynamicDrop: (this: InteractStatic, newValue?: boolean) => boolean | this
120 }
121}
122
123interface DropSignalArg {
124 interaction: Interaction&lt;'drag'>
125 dragEvent: DragEvent
126}
127
128export interface ActiveDrop {
129 dropzone: Interactable
130 element: Element
131 rect: Rect
132}
133
134export interface DropState {
135 cur: {
136 // the dropzone a drag target might be dropped into
137 dropzone: Interactable
138 // the element at the time of checking
139 element: Element
140 }
141 prev: {
142 // the dropzone that was recently dragged away from
143 dropzone: Interactable
144 // the element at the time of checking
145 element: Element
146 }
147 // wheather the potential drop was rejected from a listener
148 rejected: boolean
149 // the drop events related to the current drag event
150 events: FiredDropEvents
151 activeDrops: ActiveDrop[]
152}
153
154function install (scope: Scope) {
155 const {
156 actions,
157 /** @lends module:interact */
158 interactStatic: interact,
159 /** @lends Interactable */
160 Interactable,
161 defaults,
162 } = scope
163
164 scope.usePlugin(drag)
165
166 /**
167 *
168 * ```js
169 * interact('.drop').dropzone({
170 * accept: '.can-drop' || document.getElementById('single-drop'),
171 * overlap: 'pointer' || 'center' || zeroToOne
172 * }
173 * ```
174 *
175 * Returns or sets whether draggables can be dropped onto this target to
176 * trigger drop events
177 *
178 * Dropzones can receive the following events:
179 * - `dropactivate` and `dropdeactivate` when an acceptable drag starts and ends
180 * - `dragenter` and `dragleave` when a draggable enters and leaves the dropzone
181 * - `dragmove` when a draggable that has entered the dropzone is moved
182 * - `drop` when a draggable is dropped into this dropzone
183 *
184 * Use the `accept` option to allow only elements that match the given CSS
185 * selector or element. The value can be:
186 *
187 * - **an Element** - only that element can be dropped into this dropzone.
188 * - **a string**, - the element being dragged must match it as a CSS selector.
189 * - **`null`** - accept options is cleared - it accepts any element.
190 *
191 * Use the `overlap` option to set how drops are checked for. The allowed
192 * values are:
193 *
194 * - `'pointer'`, the pointer must be over the dropzone (default)
195 * - `'center'`, the draggable element's center must be over the dropzone
196 * - a number from 0-1 which is the `(intersection area) / (draggable area)`.
197 * e.g. `0.5` for drop to happen when half of the area of the draggable is
198 * over the dropzone
199 *
200 * Use the `checker` option to specify a function to check if a dragged element
201 * is over this Interactable.
202 *
203 * @param {boolean | object | null} [options] The new options to be set.
204 * @return {object | Interactable} The current setting or this Interactable
205 */
206 Interactable.prototype.dropzone = function (this: Interactable, options?: DropzoneOptions | boolean) {
207 return dropzoneMethod(this, options)
208 } as Interactable['dropzone']
209
210 /**
211 * ```js
212 * interact(target)
213 * .dropChecker(function(dragEvent, // related dragmove or dragend event
214 * event, // TouchEvent/PointerEvent/MouseEvent
215 * dropped, // bool result of the default checker
216 * dropzone, // dropzone Interactable
217 * dropElement, // dropzone elemnt
218 * draggable, // draggable Interactable
219 * draggableElement) {// draggable element
220 *
221 * return dropped &amp;&amp; event.target.hasAttribute('allow-drop')
222 * }
223 * ```
224 */
225 Interactable.prototype.dropCheck = function (
226 this: Interactable,
227 dragEvent,
228 event,
229 draggable,
230 draggableElement,
231 dropElement,
232 rect,
233 ) {
234 return dropCheckMethod(this, dragEvent, event, draggable, draggableElement, dropElement, rect)
235 }
236
237 /**
238 * Returns or sets whether the dimensions of dropzone elements are calculated
239 * on every dragmove or only on dragstart for the default dropChecker
240 *
241 * @param {boolean} [newValue] True to check on each move. False to check only
242 * before start
243 * @return {boolean | interact} The current setting or interact
244 */
245 interact.dynamicDrop = function (newValue?: boolean) {
246 if (is.bool(newValue)) {
247 // if (dragging &amp;&amp; scope.dynamicDrop !== newValue &amp;&amp; !newValue) {
248 // calcRects(dropzones)
249 // }
250
251 scope.dynamicDrop = newValue
252
253 return interact
254 }
255 return scope.dynamicDrop
256 }
257
258 extend(actions.phaselessTypes, {
259 dragenter: true,
260 dragleave: true,
261 dropactivate: true,
262 dropdeactivate: true,
263 dropmove: true,
264 drop: true,
265 })
266 actions.methodDict.drop = 'dropzone'
267
268 scope.dynamicDrop = false
269
270 defaults.actions.drop = drop.defaults
271}
272
273function collectDrops ({ interactables }: Scope, draggableElement: Element) {
274 const drops: ActiveDrop[] = []
275
276 // collect all dropzones and their elements which qualify for a drop
277 for (const dropzone of interactables.list) {
278 if (!dropzone.options.drop.enabled) {
279 continue
280 }
281
282 const accept = dropzone.options.drop.accept
283
284 // test the draggable draggableElement against the dropzone's accept setting
285 if (
286 (is.element(accept) &amp;&amp; accept !== draggableElement) ||
287 (is.string(accept) &amp;&amp; !domUtils.matchesSelector(draggableElement, accept)) ||
288 (is.func(accept) &amp;&amp; !accept({ dropzone, draggableElement }))
289 ) {
290 continue
291 }
292
293 // query for new elements if necessary
294 const dropElements = (is.string(dropzone.target)
295 ? dropzone._context.querySelectorAll(dropzone.target)
296 : is.array(dropzone.target)
297 ? dropzone.target
298 : [dropzone.target]) as Element[]
299
300 for (const dropzoneElement of dropElements) {
301 if (dropzoneElement !== draggableElement) {
302 drops.push({
303 dropzone,
304 element: dropzoneElement,
305 rect: dropzone.getRect(dropzoneElement),
306 })
307 }
308 }
309 }
310
311 return drops
312}
313
314function fireActivationEvents (activeDrops: ActiveDrop[], event: DropEvent) {
315 // loop through all active dropzones and trigger event
316 for (const { dropzone, element } of activeDrops.slice()) {
317 event.dropzone = dropzone
318
319 // set current element as event target
320 event.target = element
321 dropzone.fire(event)
322 event.propagationStopped = event.immediatePropagationStopped = false
323 }
324}
325
326// return a new array of possible drops. getActiveDrops should always be
327// called when a drag has just started or a drag event happens while
328// dynamicDrop is true
329function getActiveDrops (scope: Scope, dragElement: Element) {
330 // get dropzones and their elements that could receive the draggable
331 const activeDrops = collectDrops(scope, dragElement)
332
333 for (const activeDrop of activeDrops) {
334 activeDrop.rect = activeDrop.dropzone.getRect(activeDrop.element)
335 }
336
337 return activeDrops
338}
339
340function getDrop (
341 { dropState, interactable: draggable, element: dragElement }: Partial&lt;Interaction>,
342 dragEvent,
343 pointerEvent,
344) {
345 const validDrops = []
346
347 // collect all dropzones and their elements which qualify for a drop
348 for (const { dropzone, element: dropzoneElement, rect } of dropState.activeDrops) {
349 validDrops.push(
350 dropzone.dropCheck(dragEvent, pointerEvent, draggable, dragElement, dropzoneElement, rect)
351 ? dropzoneElement
352 : null,
353 )
354 }
355
356 // get the most appropriate dropzone based on DOM depth and order
357 const dropIndex = domUtils.indexOfDeepestElement(validDrops)
358
359 return dropState.activeDrops[dropIndex] || null
360}
361
362function getDropEvents (interaction: Interaction, _pointerEvent, dragEvent: DragEvent) {
363 const { dropState } = interaction
364 const dropEvents = {
365 enter: null,
366 leave: null,
367 activate: null,
368 deactivate: null,
369 move: null,
370 drop: null,
371 }
372
373 if (dragEvent.type === 'dragstart') {
374 dropEvents.activate = new DropEvent(dropState, dragEvent, 'dropactivate')
375
376 dropEvents.activate.target = null
377 dropEvents.activate.dropzone = null
378 }
379 if (dragEvent.type === 'dragend') {
380 dropEvents.deactivate = new DropEvent(dropState, dragEvent, 'dropdeactivate')
381
382 dropEvents.deactivate.target = null
383 dropEvents.deactivate.dropzone = null
384 }
385
386 if (dropState.rejected) {
387 return dropEvents
388 }
389
390 if (dropState.cur.element !== dropState.prev.element) {
391 // if there was a previous dropzone, create a dragleave event
392 if (dropState.prev.dropzone) {
393 dropEvents.leave = new DropEvent(dropState, dragEvent, 'dragleave')
394
395 dragEvent.dragLeave = dropEvents.leave.target = dropState.prev.element
396 dragEvent.prevDropzone = dropEvents.leave.dropzone = dropState.prev.dropzone
397 }
398 // if dropzone is not null, create a dragenter event
399 if (dropState.cur.dropzone) {
400 dropEvents.enter = new DropEvent(dropState, dragEvent, 'dragenter')
401
402 dragEvent.dragEnter = dropState.cur.element
403 dragEvent.dropzone = dropState.cur.dropzone
404 }
405 }
406
407 if (dragEvent.type === 'dragend' &amp;&amp; dropState.cur.dropzone) {
408 dropEvents.drop = new DropEvent(dropState, dragEvent, 'drop')
409
410 dragEvent.dropzone = dropState.cur.dropzone
411 dragEvent.relatedTarget = dropState.cur.element
412 }
413 if (dragEvent.type === 'dragmove' &amp;&amp; dropState.cur.dropzone) {
414 dropEvents.move = new DropEvent(dropState, dragEvent, 'dropmove')
415
416 dropEvents.move.dragmove = dragEvent
417 dragEvent.dropzone = dropState.cur.dropzone
418 }
419
420 return dropEvents
421}
422
423type FiredDropEvents = Partial&lt;
424Record&lt;'leave' | 'enter' | 'move' | 'drop' | 'activate' | 'deactivate', DropEvent>
425>
426
427function fireDropEvents (interaction: Interaction, events: FiredDropEvents) {
428 const { dropState } = interaction
429 const { activeDrops, cur, prev } = dropState
430
431 if (events.leave) {
432 prev.dropzone.fire(events.leave)
433 }
434 if (events.enter) {
435 cur.dropzone.fire(events.enter)
436 }
437 if (events.move) {
438 cur.dropzone.fire(events.move)
439 }
440 if (events.drop) {
441 cur.dropzone.fire(events.drop)
442 }
443
444 if (events.deactivate) {
445 fireActivationEvents(activeDrops, events.deactivate)
446 }
447
448 dropState.prev.dropzone = cur.dropzone
449 dropState.prev.element = cur.element
450}
451
452function onEventCreated ({ interaction, iEvent, event }: DoPhaseArg&lt;'drag', EventPhase>, scope: Scope) {
453 if (iEvent.type !== 'dragmove' &amp;&amp; iEvent.type !== 'dragend') {
454 return
455 }
456
457 const { dropState } = interaction
458
459 if (scope.dynamicDrop) {
460 dropState.activeDrops = getActiveDrops(scope, interaction.element)
461 }
462
463 const dragEvent = iEvent
464 const dropResult = getDrop(interaction, dragEvent, event)
465
466 // update rejected status
467 dropState.rejected =
468 dropState.rejected &amp;&amp;
469 !!dropResult &amp;&amp;
470 dropResult.dropzone === dropState.cur.dropzone &amp;&amp;
471 dropResult.element === dropState.cur.element
472
473 dropState.cur.dropzone = dropResult &amp;&amp; dropResult.dropzone
474 dropState.cur.element = dropResult &amp;&amp; dropResult.element
475
476 dropState.events = getDropEvents(interaction, event, dragEvent)
477}
478
479function dropzoneMethod(interactable: Interactable): DropzoneOptions
480function dropzoneMethod(interactable: Interactable, options: DropzoneOptions | boolean): Interactable
481function dropzoneMethod (interactable: Interactable, options?: DropzoneOptions | boolean) {
482 if (is.object(options)) {
483 interactable.options.drop.enabled = options.enabled !== false
484
485 if (options.listeners) {
486 const normalized = normalizeListeners(options.listeners)
487 // rename 'drop' to '' as it will be prefixed with 'drop'
488 const corrected = Object.keys(normalized).reduce((acc, type) => {
489 const correctedType = /^(enter|leave)/.test(type)
490 ? `drag${type}`
491 : /^(activate|deactivate|move)/.test(type)
492 ? `drop${type}`
493 : type
494
495 acc[correctedType] = normalized[type]
496
497 return acc
498 }, {})
499
500 interactable.off(interactable.options.drop.listeners)
501 interactable.on(corrected)
502 interactable.options.drop.listeners = corrected
503 }
504
505 if (is.func(options.ondrop)) {
506 interactable.on('drop', options.ondrop)
507 }
508 if (is.func(options.ondropactivate)) {
509 interactable.on('dropactivate', options.ondropactivate)
510 }
511 if (is.func(options.ondropdeactivate)) {
512 interactable.on('dropdeactivate', options.ondropdeactivate)
513 }
514 if (is.func(options.ondragenter)) {
515 interactable.on('dragenter', options.ondragenter)
516 }
517 if (is.func(options.ondragleave)) {
518 interactable.on('dragleave', options.ondragleave)
519 }
520 if (is.func(options.ondropmove)) {
521 interactable.on('dropmove', options.ondropmove)
522 }
523
524 if (/^(pointer|center)$/.test(options.overlap as string)) {
525 interactable.options.drop.overlap = options.overlap
526 } else if (is.number(options.overlap)) {
527 interactable.options.drop.overlap = Math.max(Math.min(1, options.overlap), 0)
528 }
529 if ('accept' in options) {
530 interactable.options.drop.accept = options.accept
531 }
532 if ('checker' in options) {
533 interactable.options.drop.checker = options.checker
534 }
535
536 return interactable
537 }
538
539 if (is.bool(options)) {
540 interactable.options.drop.enabled = options
541
542 return interactable
543 }
544
545 return interactable.options.drop
546}
547
548function dropCheckMethod (
549 interactable: Interactable,
550 dragEvent: InteractEvent,
551 event: PointerEventType,
552 draggable: Interactable,
553 draggableElement: Element,
554 dropElement: Element,
555 rect: any,
556) {
557 let dropped = false
558
559 // if the dropzone has no rect (eg. display: none)
560 // call the custom dropChecker or just return false
561 if (!(rect = rect || interactable.getRect(dropElement))) {
562 return interactable.options.drop.checker
563 ? interactable.options.drop.checker(
564 dragEvent,
565 event,
566 dropped,
567 interactable,
568 dropElement,
569 draggable,
570 draggableElement,
571 )
572 : false
573 }
574
575 const dropOverlap = interactable.options.drop.overlap
576
577 if (dropOverlap === 'pointer') {
578 const origin = getOriginXY(draggable, draggableElement, 'drag')
579 const page = pointerUtils.getPageXY(dragEvent)
580
581 page.x += origin.x
582 page.y += origin.y
583
584 const horizontal = page.x > rect.left &amp;&amp; page.x &lt; rect.right
585 const vertical = page.y > rect.top &amp;&amp; page.y &lt; rect.bottom
586
587 dropped = horizontal &amp;&amp; vertical
588 }
589
590 const dragRect = draggable.getRect(draggableElement)
591
592 if (dragRect &amp;&amp; dropOverlap === 'center') {
593 const cx = dragRect.left + dragRect.width / 2
594 const cy = dragRect.top + dragRect.height / 2
595
596 dropped = cx >= rect.left &amp;&amp; cx &lt;= rect.right &amp;&amp; cy >= rect.top &amp;&amp; cy &lt;= rect.bottom
597 }
598
599 if (dragRect &amp;&amp; is.number(dropOverlap)) {
600 const overlapArea =
601 Math.max(0, Math.min(rect.right, dragRect.right) - Math.max(rect.left, dragRect.left)) *
602 Math.max(0, Math.min(rect.bottom, dragRect.bottom) - Math.max(rect.top, dragRect.top))
603
604 const overlapRatio = overlapArea / (dragRect.width * dragRect.height)
605
606 dropped = overlapRatio >= dropOverlap
607 }
608
609 if (interactable.options.drop.checker) {
610 dropped = interactable.options.drop.checker(
611 dragEvent,
612 event,
613 dropped,
614 interactable,
615 dropElement,
616 draggable,
617 draggableElement,
618 )
619 }
620
621 return dropped
622}
623
624const drop: Plugin = {
625 id: 'actions/drop',
626 install,
627 listeners: {
628 'interactions:before-action-start': ({ interaction }) => {
629 if (interaction.prepared.name !== 'drag') {
630 return
631 }
632
633 interaction.dropState = {
634 cur: {
635 dropzone: null,
636 element: null,
637 },
638 prev: {
639 dropzone: null,
640 element: null,
641 },
642 rejected: null,
643 events: null,
644 activeDrops: [],
645 }
646 },
647
648 'interactions:after-action-start': (
649 { interaction, event, iEvent: dragEvent }: DoPhaseArg&lt;'drag', EventPhase>,
650 scope,
651 ) => {
652 if (interaction.prepared.name !== 'drag') {
653 return
654 }
655
656 const { dropState } = interaction
657
658 // reset active dropzones
659 dropState.activeDrops = null
660 dropState.events = null
661 dropState.activeDrops = getActiveDrops(scope, interaction.element)
662 dropState.events = getDropEvents(interaction, event, dragEvent)
663
664 if (dropState.events.activate) {
665 fireActivationEvents(dropState.activeDrops, dropState.events.activate)
666 scope.fire('actions/drop:start', { interaction, dragEvent })
667 }
668 },
669
670 'interactions:action-move': onEventCreated,
671
672 'interactions:after-action-move': (
673 { interaction, iEvent: dragEvent }: DoPhaseArg&lt;'drag', EventPhase>,
674 scope,
675 ) => {
676 if (interaction.prepared.name !== 'drag') {
677 return
678 }
679
680 fireDropEvents(interaction, interaction.dropState.events)
681
682 scope.fire('actions/drop:move', { interaction, dragEvent })
683 interaction.dropState.events = {}
684 },
685
686 'interactions:action-end': (arg: DoPhaseArg&lt;'drag', EventPhase>, scope) => {
687 if (arg.interaction.prepared.name !== 'drag') {
688 return
689 }
690
691 const { interaction, iEvent: dragEvent } = arg
692
693 onEventCreated(arg, scope)
694 fireDropEvents(interaction, interaction.dropState.events)
695 scope.fire('actions/drop:end', { interaction, dragEvent })
696 },
697
698 'interactions:stop': ({ interaction }) => {
699 if (interaction.prepared.name !== 'drag') {
700 return
701 }
702
703 const { dropState } = interaction
704
705 if (dropState) {
706 dropState.activeDrops = null
707 dropState.events = null
708 dropState.cur.dropzone = null
709 dropState.cur.element = null
710 dropState.prev.dropzone = null
711 dropState.prev.element = null
712 dropState.rejected = false
713 }
714 },
715 },
716 getActiveDrops,
717 getDrop,
718 getDropEvents,
719 fireDropEvents,
720 defaults: {
721 enabled: false,
722 accept: null,
723 overlap: 'pointer',
724 } as DropzoneOptions,
725}
726
727export default drop
728</code></pre>
729 </article>
730 </section>
731
732
733
734
735</div>
736
737<script>prettyPrint();</script>
738<script src="scripts/linenumber.js"></script>
739</body>
740</html>