svelte-dnd-action 0.9.71 → 0.9.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1413,6 +1413,8 @@
1413
1413
  var touchDragHoldTimer;
1414
1414
  var touchHoldElapsed = false;
1415
1415
  var useCursorForDetectionActive = false;
1416
+ var pendingDragOwner;
1417
+ var watchedDropZones = new Set();
1416
1418
 
1417
1419
  // a map from type to a set of drop-zones
1418
1420
  var typeToDropZones$1 = new Map();
@@ -1435,9 +1437,10 @@
1435
1437
  }
1436
1438
  }
1437
1439
  function unregisterDropZone$1(dropZoneEl, type) {
1438
- typeToDropZones$1.get(type)["delete"](dropZoneEl);
1440
+ var dropZones = typeToDropZones$1.get(type);
1441
+ if (!dropZones || !dropZones["delete"](dropZoneEl)) return;
1439
1442
  decrementActiveDropZoneCount();
1440
- if (typeToDropZones$1.get(type).size === 0) {
1443
+ if (dropZones.size === 0) {
1441
1444
  typeToDropZones$1["delete"](type);
1442
1445
  }
1443
1446
  }
@@ -1448,7 +1451,10 @@
1448
1451
  return "watching dragged element";
1449
1452
  });
1450
1453
  var dropZones = typeToDropZones$1.get(draggedElType);
1451
- var _iterator = _createForOfIteratorHelper(dropZones),
1454
+ if (!dropZones || dropZones.size === 0) return;
1455
+ if (watchedDropZones.size > 0) unWatchDraggedElement();
1456
+ watchedDropZones = new Set(dropZones);
1457
+ var _iterator = _createForOfIteratorHelper(watchedDropZones),
1452
1458
  _step;
