@react-aria/dnd 3.0.0-alpha.1 → 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,75 +1,99 @@
1
- import { AriaButtonProps } from "@react-types/button";
2
- import { DragEndEvent, DragItem, DragMoveEvent, DragStartEvent, DropOperation, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropMoveEvent, DragTypes, DroppableCollectionProps, DropTarget, KeyboardDelegate, DropItem } from "@react-types/shared";
3
- import { HTMLAttributes, RefObject, Key } from "react";
1
+ import React, { HTMLAttributes, RefObject, Key } from "react";
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";
4
3
  import { DroppableCollectionState, DraggableCollectionState } from "@react-stately/dnd";
5
- interface DragOptions {
6
- onDragStart?: (e: DragStartEvent) => void;
7
- onDragMove?: (e: DragMoveEvent) => void;
8
- onDragEnd?: (e: DragEndEvent) => void;
9
- getItems: () => DragItem[];
10
- renderPreview?: (items: DragItem[]) => JSX.Element;
11
- getAllowedDropOperations?: () => DropOperation[];
12
- }
13
- interface DragResult {
14
- dragProps: HTMLAttributes<HTMLElement>;
15
- dragButtonProps: AriaButtonProps;
16
- isDragging: boolean;
17
- }
18
- export function useDrag(options: DragOptions): DragResult;
19
- interface DropOptions {
4
+ import { AriaButtonProps } from "@react-types/button";
5
+ export interface DropOptions {
20
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
+ */
21
11
  getDropOperation?: (types: DragTypes, allowedOperations: DropOperation[]) => DropOperation;
22
12
  getDropOperationForPoint?: (types: DragTypes, allowedOperations: DropOperation[], x: number, y: number) => DropOperation;
13
+ /** Handler that is called when a valid drag enters the drop target. */
23
14
  onDropEnter?: (e: DropEnterEvent) => void;
15
+ /** Handler that is called when a valid drag is moved within the drop target. */
24
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
+ */
25
21
  onDropActivate?: (e: DropActivateEvent) => void;
22
+ /** Handler that is called when a valid drag exits the drop target. */
26
23
  onDropExit?: (e: DropExitEvent) => void;
24
+ /** Handler that is called when a valid drag is dropped on the drop target. */
27
25
  onDrop?: (e: DropEvent) => void;
28
26
  }
29
- interface DropResult {
27
+ export interface DropResult {
30
28
  dropProps: HTMLAttributes<HTMLElement>;
31
29
  isDropTarget: boolean;
32
30
  }
33
31
  export function useDrop(options: DropOptions): DropResult;
34
- interface DroppableCollectionOptions extends DroppableCollectionProps {
32
+ export interface DroppableCollectionOptions extends DroppableCollectionProps {
35
33
  keyboardDelegate: KeyboardDelegate;
36
- getDropTargetFromPoint: (x: number, y: number) => DropTarget | null;
34
+ dropTargetDelegate: DropTargetDelegate;
37
35
  }
38
- interface DroppableCollectionResult {
36
+ export interface DroppableCollectionResult {
39
37
  collectionProps: HTMLAttributes<HTMLElement>;
40
38
  }
41
39
  export function useDroppableCollection(props: DroppableCollectionOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableCollectionResult;
42
- interface DroppableItemOptions {
40
+ export interface DroppableItemOptions {
43
41
  target: DropTarget;
44
42
  }
45
- interface DroppableItemResult {
43
+ export interface DroppableItemResult {
46
44
  dropProps: HTMLAttributes<HTMLElement>;
45
+ isDropTarget: boolean;
47
46
  }
48
47
  export function useDroppableItem(options: DroppableItemOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableItemResult;
49
- interface DropIndicatorProps {
48
+ export interface DropIndicatorProps {
50
49
  target: DropTarget;
51
50
  }
52
- interface DropIndicatorAria {
51
+ export interface DropIndicatorAria {
53
52
  dropIndicatorProps: HTMLAttributes<HTMLElement>;
53
+ isDropTarget: boolean;
54
+ isHidden: boolean;
54
55
  }
55
56
  export function useDropIndicator(props: DropIndicatorProps, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DropIndicatorAria;
56
- interface DraggableItemProps {
57
+ export interface DragOptions {
58
+ onDragStart?: (e: DragStartEvent) => void;
59
+ onDragMove?: (e: DragMoveEvent) => void;
60
+ onDragEnd?: (e: DragEndEvent) => void;
61
+ getItems: () => DragItem[];
62
+ preview?: RefObject<DragPreviewRenderer>;
63
+ getAllowedDropOperations?: () => DropOperation[];
64
+ }
65
+ export interface DragResult {
66
+ dragProps: HTMLAttributes<HTMLElement>;
67
+ dragButtonProps: AriaButtonProps;
68
+ isDragging: boolean;
69
+ }
70
+ export function useDrag(options: DragOptions): DragResult;
71
+ export interface DraggableItemProps {
57
72
  key: Key;
58
73
  }
59
- interface DraggableItemResult {
74
+ export interface DraggableItemResult {
60
75
  dragProps: HTMLAttributes<HTMLElement>;
61
76
  dragButtonProps: AriaButtonProps;
62
77
  }
63
78
  export function useDraggableItem(props: DraggableItemProps, state: DraggableCollectionState): DraggableItemResult;
64
- interface ClipboardProps {
79
+ export interface DragPreviewProps {
80
+ children: (items: DragItem[]) => JSX.Element;
81
+ }
82
+ export let DragPreview: React.ForwardRefExoticComponent<DragPreviewProps & React.RefAttributes<DragPreviewRenderer>>;
83
+ export interface ClipboardProps {
65
84
  getItems?: () => DragItem[];
66
85
  onCopy?: () => void;
67
86
  onCut?: () => void;
68
87
  onPaste?: (items: DropItem[]) => void;
69
88
  }
70
- interface ClipboardResult {
71
- clipboardProps: HTMLAttributes<HTMLElement>;
89
+ export interface ClipboardResult {
90
+ clipboardProps: DOMAttributes;
72
91
  }
73
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';
74
98
 
75
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/useDrag.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/useAutoScroll.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/useDraggableItem.ts","./packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/useClipboard.ts","./packages/@react-aria/dnd/src/packages/@react-aria/dnd/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null],"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.1",
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",
@@ -18,23 +18,24 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@babel/runtime": "^7.6.2",
21
- "@react-aria/i18n": "^3.3.1",
22
- "@react-aria/interactions": "^3.4.0",
23
- "@react-aria/live-announcer": "^3.0.0",
24
- "@react-aria/overlays": "^3.6.3",
25
- "@react-aria/utils": "^3.8.0",
26
- "@react-aria/visually-hidden": "^3.2.2",
27
- "@react-stately/dnd": "3.0.0-alpha.1",
28
- "@react-stately/selection": "^3.5.0",
29
- "@react-types/button": "^3.2.1",
30
- "@react-types/shared": "^3.6.0"
21
+ "@internationalized/string": "^3.0.0",
22
+ "@react-aria/i18n": "^3.6.0",
23
+ "@react-aria/interactions": "^3.11.0",
24
+ "@react-aria/live-announcer": "^3.1.1",
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"
31
32
  },
32
33
  "peerDependencies": {
33
- "react": "^16.8.0 || ^17.0.0-rc.1",
34
- "react-dom": "^16.8.0 || ^17.0.0-rc.1"
34
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
35
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
35
36
  },
36
37
  "publishConfig": {
37
38
  "access": "public"
38
39
  },
39
- "gitHead": "3aae08e7d8a75382bedcddac7c86107e40db9296"
40
+ "gitHead": "b03ef51e6317547dd0a840f151e59d039b1e1fd3"
40
41
  }
@@ -12,8 +12,10 @@
12
12
 
13
13
  import {announce} from '@react-aria/live-announcer';
14
14
  import {ariaHideOutside} from '@react-aria/overlays';
15
- import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget} from '@react-types/shared';
15
+ import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared';
16
16
  import {getDragModality, getTypes} from './utils';
17
+ import {getInteractionModality} from '@react-aria/interactions';
18
+ import type {LocalizedStringFormatter} from '@internationalized/string';
17
19
  import {useEffect, useState} from 'react';
18
20
 
19
21
  let dropTargets = new Map<Element, DropTarget>();
@@ -22,7 +24,7 @@ let dragSession: DragSession = null;
22
24
  let subscriptions = new Set<() => void>();
23
25
 
24
26
  interface DropTarget {
25
- element: HTMLElement,
27
+ element: FocusableElement,
26
28
  getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation,
27
29
  onDropEnter?: (e: DropEnterEvent, dragTarget: DragTarget) => void,
28
30
  onDropExit?: (e: DropExitEvent) => void,
@@ -42,7 +44,7 @@ export function registerDropTarget(target: DropTarget) {
42
44
  }
43
45
 
44
46
  interface DroppableItem {
45
- element: HTMLElement,
47
+ element: FocusableElement,
46
48
  target: DroppableCollectionTarget,
47
49
  getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation
48
50
  }
@@ -55,23 +57,26 @@ export function registerDropItem(item: DroppableItem) {
55
57
  }
56
58
 
57
59
  interface DragTarget {
58
- element: HTMLElement,
60
+ element: FocusableElement,
59
61
  items: DragItem[],
60
62
  allowedDropOperations: DropOperation[],
61
63
  onDragEnd?: (e: DragEndEvent) => void
62
64
  }
63
65
 
64
- export function beginDragging(target: DragTarget, formatMessage: (key: string) => string) {
66
+ export function beginDragging(target: DragTarget, stringFormatter: LocalizedStringFormatter) {
65
67
  if (dragSession) {
66
68
  throw new Error('Cannot begin dragging while already dragging');
67
69
  }
68
70
 
69
- dragSession = new DragSession(target, formatMessage);
71
+ dragSession = new DragSession(target, stringFormatter);
70
72
  requestAnimationFrame(() => {
71
73
  dragSession.setup();
72
-
73
- if (getDragModality() === 'keyboard') {
74
- dragSession.next();
74
+ if (
75
+ getDragModality() === 'keyboard' ||
76
+ (getDragModality() === 'touch' && getInteractionModality() === 'virtual')
77
+ ) {
78
+ let target = dragSession.findNearestDropTarget();
79
+ dragSession.setCurrentDropTarget(target);
75
80
  }
76
81
  });
77
82
 
@@ -101,6 +106,16 @@ function endDragging() {
101
106
  }
102
107
  }
103
108
 
109
+ export function isValidDropTarget(element: Element): boolean {
110
+ for (let target of dropTargets.keys()) {
111
+ if (target.contains(element)) {
112
+ return true;
113
+ }
114
+ }
115
+
116
+ return false;
117
+ }
118
+
104
119
  const CANCELED_EVENTS = [
105
120
  'pointerdown',
106
121
  'pointermove',
@@ -143,19 +158,22 @@ class DragSession {
143
158
  currentDropItem: DroppableItem;
144
159
  dropOperation: DropOperation;
145
160
  private mutationObserver: MutationObserver;
146
- private mutationImmediate: NodeJS.Immediate;
147
161
  private restoreAriaHidden: () => void;
148
- private formatMessage: (key: string) => string;
162
+ private stringFormatter: LocalizedStringFormatter;
163
+ private isVirtualClick: boolean;
164
+ private initialFocused: boolean;
149
165
 
150
- constructor(target: DragTarget, formatMessage: (key: string) => string) {
166
+ constructor(target: DragTarget, stringFormatter: LocalizedStringFormatter) {
151
167
  this.dragTarget = target;
152
- this.formatMessage = formatMessage;
168
+ this.stringFormatter = stringFormatter;
153
169
 
154
170
  this.onKeyDown = this.onKeyDown.bind(this);
155
171
  this.onFocus = this.onFocus.bind(this);
156
172
  this.onBlur = this.onBlur.bind(this);
157
173
  this.onClick = this.onClick.bind(this);
174
+ this.onPointerDown = this.onPointerDown.bind(this);
158
175
  this.cancelEvent = this.cancelEvent.bind(this);
176
+ this.initialFocused = false;
159
177
  }
160
178
 
161
179
  setup() {
@@ -163,25 +181,18 @@ class DragSession {
163
181
  window.addEventListener('focus', this.onFocus, true);
164
182
  window.addEventListener('blur', this.onBlur, true);
165
183
  document.addEventListener('click', this.onClick, true);
184
+ document.addEventListener('pointerdown', this.onPointerDown, true);
166
185
 
167
186
  for (let event of CANCELED_EVENTS) {
168
187
  document.addEventListener(event, this.cancelEvent, true);
169
188
  }
170
189
 
171
- this.mutationObserver = new MutationObserver(() => {
172
- // JSDOM has a bug where MutationObserver enters an infinite loop if mutations
173
- // occur inside a MutationObserver callback. If running in Node, wait until
174
- // the next tick to update valid drop targets.
175
- // See https://github.com/jsdom/jsdom/issues/3096
176
- if (typeof setImmediate === 'function') {
177
- this.mutationImmediate = setImmediate(() => this.updateValidDropTargets());
178
- } else {
179
- this.updateValidDropTargets();
180
- }
181
- });
190
+ this.mutationObserver = new MutationObserver(() =>
191
+ this.updateValidDropTargets()
192
+ );
182
193
  this.updateValidDropTargets();
183
194
 
184
- announce(this.formatMessage(MESSAGES[getDragModality()]));
195
+ announce(this.stringFormatter.format(MESSAGES[getDragModality()]));
185
196
  }
186
197
 
187
198
  teardown() {
@@ -189,6 +200,7 @@ class DragSession {
189
200
  window.removeEventListener('focus', this.onFocus, true);
190
201
  window.removeEventListener('blur', this.onBlur, true);
191
202
  document.removeEventListener('click', this.onClick, true);
203
+ document.removeEventListener('pointerdown', this.onPointerDown, true);
192
204
 
193
205
  for (let event of CANCELED_EVENTS) {
194
206
  document.removeEventListener(event, this.cancelEvent, true);
@@ -196,9 +208,6 @@ class DragSession {
196
208
 
197
209
  this.mutationObserver.disconnect();
198
210
  this.restoreAriaHidden();
199
- if (this.mutationImmediate) {
200
- clearImmediate(this.mutationImmediate);
201
- }
202
211
  }
203
212
 
204
213
  onKeyDown(e: KeyboardEvent) {
@@ -274,25 +283,34 @@ class DragSession {
274
283
 
275
284
  onClick(e: MouseEvent) {
276
285
  this.cancelEvent(e);
286
+ if (e.detail === 0 || this.isVirtualClick) {
287
+ if (e.target === this.dragTarget.element) {
288
+ this.cancel();
289
+ return;
290
+ }
277
291
 
278
- if (e.detail !== 0) {
279
- return;
280
- }
281
-
282
- if (e.target === this.dragTarget.element) {
283
- this.cancel();
284
- return;
292
+ let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
293
+ if (dropTarget) {
294
+ let item = dropItems.get(e.target as HTMLElement);
295
+ this.setCurrentDropTarget(dropTarget, item);
296
+ this.drop(item);
297
+ }
285
298
  }
299
+ }
286
300
 
287
- let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
288
- if (dropTarget) {
289
- let item = dropItems.get(e.target as HTMLElement);
290
- this.setCurrentDropTarget(dropTarget, item);
291
- this.drop(item);
292
- }
301
+ onPointerDown(e: PointerEvent) {
302
+ // Android Talkback double tap has e.detail = 1 for onClick. Detect the virtual click in onPointerDown before onClick fires
303
+ // so we can properly perform cancel and drop operations.
304
+ this.cancelEvent(e);
305
+ this.isVirtualClick = isVirtualPointerEvent(e);
293
306
  }
294
307
 
295
308
  cancelEvent(e: Event) {
309
+ // Allow focusin and focusout on the drag target so focus ring works properly.
310
+ if ((e.type === 'focusin' || e.type === 'focusout') && e.target === this.dragTarget?.element) {
311
+ return;
312
+ }
313
+
296
314
  // Allow default for events that might cancel a click event
297
315
  if (!CLICK_EVENTS.includes(e.type)) {
298
316
  e.preventDefault();
@@ -394,6 +412,25 @@ class DragSession {
394
412
  }
395
413
  }
396
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
+
397
434
  setCurrentDropTarget(dropTarget: DropTarget, item?: DroppableItem) {
398
435
  if (dropTarget !== this.currentDropTarget) {
399
436
  if (this.currentDropTarget && typeof this.currentDropTarget.onDropExit === 'function') {
@@ -431,6 +468,13 @@ class DragSession {
431
468
 
432
469
  item?.element.focus();
433
470
  this.currentDropItem = item;
471
+
472
+ // Annouce first drop target after drag start announcement finishes.
473
+ // Otherwise, it will never get announced because drag start announcement is assertive.
474
+ if (!this.initialFocused) {
475
+ announce(item?.element.getAttribute('aria-label'), 'polite');
476
+ this.initialFocused = true;
477
+ }
434
478
  }
435
479
  }
436
480
 
@@ -448,6 +492,12 @@ class DragSession {
448
492
  });
449
493
  }
450
494
 
495
+ // Blur and re-focus the drop target so that the focus ring appears.
496
+ if (this.currentDropTarget) {
497
+ this.currentDropTarget.element.blur();
498
+ this.currentDropTarget.element.focus();
499
+ }
500
+
451
501
  this.setCurrentDropTarget(null);
452
502
  endDragging();
453
503
  }
@@ -458,7 +508,7 @@ class DragSession {
458
508
  this.dragTarget.element.focus();
459
509
  }
460
510
 
461
- announce(this.formatMessage('dropCanceled'));
511
+ announce(this.stringFormatter.format('dropCanceled'));
462
512
  }
463
513
 
464
514
  drop(item?: DroppableItem) {
@@ -496,7 +546,7 @@ class DragSession {
496
546
  }
497
547
 
498
548
  this.end();
499
- announce(this.formatMessage('dropComplete'));
549
+ announce(this.stringFormatter.format('dropComplete'));
500
550
  }
501
551
 
502
552
  activate() {
@@ -525,3 +575,19 @@ function findValidDropTargets(options: DragTarget) {
525
575
  return true;
526
576
  });
527
577
  }
578
+
579
+ function isVirtualPointerEvent(event: PointerEvent) {
580
+ // If the pointer size is zero, then we assume it's from a screen reader.
581
+ // Android TalkBack double tap will sometimes return a event with width and height of 1
582
+ // and pointerType === 'mouse' so we need to check for a specific combination of event attributes.
583
+ // Cannot use "event.pressure === 0" as the sole check due to Safari pointer events always returning pressure === 0
584
+ // instead of .5, see https://bugs.webkit.org/show_bug.cgi?id=206216
585
+ return (
586
+ (event.width === 0 && event.height === 0) ||
587
+ (event.width === 1 &&
588
+ event.height === 1 &&
589
+ event.pressure === 0 &&
590
+ event.detail === 0
591
+ )
592
+ );
593
+ }
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {DragItem, DragPreviewRenderer} from '@react-types/shared';
14
+ import {flushSync} from 'react-dom';
15
+ import React, {RefObject, useImperativeHandle, useRef, useState} from 'react';
16
+
17
+ export interface DragPreviewProps {
18
+ children: (items: DragItem[]) => JSX.Element
19
+ }
20
+
21
+ function DragPreview(props: DragPreviewProps, ref: RefObject<DragPreviewRenderer>) {
22
+ let render = props.children;
23
+ let [children, setChildren] = useState<JSX.Element>(null);
24
+ let domRef = useRef(null);
25
+
26
+ useImperativeHandle(ref, () => (items: DragItem[], callback: (node: HTMLElement) => void) => {
27
+ // This will be called during the onDragStart event by useDrag. We need to render the
28
+ // preview synchronously before this event returns so we can call event.dataTransfer.setDragImage.
29
+ flushSync(() => {
30
+ setChildren(render(items));
31
+ });
32
+
33
+ // Yield back to useDrag to set the drag image.
34
+ callback(domRef.current);
35
+
36
+ // Remove the preview from the DOM after a frame so the browser has time to paint.
37
+ requestAnimationFrame(() => {
38
+ setChildren(null);
39
+ });
40
+ }, [render]);
41
+
42
+ if (!children) {
43
+ return null;
44
+ }
45
+
46
+ return (
47
+ <div style={{zIndex: -100, position: 'absolute', top: 0, left: -100000}} ref={domRef}>
48
+ {children}
49
+ </div>
50
+ );
51
+ }
52
+
53
+ let _DragPreview = React.forwardRef(DragPreview);
54
+ export {_DragPreview as DragPreview};
@@ -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/constants.ts CHANGED
@@ -10,6 +10,8 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import {DropOperation} from '@react-types/shared';
14
+
13
15
  export enum DROP_OPERATION {
14
16
  none = 0,
15
17
  cancel = 0,
@@ -31,7 +33,7 @@ export const DROP_OPERATION_ALLOWED = {
31
33
 
32
34
  export const EFFECT_ALLOWED = invert(DROP_OPERATION_ALLOWED);
33
35
  export const DROP_EFFECT = invert(DROP_OPERATION);
34
- export const DROP_EFFECT_TO_DROP_OPERATION = {
36
+ export const DROP_EFFECT_TO_DROP_OPERATION: {[name: string]: DropOperation} = {
35
37
  none: 'cancel',
36
38
  link: 'link',
37
39
  copy: 'copy',