@react-aria/dnd 3.0.0-alpha.11 → 3.0.0-alpha.12

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/types.d.ts CHANGED
@@ -1,15 +1,27 @@
1
1
  import React, { HTMLAttributes, RefObject, Key } from "react";
2
- import { DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropMoveEvent, DropOperation, DragTypes, DroppableCollectionProps, DropTarget, KeyboardDelegate, DragEndEvent, DragItem, DragMoveEvent, DragPreviewRenderer, DragStartEvent, DOMAttributes, DropItem } from "@react-types/shared";
2
+ import { DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropMoveEvent, DropOperation, DragTypes, DroppableCollectionProps, DropTargetDelegate, KeyboardDelegate, DropTarget, DragEndEvent, DragItem, DragMoveEvent, DragPreviewRenderer, DragStartEvent, DOMAttributes, DropItem, Collection, Node } from "@react-types/shared";
3
3
  import { DroppableCollectionState, DraggableCollectionState } from "@react-stately/dnd";
4
4
  import { AriaButtonProps } from "@react-types/button";
5
5
  export interface DropOptions {
6
6
  ref: RefObject<HTMLElement>;
7
+ /**
8
+ * A function returning the drop operation to be performed when items matching the given types are dropped
9
+ * on the drop target.
10
+ */
7
11
  getDropOperation?: (types: DragTypes, allowedOperations: DropOperation[]) => DropOperation;
8
12
  getDropOperationForPoint?: (types: DragTypes, allowedOperations: DropOperation[], x: number, y: number) => DropOperation;
13
+ /** Handler that is called when a valid drag enters the drop target. */
9
14
  onDropEnter?: (e: DropEnterEvent) => void;
15
+ /** Handler that is called when a valid drag is moved within the drop target. */
10
16
  onDropMove?: (e: DropMoveEvent) => void;
17
+ /**
18
+ * Handler that is called after a valid drag is held over the drop target for a period of time.
19
+ * This typically opens the item so that the user can drop within it.
20
+ */
11
21
  onDropActivate?: (e: DropActivateEvent) => void;
22
+ /** Handler that is called when a valid drag exits the drop target. */
12
23
  onDropExit?: (e: DropExitEvent) => void;
24
+ /** Handler that is called when a valid drag is dropped on the drop target. */
13
25
  onDrop?: (e: DropEvent) => void;
14
26
  }
