Skip to content

Commit b7f8ed1

Browse files
authored
fix: Revert "feat(RAC): Tree drag and drop (#7692)" (#8243)
* Revert "feat(RAC): Tree drag and drop (#7692)" This reverts commit e4ec6b4. * Revert "fix: Tree Dnd updates (#8229)" This reverts commit 206ebd5. * lint * restore useTreeData tests
1 parent 671f0b4 commit b7f8ed1

File tree

12 files changed

+129
-746
lines changed

12 files changed

+129
-746
lines changed

packages/@react-aria/dnd/src/DragManager.ts

Lines changed: 17 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, Dr
1616
import {getDragModality, getTypes} from './utils';
1717
import {isVirtualClick, isVirtualPointerEvent} from '@react-aria/utils';
1818
import type {LocalizedStringFormatter} from '@internationalized/string';
19-
import {RefObject, useEffect, useState} from 'react';
19+
import {useEffect, useState} from 'react';
2020

2121
let dropTargets = new Map<Element, DropTarget>();
2222
let dropItems = new Map<Element, DroppableItem>();
@@ -32,8 +32,7 @@ interface DropTarget {
3232
onDropTargetEnter?: (target: DroppableCollectionTarget | null) => void,
3333
onDropActivate?: (e: DropActivateEvent, target: DroppableCollectionTarget | null) => void,
3434
onDrop?: (e: DropEvent, target: DroppableCollectionTarget | null) => void,
35-
onKeyDown?: (e: KeyboardEvent, dragTarget: DragTarget) => void,
36-
activateButtonRef?: RefObject<FocusableElement | null>
35+
onKeyDown?: (e: KeyboardEvent, dragTarget: DragTarget) => void
3736
}
3837

3938
export function registerDropTarget(target: DropTarget) {
@@ -48,8 +47,7 @@ export function registerDropTarget(target: DropTarget) {
4847
interface DroppableItem {
4948
element: FocusableElement,
5049
target: DroppableCollectionTarget,
51-
getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation,
52-
activateButtonRef?: RefObject<FocusableElement | null>
50+
getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation
5351
}
5452

5553
export function registerDropItem(item: DroppableItem) {
@@ -243,26 +241,15 @@ class DragSession {
243241
this.cancelEvent(e);
244242

245243
if (e.key === 'Enter') {
246-
if (e.altKey || this.getCurrentActivateButton()?.contains(e.target as Node)) {
247-
this.activate(this.currentDropTarget, this.currentDropItem);
244+
if (e.altKey) {
245+
this.activate();
248246
} else {
249247
this.drop();
250248
}
251249
}
252250
}
253251

254-
getCurrentActivateButton(): FocusableElement | null {
255-
return this.currentDropItem?.activateButtonRef?.current ?? this.currentDropTarget?.activateButtonRef?.current ?? null;
256-
}
257-
258252
onFocus(e: FocusEvent) {
259-
let activateButton = this.getCurrentActivateButton();
260-
if (e.target === activateButton) {
261-
// TODO: canceling this breaks the focus ring. Revisit when we support tabbing.
262-
this.cancelEvent(e);
263-
return;
264-
}
265-
266253
// Prevent focus events, except to the original drag target.
267254
if (e.target !== this.dragTarget.element) {
268255
this.cancelEvent(e);
@@ -278,9 +265,6 @@ class DragSession {
278265
this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
279266

280267
if (!dropTarget) {
281-
// if (e.target === activateButton) {
282-
// activateButton.focus();
283-
// }
284268
if (this.currentDropTarget) {
285269
this.currentDropTarget.element.focus();
286270
} else {
@@ -290,18 +274,10 @@ class DragSession {
290274
}
291275

292276
let item = dropItems.get(e.target as HTMLElement);
293-
if (dropTarget) {
294-
this.setCurrentDropTarget(dropTarget, item);
295-
}
277+
this.setCurrentDropTarget(dropTarget, item);
296278
}
297279

298280
onBlur(e: FocusEvent) {
299-
let activateButton = this.getCurrentActivateButton();
300-
if (e.relatedTarget === activateButton) {
301-
this.cancelEvent(e);
302-
return;
303-
}
304-
305281
if (e.target !== this.dragTarget.element) {
306282
this.cancelEvent(e);
307283
}
@@ -320,21 +296,14 @@ class DragSession {
320296
onClick(e: MouseEvent) {
321297
this.cancelEvent(e);
322298
if (isVirtualClick(e) || this.isVirtualClick) {
323-
let dropElements = dropItems.values();
324-
let item = [...dropElements].find(item => item.element === e.target as HTMLElement || item.activateButtonRef?.current?.contains(e.target as HTMLElement));
325-
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
326-
let activateButton = item?.activateButtonRef?.current ?? dropTarget?.activateButtonRef?.current;
327-
if (activateButton?.contains(e.target as HTMLElement) && dropTarget) {
328-
this.activate(dropTarget, item);
329-
return;
330-
}
331-
332299
if (e.target === this.dragTarget.element) {
333300
this.cancel();
334301
return;
335302
}
336303

304+
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
337305
if (dropTarget) {
306+
let item = dropItems.get(e.target as HTMLElement);
338307
this.setCurrentDropTarget(dropTarget, item);
339308
this.drop(item);
340309
}
@@ -350,7 +319,7 @@ class DragSession {
350319

351320
cancelEvent(e: Event) {
352321
// Allow focusin and focusout on the drag target so focus ring works properly.
353-
if ((e.type === 'focusin' || e.type === 'focusout') && (e.target === this.dragTarget?.element || e.target === this.getCurrentActivateButton())) {
322+
if ((e.type === 'focusin' || e.type === 'focusout') && e.target === this.dragTarget?.element) {
354323
return;
355324
}
356325

@@ -406,23 +375,14 @@ class DragSession {
406375

407376
this.restoreAriaHidden = ariaHideOutside([
408377
this.dragTarget.element,
409-
...validDropItems.flatMap(item => item.activateButtonRef?.current ? [item.element, item.activateButtonRef?.current] : [item.element]),
410-
...visibleDropTargets.flatMap(target => target.activateButtonRef?.current ? [target.element, target.activateButtonRef?.current] : [target.element])
378+
...validDropItems.map(item => item.element),
379+
...visibleDropTargets.map(target => target.element)
411380
]);
412381

413382
this.mutationObserver.observe(document.body, {subtree: true, attributes: true, attributeFilter: ['aria-hidden']});
414383
}
415384

416385
next() {
417-
// TODO: Allow tabbing to the activate button. Revisit once we fix the focus ring.
418-
// For now, the activate button is reachable by screen readers and ArrowLeft/ArrowRight
419-
// is usable specifically by Tree. Will need tabbing for other components.
420-
// let activateButton = this.getCurrentActivateButton();
421-
// if (activateButton && document.activeElement !== activateButton) {
422-
// activateButton.focus();
423-
// return;
424-
// }
425-
426386
if (!this.currentDropTarget) {
427387
this.setCurrentDropTarget(this.validDropTargets[0]);
428388
return;
@@ -449,15 +409,6 @@ class DragSession {
449409
}
450410

451411
previous() {
452-
// let activateButton = this.getCurrentActivateButton();
453-
// if (activateButton && document.activeElement === activateButton) {
454-
// let target = this.currentDropItem ?? this.currentDropTarget;
455-
// if (target) {
456-
// target.element.focus();
457-
// return;
458-
// }
459-
// }
460-
461412
if (!this.currentDropTarget) {
462413
this.setCurrentDropTarget(this.validDropTargets[this.validDropTargets.length - 1]);
463414
return;
@@ -536,6 +487,7 @@ class DragSession {
536487
if (this.currentDropTarget && typeof this.currentDropTarget.onDropTargetEnter === 'function') {
537488
this.currentDropTarget.onDropTargetEnter(item.target);
538489
}
490+
539491
item.element.focus();
540492
this.currentDropItem = item;
541493

@@ -624,15 +576,14 @@ class DragSession {
624576
announce(this.stringFormatter.format('dropComplete'));
625577
}
626578

627-
activate(dropTarget: DropTarget | null, dropItem: DroppableItem | null | undefined) {
628-
if (dropTarget && typeof dropTarget.onDropActivate === 'function') {
629-
let target = dropItem?.target ?? null;
630-
let rect = dropTarget.element.getBoundingClientRect();
631-
dropTarget.onDropActivate({
579+
activate() {
580+
if (this.currentDropTarget && typeof this.currentDropTarget.onDropActivate === 'function') {
581+
let rect = this.currentDropTarget.element.getBoundingClientRect();
582+
this.currentDropTarget.onDropActivate({
632583
type: 'dropactivate',
633584
x: rect.left + (rect.width / 2),
634585
y: rect.top + (rect.height / 2)
635-
}, target);
586+
}, null);
636587
}
637588
}
638589
}

packages/@react-aria/dnd/src/useDrop.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface DropOptions {
3636
/**
3737
* Handler that is called after a valid drag is held over the drop target for a period of time.
3838
* This typically opens the item so that the user can drop within it.
39+
* @private
3940
*/
4041
onDropActivate?: (e: DropActivateEvent) => void,
4142
/** Handler that is called when a valid drag exits the drop target. */

packages/@react-aria/dnd/src/useDropIndicator.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import * as DragManager from './DragManager';
1414
import {DroppableCollectionState} from '@react-stately/dnd';
15-
import {DropTarget, FocusableElement, Key, RefObject} from '@react-types/shared';
15+
import {DropTarget, Key, RefObject} from '@react-types/shared';
1616
import {getDroppableCollectionId} from './utils';
1717
import {HTMLAttributes} from 'react';
1818
// @ts-ignore
@@ -23,9 +23,7 @@ import {useLocalizedStringFormatter} from '@react-aria/i18n';
2323

2424
export interface DropIndicatorProps {
2525
/** The drop target that the drop indicator represents. */
26-
target: DropTarget,
27-
/** The ref to the activate button. */
28-
activateButtonRef?: RefObject<FocusableElement | null>
26+
target: DropTarget
2927
}
3028

3129
export interface DropIndicatorAria {

packages/@react-aria/dnd/src/useDroppableCollection.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ export interface DroppableCollectionOptions extends DroppableCollectionProps {
4646
/** A delegate object that implements behavior for keyboard focus movement. */
4747
keyboardDelegate: KeyboardDelegate,
4848
/** A delegate object that provides drop targets for pointer coordinates within the collection. */
49-
dropTargetDelegate: DropTargetDelegate,
50-
/** A custom keyboard event handler for drop targets. */
51-
onKeyDown?: (e: KeyboardEvent) => void
49+
dropTargetDelegate: DropTargetDelegate
5250
}
5351

5452
export interface DroppableCollectionResult {
@@ -94,7 +92,6 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
9492
onRootDrop,
9593
onItemDrop,
9694
onReorder,
97-
onMove,
9895
acceptedDragTypes = 'all',
9996
shouldAcceptItemDrop
10097
} = localState.props;
@@ -140,10 +137,6 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
140137
await onItemDrop({items: filteredItems, dropOperation, isInternal, target});
141138
}
142139

143-
if (onMove && isInternal) {
144-
await onMove({keys: draggingKeys, dropOperation, target});
145-
}
146-
147140
if (target.dropPosition !== 'on') {
148141
if (!isInternal && onInsert) {
149142
await onInsert({items: filteredItems, dropOperation, target});
@@ -208,7 +201,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
208201
autoScroll.stop();
209202
},
210203
onDropActivate(e) {
211-
if (state.target?.type === 'item' && typeof props.onDropActivate === 'function') {
204+
if (state.target?.type === 'item' && state.target?.dropPosition === 'on' && typeof props.onDropActivate === 'function') {
212205
props.onDropActivate({
213206
type: 'dropactivate',
214207
x: e.x, // todo
@@ -595,17 +588,17 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
595588
onDropTargetEnter(target) {
596589
localState.state.setTarget(target);
597590
},
598-
onDropActivate(e, target) {
591+
onDropActivate(e) {
599592
if (
600-
target?.type === 'item' &&
601-
target?.dropPosition === 'on' &&
593+
localState.state.target?.type === 'item' &&
594+
localState.state.target?.dropPosition === 'on' &&
602595
typeof localState.props.onDropActivate === 'function'
603596
) {
604597
localState.props.onDropActivate({
605598
type: 'dropactivate',
606599
x: e.x, // todo
607600
y: e.y,
608-
target
601+
target: localState.state.target
609602
});
610603
}
611604
},
@@ -755,7 +748,6 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
755748
break;
756749
}
757750
}
758-
localState.props.onKeyDown?.(e);
759751
}
760752
});
761753
}, [localState, ref, onDrop, direction]);

packages/@react-aria/dnd/src/useDroppableItem.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212

1313
import * as DragManager from './DragManager';
1414
import {DroppableCollectionState} from '@react-stately/dnd';
15-
import {DropTarget, FocusableElement, RefObject} from '@react-types/shared';
15+
import {DropTarget, RefObject} from '@react-types/shared';
1616
import {getDroppableCollectionRef, getTypes, globalDndState, isInternalDropOperation} from './utils';
1717
import {HTMLAttributes, useEffect} from 'react';
1818
import {useVirtualDrop} from './useVirtualDrop';
1919

2020
export interface DroppableItemOptions {
2121
/** The drop target represented by the item. */
22-
target: DropTarget,
23-
/** The ref to the activate button. */
24-
activateButtonRef?: RefObject<FocusableElement | null>
22+
target: DropTarget
2523
}
2624

2725
export interface DroppableItemResult {
@@ -52,11 +50,10 @@ export function useDroppableItem(options: DroppableItemOptions, state: Droppable
5250
isInternal,
5351
draggingKeys
5452
});
55-
},
56-
activateButtonRef: options.activateButtonRef
53+
}
5754
});
5855
}
59-
}, [ref, options.target, state, droppableCollectionRef, options.activateButtonRef]);
56+
}, [ref, options.target, state, droppableCollectionRef]);
6057

6158
let dragSession = DragManager.useDragSession();
6259
let {draggingKeys} = globalDndState;

packages/@react-stately/data/test/useTreeData.test.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,24 @@ describe('useTreeData', function () {
920920
expect(result.current.items.length).toEqual(2);
921921
});
922922

923+
it('should move an item to a different level after the target', function () {
924+
const initialItems = [...initial, {name: 'Emily'}, {name: 'Eli'}];
925+
let {result} = renderHook(() =>
926+
useTreeData({initialItems, getChildren, getKey})
927+
);
928+
929+
act(() => {
930+
result.current.move('Eli', 'David', 2);
931+
});
932+
expect(result.current.items[0].key).toEqual('David');
933+
934+
expect(result.current.items[0].children[0].key).toEqual('John');
935+
expect(result.current.items[0].children[1].key).toEqual('Sam');
936+
expect(result.current.items[0].children[2].key).toEqual('Eli');
937+
expect(result.current.items[1].key).toEqual('Emily');
938+
expect(result.current.items.length).toEqual(2);
939+
});
940+
923941
it('can move an item multiple times', function () {
924942
const initialItems = [...initial, {name: 'Eli'}];
925943

@@ -1079,24 +1097,6 @@ describe('useTreeData', function () {
10791097
expect(result.current.items[0].children[1].children[2].key).toEqual('project-2B');
10801098
});
10811099

1082-
it('should move an item to a different level after the target', function () {
1083-
const initialItems = [...initial, {name: 'Emily'}, {name: 'Eli'}];
1084-
let {result} = renderHook(() =>
1085-
useTreeData({initialItems, getChildren, getKey})
1086-
);
1087-
1088-
act(() => {
1089-
result.current.move('Eli', 'David', 2);
1090-
});
1091-
expect(result.current.items[0].key).toEqual('David');
1092-
1093-
expect(result.current.items[0].children[0].key).toEqual('John');
1094-
expect(result.current.items[0].children[1].key).toEqual('Sam');
1095-
expect(result.current.items[0].children[2].key).toEqual('Eli');
1096-
expect(result.current.items[1].key).toEqual('Emily');
1097-
expect(result.current.items.length).toEqual(2);
1098-
});
1099-
11001100
it('should move an item to a different level at the end when the index is greater than the node list length', function () {
11011101
const initialItems = [...initial, {name: 'Emily'}, {name: 'Eli'}];
11021102
let {result} = renderHook(() =>

0 commit comments

Comments
 (0)