@react-aria/dnd 3.9.3 → 3.10.0
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/DragManager.main.js +79 -12
- package/dist/DragManager.main.js.map +1 -1
- package/dist/DragManager.mjs +79 -12
- package/dist/DragManager.module.js +79 -12
- package/dist/DragManager.module.js.map +1 -1
- package/dist/DropTargetKeyboardNavigation.main.js +201 -0
- package/dist/DropTargetKeyboardNavigation.main.js.map +1 -0
- package/dist/DropTargetKeyboardNavigation.mjs +196 -0
- package/dist/DropTargetKeyboardNavigation.module.js +196 -0
- package/dist/DropTargetKeyboardNavigation.module.js.map +1 -0
- package/dist/ListDropTargetDelegate.main.js.map +1 -1
- package/dist/ListDropTargetDelegate.module.js.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/useDrop.main.js.map +1 -1
- package/dist/useDrop.module.js.map +1 -1
- package/dist/useDropIndicator.main.js +15 -7
- package/dist/useDropIndicator.main.js.map +1 -1
- package/dist/useDropIndicator.mjs +15 -7
- package/dist/useDropIndicator.module.js +15 -7
- package/dist/useDropIndicator.module.js.map +1 -1
- package/dist/useDroppableCollection.main.js +23 -98
- package/dist/useDroppableCollection.main.js.map +1 -1
- package/dist/useDroppableCollection.mjs +23 -98
- package/dist/useDroppableCollection.module.js +23 -98
- package/dist/useDroppableCollection.module.js.map +1 -1
- package/dist/useDroppableItem.main.js +4 -2
- package/dist/useDroppableItem.main.js.map +1 -1
- package/dist/useDroppableItem.mjs +4 -2
- package/dist/useDroppableItem.module.js +4 -2
- package/dist/useDroppableItem.module.js.map +1 -1
- package/package.json +17 -12
- package/src/.DS_Store +0 -0
- package/src/DragManager.ts +66 -17
- package/src/DropTargetKeyboardNavigation.ts +273 -0
- package/src/ListDropTargetDelegate.ts +1 -1
- package/src/useDrop.ts +0 -1
- package/src/useDropIndicator.ts +17 -11
- package/src/useDroppableCollection.ts +23 -123
- package/src/useDroppableItem.ts +7 -4
|
@@ -37,6 +37,7 @@ import * as DragManager from './DragManager';
|
|
|
37
37
|
import {DroppableCollectionState} from '@react-stately/dnd';
|
|
38
38
|
import {HTMLAttributes, useCallback, useEffect, useRef} from 'react';
|
|
39
39
|
import {mergeProps, useId, useLayoutEffect} from '@react-aria/utils';
|
|
40
|
+
import {navigate} from './DropTargetKeyboardNavigation';
|
|
40
41
|
import {setInteractionModality} from '@react-aria/interactions';
|
|
41
42
|
import {useAutoScroll} from './useAutoScroll';
|
|
42
43
|
import {useDrop} from './useDrop';
|
|
@@ -46,7 +47,9 @@ export interface DroppableCollectionOptions extends DroppableCollectionProps {
|
|
|
46
47
|
/** A delegate object that implements behavior for keyboard focus movement. */
|
|
47
48
|
keyboardDelegate: KeyboardDelegate,
|
|
48
49
|
/** A delegate object that provides drop targets for pointer coordinates within the collection. */
|
|
49
|
-
dropTargetDelegate: DropTargetDelegate
|
|
50
|
+
dropTargetDelegate: DropTargetDelegate,
|
|
51
|
+
/** A custom keyboard event handler for drop targets. */
|
|
52
|
+
onKeyDown?: (e: KeyboardEvent) => void
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export interface DroppableCollectionResult {
|
|
@@ -64,9 +67,6 @@ interface DroppingState {
|
|
|
64
67
|
timeout: ReturnType<typeof setTimeout> | undefined
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
const DROP_POSITIONS: DropPosition[] = ['before', 'on', 'after'];
|
|
68
|
-
const DROP_POSITIONS_RTL: DropPosition[] = ['after', 'on', 'before'];
|
|
69
|
-
|
|
70
70
|
/**
|
|
71
71
|
* Handles drop interactions for a collection component, with support for traditional mouse and touch
|
|
72
72
|
* based drag and drop, in addition to full parity for keyboard and screen reader users.
|
|
@@ -92,6 +92,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
92
92
|
onRootDrop,
|
|
93
93
|
onItemDrop,
|
|
94
94
|
onReorder,
|
|
95
|
+
onMove,
|
|
95
96
|
acceptedDragTypes = 'all',
|
|
96
97
|
shouldAcceptItemDrop
|
|
97
98
|
} = localState.props;
|
|
@@ -137,6 +138,10 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
137
138
|
await onItemDrop({items: filteredItems, dropOperation, isInternal, target});
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
if (onMove && isInternal) {
|
|
142
|
+
await onMove({keys: draggingKeys, dropOperation, target});
|
|
143
|
+
}
|
|
144
|
+
|
|
140
145
|
if (target.dropPosition !== 'on') {
|
|
141
146
|
if (!isInternal && onInsert) {
|
|
142
147
|
await onInsert({items: filteredItems, dropOperation, target});
|
|
@@ -201,7 +206,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
201
206
|
autoScroll.stop();
|
|
202
207
|
},
|
|
203
208
|
onDropActivate(e) {
|
|
204
|
-
if (state.target?.type === 'item' &&
|
|
209
|
+
if (state.target?.type === 'item' && typeof props.onDropActivate === 'function') {
|
|
205
210
|
props.onDropActivate({
|
|
206
211
|
type: 'dropactivate',
|
|
207
212
|
x: e.x, // todo
|
|
@@ -364,118 +369,12 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
364
369
|
return;
|
|
365
370
|
}
|
|
366
371
|
|
|
367
|
-
let getNextTarget = (target: DropTarget | null | undefined, wrap = true,
|
|
368
|
-
|
|
369
|
-
return {
|
|
370
|
-
type: 'root'
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
let {keyboardDelegate} = localState.props;
|
|
375
|
-
let nextKey: Key | null | undefined;
|
|
376
|
-
if (target?.type === 'item') {
|
|
377
|
-
nextKey = horizontal ? keyboardDelegate.getKeyRightOf?.(target.key) : keyboardDelegate.getKeyBelow?.(target.key);
|
|
378
|
-
} else {
|
|
379
|
-
nextKey = horizontal && direction === 'rtl' ? keyboardDelegate.getLastKey?.() : keyboardDelegate.getFirstKey?.();
|
|
380
|
-
}
|
|
381
|
-
let dropPositions = horizontal && direction === 'rtl' ? DROP_POSITIONS_RTL : DROP_POSITIONS;
|
|
382
|
-
let dropPosition: DropPosition = dropPositions[0];
|
|
383
|
-
|
|
384
|
-
if (target.type === 'item') {
|
|
385
|
-
// If the the keyboard delegate returned the next key in the collection,
|
|
386
|
-
// first try the other positions in the current key. Otherwise (e.g. in a grid layout),
|
|
387
|
-
// jump to the same drop position in the new key.
|
|
388
|
-
let nextCollectionKey = horizontal && direction === 'rtl' ? localState.state.collection.getKeyBefore(target.key) : localState.state.collection.getKeyAfter(target.key);
|
|
389
|
-
if (nextKey == null || nextKey === nextCollectionKey) {
|
|
390
|
-
let positionIndex = dropPositions.indexOf(target.dropPosition);
|
|
391
|
-
let nextDropPosition = dropPositions[positionIndex + 1];
|
|
392
|
-
if (positionIndex < dropPositions.length - 1 && !(nextDropPosition === dropPositions[2] && nextKey != null)) {
|
|
393
|
-
return {
|
|
394
|
-
type: 'item',
|
|
395
|
-
key: target.key,
|
|
396
|
-
dropPosition: nextDropPosition
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// If the last drop position was 'after', then 'before' on the next key is equivalent.
|
|
401
|
-
// Switch to 'on' instead.
|
|
402
|
-
if (target.dropPosition === dropPositions[2]) {
|
|
403
|
-
dropPosition = 'on';
|
|
404
|
-
}
|
|
405
|
-
} else {
|
|
406
|
-
dropPosition = target.dropPosition;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (nextKey == null) {
|
|
411
|
-
if (wrap) {
|
|
412
|
-
return {
|
|
413
|
-
type: 'root'
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return {
|
|
421
|
-
type: 'item',
|
|
422
|
-
key: nextKey,
|
|
423
|
-
dropPosition
|
|
424
|
-
};
|
|
372
|
+
let getNextTarget = (target: DropTarget | null | undefined, wrap = true, key: 'left' | 'right' | 'up' | 'down' = 'down') => {
|
|
373
|
+
return navigate(localState.props.keyboardDelegate, localState.state.collection, target, key, direction === 'rtl', wrap);
|
|
425
374
|
};
|
|
426
375
|
|
|
427
|
-
let getPreviousTarget = (target: DropTarget | null | undefined, wrap = true
|
|
428
|
-
|
|
429
|
-
let nextKey: Key | null | undefined;
|
|
430
|
-
if (target?.type === 'item') {
|
|
431
|
-
nextKey = horizontal ? keyboardDelegate.getKeyLeftOf?.(target.key) : keyboardDelegate.getKeyAbove?.(target.key);
|
|
432
|
-
} else {
|
|
433
|
-
nextKey = horizontal && direction === 'rtl' ? keyboardDelegate.getFirstKey?.() : keyboardDelegate.getLastKey?.();
|
|
434
|
-
}
|
|
435
|
-
let dropPositions = horizontal && direction === 'rtl' ? DROP_POSITIONS_RTL : DROP_POSITIONS;
|
|
436
|
-
let dropPosition: DropPosition = !target || target.type === 'root' ? dropPositions[2] : 'on';
|
|
437
|
-
|
|
438
|
-
if (target?.type === 'item') {
|
|
439
|
-
// If the the keyboard delegate returned the previous key in the collection,
|
|
440
|
-
// first try the other positions in the current key. Otherwise (e.g. in a grid layout),
|
|
441
|
-
// jump to the same drop position in the new key.
|
|
442
|
-
let prevCollectionKey = horizontal && direction === 'rtl' ? localState.state.collection.getKeyAfter(target.key) : localState.state.collection.getKeyBefore(target.key);
|
|
443
|
-
if (nextKey == null || nextKey === prevCollectionKey) {
|
|
444
|
-
let positionIndex = dropPositions.indexOf(target.dropPosition);
|
|
445
|
-
let nextDropPosition = dropPositions[positionIndex - 1];
|
|
446
|
-
if (positionIndex > 0 && nextDropPosition !== dropPositions[2]) {
|
|
447
|
-
return {
|
|
448
|
-
type: 'item',
|
|
449
|
-
key: target.key,
|
|
450
|
-
dropPosition: nextDropPosition
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// If the last drop position was 'before', then 'after' on the previous key is equivalent.
|
|
455
|
-
// Switch to 'on' instead.
|
|
456
|
-
if (target.dropPosition === dropPositions[0]) {
|
|
457
|
-
dropPosition = 'on';
|
|
458
|
-
}
|
|
459
|
-
} else {
|
|
460
|
-
dropPosition = target.dropPosition;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (nextKey == null) {
|
|
465
|
-
if (wrap) {
|
|
466
|
-
return {
|
|
467
|
-
type: 'root'
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return {
|
|
475
|
-
type: 'item',
|
|
476
|
-
key: nextKey,
|
|
477
|
-
dropPosition
|
|
478
|
-
};
|
|
376
|
+
let getPreviousTarget = (target: DropTarget | null | undefined, wrap = true) => {
|
|
377
|
+
return getNextTarget(target, wrap, 'up');
|
|
479
378
|
};
|
|
480
379
|
|
|
481
380
|
let nextValidTarget = (
|
|
@@ -588,17 +487,17 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
588
487
|
onDropTargetEnter(target) {
|
|
589
488
|
localState.state.setTarget(target);
|
|
590
489
|
},
|
|
591
|
-
onDropActivate(e) {
|
|
490
|
+
onDropActivate(e, target) {
|
|
592
491
|
if (
|
|
593
|
-
|
|
594
|
-
|
|
492
|
+
target?.type === 'item' &&
|
|
493
|
+
target?.dropPosition === 'on' &&
|
|
595
494
|
typeof localState.props.onDropActivate === 'function'
|
|
596
495
|
) {
|
|
597
496
|
localState.props.onDropActivate({
|
|
598
497
|
type: 'dropactivate',
|
|
599
498
|
x: e.x, // todo
|
|
600
499
|
y: e.y,
|
|
601
|
-
target
|
|
500
|
+
target
|
|
602
501
|
});
|
|
603
502
|
}
|
|
604
503
|
},
|
|
@@ -614,28 +513,28 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
614
513
|
switch (e.key) {
|
|
615
514
|
case 'ArrowDown': {
|
|
616
515
|
if (keyboardDelegate.getKeyBelow) {
|
|
617
|
-
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, getNextTarget);
|
|
516
|
+
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) => getNextTarget(target, wrap, 'down'));
|
|
618
517
|
localState.state.setTarget(target);
|
|
619
518
|
}
|
|
620
519
|
break;
|
|
621
520
|
}
|
|
622
521
|
case 'ArrowUp': {
|
|
623
522
|
if (keyboardDelegate.getKeyAbove) {
|
|
624
|
-
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations,
|
|
523
|
+
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) => getNextTarget(target, wrap, 'up'));
|
|
625
524
|
localState.state.setTarget(target);
|
|
626
525
|
}
|
|
627
526
|
break;
|
|
628
527
|
}
|
|
629
528
|
case 'ArrowLeft': {
|
|
630
529
|
if (keyboardDelegate.getKeyLeftOf) {
|
|
631
|
-
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) =>
|
|
530
|
+
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) => getNextTarget(target, wrap, 'left'));
|
|
632
531
|
localState.state.setTarget(target);
|
|
633
532
|
}
|
|
634
533
|
break;
|
|
635
534
|
}
|
|
636
535
|
case 'ArrowRight': {
|
|
637
536
|
if (keyboardDelegate.getKeyRightOf) {
|
|
638
|
-
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) => getNextTarget(target, wrap,
|
|
537
|
+
let target = nextValidTarget(localState.state.target, types, drag.allowedDropOperations, (target, wrap) => getNextTarget(target, wrap, 'right'));
|
|
639
538
|
localState.state.setTarget(target);
|
|
640
539
|
}
|
|
641
540
|
break;
|
|
@@ -748,6 +647,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
|
|
|
748
647
|
break;
|
|
749
648
|
}
|
|
750
649
|
}
|
|
650
|
+
localState.props.onKeyDown?.(e);
|
|
751
651
|
}
|
|
752
652
|
});
|
|
753
653
|
}, [localState, ref, onDrop, direction]);
|
package/src/useDroppableItem.ts
CHANGED
|
@@ -12,14 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
import * as DragManager from './DragManager';
|
|
14
14
|
import {DroppableCollectionState} from '@react-stately/dnd';
|
|
15
|
-
import {DropTarget, RefObject} from '@react-types/shared';
|
|
15
|
+
import {DropTarget, FocusableElement, RefObject} from '@react-types/shared';
|
|
16
16
|
import {getDroppableCollectionRef, getTypes, globalDndState, isInternalDropOperation} from './utils';
|
|
17
17
|
import {HTMLAttributes, useEffect} from 'react';
|
|
18
18
|
import {useVirtualDrop} from './useVirtualDrop';
|
|
19
19
|
|
|
20
20
|
export interface DroppableItemOptions {
|
|
21
21
|
/** The drop target represented by the item. */
|
|
22
|
-
target: DropTarget
|
|
22
|
+
target: DropTarget,
|
|
23
|
+
/** The ref to the activate button. */
|
|
24
|
+
activateButtonRef?: RefObject<FocusableElement | null>
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface DroppableItemResult {
|
|
@@ -50,10 +52,11 @@ export function useDroppableItem(options: DroppableItemOptions, state: Droppable
|
|
|
50
52
|
isInternal,
|
|
51
53
|
draggingKeys
|
|
52
54
|
});
|
|
53
|
-
}
|
|
55
|
+
},
|
|
56
|
+
activateButtonRef: options.activateButtonRef
|
|
54
57
|
});
|
|
55
58
|
}
|
|
56
|
-
}, [ref, options.target, state, droppableCollectionRef]);
|
|
59
|
+
}, [ref, options.target, state, droppableCollectionRef, options.activateButtonRef]);
|
|
57
60
|
|
|
58
61
|
let dragSession = DragManager.useDragSession();
|
|
59
62
|
let {draggingKeys} = globalDndState;
|