15
27
  export interface DropResult {
@@ -19,7 +31,7 @@ export interface DropResult {
19
31
  export function useDrop(options: DropOptions): DropResult;
20
32
  export interface DroppableCollectionOptions extends DroppableCollectionProps {
21
33
  keyboardDelegate: KeyboardDelegate;
22
- getDropTargetFromPoint: (x: number, y: number) => DropTarget | null;
34
+ dropTargetDelegate: DropTargetDelegate;
23
35
  }
24
36
  export interface DroppableCollectionResult {
25
37
  collectionProps: HTMLAttributes<HTMLElement>;
@@ -30,6 +42,7 @@ export interface DroppableItemOptions {
30
42
  }
31
43
  export interface DroppableItemResult {
32
44
  dropProps: HTMLAttributes<HTMLElement>;
45
+ isDropTarget: boolean;
33
46
  }
34
47
  export function useDroppableItem(options: DroppableItemOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableItemResult;
35
48
  export interface DropIndicatorProps {
@@ -37,6 +50,8 @@ export interface DropIndicatorProps {
37
50
  }
38
51
  export interface DropIndicatorAria {
39
52
  dropIndicatorProps: HTMLAttributes<HTMLElement>;
53
+ isDropTarget: boolean;
54
+ isHidden: boolean;
40
55
  }
41
56
  export function useDropIndicator(props: DropIndicatorProps, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DropIndicatorAria;
42
57
  export interface DragOptions {
@@ -75,5 +90,10 @@ export interface ClipboardResult {
75
90
  clipboardProps: DOMAttributes;
76
91
  }
77
92
  export function useClipboard(options: ClipboardProps): ClipboardResult;
93
+ export class ListDropTargetDelegate implements DropTargetDelegate {
94
+ constructor(collection: Collection<Node<unknown>>, ref: RefObject<HTMLElement>);
95
+ getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget;
96
+ }
97
+ export type { DropTargetDelegate } from '@react-types/shared';
78
98
 
79
99
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/constants.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/utils.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/DragManager.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useAutoScroll.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useVirtualDrop.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDrop.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDroppableCollection.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDroppableItem.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDropIndicator.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDrag.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDraggableItem.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/DragPreview.tsx","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useClipboard.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/index.ts","packages/@react-aria/dnd/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {DroppableCollectionOptions, DroppableCollectionResult} from './useDroppableCollection';\nexport type {DroppableItemOptions, DroppableItemResult} from './useDroppableItem';\nexport type {DropIndicatorProps, DropIndicatorAria} from './useDropIndicator';\nexport type {DraggableItemProps, DraggableItemResult} from './useDraggableItem';\nexport type {DragPreviewProps} from './DragPreview';\nexport type {DragOptions, DragResult} from './useDrag';\nexport type {DropOptions, DropResult} from './useDrop';\nexport type {ClipboardProps, ClipboardResult} from './useClipboard';\n\nexport {useDrag} from './useDrag';\nexport {useDrop} from './useDrop';\nexport {useDroppableCollection} from './useDroppableCollection';\nexport {useDroppableItem} from './useDroppableItem';\nexport {useDropIndicator} from './useDropIndicator';\nexport {useDraggableItem} from './useDraggableItem';\nexport {useClipboard} from './useClipboard';\nexport {DragPreview} from './DragPreview';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":"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","sources":["packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/constants.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/utils.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/DragManager.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useAutoScroll.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useVirtualDrop.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDrop.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDroppableCollection.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDroppableItem.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDropIndicator.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDrag.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useDraggableItem.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/DragPreview.tsx","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useClipboard.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/ListDropTargetDelegate.ts","packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/index.ts","packages/@react-aria/dnd/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {DroppableCollectionOptions, DroppableCollectionResult} from './useDroppableCollection';\nexport type {DroppableItemOptions, DroppableItemResult} from './useDroppableItem';\nexport type {DropIndicatorProps, DropIndicatorAria} from './useDropIndicator';\nexport type {DraggableItemProps, DraggableItemResult} from './useDraggableItem';\nexport type {DragPreviewProps} from './DragPreview';\nexport type {DragOptions, DragResult} from './useDrag';\nexport type {DropOptions, DropResult} from './useDrop';\nexport type {ClipboardProps, ClipboardResult} from './useClipboard';\nexport type {DropTargetDelegate} from '@react-types/shared';\n\nexport {useDrag} from './useDrag';\nexport {useDrop} from './useDrop';\nexport {useDroppableCollection} from './useDroppableCollection';\nexport {useDroppableItem} from './useDroppableItem';\nexport {useDropIndicator} from './useDropIndicator';\nexport {useDraggableItem} from './useDraggableItem';\nexport {useClipboard} from './useClipboard';\nexport {DragPreview} from './DragPreview';\nexport {ListDropTargetDelegate} from './ListDropTargetDelegate';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/dnd",
3
- "version": "3.0.0-alpha.11",
3
+ "version": "3.0.0-alpha.12",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -19,16 +19,16 @@
19
19
  "dependencies": {
20
20
  "@babel/runtime": "^7.6.2",
21
21
  "@internationalized/string": "^3.0.0",
22
- "@react-aria/i18n": "^3.5.0",
23
- "@react-aria/interactions": "^3.10.0",
22
+ "@react-aria/i18n": "^3.6.0",
23
+ "@react-aria/interactions": "^3.11.0",
24
24
  "@react-aria/live-announcer": "^3.1.1",
25
- "@react-aria/overlays": "^3.10.0",
26
- "@react-aria/utils": "^3.13.2",
27
- "@react-aria/visually-hidden": "^3.4.0",
28
- "@react-stately/dnd": "3.0.0-alpha.9",
29
- "@react-stately/selection": "^3.10.2",
30
- "@react-types/button": "^3.6.0",
31
- "@react-types/shared": "^3.14.0"
25
+ "@react-aria/overlays": "^3.10.1",
26
+ "@react-aria/utils": "^3.13.3",
27
+ "@react-aria/visually-hidden": "^3.4.1",
28
+ "@react-stately/dnd": "3.0.0-alpha.10",
29
+ "@react-stately/selection": "^3.10.3",
30
+ "@react-types/button": "^3.6.1",
31
+ "@react-types/shared": "^3.14.1"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "cd7c0ec917122c7612f653c22f8ed558f8b66ecd"
40
+ "gitHead": "b03ef51e6317547dd0a840f151e59d039b1e1fd3"
41
41
  }
@@ -75,7 +75,8 @@ export function beginDragging(target: DragTarget, stringFormatter: LocalizedStri
75
75
  getDragModality() === 'keyboard' ||
76
76
  (getDragModality() === 'touch' && getInteractionModality() === 'virtual')
77
77
  ) {
78
- dragSession.next();
78
+ let target = dragSession.findNearestDropTarget();
79
+ dragSession.setCurrentDropTarget(target);
79
80
  }
80
81
  });
81
82
 
@@ -411,6 +412,25 @@ class DragSession {
411
412
  }
412
413
  }
413
414
 
415
+ findNearestDropTarget(): DropTarget {
416
+ let dragTargetRect = this.dragTarget.element.getBoundingClientRect();
417
+
418
+ let minDistance = Infinity;
419
+ let nearest = null;
420
+ for (let dropTarget of this.validDropTargets) {
421
+ let rect = dropTarget.element.getBoundingClientRect();
422
+ let dx = rect.left - dragTargetRect.left;
423
+ let dy = rect.top - dragTargetRect.top;
424
+ let dist = (dx * dx) + (dy * dy);
425
+ if (dist < minDistance) {
426
+ minDistance = dist;
427
+ nearest = dropTarget;
428
+ }
429
+ }
430
+
431
+ return nearest;
432
+ }
433
+
414
434
  setCurrentDropTarget(dropTarget: DropTarget, item?: DroppableItem) {
415
435
  if (dropTarget !== this.currentDropTarget) {
416
436
  if (this.currentDropTarget && typeof this.currentDropTarget.onDropExit === 'function') {
@@ -0,0 +1,90 @@
1
+ import {Collection, DropTarget, DropTargetDelegate, Node} from '@react-types/shared';
2
+ import {RefObject} from 'react';
3
+
4
+ export class ListDropTargetDelegate implements DropTargetDelegate {
5
+ private collection: Collection<Node<unknown>>;
6
+ private ref: RefObject<HTMLElement>;
7
+
8
+ constructor(collection: Collection<Node<unknown>>, ref: RefObject<HTMLElement>) {
9
+ this.collection = collection;
10
+ this.ref = ref;
11
+ }
12
+
13
+ getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget {
14
+ if (this.collection.size === 0) {
15
+ return;
16
+ }
17
+
18
+ let rect = this.ref.current.getBoundingClientRect();
19
+ x += rect.x;
20
+ y += rect.y;
21
+
22
+ let elements = this.ref.current.querySelectorAll('[data-key]');
23
+ let elementMap = new Map<string, HTMLElement>();
24
+ for (let item of elements) {
25
+ if (item instanceof HTMLElement) {
26
+ elementMap.set(item.dataset.key, item);
27
+ }
28
+ }
29
+
30
+ let items = [...this.collection];
31
+ let low = 0;
32
+ let high = items.length;
33
+ while (low < high) {
34
+ let mid = Math.floor((low + high) / 2);
35
+ let item = items[mid];
36
+ let element = elementMap.get(String(item.key));
37
+ let rect = element.getBoundingClientRect();
38
+
39
+ if (y < rect.top) {
40
+ high = mid;
41
+ } else if (y > rect.bottom) {
42
+ low = mid + 1;
43
+ } else {
44
+ let target: DropTarget = {
45
+ type: 'item',
46
+ key: item.key,
47
+ dropPosition: 'on'
48
+ };
49
+
50
+ if (isValidDropTarget(target)) {
51
+ // Otherwise, if dropping on the item is accepted, try the before/after positions if within 5px
52
+ // of the top or bottom of the item.
53
+ if (y <= rect.top + 5 && isValidDropTarget({...target, dropPosition: 'before'})) {
54
+ target.dropPosition = 'before';
55
+ } else if (y >= rect.bottom - 5 && isValidDropTarget({...target, dropPosition: 'after'})) {
56
+ target.dropPosition = 'after';
57
+ }
58
+ } else {
59
+ // If dropping on the item isn't accepted, try the target before or after depending on the y position.
60
+ let midY = rect.top + rect.height / 2;
61
+ if (y <= midY && isValidDropTarget({...target, dropPosition: 'before'})) {
62
+ target.dropPosition = 'before';
63
+ } else if (y >= midY && isValidDropTarget({...target, dropPosition: 'after'})) {
64
+ target.dropPosition = 'after';
65
+ }
66
+ }
67
+
68
+ return target;
69
+ }
70
+ }
71
+
72
+ let item = items[Math.min(low, items.length - 1)];
73
+ let element = elementMap.get(String(item.key));
74
+ rect = element.getBoundingClientRect();
75
+
76
+ if (Math.abs(y - rect.top) < Math.abs(y - rect.bottom)) {
77
+ return {
78
+ type: 'item',
79
+ key: item.key,
80
+ dropPosition: 'before'
81
+ };
82
+ }
83
+
84
+ return {
85
+ type: 'item',
86
+ key: item.key,
87
+ dropPosition: 'after'
88
+ };
89
+ }
90
+ }
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export type {DragPreviewProps} from './DragPreview';
18
18
  export type {DragOptions, DragResult} from './useDrag';
19
19
  export type {DropOptions, DropResult} from './useDrop';
20
20
  export type {ClipboardProps, ClipboardResult} from './useClipboard';
21
+ export type {DropTargetDelegate} from '@react-types/shared';
21
22
 
22
23
  export {useDrag} from './useDrag';
23
24
  export {useDrop} from './useDrop';
@@ -27,3 +28,4 @@ export {useDropIndicator} from './useDropIndicator';
27
28
  export {useDraggableItem} from './useDraggableItem';
28
29
  export {useClipboard} from './useClipboard';
29
30
  export {DragPreview} from './DragPreview';
31
+ export {ListDropTargetDelegate} from './ListDropTargetDelegate';
@@ -34,6 +34,7 @@ export function useDraggableItem(props: DraggableItemProps, state: DraggableColl
34
34
  return state.getItems(props.key);
35
35
  },
36
36
  preview: state.preview,
37
+ getAllowedDropOperations: state.getAllowedDropOperations,
37
38
  onDragStart(e) {
38
39
  state.startDrag(props.key, e);
39
40
  },
package/src/useDrop.ts CHANGED
@@ -20,14 +20,24 @@ import {useVirtualDrop} from './useVirtualDrop';
20
20
 
21
21
  export interface DropOptions {
22
22
  ref: RefObject<HTMLElement>,
23
+ /**
24
+ * A function returning the drop operation to be performed when items matching the given types are dropped
25
+ * on the drop target.
26
+ */
23
27
  getDropOperation?: (types: IDragTypes, allowedOperations: DropOperation[]) => DropOperation,
24
28
  getDropOperationForPoint?: (types: IDragTypes, allowedOperations: DropOperation[], x: number, y: number) => DropOperation,
29
+ /** Handler that is called when a valid drag enters the drop target. */
25
30
  onDropEnter?: (e: DropEnterEvent) => void,
31
+ /** Handler that is called when a valid drag is moved within the drop target. */
26
32
  onDropMove?: (e: DropMoveEvent) => void,
27
- // When the user hovers over the drop target for a period of time.
28
- // typically opens that item. macOS/iOS call this "spring loading".
33
+ /**
34
+ * Handler that is called after a valid drag is held over the drop target for a period of time.
35
+ * This typically opens the item so that the user can drop within it.
36
+ */
29
37
  onDropActivate?: (e: DropActivateEvent) => void,
38
+ /** Handler that is called when a valid drag exits the drop target. */
30
39
  onDropExit?: (e: DropExitEvent) => void,
40
+ /** Handler that is called when a valid drag is dropped on the drop target. */
31
41
  onDrop?: (e: DropEvent) => void
32
42
  }
33
43
 
@@ -43,16 +53,43 @@ export function useDrop(options: DropOptions): DropResult {
43
53
  let state = useRef({
44
54
  x: 0,
45
55
  y: 0,
46
- dragEnterCount: 0,
56
+ dragOverElements: new Set<Element>(),
47
57
  dropEffect: 'none' as DataTransfer['dropEffect'],
58
+ effectAllowed: 'none' as DataTransfer['effectAllowed'],
48
59
  dropActivateTimer: null
49
60
  }).current;
50
61
 
62
+ let fireDropEnter = (e: DragEvent) => {
63
+ setDropTarget(true);
64
+
65
+ if (typeof options.onDropEnter === 'function') {
66
+ let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
67
+ options.onDropEnter({
68
+ type: 'dropenter',
69
+ x: e.clientX - rect.x,
70
+ y: e.clientY - rect.y
71
+ });
72
+ }
73
+ };
74
+
75
+ let fireDropExit = (e: DragEvent) => {
76
+ setDropTarget(false);
77
+
78
+ if (typeof options.onDropExit === 'function') {
79
+ let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
80
+ options.onDropExit({
81
+ type: 'dropexit',
82
+ x: e.clientX - rect.x,
83
+ y: e.clientY - rect.y
84
+ });
85
+ }
86
+ };
87
+
51
88
  let onDragOver = (e: DragEvent) => {
52
89
  e.preventDefault();
53
90
  e.stopPropagation();
54
91
 
55
- if (e.clientX === state.x && e.clientY === state.y) {
92
+ if (e.clientX === state.x && e.clientY === state.y && e.dataTransfer.effectAllowed === state.effectAllowed) {
56
93
  e.dataTransfer.dropEffect = state.dropEffect;
57
94
  return;
58
95
  }
@@ -60,17 +97,42 @@ export function useDrop(options: DropOptions): DropResult {
60
97
  state.x = e.clientX;
61
98
  state.y = e.clientY;
62
99
 
100
+ let prevDropEffect = state.dropEffect;
101
+
102
+ // Update drop effect if allowed drop operations changed (e.g. user pressed modifier key).
103
+ if (e.dataTransfer.effectAllowed !== state.effectAllowed) {
104
+ let allowedOperations = effectAllowedToOperations(e.dataTransfer.effectAllowed);
105
+ let dropOperation = allowedOperations[0];
106
+ if (typeof options.getDropOperation === 'function') {
107
+ let types = new DragTypes(e.dataTransfer);
108
+ dropOperation = getDropOperation(e.dataTransfer.effectAllowed, options.getDropOperation(types, allowedOperations));
109
+ }
110
+
111
+ state.dropEffect = DROP_OPERATION_TO_DROP_EFFECT[dropOperation] || 'none';
112
+ }
113
+
63
114
  if (typeof options.getDropOperationForPoint === 'function') {
64
115
  let allowedOperations = effectAllowedToOperations(e.dataTransfer.effectAllowed);
65
116
  let types = new DragTypes(e.dataTransfer);
66
117
  let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
67
- let dropOperation = options.getDropOperationForPoint(types, allowedOperations, state.x - rect.x, state.y - rect.y);
118
+ let dropOperation = getDropOperation(
119
+ e.dataTransfer.effectAllowed,
120
+ options.getDropOperationForPoint(types, allowedOperations, state.x - rect.x, state.y - rect.y)
121
+ );
68
122
  state.dropEffect = DROP_OPERATION_TO_DROP_EFFECT[dropOperation] || 'none';
69
123
  }
70
124
 
125
+ state.effectAllowed = e.dataTransfer.effectAllowed;
71
126
  e.dataTransfer.dropEffect = state.dropEffect;
72
127
 
73
- if (typeof options.onDropMove === 'function') {
128
+ // If the drop operation changes, update state and fire events appropriately.
129
+ if (state.dropEffect === 'none' && prevDropEffect !== 'none') {
130
+ fireDropExit(e);
131
+ } else if (state.dropEffect !== 'none' && prevDropEffect === 'none') {
132
+ fireDropEnter(e);
133
+ }
134
+
135
+ if (typeof options.onDropMove === 'function' && state.dropEffect !== 'none') {
74
136
  let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
75
137
  options.onDropMove({
76
138
  type: 'dropmove',
@@ -95,8 +157,8 @@ export function useDrop(options: DropOptions): DropResult {
95
157
 
96
158
  let onDragEnter = (e: DragEvent) => {
97
159
  e.stopPropagation();
98
- state.dragEnterCount++;
99
- if (state.dragEnterCount > 1) {
160
+ state.dragOverElements.add(e.target as Element);
161
+ if (state.dragOverElements.size > 1) {
100
162
  return;
101
163
  }
102
164
 
@@ -105,52 +167,55 @@ export function useDrop(options: DropOptions): DropResult {
105
167
 
106
168
  if (typeof options.getDropOperation === 'function') {
107
169
  let types = new DragTypes(e.dataTransfer);
108
- dropOperation = options.getDropOperation(types, allowedOperations);
109
- }
110
-
111
- if (dropOperation !== 'cancel') {
112
- setDropTarget(true);
170
+ dropOperation = getDropOperation(e.dataTransfer.effectAllowed, options.getDropOperation(types, allowedOperations));
113
171
  }
114
172
 
115
173
  if (typeof options.getDropOperationForPoint === 'function') {
116
174
  let types = new DragTypes(e.dataTransfer);
117
175
  let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
118
- dropOperation = options.getDropOperationForPoint(types, allowedOperations, e.clientX - rect.x, e.clientY - rect.y);
176
+ dropOperation = getDropOperation(
177
+ e.dataTransfer.effectAllowed,
178
+ options.getDropOperationForPoint(types, allowedOperations, e.clientX - rect.x, e.clientY - rect.y)
179
+ );
119
180
  }
120
181
 
182
+ state.x = e.clientX;
183
+ state.y = e.clientY;
184
+ state.effectAllowed = e.dataTransfer.effectAllowed;
121
185
  state.dropEffect = DROP_OPERATION_TO_DROP_EFFECT[dropOperation] || 'none';
122
186
  e.dataTransfer.dropEffect = state.dropEffect;
123
187
 
124
- if (typeof options.onDropEnter === 'function' && dropOperation !== 'cancel') {
125
- let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
126
- options.onDropEnter({
127
- type: 'dropenter',
128
- x: e.clientX - rect.x,
129
- y: e.clientY - rect.y
130
- });
188
+ if (dropOperation !== 'cancel') {
189
+ fireDropEnter(e);
131
190
  }
132
-
133
- state.x = e.clientX;
134
- state.y = e.clientY;
135
191
  };
136
192
 
137
193
  let onDragLeave = (e: DragEvent) => {
138
194
  e.stopPropagation();
139
- state.dragEnterCount--;
140
- if (state.dragEnterCount > 0) {
195
+
196
+ // We would use e.relatedTarget to detect if the drag is still inside the drop target,
197
+ // but it is always null in WebKit. https://bugs.webkit.org/show_bug.cgi?id=66547
198
+ // Instead, we track all of the targets of dragenter events in a set, and remove them
199
+ // in dragleave. When the set becomes empty, we've left the drop target completely.
200
+ // We must also remove any elements that are no longer in the DOM, because dragleave
201
+ // events will never be fired for these. This can happen, for example, with drop
202
+ // indicators between items, which disappear when the drop target changes.
203
+
204
+ state.dragOverElements.delete(e.target as Element);
205
+ for (let element of state.dragOverElements) {
206
+ if (!e.currentTarget.contains(element)) {
207
+ state.dragOverElements.delete(element);
208
+ }
209
+ }
210
+
211
+ if (state.dragOverElements.size > 0) {
141
212
  return;
142
213
  }
143
214
 
144
- if (typeof options.onDropExit === 'function' && state.dropEffect !== 'none') {
145
- let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
146
- options.onDropExit({
147
- type: 'dropexit',
148
- x: e.clientX - rect.x,
149
- y: e.clientY - rect.y
150
- });
215
+ if (state.dropEffect !== 'none') {
216
+ fireDropExit(e);
151
217
  }
152
218
 
153
- setDropTarget(false);
154
219
  clearTimeout(state.dropActivateTimer);
155
220
  };
156
221
 
@@ -180,17 +245,8 @@ export function useDrop(options: DropOptions): DropResult {
180
245
  }, 0);
181
246
  }
182
247
 
183
- if (typeof options.onDropExit === 'function') {
184
- let rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
185
- options.onDropExit({
186
- type: 'dropexit',
187
- x: e.clientX - rect.x,
188
- y: e.clientY - rect.y
189
- });
190
- }
191
-
192
- state.dragEnterCount = 0;
193
- setDropTarget(false);
248
+ state.dragOverElements.clear();
249
+ fireDropExit(e);
194
250
  clearTimeout(state.dropActivateTimer);
195
251
  };
196
252
 
@@ -255,3 +311,9 @@ function effectAllowedToOperations(effectAllowed: string) {
255
311
 
256
312
  return allowedOperations;
257
313
  }
314
+
315
+ function getDropOperation(effectAllowed: string, operation: DropOperation) {
316
+ let allowedOperationsBits = DROP_OPERATION_ALLOWED[effectAllowed];
317
+ let op = DROP_OPERATION[operation];
318
+ return allowedOperationsBits & op ? operation : 'cancel';
319
+ }
@@ -26,7 +26,9 @@ export interface DropIndicatorProps {
26
26
  }
27
27
 
28
28
  export interface DropIndicatorAria {
29
- dropIndicatorProps: HTMLAttributes<HTMLElement>
29
+ dropIndicatorProps: HTMLAttributes<HTMLElement>,
30
+ isDropTarget: boolean,
31
+ isHidden: boolean
30
32
  }
31
33
 
32
34
  export function useDropIndicator(props: DropIndicatorProps, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DropIndicatorAria {
@@ -72,6 +74,8 @@ export function useDropIndicator(props: DropIndicatorProps, state: DroppableColl
72
74
  }
73
75
  }
74
76
 
77
+ let isDropTarget = state.isDropTarget(target);
78
+ let ariaHidden = !dragSession ? 'true' : dropProps['aria-hidden'];
75
79
  return {
76
80
  dropIndicatorProps: {
77
81
  ...dropProps,
@@ -79,8 +83,13 @@ export function useDropIndicator(props: DropIndicatorProps, state: DroppableColl
79
83
  'aria-roledescription': stringFormatter.format('dropIndicator'),
80
84
  'aria-label': label,
81
85
  'aria-labelledby': labelledBy,
82
- 'aria-hidden': !dragSession ? 'true' : dropProps['aria-hidden'],
86
+ 'aria-hidden': ariaHidden,
83
87
  tabIndex: -1
84
- }
88
+ },
89
+ isDropTarget,
90
+ // If aria-hidden, we are either not in a drag session or the drop target is invalid.
91
+ // In that case, there's no need to render anything at all unless we need to show the indicator visually.
92
+ // This can happen when dragging using the native DnD API as opposed to keyboard dragging.
93
+ isHidden: !isDropTarget && !!ariaHidden
85
94
  };
86
95
  }
@@ -10,7 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {Collection, DropEvent, DropOperation, DroppableCollectionProps, DropPosition, DropTarget, KeyboardDelegate, Node} from '@react-types/shared';
13
+ import {Collection, DropEvent, DropOperation, DroppableCollectionProps, DropPosition, DropTarget, DropTargetDelegate, KeyboardDelegate, Node} from '@react-types/shared';
14
14
  import * as DragManager from './DragManager';
15
15
  import {DroppableCollectionState} from '@react-stately/dnd';
16
16
  import {getTypes} from './utils';
@@ -23,7 +23,7 @@ import {useDroppableCollectionId} from './utils';
23
23
 
24
24
  export interface DroppableCollectionOptions extends DroppableCollectionProps {
25
25
  keyboardDelegate: KeyboardDelegate,
26
- getDropTargetFromPoint: (x: number, y: number) => DropTarget | null
26
+ dropTargetDelegate: DropTargetDelegate
27
27
  }
28
28
 
29
29
  export interface DroppableCollectionResult {
@@ -52,16 +52,16 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
52
52
  let autoScroll = useAutoScroll(ref);
53
53
  let {dropProps} = useDrop({
54
54
  ref,
55
- onDropEnter(e) {
56
- let target = props.getDropTargetFromPoint(e.x, e.y);
57
- state.setTarget(target);
55
+ onDropEnter() {
56
+ state.setTarget(localState.nextTarget);
58
57
  },
59
58
  onDropMove(e) {
60
59
  state.setTarget(localState.nextTarget);
61
60
  autoScroll.move(e.x, e.y);
62
61
  },
63
62
  getDropOperationForPoint(types, allowedOperations, x, y) {
64
- let target = props.getDropTargetFromPoint(x, y);
63
+ let isValidDropTarget = (target) => state.getDropOperation(target, types, allowedOperations) !== 'cancel';
64
+ let target = props.dropTargetDelegate.getDropTargetFromPoint(x, y, isValidDropTarget);
65
65
  if (!target) {
66
66
  localState.dropOperation = 'cancel';
67
67
  localState.nextTarget = null;
@@ -22,7 +22,8 @@ export interface DroppableItemOptions {
22
22
  }
23
23
 
24
24
  export interface DroppableItemResult {
25
- dropProps: HTMLAttributes<HTMLElement>
25
+ dropProps: HTMLAttributes<HTMLElement>,
26
+ isDropTarget: boolean
26
27
  }
27
28
 
28
29
  export function useDroppableItem(options: DroppableItemOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableItemResult {
@@ -62,6 +63,7 @@ export function useDroppableItem(options: DroppableItemOptions, state: Droppable
62
63
  dropProps: {
63
64
  ...dropProps,
64
65
  'aria-hidden': !dragSession || isValidDropTarget ? undefined : 'true'
65
- }
66
+ },
67
+ isDropTarget
66
68
  };
67
69
  }