1453
1459
  try {
1454
1460
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -1465,11 +1471,11 @@
1465
1471
  window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1466
1472
 
1467
1473
  // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth
1468
- var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(dropZones.keys()).map(function (dz) {
1474
+ var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(watchedDropZones).map(function (dz) {
1469
1475
  return dzToConfig$1.get(dz).dropAnimationDurationMs;
1470
1476
  })));
1471
1477
  var observationIntervalMs = setIntervalMs === 0 ? DISABLED_OBSERVATION_INTERVAL_MS : Math.max(setIntervalMs, MIN_OBSERVATION_INTERVAL_MS); // if setIntervalMs is 0 it goes to 20, otherwise it is max between it and min observation.
1472
- multiScroller = createMultiScroller(dropZones, function () {
1478
+ multiScroller = createMultiScroller(watchedDropZones, function () {
1473
1479
  return currentMousePosition;
1474
1480
  });
1475
1481
  // Returns reference point in document coordinates - either cursor position or element center
@@ -1481,14 +1487,13 @@
1481
1487
  } : function () {
1482
1488
  return findCenterOfElement(draggedEl);
1483
1489
  };
1484
- observe(draggedEl, dropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
1490
+ observe(draggedEl, watchedDropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
1485
1491
  }
1486
1492
  function unWatchDraggedElement() {
1487
1493
  printDebug(function () {
1488
1494
  return "unwatching dragged element";
1489
1495
  });
1490
- var dropZones = typeToDropZones$1.get(draggedElType);
1491
- var _iterator2 = _createForOfIteratorHelper(dropZones),
1496
+ var _iterator2 = _createForOfIteratorHelper(watchedDropZones),
1492
1497
  _step2;
1493
1498
  try {
1494
1499
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
@@ -1502,6 +1507,7 @@
1502
1507
  } finally {
1503
1508
  _iterator2.f();
1504
1509
  }
1510
+ watchedDropZones = new Set();
1505
1511
  window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1506
1512
  // ensuring multiScroller is not already destroyed before destroying
1507
1513
  if (multiScroller) {
@@ -1733,11 +1739,13 @@
1733
1739
  window.setTimeout(callback, dropAnimationDurationMs);
1734
1740
  }
1735
1741
  function scheduleDZForRemovalAfterDrop(dz, destroy) {
1736
- scheduledForRemovalAfterDrop.push({
1742
+ var scheduledRemoval = {
1737
1743
  dz: dz,
1738
1744
  destroy: destroy
1739
- });
1745
+ };
1746
+ scheduledForRemovalAfterDrop.push(scheduledRemoval);
1740
1747
  window.requestAnimationFrame(function () {
1748
+ if (!scheduledForRemovalAfterDrop.includes(scheduledRemoval)) return;
1741
1749
  hideElement(dz);
1742
1750
  document.body.appendChild(dz);
1743
1751
  });
@@ -1771,6 +1779,7 @@
1771
1779
  touchDragHoldTimer = undefined;
1772
1780
  touchHoldElapsed = false;
1773
1781
  useCursorForDetectionActive = false;
1782
+ pendingDragOwner = undefined;
1774
1783
  if (scheduledForRemovalAfterDrop.length) {
1775
1784
  printDebug(function () {
1776
1785
  return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop];
@@ -1786,6 +1795,7 @@
1786
1795
  }
1787
1796
  function dndzone$2(node, options) {
1788
1797
  var initialized = false;
1798
+ var destroyed = false;
1789
1799
  var config = {
1790
1800
  items: undefined,
1791
1801
  type: undefined,
@@ -1833,11 +1843,17 @@
1833
1843
  touchHoldElapsed = false;
1834
1844
  }
1835
1845
  }
1836
- function handleFalseAlarm(e) {
1846
+ function cancelPendingDrag() {
1847
+ if (pendingDragOwner !== node) return;
1837
1848
  removeMaybeListeners();
1849
+ pendingDragOwner = undefined;
1838
1850
  originalDragTarget = undefined;
1839
1851
  dragStartMousePosition = undefined;
1840
1852
  currentMousePosition = undefined;
1853
+ }
1854
+ function handleFalseAlarm(e) {
1855
+ if (pendingDragOwner !== node) return;
1856
+ cancelPendingDrag();
1841
1857
 
1842
1858
  // dragging initiated by touch events prevents onclick from initially firing
1843
1859
  if (e.type === "touchend") {
@@ -1850,6 +1866,7 @@
1850
1866
  }
1851
1867
  }
1852
1868
  function handleMouseMoveMaybeDragStart(e) {
1869
+ if (destroyed || pendingDragOwner !== node) return;
1853
1870
  var isTouch = !!e.touches;
1854
1871
  var c = isTouch ? e.touches[0] : e;
1855
1872
  // If touch drag delay is configured and not elapsed yet, allow scrolling until either
@@ -1878,7 +1895,6 @@
1878
1895
  y: c.clientY
1879
1896
  };
1880
1897
  if (Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX || Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX) {
1881
- removeMaybeListeners();
1882
1898
  handleDragStart();
1883
1899
  }
1884
1900
  }
@@ -1897,9 +1913,9 @@
1897
1913
  });
1898
1914
  return;
1899
1915
  }
1900
- if (isWorkingOnPreviousDrag) {
1916
+ if (destroyed || isWorkingOnPreviousDrag || pendingDragOwner) {
1901
1917
  printDebug(function () {
1902
- return "cannot start a new drag before finalizing previous one";
1918
+ return "cannot start a new drag before finalizing the current drag or pending gesture";
1903
1919
  });
1904
1920
  return;
1905
1921
  }
@@ -1909,6 +1925,7 @@
1909
1925
  e.preventDefault();
1910
1926
  }
1911
1927
  e.stopPropagation();
1928
+ pendingDragOwner = node;
1912
1929
  var c = isTouchStart ? e.touches[0] : e;
1913
1930
  dragStartMousePosition = {
1914
1931
  x: c.clientX,
@@ -1917,18 +1934,25 @@
1917
1934
  currentMousePosition = _objectSpread2({}, dragStartMousePosition);
1918
1935
  originalDragTarget = e.currentTarget;
1919
1936
  if (useDelay) {
1937
+ var pendingTarget = originalDragTarget;
1920
1938
  touchHoldElapsed = false;
1921
1939
  touchDragHoldTimer = window.setTimeout(function () {
1922
- // If the finger is still down and no false-alarm happened
1923
- if (!originalDragTarget) return;
1940
+ // If this action still owns the same pending gesture, transition it to a drag.
1941
+ if (destroyed || pendingDragOwner !== node || originalDragTarget !== pendingTarget) return;
1924
1942
  touchHoldElapsed = true;
1925
- removeMaybeListeners();
1926
1943
  handleDragStart();
1927
1944
  }, config.delayTouchStartMs);
1928
1945
  }
1929
1946
  addMaybeListeners();
1930
1947
  }
1931
1948
  function handleDragStart() {
1949
+ var _originalDragTarget;
1950
+ if (destroyed || pendingDragOwner !== node || isWorkingOnPreviousDrag || !((_originalDragTarget = originalDragTarget) !== null && _originalDragTarget !== void 0 && _originalDragTarget.parentElement)) {
1951
+ cancelPendingDrag();
1952
+ return;
1953
+ }
1954
+ removeMaybeListeners();
1955
+ pendingDragOwner = undefined;
1932
1956
  printDebug(function () {
1933
1957
  return ["drag start config: ".concat(toString(config)), originalDragTarget];
1934
1958
  });
@@ -2127,11 +2151,29 @@
2127
2151
  configure(newOptions);
2128
2152
  },
2129
2153
  destroy: function destroy() {
2154
+ if (destroyed) return;
2155
+ destroyed = true;
2156
+ cancelPendingDrag();
2157
+ var _iterator3 = _createForOfIteratorHelper(node.children),
2158
+ _step3;
2159
+ try {
2160
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
2161
+ var draggableEl = _step3.value;
2162
+ draggableEl.removeEventListener("mousedown", elToMouseDownListener.get(draggableEl));
2163
+ draggableEl.removeEventListener("touchstart", elToMouseDownListener.get(draggableEl));
2164
+ }
2165
+ } catch (err) {
2166
+ _iterator3.e(err);
2167
+ } finally {
2168
+ _iterator3.f();
2169
+ }
2130
2170
  function destroyDz() {
2131
2171
  printDebug(function () {
2132
2172
  return "pointer dndzone will destroy";
2133
2173
  });
2134
- unregisterDropZone$1(node, dzToConfig$1.get(node).type);
2174
+ var registeredConfig = dzToConfig$1.get(node);
2175
+ if (!registeredConfig) return;
2176
+ unregisterDropZone$1(node, registeredConfig.type);
2135
2177
  dzToConfig$1["delete"](node);
2136
2178
  }
2137
2179
  if (isWorkingOnPreviousDrag && !node.closest("[".concat(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, "]"))) {
@@ -2288,12 +2330,13 @@
2288
2330
  printDebug(function () {
2289
2331
  return "unregistering drop-zone";
2290
2332
  });
2291
- if (focusedDz === dropZoneEl) {
2333
+ if (isDragging && focusedDz === dropZoneEl) {
2292
2334
  handleDrop();
2293
2335
  }
2294
- typeToDropZones.get(type)["delete"](dropZoneEl);
2336
+ var dropZones = typeToDropZones.get(type);
2337
+ if (!dropZones || !dropZones["delete"](dropZoneEl)) return;
2295
2338
  decrementActiveDropZoneCount();
2296
- if (typeToDropZones.get(type).size === 0) {
2339
+ if (dropZones.size === 0) {
2297
2340
  typeToDropZones["delete"](type);
2298
2341
  }
2299
2342
  if (typeToDropZones.size === 0) {
@@ -2355,17 +2398,20 @@
2355
2398
  }
2356
2399
  }
2357
2400
  var dzFrom = focusedDz;
2401
+ var movedItemId = focusedItemId;
2402
+ focusedDz = newlyFocusedDz;
2358
2403
  dispatchFinalizeEvent(dzFrom, originItems, {
2359
2404
  trigger: TRIGGERS.DROPPED_INTO_ANOTHER,
2360
- id: focusedItemId,
2361
- source: SOURCES.KEYBOARD
2362
- });
2363
- dispatchFinalizeEvent(newlyFocusedDz, targetItems, {
2364
- trigger: TRIGGERS.DROPPED_INTO_ZONE,
2365
- id: focusedItemId,
2405
+ id: movedItemId,
2366
2406
  source: SOURCES.KEYBOARD
2367
2407
  });
2368
- focusedDz = newlyFocusedDz;
2408
+ if (dzToConfig.has(newlyFocusedDz)) {
2409
+ dispatchFinalizeEvent(newlyFocusedDz, targetItems, {
2410
+ trigger: TRIGGERS.DROPPED_INTO_ZONE,
2411
+ id: movedItemId,
2412
+ source: SOURCES.KEYBOARD
2413
+ });
2414
+ }
2369
2415
  }
2370
2416
  function triggerAllDzsUpdate() {
2371
2417
  dzToHandles.forEach(function (_ref, dz) {
@@ -2375,27 +2421,23 @@
2375
2421
  }
2376
2422
  function handleDrop() {
2377
2423
  var dispatchConsider = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
2424
+ if (!isDragging || !focusedDz) return;
2378
2425
  printDebug(function () {
2379
2426
  return "drop";
2380
2427
  });
2381
- if (!dzToConfig.get(focusedDz).autoAriaDisabled) {
2428
+ var droppedDz = focusedDz;
2429
+ var droppedConfig = dzToConfig.get(droppedDz);
2430
+ var droppedItemId = focusedItemId;
2431
+ var droppedItemType = draggedItemType;
2432
+ if (!droppedConfig) return;
2433
+ if (!droppedConfig.autoAriaDisabled) {
2382
2434
  alertToScreenReader("Stopped dragging item ".concat(focusedItemLabel));
2383
2435
  }
2384
2436
  if (allDragTargets.has(document.activeElement)) {
2385
2437
  document.activeElement.blur();
2386
2438
  }
2387
- if (dispatchConsider) {
2388
- dispatchConsiderEvent(focusedDz, dzToConfig.get(focusedDz).items, {
2389
- trigger: TRIGGERS.DRAG_STOPPED,
2390
- id: focusedItemId,
2391
- source: SOURCES.KEYBOARD
2392
- });
2393
- }
2394
- styleInactiveDropZones(typeToDropZones.get(draggedItemType), function (dz) {
2395
- return dzToConfig.get(dz).dropTargetStyle;
2396
- }, function (dz) {
2397
- return dzToConfig.get(dz).dropTargetClasses;
2398
- });
2439
+ // Clear global drag state before dispatching. A synchronous handler may destroy the
2440
+ // focused zone, and unregisterDropZone must not recursively enter handleDrop.
2399
2441
  focusedItem = null;
2400
2442
  focusedItemId = null;
2401
2443
  focusedItemLabel = "";
@@ -2403,10 +2445,26 @@
2403
2445
  focusedDz = null;
2404
2446
  focusedDzLabel = "";
2405
2447
  isDragging = false;
2448
+ if (dispatchConsider) {
2449
+ dispatchConsiderEvent(droppedDz, droppedConfig.items, {
2450
+ trigger: TRIGGERS.DRAG_STOPPED,
2451
+ id: droppedItemId,
2452
+ source: SOURCES.KEYBOARD
2453
+ });
2454
+ }
2455
+ var dropZones = typeToDropZones.get(droppedItemType);
2456
+ if (dropZones) {
2457
+ styleInactiveDropZones(dropZones, function (dz) {
2458
+ return dzToConfig.get(dz).dropTargetStyle;
2459
+ }, function (dz) {
2460
+ return dzToConfig.get(dz).dropTargetClasses;
2461
+ });
2462
+ }
2406
2463
  triggerAllDzsUpdate();
2407
2464
  }
2408
2465
  //////
2409
2466
  function dndzone$1(node, options) {
2467
+ var destroyed = false;
2410
2468
  var config = {
2411
2469
  items: undefined,
2412
2470
  type: undefined,
@@ -2631,9 +2689,25 @@
2631
2689
  configure(newOptions);
2632
2690
  },
2633
2691
  destroy: function destroy() {
2692
+ if (destroyed) return;
2693
+ destroyed = true;
2634
2694
  printDebug(function () {
2635
2695
  return "keyboard dndzone will destroy";
2636
2696
  });
2697
+ node.removeEventListener("focus", handleZoneFocus);
2698
+ var _iterator = _createForOfIteratorHelper(node.children),
2699
+ _step;
2700
+ try {
2701
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
2702
+ var draggableEl = _step.value;
2703
+ draggableEl.removeEventListener("keydown", elToKeyDownListeners.get(draggableEl));
2704
+ draggableEl.removeEventListener("click", elToFocusListeners.get(draggableEl));
2705
+ }
2706
+ } catch (err) {
2707
+ _iterator.e(err);
2708
+ } finally {
2709
+ _iterator.f();
2710
+ }
2637
2711
  unregisterDropZone(node, config.type);
2638
2712
  dzToConfig["delete"](node);
2639
2713
  dzToHandles["delete"](node);
@@ -2842,6 +2916,8 @@
2842
2916
  updateZone();
2843
2917
  },
2844
2918
  destroy: function destroy() {
2919
+ isItemsDragDisabled.set(true);
2920
+ zone.destroy();
2845
2921
  node.removeEventListener("consider", consider);
2846
2922
  node.removeEventListener("finalize", finalize);
2847
2923
  isItemsDragDisabled.unsubscribe(updateZone);
package/dist/index.mjs CHANGED
@@ -1407,6 +1407,8 @@ var multiScroller;
1407
1407
  var touchDragHoldTimer;
1408
1408
  var touchHoldElapsed = false;
1409
1409
  var useCursorForDetectionActive = false;
1410
+ var pendingDragOwner;
1411
+ var watchedDropZones = new Set();
1410
1412
 
1411
1413
  // a map from type to a set of drop-zones
1412
1414
  var typeToDropZones$1 = new Map();
@@ -1429,9 +1431,10 @@ function registerDropZone$1(dropZoneEl, type) {
1429
1431
  }
1430
1432
  }
1431
1433
  function unregisterDropZone$1(dropZoneEl, type) {
1432
- typeToDropZones$1.get(type)["delete"](dropZoneEl);
1434
+ var dropZones = typeToDropZones$1.get(type);
1435
+ if (!dropZones || !dropZones["delete"](dropZoneEl)) return;
1433
1436
  decrementActiveDropZoneCount();
1434
- if (typeToDropZones$1.get(type).size === 0) {
1437
+ if (dropZones.size === 0) {
1435
1438
  typeToDropZones$1["delete"](type);
1436
1439
  }
1437
1440
  }
@@ -1442,7 +1445,10 @@ function watchDraggedElement() {
1442
1445
  return "watching dragged element";
1443
1446
  });
1444
1447
  var dropZones = typeToDropZones$1.get(draggedElType);
1445
- var _iterator = _createForOfIteratorHelper(dropZones),
1448
+ if (!dropZones || dropZones.size === 0) return;
1449
+ if (watchedDropZones.size > 0) unWatchDraggedElement();
1450
+ watchedDropZones = new Set(dropZones);
1451
+ var _iterator = _createForOfIteratorHelper(watchedDropZones),
1446
1452
  _step;
1447
1453
  try {
1448
1454
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -1459,11 +1465,11 @@ function watchDraggedElement() {
1459
1465
  window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1460
1466
 
1461
1467
  // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth
1462
- var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(dropZones.keys()).map(function (dz) {
1468
+ var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(watchedDropZones).map(function (dz) {
1463
1469
  return dzToConfig$1.get(dz).dropAnimationDurationMs;
1464
1470
  })));
1465
1471
  var observationIntervalMs = setIntervalMs === 0 ? DISABLED_OBSERVATION_INTERVAL_MS : Math.max(setIntervalMs, MIN_OBSERVATION_INTERVAL_MS); // if setIntervalMs is 0 it goes to 20, otherwise it is max between it and min observation.
1466
- multiScroller = createMultiScroller(dropZones, function () {
1472
+ multiScroller = createMultiScroller(watchedDropZones, function () {
1467
1473
  return currentMousePosition;
1468
1474
  });
1469
1475
  // Returns reference point in document coordinates - either cursor position or element center
@@ -1475,14 +1481,13 @@ function watchDraggedElement() {
1475
1481
  } : function () {
1476
1482
  return findCenterOfElement(draggedEl);
1477
1483
  };
1478
- observe(draggedEl, dropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
1484
+ observe(draggedEl, watchedDropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
1479
1485
  }
1480
1486
  function unWatchDraggedElement() {
1481
1487
  printDebug(function () {
1482
1488
  return "unwatching dragged element";
1483
1489
  });
1484
- var dropZones = typeToDropZones$1.get(draggedElType);
1485
- var _iterator2 = _createForOfIteratorHelper(dropZones),
1490
+ var _iterator2 = _createForOfIteratorHelper(watchedDropZones),
1486
1491
  _step2;
1487
1492
  try {
1488
1493
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
@@ -1496,6 +1501,7 @@ function unWatchDraggedElement() {
1496
1501
  } finally {
1497
1502
  _iterator2.f();
1498
1503
  }
1504
+ watchedDropZones = new Set();
1499
1505
  window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1500
1506
  // ensuring multiScroller is not already destroyed before destroying
1501
1507
  if (multiScroller) {
@@ -1727,11 +1733,13 @@ function animateDraggedToFinalPosition(shadowElIdx, callback) {
1727
1733
  window.setTimeout(callback, dropAnimationDurationMs);
1728
1734
  }
1729
1735
  function scheduleDZForRemovalAfterDrop(dz, destroy) {
1730
- scheduledForRemovalAfterDrop.push({
1736
+ var scheduledRemoval = {
1731
1737
  dz: dz,
1732
1738
  destroy: destroy
1733
- });
1739
+ };
1740
+ scheduledForRemovalAfterDrop.push(scheduledRemoval);
1734
1741
  window.requestAnimationFrame(function () {
1742
+ if (!scheduledForRemovalAfterDrop.includes(scheduledRemoval)) return;
1735
1743
  hideElement(dz);
1736
1744
  document.body.appendChild(dz);
1737
1745
  });
@@ -1765,6 +1773,7 @@ function cleanupPostDrop() {
1765
1773
  touchDragHoldTimer = undefined;
1766
1774
  touchHoldElapsed = false;
1767
1775
  useCursorForDetectionActive = false;
1776
+ pendingDragOwner = undefined;
1768
1777
  if (scheduledForRemovalAfterDrop.length) {
1769
1778
  printDebug(function () {
1770
1779
  return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop];
@@ -1780,6 +1789,7 @@ function cleanupPostDrop() {
1780
1789
  }
1781
1790
  function dndzone$2(node, options) {
1782
1791
  var initialized = false;
1792
+ var destroyed = false;
1783
1793
  var config = {
1784
1794
  items: undefined,
1785
1795
  type: undefined,
@@ -1827,11 +1837,17 @@ function dndzone$2(node, options) {
1827
1837
  touchHoldElapsed = false;
1828
1838
  }
1829
1839
  }
1830
- function handleFalseAlarm(e) {
1840
+ function cancelPendingDrag() {
1841
+ if (pendingDragOwner !== node) return;
1831
1842
  removeMaybeListeners();
1843
+ pendingDragOwner = undefined;
1832
1844
  originalDragTarget = undefined;
1833
1845
  dragStartMousePosition = undefined;
1834
1846
  currentMousePosition = undefined;
1847
+ }
1848
+ function handleFalseAlarm(e) {
1849
+ if (pendingDragOwner !== node) return;
1850
+ cancelPendingDrag();
1835
1851
 
1836
1852
  // dragging initiated by touch events prevents onclick from initially firing
1837
1853
  if (e.type === "touchend") {
@@ -1844,6 +1860,7 @@ function dndzone$2(node, options) {
1844
1860
  }
1845
1861
  }
1846
1862
  function handleMouseMoveMaybeDragStart(e) {
1863
+ if (destroyed || pendingDragOwner !== node) return;
1847
1864
  var isTouch = !!e.touches;
1848
1865
  var c = isTouch ? e.touches[0] : e;
1849
1866
  // If touch drag delay is configured and not elapsed yet, allow scrolling until either
@@ -1872,7 +1889,6 @@ function dndzone$2(node, options) {
1872
1889
  y: c.clientY
1873
1890
  };
1874
1891
  if (Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX || Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX) {
1875
- removeMaybeListeners();
1876
1892
  handleDragStart();
1877
1893
  }
1878
1894
  }
@@ -1891,9 +1907,9 @@ function dndzone$2(node, options) {
1891
1907
  });
1892
1908
  return;
1893
1909
  }
1894
- if (isWorkingOnPreviousDrag) {
1910
+ if (destroyed || isWorkingOnPreviousDrag || pendingDragOwner) {
1895
1911
  printDebug(function () {
1896
- return "cannot start a new drag before finalizing previous one";
1912
+ return "cannot start a new drag before finalizing the current drag or pending gesture";
1897
1913
  });
1898
1914
  return;
1899
1915
  }
@@ -1903,6 +1919,7 @@ function dndzone$2(node, options) {
1903
1919
  e.preventDefault();
1904
1920
  }
1905
1921
  e.stopPropagation();
1922
+ pendingDragOwner = node;
1906
1923
  var c = isTouchStart ? e.touches[0] : e;
1907
1924
  dragStartMousePosition = {
1908
1925
  x: c.clientX,
@@ -1911,18 +1928,25 @@ function dndzone$2(node, options) {
1911
1928
  currentMousePosition = _objectSpread2({}, dragStartMousePosition);
1912
1929
  originalDragTarget = e.currentTarget;
1913
1930
  if (useDelay) {
1931
+ var pendingTarget = originalDragTarget;
1914
1932
  touchHoldElapsed = false;
1915
1933
  touchDragHoldTimer = window.setTimeout(function () {
1916
- // If the finger is still down and no false-alarm happened
1917
- if (!originalDragTarget) return;
1934
+ // If this action still owns the same pending gesture, transition it to a drag.
1935
+ if (destroyed || pendingDragOwner !== node || originalDragTarget !== pendingTarget) return;
1918
1936
  touchHoldElapsed = true;
1919
- removeMaybeListeners();
1920
1937
  handleDragStart();
1921
1938
  }, config.delayTouchStartMs);
1922
1939
  }
1923
1940
  addMaybeListeners();
1924
1941
  }
1925
1942
  function handleDragStart() {
1943
+ var _originalDragTarget;
1944
+ if (destroyed || pendingDragOwner !== node || isWorkingOnPreviousDrag || !((_originalDragTarget = originalDragTarget) !== null && _originalDragTarget !== void 0 && _originalDragTarget.parentElement)) {
1945
+ cancelPendingDrag();
1946
+ return;
1947
+ }
1948
+ removeMaybeListeners();
1949
+ pendingDragOwner = undefined;
1926
1950
  printDebug(function () {
1927
1951
  return ["drag start config: ".concat(toString(config)), originalDragTarget];
1928
1952
  });
@@ -2121,11 +2145,29 @@ function dndzone$2(node, options) {
2121
2145
  configure(newOptions);
2122
2146
  },
2123
2147
  destroy: function destroy() {
2148
+ if (destroyed) return;
2149
+ destroyed = true;
2150
+ cancelPendingDrag();
2151
+ var _iterator3 = _createForOfIteratorHelper(node.children),
2152
+ _step3;
2153
+ try {
2154
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
2155
+ var draggableEl = _step3.value;
2156
+ draggableEl.removeEventListener("mousedown", elToMouseDownListener.get(draggableEl));
2157
+ draggableEl.removeEventListener("touchstart", elToMouseDownListener.get(draggableEl));
2158
+ }
2159
+ } catch (err) {
2160
+ _iterator3.e(err);
2161
+ } finally {
2162
+ _iterator3.f();
2163
+ }
2124
2164
  function destroyDz() {
2125
2165
  printDebug(function () {
2126
2166
  return "pointer dndzone will destroy";
2127
2167
  });
2128
- unregisterDropZone$1(node, dzToConfig$1.get(node).type);
2168
+ var registeredConfig = dzToConfig$1.get(node);
2169
+ if (!registeredConfig) return;
2170
+ unregisterDropZone$1(node, registeredConfig.type);
2129
2171
  dzToConfig$1["delete"](node);
2130
2172
  }
2131
2173
  if (isWorkingOnPreviousDrag && !node.closest("[".concat(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, "]"))) {
@@ -2282,12 +2324,13 @@ function unregisterDropZone(dropZoneEl, type) {
2282
2324
  printDebug(function () {
2283
2325
  return "unregistering drop-zone";
2284
2326
  });
2285
- if (focusedDz === dropZoneEl) {
2327
+ if (isDragging && focusedDz === dropZoneEl) {
2286
2328
  handleDrop();
2287
2329
  }
2288
- typeToDropZones.get(type)["delete"](dropZoneEl);
2330
+ var dropZones = typeToDropZones.get(type);
2331
+ if (!dropZones || !dropZones["delete"](dropZoneEl)) return;
2289
2332
  decrementActiveDropZoneCount();
2290
- if (typeToDropZones.get(type).size === 0) {
2333
+ if (dropZones.size === 0) {
2291
2334
  typeToDropZones["delete"](type);
2292
2335
  }
2293
2336
  if (typeToDropZones.size === 0) {
@@ -2349,17 +2392,20 @@ function handleZoneFocus(e) {
2349
2392
  }
2350
2393
  }
2351
2394
  var dzFrom = focusedDz;
2395
+ var movedItemId = focusedItemId;
2396
+ focusedDz = newlyFocusedDz;
2352
2397
  dispatchFinalizeEvent(dzFrom, originItems, {
2353
2398
  trigger: TRIGGERS.DROPPED_INTO_ANOTHER,
2354
- id: focusedItemId,
2355
- source: SOURCES.KEYBOARD
2356
- });
2357
- dispatchFinalizeEvent(newlyFocusedDz, targetItems, {
2358
- trigger: TRIGGERS.DROPPED_INTO_ZONE,
2359
- id: focusedItemId,
2399
+ id: movedItemId,
2360
2400
  source: SOURCES.KEYBOARD
2361
2401
  });
2362
- focusedDz = newlyFocusedDz;
2402
+ if (dzToConfig.has(newlyFocusedDz)) {
2403
+ dispatchFinalizeEvent(newlyFocusedDz, targetItems, {
2404
+ trigger: TRIGGERS.DROPPED_INTO_ZONE,
2405
+ id: movedItemId,
2406
+ source: SOURCES.KEYBOARD
2407
+ });
2408
+ }
2363
2409
  }
2364
2410
  function triggerAllDzsUpdate() {
2365
2411
  dzToHandles.forEach(function (_ref, dz) {
@@ -2369,27 +2415,23 @@ function triggerAllDzsUpdate() {
2369
2415
  }
2370
2416
  function handleDrop() {
2371
2417
  var dispatchConsider = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
2418
+ if (!isDragging || !focusedDz) return;
2372
2419
  printDebug(function () {
2373
2420
  return "drop";
2374
2421
  });
2375
- if (!dzToConfig.get(focusedDz).autoAriaDisabled) {
2422
+ var droppedDz = focusedDz;
2423
+ var droppedConfig = dzToConfig.get(droppedDz);
2424
+ var droppedItemId = focusedItemId;
2425
+ var droppedItemType = draggedItemType;
2426
+ if (!droppedConfig) return;
2427
+ if (!droppedConfig.autoAriaDisabled) {
2376
2428
  alertToScreenReader("Stopped dragging item ".concat(focusedItemLabel));
2377
2429
  }
2378
2430
  if (allDragTargets.has(document.activeElement)) {
2379
2431
  document.activeElement.blur();
2380
2432
  }
2381
- if (dispatchConsider) {
2382
- dispatchConsiderEvent(focusedDz, dzToConfig.get(focusedDz).items, {
2383
- trigger: TRIGGERS.DRAG_STOPPED,
2384
- id: focusedItemId,
2385
- source: SOURCES.KEYBOARD
2386
- });
2387
- }
2388
- styleInactiveDropZones(typeToDropZones.get(draggedItemType), function (dz) {
2389
- return dzToConfig.get(dz).dropTargetStyle;
2390
- }, function (dz) {
2391
- return dzToConfig.get(dz).dropTargetClasses;
2392
- });
2433
+ // Clear global drag state before dispatching. A synchronous handler may destroy the
2434
+ // focused zone, and unregisterDropZone must not recursively enter handleDrop.
2393
2435
  focusedItem = null;
2394
2436
  focusedItemId = null;
2395
2437
  focusedItemLabel = "";
@@ -2397,10 +2439,26 @@ function handleDrop() {
2397
2439
  focusedDz = null;
2398
2440
  focusedDzLabel = "";
2399
2441
  isDragging = false;
2442
+ if (dispatchConsider) {
2443
+ dispatchConsiderEvent(droppedDz, droppedConfig.items, {
2444
+ trigger: TRIGGERS.DRAG_STOPPED,
2445
+ id: droppedItemId,
2446
+ source: SOURCES.KEYBOARD
2447
+ });
2448
+ }
2449
+ var dropZones = typeToDropZones.get(droppedItemType);
2450
+ if (dropZones) {
2451
+ styleInactiveDropZones(dropZones, function (dz) {
2452
+ return dzToConfig.get(dz).dropTargetStyle;
2453
+ }, function (dz) {
2454
+ return dzToConfig.get(dz).dropTargetClasses;
2455
+ });
2456
+ }
2400
2457
  triggerAllDzsUpdate();
2401
2458
  }
2402
2459
  //////
2403
2460
  function dndzone$1(node, options) {
2461
+ var destroyed = false;
2404
2462
  var config = {
2405
2463
  items: undefined,
2406
2464
  type: undefined,
@@ -2625,9 +2683,25 @@ function dndzone$1(node, options) {
2625
2683
  configure(newOptions);
2626
2684
  },
2627
2685
  destroy: function destroy() {
2686
+ if (destroyed) return;
2687
+ destroyed = true;
2628
2688
  printDebug(function () {
2629
2689
  return "keyboard dndzone will destroy";
2630
2690
  });
2691
+ node.removeEventListener("focus", handleZoneFocus);
2692
+ var _iterator = _createForOfIteratorHelper(node.children),
2693
+ _step;
2694
+ try {
2695
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
2696
+ var draggableEl = _step.value;
2697
+ draggableEl.removeEventListener("keydown", elToKeyDownListeners.get(draggableEl));
2698
+ draggableEl.removeEventListener("click", elToFocusListeners.get(draggableEl));
2699
+ }
2700
+ } catch (err) {
2701
+ _iterator.e(err);
2702
+ } finally {
2703
+ _iterator.f();
2704
+ }
2631
2705
  unregisterDropZone(node, config.type);
2632
2706
  dzToConfig["delete"](node);
2633
2707
  dzToHandles["delete"](node);
@@ -2836,6 +2910,8 @@ function dragHandleZone(node, options) {
2836
2910
  updateZone();
2837
2911
  },
2838
2912
  destroy: function destroy() {
2913
+ isItemsDragDisabled.set(true);
2914
+ zone.destroy();
2839
2915
  node.removeEventListener("consider", consider);
2840
2916
  node.removeEventListener("finalize", finalize);
2841
2917
  isItemsDragDisabled.unsubscribe(updateZone);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svelte-dnd-action",
3
3
  "description": "*An awesome drag and drop library for Svelte 3 and 4 (not using the browser's built-in dnd, thanks god): Rich animations, nested containers, touch support and more *",
4
- "version": "0.9.71",
4
+ "version": "0.9.72",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/isaacHagoel/svelte-dnd-action.git"
@@ -50,12 +50,13 @@ function registerDropZone(dropZoneEl, type) {
50
50
  }
51
51
  function unregisterDropZone(dropZoneEl, type) {
52
52
  printDebug(() => "unregistering drop-zone");
53
- if (focusedDz === dropZoneEl) {
53
+ if (isDragging && focusedDz === dropZoneEl) {
54
54
  handleDrop();
55
55
  }
56
- typeToDropZones.get(type).delete(dropZoneEl);
56
+ const dropZones = typeToDropZones.get(type);
57
+ if (!dropZones || !dropZones.delete(dropZoneEl)) return;
57
58
  decrementActiveDropZoneCount();
58
- if (typeToDropZones.get(type).size === 0) {
59
+ if (dropZones.size === 0) {
59
60
  typeToDropZones.delete(type);
60
61
  }
61
62
  if (typeToDropZones.size === 0) {
@@ -112,9 +113,12 @@ function handleZoneFocus(e) {
112
113
  }
113
114
  }
114
115
  const dzFrom = focusedDz;
115
- dispatchFinalizeEvent(dzFrom, originItems, {trigger: TRIGGERS.DROPPED_INTO_ANOTHER, id: focusedItemId, source: SOURCES.KEYBOARD});
116
- dispatchFinalizeEvent(newlyFocusedDz, targetItems, {trigger: TRIGGERS.DROPPED_INTO_ZONE, id: focusedItemId, source: SOURCES.KEYBOARD});
116
+ const movedItemId = focusedItemId;
117
117
  focusedDz = newlyFocusedDz;
118
+ dispatchFinalizeEvent(dzFrom, originItems, {trigger: TRIGGERS.DROPPED_INTO_ANOTHER, id: movedItemId, source: SOURCES.KEYBOARD});
119
+ if (dzToConfig.has(newlyFocusedDz)) {
120
+ dispatchFinalizeEvent(newlyFocusedDz, targetItems, {trigger: TRIGGERS.DROPPED_INTO_ZONE, id: movedItemId, source: SOURCES.KEYBOARD});
121
+ }
118
122
  }
119
123
 
120
124
  function triggerAllDzsUpdate() {
@@ -122,25 +126,22 @@ function triggerAllDzsUpdate() {
122
126
  }
123
127
 
124
128
  function handleDrop(dispatchConsider = true) {
129
+ if (!isDragging || !focusedDz) return;
125
130
  printDebug(() => "drop");
126
- if (!dzToConfig.get(focusedDz).autoAriaDisabled) {
131
+ const droppedDz = focusedDz;
132
+ const droppedConfig = dzToConfig.get(droppedDz);
133
+ const droppedItemId = focusedItemId;
134
+ const droppedItemType = draggedItemType;
135
+ if (!droppedConfig) return;
136
+
137
+ if (!droppedConfig.autoAriaDisabled) {
127
138
  alertToScreenReader(`Stopped dragging item ${focusedItemLabel}`);
128
139
  }
129
140
  if (allDragTargets.has(document.activeElement)) {
130
141
  document.activeElement.blur();
131
142
  }
132
- if (dispatchConsider) {
133
- dispatchConsiderEvent(focusedDz, dzToConfig.get(focusedDz).items, {
134
- trigger: TRIGGERS.DRAG_STOPPED,
135
- id: focusedItemId,
136
- source: SOURCES.KEYBOARD
137
- });
138
- }
139
- styleInactiveDropZones(
140
- typeToDropZones.get(draggedItemType),
141
- dz => dzToConfig.get(dz).dropTargetStyle,
142
- dz => dzToConfig.get(dz).dropTargetClasses
143
- );
143
+ // Clear global drag state before dispatching. A synchronous handler may destroy the
144
+ // focused zone, and unregisterDropZone must not recursively enter handleDrop.
144
145
  focusedItem = null;
145
146
  focusedItemId = null;
146
147
  focusedItemLabel = "";
@@ -148,10 +149,27 @@ function handleDrop(dispatchConsider = true) {
148
149
  focusedDz = null;
149
150
  focusedDzLabel = "";
150
151
  isDragging = false;
152
+
153
+ if (dispatchConsider) {
154
+ dispatchConsiderEvent(droppedDz, droppedConfig.items, {
155
+ trigger: TRIGGERS.DRAG_STOPPED,
156
+ id: droppedItemId,
157
+ source: SOURCES.KEYBOARD
158
+ });
159
+ }
160
+ const dropZones = typeToDropZones.get(droppedItemType);
161
+ if (dropZones) {
162
+ styleInactiveDropZones(
163
+ dropZones,
164
+ dz => dzToConfig.get(dz).dropTargetStyle,
165
+ dz => dzToConfig.get(dz).dropTargetClasses
166
+ );
167
+ }
151
168
  triggerAllDzsUpdate();
152
169
  }
153
170
  //////
154
171
  export function dndzone(node, options) {
172
+ let destroyed = false;
155
173
  const config = {
156
174
  items: undefined,
157
175
  type: undefined,
@@ -344,7 +362,14 @@ export function dndzone(node, options) {
344
362
  configure(newOptions);
345
363
  },
346
364
  destroy: () => {
365
+ if (destroyed) return;
366
+ destroyed = true;
347
367
  printDebug(() => "keyboard dndzone will destroy");
368
+ node.removeEventListener("focus", handleZoneFocus);
369
+ for (const draggableEl of node.children) {
370
+ draggableEl.removeEventListener("keydown", elToKeyDownListeners.get(draggableEl));
371
+ draggableEl.removeEventListener("click", elToFocusListeners.get(draggableEl));
372
+ }
348
373
  unregisterDropZone(node, config.type);
349
374
  dzToConfig.delete(node);
350
375
  dzToHandles.delete(node);
@@ -64,6 +64,8 @@ let multiScroller;
64
64
  let touchDragHoldTimer;
65
65
  let touchHoldElapsed = false;
66
66
  let useCursorForDetectionActive = false;
67
+ let pendingDragOwner;
68
+ let watchedDropZones = new Set();
67
69
 
68
70
  // a map from type to a set of drop-zones
69
71
  const typeToDropZones = new Map();
@@ -84,9 +86,10 @@ function registerDropZone(dropZoneEl, type) {
84
86
  }
85
87
  }
86
88
  function unregisterDropZone(dropZoneEl, type) {
87
- typeToDropZones.get(type).delete(dropZoneEl);
89
+ const dropZones = typeToDropZones.get(type);
90
+ if (!dropZones || !dropZones.delete(dropZoneEl)) return;
88
91
  decrementActiveDropZoneCount();
89
- if (typeToDropZones.get(type).size === 0) {
92
+ if (dropZones.size === 0) {
90
93
  typeToDropZones.delete(type);
91
94
  }
92
95
  }
@@ -95,8 +98,11 @@ function unregisterDropZone(dropZoneEl, type) {
95
98
  function watchDraggedElement() {
96
99
  printDebug(() => "watching dragged element");
97
100
  const dropZones = typeToDropZones.get(draggedElType);
101
+ if (!dropZones || dropZones.size === 0) return;
102
+ if (watchedDropZones.size > 0) unWatchDraggedElement();
103
+ watchedDropZones = new Set(dropZones);
98
104
 
99
- for (const dz of dropZones) {
105
+ for (const dz of watchedDropZones) {
100
106
  dz.addEventListener(DRAGGED_ENTERED_EVENT_NAME, handleDraggedEntered);
101
107
  dz.addEventListener(DRAGGED_LEFT_EVENT_NAME, handleDraggedLeft);
102
108
  dz.addEventListener(DRAGGED_OVER_INDEX_EVENT_NAME, handleDraggedIsOverIndex);
@@ -104,9 +110,9 @@ function watchDraggedElement() {
104
110
  window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop);
105
111
 
106
112
  // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth
107
- const setIntervalMs = Math.max(...Array.from(dropZones.keys()).map(dz => dzToConfig.get(dz).dropAnimationDurationMs));
113
+ const setIntervalMs = Math.max(...Array.from(watchedDropZones).map(dz => dzToConfig.get(dz).dropAnimationDurationMs));
108
114
  const observationIntervalMs = setIntervalMs === 0 ? DISABLED_OBSERVATION_INTERVAL_MS : Math.max(setIntervalMs, MIN_OBSERVATION_INTERVAL_MS); // if setIntervalMs is 0 it goes to 20, otherwise it is max between it and min observation.
109
- multiScroller = createMultiScroller(dropZones, () => currentMousePosition);
115
+ multiScroller = createMultiScroller(watchedDropZones, () => currentMousePosition);
110
116
  // Returns reference point in document coordinates - either cursor position or element center
111
117
  const getReferencePoint = useCursorForDetectionActive
112
118
  ? () => ({
@@ -114,16 +120,16 @@ function watchDraggedElement() {
114
120
  y: currentMousePosition.y + window.scrollY
115
121
  })
116
122
  : () => findCenterOfElement(draggedEl);
117
- observe(draggedEl, dropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
123
+ observe(draggedEl, watchedDropZones, observationIntervalMs * 1.07, multiScroller, getReferencePoint);
118
124
  }
119
125
  function unWatchDraggedElement() {
120
126
  printDebug(() => "unwatching dragged element");
121
- const dropZones = typeToDropZones.get(draggedElType);
122
- for (const dz of dropZones) {
127
+ for (const dz of watchedDropZones) {
123
128
  dz.removeEventListener(DRAGGED_ENTERED_EVENT_NAME, handleDraggedEntered);
124
129
  dz.removeEventListener(DRAGGED_LEFT_EVENT_NAME, handleDraggedLeft);
125
130
  dz.removeEventListener(DRAGGED_OVER_INDEX_EVENT_NAME, handleDraggedIsOverIndex);
126
131
  }
132
+ watchedDropZones = new Set();
127
133
  window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop);
128
134
  // ensuring multiScroller is not already destroyed before destroying
129
135
  if (multiScroller) {
@@ -315,8 +321,10 @@ function animateDraggedToFinalPosition(shadowElIdx, callback) {
315
321
  }
316
322
 
317
323
  function scheduleDZForRemovalAfterDrop(dz, destroy) {
318
- scheduledForRemovalAfterDrop.push({dz, destroy});
324
+ const scheduledRemoval = {dz, destroy};
325
+ scheduledForRemovalAfterDrop.push(scheduledRemoval);
319
326
  window.requestAnimationFrame(() => {
327
+ if (!scheduledForRemovalAfterDrop.includes(scheduledRemoval)) return;
320
328
  hideElement(dz);
321
329
  document.body.appendChild(dz);
322
330
  });
@@ -351,6 +359,7 @@ function cleanupPostDrop() {
351
359
  touchDragHoldTimer = undefined;
352
360
  touchHoldElapsed = false;
353
361
  useCursorForDetectionActive = false;
362
+ pendingDragOwner = undefined;
354
363
  if (scheduledForRemovalAfterDrop.length) {
355
364
  printDebug(() => ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]);
356
365
  scheduledForRemovalAfterDrop.forEach(({dz, destroy}) => {
@@ -363,6 +372,7 @@ function cleanupPostDrop() {
363
372
 
364
373
  export function dndzone(node, options) {
365
374
  let initialized = false;
375
+ let destroyed = false;
366
376
  const config = {
367
377
  items: undefined,
368
378
  type: undefined,
@@ -398,11 +408,17 @@ export function dndzone(node, options) {
398
408
  touchHoldElapsed = false;
399
409
  }
400
410
  }
401
- function handleFalseAlarm(e) {
411
+ function cancelPendingDrag() {
412
+ if (pendingDragOwner !== node) return;
402
413
  removeMaybeListeners();
414
+ pendingDragOwner = undefined;
403
415
  originalDragTarget = undefined;
404
416
  dragStartMousePosition = undefined;
405
417
  currentMousePosition = undefined;
418
+ }
419
+ function handleFalseAlarm(e) {
420
+ if (pendingDragOwner !== node) return;
421
+ cancelPendingDrag();
406
422
 
407
423
  // dragging initiated by touch events prevents onclick from initially firing
408
424
  if (e.type === "touchend") {
@@ -416,6 +432,7 @@ export function dndzone(node, options) {
416
432
  }
417
433
 
418
434
  function handleMouseMoveMaybeDragStart(e) {
435
+ if (destroyed || pendingDragOwner !== node) return;
419
436
  const isTouch = !!e.touches;
420
437
  const c = isTouch ? e.touches[0] : e;
421
438
  // If touch drag delay is configured and not elapsed yet, allow scrolling until either
@@ -445,7 +462,6 @@ export function dndzone(node, options) {
445
462
  Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX ||
446
463
  Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX
447
464
  ) {
448
- removeMaybeListeners();
449
465
  handleDragStart();
450
466
  }
451
467
  }
@@ -460,8 +476,8 @@ export function dndzone(node, options) {
460
476
  printDebug(() => `ignoring none left click button: ${e.button}`);
461
477
  return;
462
478
  }
463
- if (isWorkingOnPreviousDrag) {
464
- printDebug(() => "cannot start a new drag before finalizing previous one");
479
+ if (destroyed || isWorkingOnPreviousDrag || pendingDragOwner) {
480
+ printDebug(() => "cannot start a new drag before finalizing the current drag or pending gesture");
465
481
  return;
466
482
  }
467
483
  const isTouchStart = !!e.touches;
@@ -471,6 +487,7 @@ export function dndzone(node, options) {
471
487
  e.preventDefault();
472
488
  }
473
489
  e.stopPropagation();
490
+ pendingDragOwner = node;
474
491
 
475
492
  const c = isTouchStart ? e.touches[0] : e;
476
493
  dragStartMousePosition = {x: c.clientX, y: c.clientY};
@@ -478,12 +495,12 @@ export function dndzone(node, options) {
478
495
  originalDragTarget = e.currentTarget;
479
496
 
480
497
  if (useDelay) {
498
+ const pendingTarget = originalDragTarget;
481
499
  touchHoldElapsed = false;
482
500
  touchDragHoldTimer = window.setTimeout(() => {
483
- // If the finger is still down and no false-alarm happened
484
- if (!originalDragTarget) return;
501
+ // If this action still owns the same pending gesture, transition it to a drag.
502
+ if (destroyed || pendingDragOwner !== node || originalDragTarget !== pendingTarget) return;
485
503
  touchHoldElapsed = true;
486
- removeMaybeListeners();
487
504
  handleDragStart();
488
505
  }, config.delayTouchStartMs);
489
506
  }
@@ -492,6 +509,12 @@ export function dndzone(node, options) {
492
509
  }
493
510
 
494
511
  function handleDragStart() {
512
+ if (destroyed || pendingDragOwner !== node || isWorkingOnPreviousDrag || !originalDragTarget?.parentElement) {
513
+ cancelPendingDrag();
514
+ return;
515
+ }
516
+ removeMaybeListeners();
517
+ pendingDragOwner = undefined;
495
518
  printDebug(() => [`drag start config: ${toString(config)}`, originalDragTarget]);
496
519
  isWorkingOnPreviousDrag = true;
497
520
 
@@ -670,9 +693,18 @@ export function dndzone(node, options) {
670
693
  configure(newOptions);
671
694
  },
672
695
  destroy: () => {
696
+ if (destroyed) return;
697
+ destroyed = true;
698
+ cancelPendingDrag();
699
+ for (const draggableEl of node.children) {
700
+ draggableEl.removeEventListener("mousedown", elToMouseDownListener.get(draggableEl));
701
+ draggableEl.removeEventListener("touchstart", elToMouseDownListener.get(draggableEl));
702
+ }
673
703
  function destroyDz() {
674
704
  printDebug(() => "pointer dndzone will destroy");
675
- unregisterDropZone(node, dzToConfig.get(node).type);
705
+ const registeredConfig = dzToConfig.get(node);
706
+ if (!registeredConfig) return;
707
+ unregisterDropZone(node, registeredConfig.type);
676
708
  dzToConfig.delete(node);
677
709
  }
678
710
  if (isWorkingOnPreviousDrag && !node.closest(`[${ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE}]`)) {
@@ -77,6 +77,8 @@ export function dragHandleZone(node, options) {
77
77
  updateZone();
78
78
  },
79
79
  destroy: () => {
80
+ isItemsDragDisabled.set(true);
81
+ zone.destroy();
80
82
  node.removeEventListener("consider", consider);
81
83
  node.removeEventListener("finalize", finalize);
82
84
  isItemsDragDisabled.unsubscribe(updateZone);