svelte-dnd-action 0.9.62 → 0.9.63
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/README.md +16 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +92 -19
- package/dist/index.mjs +92 -19
- package/package.json +1 -1
- package/src/action.js +14 -0
- package/src/pointerAction.js +84 -14
package/README.md
CHANGED
|
@@ -119,6 +119,10 @@ An options-object with the following attributes:
|
|
|
119
119
|
| `autoAriaDisabled` | Boolean | No | `false` | Setting it to true will disable all the automatically added aria attributes and aria alerts (for example when the user starts/ stops dragging using the keyboard).<br /> **Use it only if you intend to implement your own custom instructions, roles and alerts.** In such a case, you might find the exported function `alertToScreenReader(string)` useful. |
|
|
120
120
|
| `centreDraggedOnCursor` | Boolean | No | `false` | Setting it to true will cause elements from this dnd-zone to position their center on the cursor on drag start, effectively turning the cursor to the focal point that triggers all the dnd events (ex: entering another zone). Useful for dnd-zones with large items that can be dragged over small items. |
|
|
121
121
|
| `dropAnimationDisabled` | Boolean | No | `false` | Setting it to true will disable the animation of the dropped element to its final place. |
|
|
122
|
+
| `delayTouchStart` | Boolean \| Number | No | `false` | Prevents accidental drags on touch devices that should have been scrolls.
|
|
123
|
+
• `true` – enable with sensible default (80 ms).
|
|
124
|
+
• `number` – custom delay in milliseconds.
|
|
125
|
+
• `false` / omitted – legacy behaviour (drag starts immediately). |
|
|
122
126
|
|
|
123
127
|
##### Output:
|
|
124
128
|
|
|
@@ -477,3 +481,15 @@ Starting with version 0.9.42. if you use the hint make sure to include it in the
|
|
|
477
481
|
There is still quite a lot to do. If you'd like to contribute please get in touch (raise an issue or comment on an existing one).
|
|
478
482
|
Ideally, be specific about which area you'd like to help with.
|
|
479
483
|
Thank you for reading :)
|
|
484
|
+
|
|
485
|
+
If your page is scrollable and you found that quick swipes on touch devices sometimes start a drag instead of scrolling, enable the new `delayTouchStart` option:
|
|
486
|
+
|
|
487
|
+
```sveltehtml
|
|
488
|
+
<section use:dndzone={{ items, flipDurationMs, delayTouchStart: true }}>
|
|
489
|
+
…
|
|
490
|
+
</section>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Setting it to `true` waits 80 ms before hijacking the gesture; passing a number lets you tune the threshold.
|
|
494
|
+
|
|
495
|
+
If you want screen-readers to tell the user which item is being dragged and which container it interacts with, **please add `aria-label` on the container and on every draggable item**. The library will take care of the rest.
|
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,11 @@ export interface Options<T extends Item = Item> {
|
|
|
59
59
|
autoAriaDisabled?: boolean;
|
|
60
60
|
centreDraggedOnCursor?: boolean;
|
|
61
61
|
dropAnimationDisabled?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Improve touch UX: true => default 80 ms delay, number => custom delay in ms.
|
|
64
|
+
* false/undefined => drag starts instantly (legacy behaviour)
|
|
65
|
+
*/
|
|
66
|
+
delayTouchStart?: boolean | number;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
export interface DndZoneAttributes<T> {
|
package/dist/index.js
CHANGED
|
@@ -1331,6 +1331,7 @@
|
|
|
1331
1331
|
var MIN_OBSERVATION_INTERVAL_MS = 100;
|
|
1332
1332
|
var DISABLED_OBSERVATION_INTERVAL_MS = 20;
|
|
1333
1333
|
var MIN_MOVEMENT_BEFORE_DRAG_START_PX = 3;
|
|
1334
|
+
var DEFAULT_TOUCH_DELAY_MS = 80;
|
|
1334
1335
|
var DEFAULT_DROP_TARGET_STYLE$1 = {
|
|
1335
1336
|
outline: "rgba(255, 255, 102, 0.7) solid 2px"
|
|
1336
1337
|
};
|
|
@@ -1351,6 +1352,8 @@
|
|
|
1351
1352
|
var isDraggedOutsideOfAnyDz = false;
|
|
1352
1353
|
var scheduledForRemovalAfterDrop = [];
|
|
1353
1354
|
var multiScroller;
|
|
1355
|
+
var touchDragHoldTimer;
|
|
1356
|
+
var touchHoldElapsed = false;
|
|
1354
1357
|
|
|
1355
1358
|
// a map from type to a set of drop-zones
|
|
1356
1359
|
var typeToDropZones$1 = new Map();
|
|
@@ -1673,19 +1676,12 @@
|
|
|
1673
1676
|
}
|
|
1674
1677
|
/* cleanup */
|
|
1675
1678
|
function cleanupPostDrop() {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
scheduledForRemovalAfterDrop.forEach(function (_ref) {
|
|
1683
|
-
var dz = _ref.dz,
|
|
1684
|
-
destroy = _ref.destroy;
|
|
1685
|
-
destroy();
|
|
1686
|
-
dz.remove();
|
|
1687
|
-
});
|
|
1688
|
-
scheduledForRemovalAfterDrop = [];
|
|
1679
|
+
// Remove the temporary elements that were kept in the DOM during the drag
|
|
1680
|
+
if (draggedEl && draggedEl.remove) {
|
|
1681
|
+
draggedEl.remove();
|
|
1682
|
+
}
|
|
1683
|
+
if (originalDragTarget && originalDragTarget.remove) {
|
|
1684
|
+
originalDragTarget.remove();
|
|
1689
1685
|
}
|
|
1690
1686
|
draggedEl = undefined;
|
|
1691
1687
|
originalDragTarget = undefined;
|
|
@@ -1701,6 +1697,23 @@
|
|
|
1701
1697
|
finalizingPreviousDrag = false;
|
|
1702
1698
|
unlockOriginDzMinDimensions = undefined;
|
|
1703
1699
|
isDraggedOutsideOfAnyDz = false;
|
|
1700
|
+
if (touchDragHoldTimer) {
|
|
1701
|
+
clearTimeout(touchDragHoldTimer);
|
|
1702
|
+
}
|
|
1703
|
+
touchDragHoldTimer = undefined;
|
|
1704
|
+
touchHoldElapsed = false;
|
|
1705
|
+
if (scheduledForRemovalAfterDrop.length) {
|
|
1706
|
+
printDebug(function () {
|
|
1707
|
+
return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop];
|
|
1708
|
+
});
|
|
1709
|
+
scheduledForRemovalAfterDrop.forEach(function (_ref) {
|
|
1710
|
+
var dz = _ref.dz,
|
|
1711
|
+
destroy = _ref.destroy;
|
|
1712
|
+
destroy();
|
|
1713
|
+
dz.remove();
|
|
1714
|
+
});
|
|
1715
|
+
scheduledForRemovalAfterDrop = [];
|
|
1716
|
+
}
|
|
1704
1717
|
}
|
|
1705
1718
|
function dndzone$2(node, options) {
|
|
1706
1719
|
var initialized = false;
|
|
@@ -1715,7 +1728,8 @@
|
|
|
1715
1728
|
dropTargetClasses: [],
|
|
1716
1729
|
transformDraggedElement: function transformDraggedElement() {},
|
|
1717
1730
|
centreDraggedOnCursor: false,
|
|
1718
|
-
dropAnimationDisabled: false
|
|
1731
|
+
dropAnimationDisabled: false,
|
|
1732
|
+
delayTouchStartMs: 0
|
|
1719
1733
|
};
|
|
1720
1734
|
printDebug(function () {
|
|
1721
1735
|
return ["dndzone good to go options: ".concat(toString(options), ", config: ").concat(toString(config)), {
|
|
@@ -1743,6 +1757,11 @@
|
|
|
1743
1757
|
window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart);
|
|
1744
1758
|
window.removeEventListener("mouseup", handleFalseAlarm);
|
|
1745
1759
|
window.removeEventListener("touchend", handleFalseAlarm);
|
|
1760
|
+
if (touchDragHoldTimer) {
|
|
1761
|
+
clearTimeout(touchDragHoldTimer);
|
|
1762
|
+
touchDragHoldTimer = undefined;
|
|
1763
|
+
touchHoldElapsed = false;
|
|
1764
|
+
}
|
|
1746
1765
|
}
|
|
1747
1766
|
function handleFalseAlarm(e) {
|
|
1748
1767
|
removeMaybeListeners();
|
|
@@ -1761,8 +1780,29 @@
|
|
|
1761
1780
|
}
|
|
1762
1781
|
}
|
|
1763
1782
|
function handleMouseMoveMaybeDragStart(e) {
|
|
1783
|
+
var isTouch = !!e.touches;
|
|
1784
|
+
var c = isTouch ? e.touches[0] : e;
|
|
1785
|
+
// If touch drag delay is configured and not elapsed yet, allow scrolling until either
|
|
1786
|
+
// the delay elapses (timer will call handleDragStart) or the user moves significantly,
|
|
1787
|
+
// in which case we cancel the potential drag and let the interaction be a scroll.
|
|
1788
|
+
if (isTouch && config.delayTouchStartMs > 0 && !touchHoldElapsed) {
|
|
1789
|
+
currentMousePosition = {
|
|
1790
|
+
x: c.clientX,
|
|
1791
|
+
y: c.clientY
|
|
1792
|
+
};
|
|
1793
|
+
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) {
|
|
1794
|
+
// User started scrolling, cancel drag attempt.
|
|
1795
|
+
if (touchDragHoldTimer) {
|
|
1796
|
+
clearTimeout(touchDragHoldTimer);
|
|
1797
|
+
touchDragHoldTimer = undefined;
|
|
1798
|
+
}
|
|
1799
|
+
handleFalseAlarm(e);
|
|
1800
|
+
}
|
|
1801
|
+
return; // Do not preventDefault so scrolling works.
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// legacy / post-delay path – block scrolling and maybe start drag
|
|
1764
1805
|
e.preventDefault();
|
|
1765
|
-
var c = e.touches ? e.touches[0] : e;
|
|
1766
1806
|
currentMousePosition = {
|
|
1767
1807
|
x: c.clientX,
|
|
1768
1808
|
y: c.clientY
|
|
@@ -1793,15 +1833,29 @@
|
|
|
1793
1833
|
});
|
|
1794
1834
|
return;
|
|
1795
1835
|
}
|
|
1796
|
-
e.
|
|
1836
|
+
var isTouchStart = !!e.touches;
|
|
1837
|
+
var useDelay = isTouchStart && config.delayTouchStartMs > 0;
|
|
1838
|
+
if (!useDelay) {
|
|
1839
|
+
e.preventDefault();
|
|
1840
|
+
}
|
|
1797
1841
|
e.stopPropagation();
|
|
1798
|
-
var c =
|
|
1842
|
+
var c = isTouchStart ? e.touches[0] : e;
|
|
1799
1843
|
dragStartMousePosition = {
|
|
1800
1844
|
x: c.clientX,
|
|
1801
1845
|
y: c.clientY
|
|
1802
1846
|
};
|
|
1803
1847
|
currentMousePosition = _objectSpread2({}, dragStartMousePosition);
|
|
1804
1848
|
originalDragTarget = e.currentTarget;
|
|
1849
|
+
if (useDelay) {
|
|
1850
|
+
touchHoldElapsed = false;
|
|
1851
|
+
touchDragHoldTimer = window.setTimeout(function () {
|
|
1852
|
+
// If the finger is still down and no false-alarm happened
|
|
1853
|
+
if (!originalDragTarget) return;
|
|
1854
|
+
touchHoldElapsed = true;
|
|
1855
|
+
removeMaybeListeners();
|
|
1856
|
+
handleDragStart();
|
|
1857
|
+
}, config.delayTouchStartMs);
|
|
1858
|
+
}
|
|
1805
1859
|
addMaybeListeners();
|
|
1806
1860
|
}
|
|
1807
1861
|
function handleDragStart() {
|
|
@@ -1899,8 +1953,17 @@
|
|
|
1899
1953
|
_ref2$centreDraggedOn = _ref2.centreDraggedOnCursor,
|
|
1900
1954
|
centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn,
|
|
1901
1955
|
_ref2$dropAnimationDi = _ref2.dropAnimationDisabled,
|
|
1902
|
-
dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi
|
|
1956
|
+
dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi,
|
|
1957
|
+
_ref2$delayTouchStart = _ref2.delayTouchStart,
|
|
1958
|
+
delayTouchStartOpt = _ref2$delayTouchStart === void 0 ? false : _ref2$delayTouchStart;
|
|
1903
1959
|
config.dropAnimationDurationMs = dropAnimationDurationMs;
|
|
1960
|
+
var effectiveDelayMs = 0;
|
|
1961
|
+
if (delayTouchStartOpt === true) {
|
|
1962
|
+
effectiveDelayMs = DEFAULT_TOUCH_DELAY_MS;
|
|
1963
|
+
} else if (typeof delayTouchStartOpt === "number" && isFinite(delayTouchStartOpt) && delayTouchStartOpt >= 0) {
|
|
1964
|
+
effectiveDelayMs = delayTouchStartOpt;
|
|
1965
|
+
}
|
|
1966
|
+
config.delayTouchStartMs = effectiveDelayMs;
|
|
1904
1967
|
if (config.type && newType !== config.type) {
|
|
1905
1968
|
unregisterDropZone$1(node, config.type);
|
|
1906
1969
|
}
|
|
@@ -2502,7 +2565,7 @@
|
|
|
2502
2565
|
return handles;
|
|
2503
2566
|
}
|
|
2504
2567
|
|
|
2505
|
-
var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "dropAnimationDisabled"];
|
|
2568
|
+
var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "delayTouchStart", "dropAnimationDisabled"];
|
|
2506
2569
|
|
|
2507
2570
|
/**
|
|
2508
2571
|
* A custom action to turn any container to a dnd zone and all of its direct children to draggables
|
|
@@ -2521,6 +2584,8 @@
|
|
|
2521
2584
|
* @property {number} [zoneItemTabIndex] - set the tabindex of the list container items when not dragging
|
|
2522
2585
|
* @property {object} [dropTargetStyle]
|
|
2523
2586
|
* @property {string[]} [dropTargetClasses]
|
|
2587
|
+
* @property {boolean|number} [delayTouchStart] - On touch devices, wait this long before converting the gesture to a drag.
|
|
2588
|
+
* `true` uses the built-in default (80 ms).
|
|
2524
2589
|
* @property {boolean} [dropAnimationDisabled] - cancels the drop animation to place
|
|
2525
2590
|
* @property {function} [transformDraggedElement]
|
|
2526
2591
|
* @param {HTMLElement} node - the element to enhance
|
|
@@ -2574,6 +2639,7 @@
|
|
|
2574
2639
|
options.transformDraggedElement;
|
|
2575
2640
|
options.autoAriaDisabled;
|
|
2576
2641
|
options.centreDraggedOnCursor;
|
|
2642
|
+
var delayTouchStart = options.delayTouchStart;
|
|
2577
2643
|
options.dropAnimationDisabled;
|
|
2578
2644
|
var rest = _objectWithoutProperties(options, _excluded);
|
|
2579
2645
|
/*eslint-enable*/
|
|
@@ -2598,6 +2664,13 @@
|
|
|
2598
2664
|
if (zoneItemTabIndex && !isInt(zoneItemTabIndex)) {
|
|
2599
2665
|
throw new Error("zoneItemTabIndex should be a number but instead it is a ".concat(_typeof(zoneItemTabIndex), ", ").concat(toString(zoneItemTabIndex)));
|
|
2600
2666
|
}
|
|
2667
|
+
if (delayTouchStart !== undefined && delayTouchStart !== false) {
|
|
2668
|
+
var validBoolean = delayTouchStart === true;
|
|
2669
|
+
var validNumber = typeof delayTouchStart === "number" && isFinite(delayTouchStart) && delayTouchStart >= 0;
|
|
2670
|
+
if (!validBoolean && !validNumber) {
|
|
2671
|
+
throw new Error("delayTouchStart should be a boolean (true/false) or a non-negative number but instead it is a ".concat(_typeof(delayTouchStart), ", ").concat(toString(delayTouchStart)));
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2601
2674
|
}
|
|
2602
2675
|
function isInt(value) {
|
|
2603
2676
|
return !isNaN(value) && function (x) {
|
package/dist/index.mjs
CHANGED
|
@@ -1325,6 +1325,7 @@ var DEFAULT_DROP_ZONE_TYPE$1 = "--any--";
|
|
|
1325
1325
|
var MIN_OBSERVATION_INTERVAL_MS = 100;
|
|
1326
1326
|
var DISABLED_OBSERVATION_INTERVAL_MS = 20;
|
|
1327
1327
|
var MIN_MOVEMENT_BEFORE_DRAG_START_PX = 3;
|
|
1328
|
+
var DEFAULT_TOUCH_DELAY_MS = 80;
|
|
1328
1329
|
var DEFAULT_DROP_TARGET_STYLE$1 = {
|
|
1329
1330
|
outline: "rgba(255, 255, 102, 0.7) solid 2px"
|
|
1330
1331
|
};
|
|
@@ -1345,6 +1346,8 @@ var unlockOriginDzMinDimensions;
|
|
|
1345
1346
|
var isDraggedOutsideOfAnyDz = false;
|
|
1346
1347
|
var scheduledForRemovalAfterDrop = [];
|
|
1347
1348
|
var multiScroller;
|
|
1349
|
+
var touchDragHoldTimer;
|
|
1350
|
+
var touchHoldElapsed = false;
|
|
1348
1351
|
|
|
1349
1352
|
// a map from type to a set of drop-zones
|
|
1350
1353
|
var typeToDropZones$1 = new Map();
|
|
@@ -1667,19 +1670,12 @@ function scheduleDZForRemovalAfterDrop(dz, destroy) {
|
|
|
1667
1670
|
}
|
|
1668
1671
|
/* cleanup */
|
|
1669
1672
|
function cleanupPostDrop() {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
scheduledForRemovalAfterDrop.forEach(function (_ref) {
|
|
1677
|
-
var dz = _ref.dz,
|
|
1678
|
-
destroy = _ref.destroy;
|
|
1679
|
-
destroy();
|
|
1680
|
-
dz.remove();
|
|
1681
|
-
});
|
|
1682
|
-
scheduledForRemovalAfterDrop = [];
|
|
1673
|
+
// Remove the temporary elements that were kept in the DOM during the drag
|
|
1674
|
+
if (draggedEl && draggedEl.remove) {
|
|
1675
|
+
draggedEl.remove();
|
|
1676
|
+
}
|
|
1677
|
+
if (originalDragTarget && originalDragTarget.remove) {
|
|
1678
|
+
originalDragTarget.remove();
|
|
1683
1679
|
}
|
|
1684
1680
|
draggedEl = undefined;
|
|
1685
1681
|
originalDragTarget = undefined;
|
|
@@ -1695,6 +1691,23 @@ function cleanupPostDrop() {
|
|
|
1695
1691
|
finalizingPreviousDrag = false;
|
|
1696
1692
|
unlockOriginDzMinDimensions = undefined;
|
|
1697
1693
|
isDraggedOutsideOfAnyDz = false;
|
|
1694
|
+
if (touchDragHoldTimer) {
|
|
1695
|
+
clearTimeout(touchDragHoldTimer);
|
|
1696
|
+
}
|
|
1697
|
+
touchDragHoldTimer = undefined;
|
|
1698
|
+
touchHoldElapsed = false;
|
|
1699
|
+
if (scheduledForRemovalAfterDrop.length) {
|
|
1700
|
+
printDebug(function () {
|
|
1701
|
+
return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop];
|
|
1702
|
+
});
|
|
1703
|
+
scheduledForRemovalAfterDrop.forEach(function (_ref) {
|
|
1704
|
+
var dz = _ref.dz,
|
|
1705
|
+
destroy = _ref.destroy;
|
|
1706
|
+
destroy();
|
|
1707
|
+
dz.remove();
|
|
1708
|
+
});
|
|
1709
|
+
scheduledForRemovalAfterDrop = [];
|
|
1710
|
+
}
|
|
1698
1711
|
}
|
|
1699
1712
|
function dndzone$2(node, options) {
|
|
1700
1713
|
var initialized = false;
|
|
@@ -1709,7 +1722,8 @@ function dndzone$2(node, options) {
|
|
|
1709
1722
|
dropTargetClasses: [],
|
|
1710
1723
|
transformDraggedElement: function transformDraggedElement() {},
|
|
1711
1724
|
centreDraggedOnCursor: false,
|
|
1712
|
-
dropAnimationDisabled: false
|
|
1725
|
+
dropAnimationDisabled: false,
|
|
1726
|
+
delayTouchStartMs: 0
|
|
1713
1727
|
};
|
|
1714
1728
|
printDebug(function () {
|
|
1715
1729
|
return ["dndzone good to go options: ".concat(toString(options), ", config: ").concat(toString(config)), {
|
|
@@ -1737,6 +1751,11 @@ function dndzone$2(node, options) {
|
|
|
1737
1751
|
window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart);
|
|
1738
1752
|
window.removeEventListener("mouseup", handleFalseAlarm);
|
|
1739
1753
|
window.removeEventListener("touchend", handleFalseAlarm);
|
|
1754
|
+
if (touchDragHoldTimer) {
|
|
1755
|
+
clearTimeout(touchDragHoldTimer);
|
|
1756
|
+
touchDragHoldTimer = undefined;
|
|
1757
|
+
touchHoldElapsed = false;
|
|
1758
|
+
}
|
|
1740
1759
|
}
|
|
1741
1760
|
function handleFalseAlarm(e) {
|
|
1742
1761
|
removeMaybeListeners();
|
|
@@ -1755,8 +1774,29 @@ function dndzone$2(node, options) {
|
|
|
1755
1774
|
}
|
|
1756
1775
|
}
|
|
1757
1776
|
function handleMouseMoveMaybeDragStart(e) {
|
|
1777
|
+
var isTouch = !!e.touches;
|
|
1778
|
+
var c = isTouch ? e.touches[0] : e;
|
|
1779
|
+
// If touch drag delay is configured and not elapsed yet, allow scrolling until either
|
|
1780
|
+
// the delay elapses (timer will call handleDragStart) or the user moves significantly,
|
|
1781
|
+
// in which case we cancel the potential drag and let the interaction be a scroll.
|
|
1782
|
+
if (isTouch && config.delayTouchStartMs > 0 && !touchHoldElapsed) {
|
|
1783
|
+
currentMousePosition = {
|
|
1784
|
+
x: c.clientX,
|
|
1785
|
+
y: c.clientY
|
|
1786
|
+
};
|
|
1787
|
+
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) {
|
|
1788
|
+
// User started scrolling, cancel drag attempt.
|
|
1789
|
+
if (touchDragHoldTimer) {
|
|
1790
|
+
clearTimeout(touchDragHoldTimer);
|
|
1791
|
+
touchDragHoldTimer = undefined;
|
|
1792
|
+
}
|
|
1793
|
+
handleFalseAlarm(e);
|
|
1794
|
+
}
|
|
1795
|
+
return; // Do not preventDefault so scrolling works.
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// legacy / post-delay path – block scrolling and maybe start drag
|
|
1758
1799
|
e.preventDefault();
|
|
1759
|
-
var c = e.touches ? e.touches[0] : e;
|
|
1760
1800
|
currentMousePosition = {
|
|
1761
1801
|
x: c.clientX,
|
|
1762
1802
|
y: c.clientY
|
|
@@ -1787,15 +1827,29 @@ function dndzone$2(node, options) {
|
|
|
1787
1827
|
});
|
|
1788
1828
|
return;
|
|
1789
1829
|
}
|
|
1790
|
-
e.
|
|
1830
|
+
var isTouchStart = !!e.touches;
|
|
1831
|
+
var useDelay = isTouchStart && config.delayTouchStartMs > 0;
|
|
1832
|
+
if (!useDelay) {
|
|
1833
|
+
e.preventDefault();
|
|
1834
|
+
}
|
|
1791
1835
|
e.stopPropagation();
|
|
1792
|
-
var c =
|
|
1836
|
+
var c = isTouchStart ? e.touches[0] : e;
|
|
1793
1837
|
dragStartMousePosition = {
|
|
1794
1838
|
x: c.clientX,
|
|
1795
1839
|
y: c.clientY
|
|
1796
1840
|
};
|
|
1797
1841
|
currentMousePosition = _objectSpread2({}, dragStartMousePosition);
|
|
1798
1842
|
originalDragTarget = e.currentTarget;
|
|
1843
|
+
if (useDelay) {
|
|
1844
|
+
touchHoldElapsed = false;
|
|
1845
|
+
touchDragHoldTimer = window.setTimeout(function () {
|
|
1846
|
+
// If the finger is still down and no false-alarm happened
|
|
1847
|
+
if (!originalDragTarget) return;
|
|
1848
|
+
touchHoldElapsed = true;
|
|
1849
|
+
removeMaybeListeners();
|
|
1850
|
+
handleDragStart();
|
|
1851
|
+
}, config.delayTouchStartMs);
|
|
1852
|
+
}
|
|
1799
1853
|
addMaybeListeners();
|
|
1800
1854
|
}
|
|
1801
1855
|
function handleDragStart() {
|
|
@@ -1893,8 +1947,17 @@ function dndzone$2(node, options) {
|
|
|
1893
1947
|
_ref2$centreDraggedOn = _ref2.centreDraggedOnCursor,
|
|
1894
1948
|
centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn,
|
|
1895
1949
|
_ref2$dropAnimationDi = _ref2.dropAnimationDisabled,
|
|
1896
|
-
dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi
|
|
1950
|
+
dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi,
|
|
1951
|
+
_ref2$delayTouchStart = _ref2.delayTouchStart,
|
|
1952
|
+
delayTouchStartOpt = _ref2$delayTouchStart === void 0 ? false : _ref2$delayTouchStart;
|
|
1897
1953
|
config.dropAnimationDurationMs = dropAnimationDurationMs;
|
|
1954
|
+
var effectiveDelayMs = 0;
|
|
1955
|
+
if (delayTouchStartOpt === true) {
|
|
1956
|
+
effectiveDelayMs = DEFAULT_TOUCH_DELAY_MS;
|
|
1957
|
+
} else if (typeof delayTouchStartOpt === "number" && isFinite(delayTouchStartOpt) && delayTouchStartOpt >= 0) {
|
|
1958
|
+
effectiveDelayMs = delayTouchStartOpt;
|
|
1959
|
+
}
|
|
1960
|
+
config.delayTouchStartMs = effectiveDelayMs;
|
|
1898
1961
|
if (config.type && newType !== config.type) {
|
|
1899
1962
|
unregisterDropZone$1(node, config.type);
|
|
1900
1963
|
}
|
|
@@ -2496,7 +2559,7 @@ function dndzone$1(node, options) {
|
|
|
2496
2559
|
return handles;
|
|
2497
2560
|
}
|
|
2498
2561
|
|
|
2499
|
-
var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "dropAnimationDisabled"];
|
|
2562
|
+
var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "delayTouchStart", "dropAnimationDisabled"];
|
|
2500
2563
|
|
|
2501
2564
|
/**
|
|
2502
2565
|
* A custom action to turn any container to a dnd zone and all of its direct children to draggables
|
|
@@ -2515,6 +2578,8 @@ var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabl
|
|
|
2515
2578
|
* @property {number} [zoneItemTabIndex] - set the tabindex of the list container items when not dragging
|
|
2516
2579
|
* @property {object} [dropTargetStyle]
|
|
2517
2580
|
* @property {string[]} [dropTargetClasses]
|
|
2581
|
+
* @property {boolean|number} [delayTouchStart] - On touch devices, wait this long before converting the gesture to a drag.
|
|
2582
|
+
* `true` uses the built-in default (80 ms).
|
|
2518
2583
|
* @property {boolean} [dropAnimationDisabled] - cancels the drop animation to place
|
|
2519
2584
|
* @property {function} [transformDraggedElement]
|
|
2520
2585
|
* @param {HTMLElement} node - the element to enhance
|
|
@@ -2568,6 +2633,7 @@ function validateOptions(options) {
|
|
|
2568
2633
|
options.transformDraggedElement;
|
|
2569
2634
|
options.autoAriaDisabled;
|
|
2570
2635
|
options.centreDraggedOnCursor;
|
|
2636
|
+
var delayTouchStart = options.delayTouchStart;
|
|
2571
2637
|
options.dropAnimationDisabled;
|
|
2572
2638
|
var rest = _objectWithoutProperties(options, _excluded);
|
|
2573
2639
|
/*eslint-enable*/
|
|
@@ -2592,6 +2658,13 @@ function validateOptions(options) {
|
|
|
2592
2658
|
if (zoneItemTabIndex && !isInt(zoneItemTabIndex)) {
|
|
2593
2659
|
throw new Error("zoneItemTabIndex should be a number but instead it is a ".concat(_typeof(zoneItemTabIndex), ", ").concat(toString(zoneItemTabIndex)));
|
|
2594
2660
|
}
|
|
2661
|
+
if (delayTouchStart !== undefined && delayTouchStart !== false) {
|
|
2662
|
+
var validBoolean = delayTouchStart === true;
|
|
2663
|
+
var validNumber = typeof delayTouchStart === "number" && isFinite(delayTouchStart) && delayTouchStart >= 0;
|
|
2664
|
+
if (!validBoolean && !validNumber) {
|
|
2665
|
+
throw new Error("delayTouchStart should be a boolean (true/false) or a non-negative number but instead it is a ".concat(_typeof(delayTouchStart), ", ").concat(toString(delayTouchStart)));
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2595
2668
|
}
|
|
2596
2669
|
function isInt(value) {
|
|
2597
2670
|
return !isNaN(value) && function (x) {
|
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.
|
|
4
|
+
"version": "0.9.63",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/isaacHagoel/svelte-dnd-action.git"
|
package/src/action.js
CHANGED
|
@@ -20,6 +20,8 @@ import {toString} from "./helpers/util";
|
|
|
20
20
|
* @property {number} [zoneItemTabIndex] - set the tabindex of the list container items when not dragging
|
|
21
21
|
* @property {object} [dropTargetStyle]
|
|
22
22
|
* @property {string[]} [dropTargetClasses]
|
|
23
|
+
* @property {boolean|number} [delayTouchStart] - On touch devices, wait this long before converting the gesture to a drag.
|
|
24
|
+
* `true` uses the built-in default (80 ms).
|
|
23
25
|
* @property {boolean} [dropAnimationDisabled] - cancels the drop animation to place
|
|
24
26
|
* @property {function} [transformDraggedElement]
|
|
25
27
|
* @param {HTMLElement} node - the element to enhance
|
|
@@ -75,6 +77,7 @@ function validateOptions(options) {
|
|
|
75
77
|
transformDraggedElement,
|
|
76
78
|
autoAriaDisabled,
|
|
77
79
|
centreDraggedOnCursor,
|
|
80
|
+
delayTouchStart,
|
|
78
81
|
dropAnimationDisabled,
|
|
79
82
|
...rest
|
|
80
83
|
} = options;
|
|
@@ -98,6 +101,17 @@ function validateOptions(options) {
|
|
|
98
101
|
if (zoneItemTabIndex && !isInt(zoneItemTabIndex)) {
|
|
99
102
|
throw new Error(`zoneItemTabIndex should be a number but instead it is a ${typeof zoneItemTabIndex}, ${toString(zoneItemTabIndex)}`);
|
|
100
103
|
}
|
|
104
|
+
if (delayTouchStart !== undefined && delayTouchStart !== false) {
|
|
105
|
+
const validBoolean = delayTouchStart === true;
|
|
106
|
+
const validNumber = typeof delayTouchStart === "number" && isFinite(delayTouchStart) && delayTouchStart >= 0;
|
|
107
|
+
if (!validBoolean && !validNumber) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`delayTouchStart should be a boolean (true/false) or a non-negative number but instead it is a ${typeof delayTouchStart}, ${toString(
|
|
110
|
+
delayTouchStart
|
|
111
|
+
)}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
function isInt(value) {
|
package/src/pointerAction.js
CHANGED
|
@@ -39,6 +39,7 @@ const DEFAULT_DROP_ZONE_TYPE = "--any--";
|
|
|
39
39
|
const MIN_OBSERVATION_INTERVAL_MS = 100;
|
|
40
40
|
const DISABLED_OBSERVATION_INTERVAL_MS = 20;
|
|
41
41
|
const MIN_MOVEMENT_BEFORE_DRAG_START_PX = 3;
|
|
42
|
+
const DEFAULT_TOUCH_DELAY_MS = 80;
|
|
42
43
|
const DEFAULT_DROP_TARGET_STYLE = {
|
|
43
44
|
outline: "rgba(255, 255, 102, 0.7) solid 2px"
|
|
44
45
|
};
|
|
@@ -60,6 +61,8 @@ let unlockOriginDzMinDimensions;
|
|
|
60
61
|
let isDraggedOutsideOfAnyDz = false;
|
|
61
62
|
let scheduledForRemovalAfterDrop = [];
|
|
62
63
|
let multiScroller;
|
|
64
|
+
let touchDragHoldTimer;
|
|
65
|
+
let touchHoldElapsed = false;
|
|
63
66
|
|
|
64
67
|
// a map from type to a set of drop-zones
|
|
65
68
|
const typeToDropZones = new Map();
|
|
@@ -310,16 +313,14 @@ function scheduleDZForRemovalAfterDrop(dz, destroy) {
|
|
|
310
313
|
}
|
|
311
314
|
/* cleanup */
|
|
312
315
|
function cleanupPostDrop() {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
printDebug(() => ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]);
|
|
317
|
-
scheduledForRemovalAfterDrop.forEach(({dz, destroy}) => {
|
|
318
|
-
destroy();
|
|
319
|
-
dz.remove();
|
|
320
|
-
});
|
|
321
|
-
scheduledForRemovalAfterDrop = [];
|
|
316
|
+
// Remove the temporary elements that were kept in the DOM during the drag
|
|
317
|
+
if (draggedEl && draggedEl.remove) {
|
|
318
|
+
draggedEl.remove();
|
|
322
319
|
}
|
|
320
|
+
if (originalDragTarget && originalDragTarget.remove) {
|
|
321
|
+
originalDragTarget.remove();
|
|
322
|
+
}
|
|
323
|
+
|
|
323
324
|
draggedEl = undefined;
|
|
324
325
|
originalDragTarget = undefined;
|
|
325
326
|
draggedElData = undefined;
|
|
@@ -334,6 +335,19 @@ function cleanupPostDrop() {
|
|
|
334
335
|
finalizingPreviousDrag = false;
|
|
335
336
|
unlockOriginDzMinDimensions = undefined;
|
|
336
337
|
isDraggedOutsideOfAnyDz = false;
|
|
338
|
+
if (touchDragHoldTimer) {
|
|
339
|
+
clearTimeout(touchDragHoldTimer);
|
|
340
|
+
}
|
|
341
|
+
touchDragHoldTimer = undefined;
|
|
342
|
+
touchHoldElapsed = false;
|
|
343
|
+
if (scheduledForRemovalAfterDrop.length) {
|
|
344
|
+
printDebug(() => ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]);
|
|
345
|
+
scheduledForRemovalAfterDrop.forEach(({dz, destroy}) => {
|
|
346
|
+
destroy();
|
|
347
|
+
dz.remove();
|
|
348
|
+
});
|
|
349
|
+
scheduledForRemovalAfterDrop = [];
|
|
350
|
+
}
|
|
337
351
|
}
|
|
338
352
|
|
|
339
353
|
export function dndzone(node, options) {
|
|
@@ -349,7 +363,8 @@ export function dndzone(node, options) {
|
|
|
349
363
|
dropTargetClasses: [],
|
|
350
364
|
transformDraggedElement: () => {},
|
|
351
365
|
centreDraggedOnCursor: false,
|
|
352
|
-
dropAnimationDisabled: false
|
|
366
|
+
dropAnimationDisabled: false,
|
|
367
|
+
delayTouchStartMs: 0
|
|
353
368
|
};
|
|
354
369
|
printDebug(() => [`dndzone good to go options: ${toString(options)}, config: ${toString(config)}`, {node}]);
|
|
355
370
|
let elToIdx = new Map();
|
|
@@ -365,6 +380,11 @@ export function dndzone(node, options) {
|
|
|
365
380
|
window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart);
|
|
366
381
|
window.removeEventListener("mouseup", handleFalseAlarm);
|
|
367
382
|
window.removeEventListener("touchend", handleFalseAlarm);
|
|
383
|
+
if (touchDragHoldTimer) {
|
|
384
|
+
clearTimeout(touchDragHoldTimer);
|
|
385
|
+
touchDragHoldTimer = undefined;
|
|
386
|
+
touchHoldElapsed = false;
|
|
387
|
+
}
|
|
368
388
|
}
|
|
369
389
|
function handleFalseAlarm(e) {
|
|
370
390
|
removeMaybeListeners();
|
|
@@ -384,8 +404,30 @@ export function dndzone(node, options) {
|
|
|
384
404
|
}
|
|
385
405
|
|
|
386
406
|
function handleMouseMoveMaybeDragStart(e) {
|
|
407
|
+
const isTouch = !!e.touches;
|
|
408
|
+
const c = isTouch ? e.touches[0] : e;
|
|
409
|
+
// If touch drag delay is configured and not elapsed yet, allow scrolling until either
|
|
410
|
+
// the delay elapses (timer will call handleDragStart) or the user moves significantly,
|
|
411
|
+
// in which case we cancel the potential drag and let the interaction be a scroll.
|
|
412
|
+
if (isTouch && config.delayTouchStartMs > 0 && !touchHoldElapsed) {
|
|
413
|
+
currentMousePosition = {x: c.clientX, y: c.clientY};
|
|
414
|
+
if (
|
|
415
|
+
Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX ||
|
|
416
|
+
Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX
|
|
417
|
+
) {
|
|
418
|
+
// User started scrolling, cancel drag attempt.
|
|
419
|
+
if (touchDragHoldTimer) {
|
|
420
|
+
clearTimeout(touchDragHoldTimer);
|
|
421
|
+
touchDragHoldTimer = undefined;
|
|
422
|
+
}
|
|
423
|
+
handleFalseAlarm(e);
|
|
424
|
+
}
|
|
425
|
+
return; // Do not preventDefault so scrolling works.
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// legacy / post-delay path – block scrolling and maybe start drag
|
|
387
429
|
e.preventDefault();
|
|
388
|
-
|
|
430
|
+
|
|
389
431
|
currentMousePosition = {x: c.clientX, y: c.clientY};
|
|
390
432
|
if (
|
|
391
433
|
Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX ||
|
|
@@ -410,12 +452,30 @@ export function dndzone(node, options) {
|
|
|
410
452
|
printDebug(() => "cannot start a new drag before finalizing previous one");
|
|
411
453
|
return;
|
|
412
454
|
}
|
|
413
|
-
e.
|
|
455
|
+
const isTouchStart = !!e.touches;
|
|
456
|
+
const useDelay = isTouchStart && config.delayTouchStartMs > 0;
|
|
457
|
+
|
|
458
|
+
if (!useDelay) {
|
|
459
|
+
e.preventDefault();
|
|
460
|
+
}
|
|
414
461
|
e.stopPropagation();
|
|
415
|
-
|
|
462
|
+
|
|
463
|
+
const c = isTouchStart ? e.touches[0] : e;
|
|
416
464
|
dragStartMousePosition = {x: c.clientX, y: c.clientY};
|
|
417
465
|
currentMousePosition = {...dragStartMousePosition};
|
|
418
466
|
originalDragTarget = e.currentTarget;
|
|
467
|
+
|
|
468
|
+
if (useDelay) {
|
|
469
|
+
touchHoldElapsed = false;
|
|
470
|
+
touchDragHoldTimer = window.setTimeout(() => {
|
|
471
|
+
// If the finger is still down and no false-alarm happened
|
|
472
|
+
if (!originalDragTarget) return;
|
|
473
|
+
touchHoldElapsed = true;
|
|
474
|
+
removeMaybeListeners();
|
|
475
|
+
handleDragStart();
|
|
476
|
+
}, config.delayTouchStartMs);
|
|
477
|
+
}
|
|
478
|
+
|
|
419
479
|
addMaybeListeners();
|
|
420
480
|
}
|
|
421
481
|
|
|
@@ -487,9 +547,19 @@ export function dndzone(node, options) {
|
|
|
487
547
|
dropTargetClasses = [],
|
|
488
548
|
transformDraggedElement = () => {},
|
|
489
549
|
centreDraggedOnCursor = false,
|
|
490
|
-
dropAnimationDisabled = false
|
|
550
|
+
dropAnimationDisabled = false,
|
|
551
|
+
delayTouchStart: delayTouchStartOpt = false
|
|
491
552
|
}) {
|
|
492
553
|
config.dropAnimationDurationMs = dropAnimationDurationMs;
|
|
554
|
+
|
|
555
|
+
let effectiveDelayMs = 0;
|
|
556
|
+
if (delayTouchStartOpt === true) {
|
|
557
|
+
effectiveDelayMs = DEFAULT_TOUCH_DELAY_MS;
|
|
558
|
+
} else if (typeof delayTouchStartOpt === "number" && isFinite(delayTouchStartOpt) && delayTouchStartOpt >= 0) {
|
|
559
|
+
effectiveDelayMs = delayTouchStartOpt;
|
|
560
|
+
}
|
|
561
|
+
config.delayTouchStartMs = effectiveDelayMs;
|
|
562
|
+
|
|
493
563
|
if (config.type && newType !== config.type) {
|
|
494
564
|
unregisterDropZone(node, config.type);
|
|
495
565
|
}
|