svelte-dnd-action 0.9.45 → 0.9.47

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 CHANGED
@@ -170,6 +170,73 @@ If you want to implement your own custom screen-reader alerts, roles and instruc
170
170
  - Mouse drag and drop can be preformed independently of keyboard dragging (as in an item can be dragged with the mouse while in or out of keyboard initiated dragging-mode)
171
171
  - Keyboard drag uses the same `consider` (only on drag start) and `finalize` (every time the item is moved) events but share only some of the `TRIGGERS`. The same handlers should work fine for both.
172
172
 
173
+ ### Drag Handles Support
174
+
175
+ Due to popular demand, starting in version 0.9.46 the library exports a wrapper action that greatly improves the ergonomics around using drag handles.
176
+ Notes:
177
+
178
+ - A draggable item within a `dragHandleZone` would not be draggable unless it has an element that uses the `dragHandle` action inside (doesn't have to be a direct child but has to be inside the bounding rect of the item).
179
+ - Don't forget an aria-label on the handle
180
+ Usage:
181
+
182
+ ```html
183
+ <script>
184
+ import {dragHandleZone, dragHandle} from "svelte-dnd-action";
185
+ import {flip} from "svelte/animate";
186
+
187
+ let items = [
188
+ {
189
+ id: 1,
190
+ text: "Item 1"
191
+ },
192
+ {
193
+ id: 2,
194
+ text: "Item 2"
195
+ },
196
+ {
197
+ id: 3,
198
+ text: "Item 3"
199
+ }
200
+ ];
201
+ const flipDurationMs = 100;
202
+
203
+ function handleSort(e) {
204
+ items = e.detail.items;
205
+ }
206
+ </script>
207
+
208
+ <style>
209
+ div {
210
+ position: relative;
211
+ height: 1.5em;
212
+ width: 10em;
213
+ text-align: center;
214
+ border: 1px solid black;
215
+ margin: 0.2em;
216
+ padding: 0.3em;
217
+ }
218
+ .handle {
219
+ position: absolute;
220
+ right: 0;
221
+ width: 1em;
222
+ height: 0.5em;
223
+ background-color: grey;
224
+ }
225
+ </style>
226
+
227
+ <h3>Drag Handles</h3>
228
+ <p>Items can be dragged using the grey handles via mouse, touch or keyboard. The text on the items can be selected without starting a drag</p>
229
+ <hr />
230
+ <section use:dragHandleZone="{{ items, flipDurationMs }}" on:consider="{handleSort}" on:finalize="{handleSort}">
231
+ {#each items as item (item.id)}
232
+ <div animate:flip="{{ duration: flipDurationMs }}">
233
+ <div use:dragHandle aria-label="drag-handle for {item.text}" class="handle" />
234
+ <span>{item.text}</span>
235
+ </div>
236
+ {/each}
237
+ </section>
238
+ ```
239
+
173
240
  ### Examples and Recipes
174
241
 
175
242
  - [Super basic, single list, no animation](https://svelte.dev/repl/bbd709b1a00b453e94658392c97a018a?version=3)
@@ -184,7 +251,8 @@ If you want to implement your own custom screen-reader alerts, roles and instruc
184
251
 
185
252
  - [Copy on drag, simple and Dragula like](https://svelte.dev/repl/924b4cc920524065a637fa910fe10193?version=3)
186
253
  - [Copy on drop and a drop area with a single slot](https://svelte.dev/repl/b4e120c45c3e48e49a0d637f0cf097d9?version=3)
187
- - [Drag handles](https://svelte.dev/repl/4949485c5a8f46e7bdbeb73ed565a9c7?version=3), courtesy of @gleuch
254
+ - [Drag handles using wrapper actions](https://svelte.dev/repl/cc1bc63be7a74830b4c97d428f62054d?version=4.2.17), for nested scenarios (same usage, it "just works"), see [here](https://svelte.dev/repl/4f7cbeb7b11b470b948e9af03b82a073?version=4.2.17) and [here](https://svelte.dev/repl/47c5f52f4c774cad8c367516395c7f99?version=4.2.17)
255
+ - [Drag handles - legacy](https://svelte.dev/repl/4949485c5a8f46e7bdbeb73ed565a9c7?version=3), use before version 0.9.46, courtesy of @gleuch
188
256
  - [Interaction (save/get items) with an asynchronous server](https://svelte.dev/repl/964fdac31cb9496da9ded35002300abb?version=3)
189
257
  - [Unsortable lists with custom aria instructions](https://svelte.dev/repl/e020ea1051dc4ae3ac2b697064f234bc?version=3)
190
258
  - [Crazy nesting](https://svelte.dev/repl/fe8c9eca04f9417a94a8b6041df77139?version=3), courtesy of @zahachtah
package/dist/index.d.ts CHANGED
@@ -16,6 +16,27 @@ export declare function dndzone<T extends Item>(
16
16
  destroy: () => void;
17
17
  };
18
18
 
19
+ /**
20
+ * A wrapper action to make it easy to work with drag handles.
21
+ * When using this you must also use the 'dragHandle' action on an element inside each item within the zone.
22
+ */
23
+ export declare function dragHandleZone<T extends Item>(node: HTMLElement, options: Options<T>): ActionReturn<Options<T>, DndZoneAttributes<T>>;
24
+ export declare function dragHandleZone<T extends Item>(
25
+ node: HTMLElement,
26
+ options: Options<T>
27
+ ): {
28
+ update: (newOptions: Options<T>) => void;
29
+ destroy: () => void;
30
+ };
31
+
32
+ /**
33
+ * This should be used to mark drag handles inside items that belong to a 'dragHandleZone'
34
+ */
35
+ export declare function dragHandle(node: HTMLElement): {
36
+ update: () => void;
37
+ destroy: () => void;
38
+ };
39
+
19
40
  export type TransformDraggedElementFunction = (
20
41
  element?: HTMLElement, // the dragged element.
21
42
  draggedElementData?: Item, // the data of the item from the items array
package/dist/index.js CHANGED
@@ -2524,6 +2524,117 @@
2524
2524
  }(parseFloat(value));
2525
2525
  }
2526
2526
 
2527
+ function createStore(initialValue) {
2528
+ var _val = initialValue;
2529
+ var subs = new Set();
2530
+ return {
2531
+ get: function get() {
2532
+ return _val;
2533
+ },
2534
+ set: function set(newVal) {
2535
+ _val = newVal;
2536
+ Array.from(subs).forEach(function (cb) {
2537
+ return cb(_val);
2538
+ });
2539
+ },
2540
+ subscribe: function subscribe(cb) {
2541
+ subs.add(cb);
2542
+ cb(_val);
2543
+ },
2544
+ unsubscribe: function unsubscribe(cb) {
2545
+ subs["delete"](cb);
2546
+ }
2547
+ };
2548
+ }
2549
+
2550
+ var isItemsDragDisabled = createStore(true);
2551
+ function getAddedOptions() {
2552
+ var isItemsDragDisabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
2553
+ return {
2554
+ dragDisabled: isItemsDragDisabled,
2555
+ zoneItemTabIndex: -1
2556
+ };
2557
+ }
2558
+
2559
+ /**
2560
+ * This is an action that wraps around the dndzone action to make it easy to work with drag handles
2561
+ * When using this you must also use the 'dragHandle' action (see below) on an element inside each item within the zone
2562
+ * Credit for the idea and initial implementation goes to @gleuch (Greg Leuch) and @geovie (Georg Vienna)
2563
+ *
2564
+ * @param {HTMLElement} node
2565
+ * @param options - will be passed down to the dndzone
2566
+ * @return {{update: (newOptions: Object) => {}, destroy: () => {}}}
2567
+ */
2568
+ function dragHandleZone(node, options) {
2569
+ var currentOptions = options;
2570
+ var zone = dndzone$2(node, _objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions()));
2571
+ function isItemDisabledCB(isItemsDragDisabled) {
2572
+ zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions(isItemsDragDisabled)));
2573
+ }
2574
+ isItemsDragDisabled.subscribe(isItemDisabledCB);
2575
+ function consider(e) {
2576
+ var _e$detail$info = e.detail.info,
2577
+ source = _e$detail$info.source,
2578
+ trigger = _e$detail$info.trigger;
2579
+ // Ensure dragging is stopped on drag finish via keyboard
2580
+ if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED) {
2581
+ isItemsDragDisabled.set(true);
2582
+ }
2583
+ }
2584
+ function finalize(e) {
2585
+ var source = e.detail.info.source;
2586
+ // Ensure dragging is stopped on drag finish via pointer (mouse, touch)
2587
+ if (source === SOURCES.POINTER) {
2588
+ isItemsDragDisabled.set(true);
2589
+ }
2590
+ }
2591
+ node.addEventListener("consider", consider);
2592
+ node.addEventListener("finalize", finalize);
2593
+ return {
2594
+ update: function update(newOptions) {
2595
+ currentOptions = newOptions;
2596
+ zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions(isItemsDragDisabled.get())));
2597
+ },
2598
+ destroy: function destroy() {
2599
+ node.removeEventListener("consider", consider);
2600
+ node.removeEventListener("finalize", finalize);
2601
+ isItemsDragDisabled.unsubscribe(isItemDisabledCB);
2602
+ }
2603
+ };
2604
+ }
2605
+
2606
+ /**
2607
+ * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' (see above)
2608
+ * @param {HTMLElement} handle
2609
+ * @return {{update: *, destroy: *}}
2610
+ */
2611
+ function dragHandle(handle) {
2612
+ handle.setAttribute("role", "button");
2613
+ function startDrag(e) {
2614
+ // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling)
2615
+ e.preventDefault();
2616
+ isItemsDragDisabled.set(false);
2617
+ }
2618
+ function handleKeyDown(e) {
2619
+ if (e.key === "Enter" || e.key === " ") isItemsDragDisabled.set(false);
2620
+ }
2621
+ isItemsDragDisabled.subscribe(function (disabled) {
2622
+ handle.tabIndex = disabled ? 0 : -1;
2623
+ handle.style.cursor = disabled ? "grab" : "grabbing";
2624
+ });
2625
+ handle.addEventListener("mousedown", startDrag);
2626
+ handle.addEventListener("touchstart", startDrag);
2627
+ handle.addEventListener("keydown", handleKeyDown);
2628
+ return {
2629
+ update: function update() {},
2630
+ destroy: function destroy() {
2631
+ handle.removeEventListener("mousedown", startDrag);
2632
+ handle.removeEventListener("touchstart", startDrag);
2633
+ handle.removeEventListener("keydown", handleKeyDown);
2634
+ }
2635
+ };
2636
+ }
2637
+
2527
2638
  exports.DRAGGED_ELEMENT_ID = DRAGGED_ELEMENT_ID;
2528
2639
  exports.FEATURE_FLAG_NAMES = FEATURE_FLAG_NAMES;
2529
2640
  exports.SHADOW_ITEM_MARKER_PROPERTY_NAME = SHADOW_ITEM_MARKER_PROPERTY_NAME;
@@ -2532,6 +2643,8 @@
2532
2643
  exports.TRIGGERS = TRIGGERS;
2533
2644
  exports.alertToScreenReader = alertToScreenReader;
2534
2645
  exports.dndzone = dndzone$2;
2646
+ exports.dragHandle = dragHandle;
2647
+ exports.dragHandleZone = dragHandleZone;
2535
2648
  exports.overrideItemIdKeyNameBeforeInitialisingDndZones = overrideItemIdKeyNameBeforeInitialisingDndZones;
2536
2649
  exports.setDebugMode = setDebugMode;
2537
2650
  exports.setFeatureFlag = setFeatureFlag;
package/dist/index.mjs CHANGED
@@ -2518,4 +2518,115 @@ function isInt(value) {
2518
2518
  }(parseFloat(value));
2519
2519
  }
2520
2520
 
2521
- export { DRAGGED_ELEMENT_ID, FEATURE_FLAG_NAMES, SHADOW_ITEM_MARKER_PROPERTY_NAME, SHADOW_PLACEHOLDER_ITEM_ID, SOURCES, TRIGGERS, alertToScreenReader, dndzone$2 as dndzone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode, setFeatureFlag };
2521
+ function createStore(initialValue) {
2522
+ var _val = initialValue;
2523
+ var subs = new Set();
2524
+ return {
2525
+ get: function get() {
2526
+ return _val;
2527
+ },
2528
+ set: function set(newVal) {
2529
+ _val = newVal;
2530
+ Array.from(subs).forEach(function (cb) {
2531
+ return cb(_val);
2532
+ });
2533
+ },
2534
+ subscribe: function subscribe(cb) {
2535
+ subs.add(cb);
2536
+ cb(_val);
2537
+ },
2538
+ unsubscribe: function unsubscribe(cb) {
2539
+ subs["delete"](cb);
2540
+ }
2541
+ };
2542
+ }
2543
+
2544
+ var isItemsDragDisabled = createStore(true);
2545
+ function getAddedOptions() {
2546
+ var isItemsDragDisabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
2547
+ return {
2548
+ dragDisabled: isItemsDragDisabled,
2549
+ zoneItemTabIndex: -1
2550
+ };
2551
+ }
2552
+
2553
+ /**
2554
+ * This is an action that wraps around the dndzone action to make it easy to work with drag handles
2555
+ * When using this you must also use the 'dragHandle' action (see below) on an element inside each item within the zone
2556
+ * Credit for the idea and initial implementation goes to @gleuch (Greg Leuch) and @geovie (Georg Vienna)
2557
+ *
2558
+ * @param {HTMLElement} node
2559
+ * @param options - will be passed down to the dndzone
2560
+ * @return {{update: (newOptions: Object) => {}, destroy: () => {}}}
2561
+ */
2562
+ function dragHandleZone(node, options) {
2563
+ var currentOptions = options;
2564
+ var zone = dndzone$2(node, _objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions()));
2565
+ function isItemDisabledCB(isItemsDragDisabled) {
2566
+ zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions(isItemsDragDisabled)));
2567
+ }
2568
+ isItemsDragDisabled.subscribe(isItemDisabledCB);
2569
+ function consider(e) {
2570
+ var _e$detail$info = e.detail.info,
2571
+ source = _e$detail$info.source,
2572
+ trigger = _e$detail$info.trigger;
2573
+ // Ensure dragging is stopped on drag finish via keyboard
2574
+ if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED) {
2575
+ isItemsDragDisabled.set(true);
2576
+ }
2577
+ }
2578
+ function finalize(e) {
2579
+ var source = e.detail.info.source;
2580
+ // Ensure dragging is stopped on drag finish via pointer (mouse, touch)
2581
+ if (source === SOURCES.POINTER) {
2582
+ isItemsDragDisabled.set(true);
2583
+ }
2584
+ }
2585
+ node.addEventListener("consider", consider);
2586
+ node.addEventListener("finalize", finalize);
2587
+ return {
2588
+ update: function update(newOptions) {
2589
+ currentOptions = newOptions;
2590
+ zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions(isItemsDragDisabled.get())));
2591
+ },
2592
+ destroy: function destroy() {
2593
+ node.removeEventListener("consider", consider);
2594
+ node.removeEventListener("finalize", finalize);
2595
+ isItemsDragDisabled.unsubscribe(isItemDisabledCB);
2596
+ }
2597
+ };
2598
+ }
2599
+
2600
+ /**
2601
+ * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' (see above)
2602
+ * @param {HTMLElement} handle
2603
+ * @return {{update: *, destroy: *}}
2604
+ */
2605
+ function dragHandle(handle) {
2606
+ handle.setAttribute("role", "button");
2607
+ function startDrag(e) {
2608
+ // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling)
2609
+ e.preventDefault();
2610
+ isItemsDragDisabled.set(false);
2611
+ }
2612
+ function handleKeyDown(e) {
2613
+ if (e.key === "Enter" || e.key === " ") isItemsDragDisabled.set(false);
2614
+ }
2615
+ isItemsDragDisabled.subscribe(function (disabled) {
2616
+ handle.tabIndex = disabled ? 0 : -1;
2617
+ handle.style.cursor = disabled ? "grab" : "grabbing";
2618
+ });
2619
+ handle.addEventListener("mousedown", startDrag);
2620
+ handle.addEventListener("touchstart", startDrag);
2621
+ handle.addEventListener("keydown", handleKeyDown);
2622
+ return {
2623
+ update: function update() {},
2624
+ destroy: function destroy() {
2625
+ handle.removeEventListener("mousedown", startDrag);
2626
+ handle.removeEventListener("touchstart", startDrag);
2627
+ handle.removeEventListener("keydown", handleKeyDown);
2628
+ }
2629
+ };
2630
+ }
2631
+
2632
+ export { DRAGGED_ELEMENT_ID, FEATURE_FLAG_NAMES, SHADOW_ITEM_MARKER_PROPERTY_NAME, SHADOW_PLACEHOLDER_ITEM_ID, SOURCES, TRIGGERS, alertToScreenReader, dndzone$2 as dndzone, dragHandle, dragHandleZone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode, setFeatureFlag };
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.45",
4
+ "version": "0.9.47",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/isaacHagoel/svelte-dnd-action.git"
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export {dndzone} from "./action.js";
2
+ export {dragHandleZone, dragHandle} from "./wrappers/withDragHandles";
2
3
  export {alertToScreenReader} from "./helpers/aria";
