diff --git a/addon/src/modifiers/sortable-item.js b/addon/src/modifiers/sortable-item.js index 458b28c3..8f00c3f9 100644 --- a/addon/src/modifiers/sortable-item.js +++ b/addon/src/modifiers/sortable-item.js @@ -366,6 +366,10 @@ export default class SortableItemModifier extends Modifier { * @private */ _prepareDrag(startEvent, event) { + // Block drag start while any item has busy state + if (this.sortableGroup.sortedItems.some((x) => x.isBusy)) { + return; + } let distance = this.distance; let dx = Math.abs(getX(startEvent) - getX(event)); let dy = Math.abs(getY(startEvent) - getY(event)); @@ -618,7 +622,10 @@ export default class SortableItemModifier extends Modifier { set(this, 'isDropping', true); this.sortableGroup.update(); - transitionPromise.then(() => this._complete()); + + let allTransitionPromise = this._waitForAllTransitions(); + + Promise.all([transitionPromise, allTransitionPromise]).then(() => this._complete()); } /** @@ -678,6 +685,42 @@ export default class SortableItemModifier extends Modifier { return transitionPromise; } + /** + @method _waitForTransitions + @private + @return Promise + */ + _waitForAllTransitions() { + let waiterToken; + + if (DEBUG) { + waiterToken = sortableItemWaiter.beginAsync(); + } + + let transitionPromise; + + if (this.isAnimated) { + const animations = this.sortableGroup.sortedItems.map((x) => x.element.getAnimations()); + + const animationPromises = animations.map((animation) => { + return animation.finished; + }); + + transitionPromise = Promise.all(animationPromises); + } else { + const duration = this.isAnimated ? this.transitionDuration : 200; + transitionPromise = new Promise((resolve) => later(resolve, duration)); + } + + if (DEBUG) { + transitionPromise = transitionPromise.finally(() => { + sortableItemWaiter.endAsync(waiterToken); + }); + } + + return transitionPromise; + } + /** @method _complete @private @@ -706,7 +749,8 @@ export default class SortableItemModifier extends Modifier { @type Number */ get transitionDuration() { - let el = this.element; + const items = this.sortableGroup.sortedItems.filter((x) => !x.isDragging && !x.isDropping); + let el = items[0].element ?? this.element; // Fallback when only one element is present in list let rule = getComputedStyle(el).transitionDuration; let match = rule.match(/([\d.]+)([ms]*)/); diff --git a/addon/src/test-support/helpers/drag.js b/addon/src/test-support/helpers/drag.js index 2d7ec0a0..8fc2d30f 100644 --- a/addon/src/test-support/helpers/drag.js +++ b/addon/src/test-support/helpers/drag.js @@ -1,4 +1,4 @@ -import { triggerEvent, find, settled } from '@ember/test-helpers'; +import { triggerEvent, find, settled, waitUntil } from '@ember/test-helpers'; import { getOffset } from '../utils/offset'; /** @@ -108,4 +108,11 @@ export async function drag(mode, itemSelector, offsetFn, callbacks = {}) { await callbacks.dragend(); await settled(); } + + await waitUntil( + () => { + return !find('.is-dropping'); + }, + { timeout: 2000 } + ); }