3
4
  export {
4
5
  TRIGGERS,
@@ -0,0 +1,18 @@
1
+ export function createStore(initialValue) {
2
+ let _val = initialValue;
3
+ const subs = new Set();
4
+ return {
5
+ get: () => _val,
6
+ set: newVal => {
7
+ _val = newVal;
8
+ Array.from(subs).forEach(cb => cb(_val));
9
+ },
10
+ subscribe: cb => {
11
+ subs.add(cb);
12
+ cb(_val);
13
+ },
14
+ unsubscribe: cb => {
15
+ subs.delete(cb);
16
+ }
17
+ };
18
+ }
@@ -0,0 +1,108 @@
1
+ import {SOURCES, TRIGGERS} from "../constants";
2
+ import {dndzone} from "../action";
3
+ import {createStore} from "./simpleStore";
4
+
5
+ const isItemsDragDisabled = createStore(true);
6
+
7
+ function getAddedOptions(isItemsDragDisabled = true) {
8
+ return {
9
+ dragDisabled: isItemsDragDisabled,
10
+ zoneItemTabIndex: -1
11
+ };
12
+ }
13
+
14
+ /**
15
+ * This is an action that wraps around the dndzone action to make it easy to work with drag handles
16
+ * When using this you must also use the 'dragHandle' action (see below) on an element inside each item within the zone
17
+ * Credit for the idea and initial implementation goes to @gleuch (Greg Leuch) and @geovie (Georg Vienna)
18
+ *
19
+ * @param {HTMLElement} node
20
+ * @param options - will be passed down to the dndzone
21
+ * @return {{update: (newOptions: Object) => {}, destroy: () => {}}}
22
+ */
23
+ export function dragHandleZone(node, options) {
24
+ let currentOptions = options;
25
+ const zone = dndzone(node, {
26
+ ...currentOptions,
27
+ ...getAddedOptions()
28
+ });
29
+ function isItemDisabledCB(isItemsDragDisabled) {
30
+ zone.update({
31
+ ...currentOptions,
32
+ ...getAddedOptions(isItemsDragDisabled)
33
+ });
34
+ }
35
+ isItemsDragDisabled.subscribe(isItemDisabledCB);
36
+ function consider(e) {
37
+ const {
38
+ info: {source, trigger}
39
+ } = e.detail;
40
+ // Ensure dragging is stopped on drag finish via keyboard
41
+ if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED) {
42
+ isItemsDragDisabled.set(true);
43
+ }
44
+ }
45
+
46
+ function finalize(e) {
47
+ const {
48
+ info: {source}
49
+ } = e.detail;
50
+ // Ensure dragging is stopped on drag finish via pointer (mouse, touch)
51
+ if (source === SOURCES.POINTER) {
52
+ isItemsDragDisabled.set(true);
53
+ }
54
+ }
55
+
56
+ node.addEventListener("consider", consider);
57
+ node.addEventListener("finalize", finalize);
58
+
59
+ return {
60
+ update: newOptions => {
61
+ currentOptions = newOptions;
62
+ zone.update({
63
+ ...currentOptions,
64
+ ...getAddedOptions(isItemsDragDisabled.get())
65
+ });
66
+ },
67
+ destroy: () => {
68
+ node.removeEventListener("consider", consider);
69
+ node.removeEventListener("finalize", finalize);
70
+ isItemsDragDisabled.unsubscribe(isItemDisabledCB);
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' (see above)
77
+ * @param {HTMLElement} handle
78
+ * @return {{update: *, destroy: *}}
79
+ */
80
+ export function dragHandle(handle) {
81
+ handle.setAttribute("role", "button");
82
+
83
+ function startDrag(e) {
84
+ // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling)
85
+ e.preventDefault();
86
+ isItemsDragDisabled.set(false);
87
+ }
88
+ function handleKeyDown(e) {
89
+ if (e.key === "Enter" || e.key === " ") isItemsDragDisabled.set(false);
90
+ }
91
+
92
+ isItemsDragDisabled.subscribe(disabled => {
93
+ handle.tabIndex = disabled ? 0 : -1;
94
+ handle.style.cursor = disabled ? "grab" : "grabbing";
95
+ });
96
+
97
+ handle.addEventListener("mousedown", startDrag);
98
+ handle.addEventListener("touchstart", startDrag);
99
+ handle.addEventListener("keydown", handleKeyDown);
100
+ return {
101
+ update: () => {},
102
+ destroy: () => {
103
+ handle.removeEventListener("mousedown", startDrag);
104
+ handle.removeEventListener("touchstart", startDrag);
105
+ handle.removeEventListener("keydown", handleKeyDown);
106
+ }
107
+ };
108
+ }