@react-aria/dnd 3.0.0-alpha.0 → 3.0.0-alpha.11
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/main.js +2725 -1839
- package/dist/main.js.map +1 -1
- package/dist/module.js +2717 -1783
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +34 -30
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -14
- package/src/DragManager.ts +91 -43
- package/src/DragPreview.tsx +54 -0
- package/src/constants.ts +3 -1
- package/src/index.ts +17 -7
- package/src/useClipboard.ts +5 -5
- package/src/useDrag.ts +40 -39
- package/src/useDraggableItem.ts +9 -11
- package/src/useDrop.ts +9 -4
- package/src/useDropIndicator.ts +10 -10
- package/src/useDroppableCollection.ts +170 -31
- package/src/useDroppableItem.ts +2 -2
- package/src/useVirtualDrop.ts +5 -5
- package/src/utils.ts +6 -24
package/dist/types.d.ts
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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, DropTarget, KeyboardDelegate, DragEndEvent, DragItem, DragMoveEvent, DragPreviewRenderer, DragStartEvent, DOMAttributes, DropItem } from "@react-types/shared";
|
|
4
3
|
import { DroppableCollectionState, DraggableCollectionState } from "@react-stately/dnd";
|
|
5
|
-
|
|
6
|
-
|
|
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>;
|
|
21
7
|
getDropOperation?: (types: DragTypes, allowedOperations: DropOperation[]) => DropOperation;
|
|
22
8
|
getDropOperationForPoint?: (types: DragTypes, allowedOperations: DropOperation[], x: number, y: number) => DropOperation;
|
|
@@ -26,49 +12,67 @@ interface DropOptions {
|
|
|
26
12
|
onDropExit?: (e: DropExitEvent) => void;
|
|
27
13
|
onDrop?: (e: DropEvent) => void;
|
|
28
14
|
}
|
|
29
|
-
interface DropResult {
|
|
15
|
+
export interface DropResult {
|
|
30
16
|
dropProps: HTMLAttributes<HTMLElement>;
|
|
31
17
|
isDropTarget: boolean;
|
|
32
18
|
}
|
|
33
19
|
export function useDrop(options: DropOptions): DropResult;
|
|
34
|
-
interface DroppableCollectionOptions extends DroppableCollectionProps {
|
|
20
|
+
export interface DroppableCollectionOptions extends DroppableCollectionProps {
|
|
35
21
|
keyboardDelegate: KeyboardDelegate;
|
|
36
22
|
getDropTargetFromPoint: (x: number, y: number) => DropTarget | null;
|
|
37
23
|
}
|
|
38
|
-
interface DroppableCollectionResult {
|
|
24
|
+
export interface DroppableCollectionResult {
|
|
39
25
|
collectionProps: HTMLAttributes<HTMLElement>;
|
|
40
26
|
}
|
|
41
27
|
export function useDroppableCollection(props: DroppableCollectionOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableCollectionResult;
|
|
42
|
-
interface DroppableItemOptions {
|
|
28
|
+
export interface DroppableItemOptions {
|
|
43
29
|
target: DropTarget;
|
|
44
30
|
}
|
|
45
|
-
interface DroppableItemResult {
|
|
31
|
+
export interface DroppableItemResult {
|
|
46
32
|
dropProps: HTMLAttributes<HTMLElement>;
|
|
47
33
|
}
|
|
48
34
|
export function useDroppableItem(options: DroppableItemOptions, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DroppableItemResult;
|
|
49
|
-
interface DropIndicatorProps {
|
|
35
|
+
export interface DropIndicatorProps {
|
|
50
36
|
target: DropTarget;
|
|
51
37
|
}
|
|
52
|
-
interface DropIndicatorAria {
|
|
38
|
+
export interface DropIndicatorAria {
|
|
53
39
|
dropIndicatorProps: HTMLAttributes<HTMLElement>;
|
|
54
40
|
}
|
|
55
41
|
export function useDropIndicator(props: DropIndicatorProps, state: DroppableCollectionState, ref: RefObject<HTMLElement>): DropIndicatorAria;
|
|
56
|
-
interface
|
|
42
|
+
export interface DragOptions {
|
|
43
|
+
onDragStart?: (e: DragStartEvent) => void;
|
|
44
|
+
onDragMove?: (e: DragMoveEvent) => void;
|
|
45
|
+
onDragEnd?: (e: DragEndEvent) => void;
|
|
46
|
+
getItems: () => DragItem[];
|
|
47
|
+
preview?: RefObject<DragPreviewRenderer>;
|
|
48
|
+
getAllowedDropOperations?: () => DropOperation[];
|
|
49
|
+
}
|
|
50
|
+
export interface DragResult {
|
|
51
|
+
dragProps: HTMLAttributes<HTMLElement>;
|
|
52
|
+
dragButtonProps: AriaButtonProps;
|
|
53
|
+
isDragging: boolean;
|
|
54
|
+
}
|
|
55
|
+
export function useDrag(options: DragOptions): DragResult;
|
|
56
|
+
export interface DraggableItemProps {
|
|
57
57
|
key: Key;
|
|
58
58
|
}
|
|
59
|
-
interface DraggableItemResult {
|
|
59
|
+
export interface DraggableItemResult {
|
|
60
60
|
dragProps: HTMLAttributes<HTMLElement>;
|
|
61
61
|
dragButtonProps: AriaButtonProps;
|
|
62
62
|
}
|
|
63
63
|
export function useDraggableItem(props: DraggableItemProps, state: DraggableCollectionState): DraggableItemResult;
|
|
64
|
-
interface
|
|
64
|
+
export interface DragPreviewProps {
|
|
65
|
+
children: (items: DragItem[]) => JSX.Element;
|
|
66
|
+
}
|
|
67
|
+
export let DragPreview: React.ForwardRefExoticComponent<DragPreviewProps & React.RefAttributes<DragPreviewRenderer>>;
|
|
68
|
+
export interface ClipboardProps {
|
|
65
69
|
getItems?: () => DragItem[];
|
|
66
70
|
onCopy?: () => void;
|
|
67
71
|
onCut?: () => void;
|
|
68
72
|
onPaste?: (items: DropItem[]) => void;
|
|
69
73
|
}
|
|
70
|
-
interface ClipboardResult {
|
|
71
|
-
clipboardProps:
|
|
74
|
+
export interface ClipboardResult {
|
|
75
|
+
clipboardProps: DOMAttributes;
|
|
72
76
|
}
|
|
73
77
|
export function useClipboard(options: ClipboardProps): ClipboardResult;
|
|
74
78
|
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/dnd",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.11",
|
|
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
|
-
"@
|
|
22
|
-
"@react-aria/
|
|
23
|
-
"@react-aria/
|
|
24
|
-
"@react-aria/
|
|
25
|
-
"@react-aria/
|
|
26
|
-
"@react-aria/
|
|
27
|
-
"@react-
|
|
28
|
-
"@react-stately/
|
|
29
|
-
"@react-
|
|
30
|
-
"@react-types/
|
|
21
|
+
"@internationalized/string": "^3.0.0",
|
|
22
|
+
"@react-aria/i18n": "^3.5.0",
|
|
23
|
+
"@react-aria/interactions": "^3.10.0",
|
|
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"
|
|
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": "
|
|
40
|
+
"gitHead": "cd7c0ec917122c7612f653c22f8ed558f8b66ecd"
|
|
40
41
|
}
|
package/src/DragManager.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
47
|
+
element: FocusableElement,
|
|
46
48
|
target: DroppableCollectionTarget,
|
|
47
49
|
getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation
|
|
48
50
|
}
|
|
@@ -55,22 +57,24 @@ export function registerDropItem(item: DroppableItem) {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
interface DragTarget {
|
|
58
|
-
element:
|
|
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,
|
|
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,
|
|
71
|
+
dragSession = new DragSession(target, stringFormatter);
|
|
70
72
|
requestAnimationFrame(() => {
|
|
71
73
|
dragSession.setup();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
if (
|
|
75
|
+
getDragModality() === 'keyboard' ||
|
|
76
|
+
(getDragModality() === 'touch' && getInteractionModality() === 'virtual')
|
|
77
|
+
) {
|
|
74
78
|
dragSession.next();
|
|
75
79
|
}
|
|
76
80
|
});
|
|
@@ -101,6 +105,16 @@ function endDragging() {
|
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
export function isValidDropTarget(element: Element): boolean {
|
|
109
|
+
for (let target of dropTargets.keys()) {
|
|
110
|
+
if (target.contains(element)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
const CANCELED_EVENTS = [
|
|
105
119
|
'pointerdown',
|
|
106
120
|
'pointermove',
|
|
@@ -119,7 +133,9 @@ const CANCELED_EVENTS = [
|
|
|
119
133
|
'touchstart',
|
|
120
134
|
'touchmove',
|
|
121
135
|
'touchend',
|
|
122
|
-
'keyup'
|
|
136
|
+
'keyup',
|
|
137
|
+
'focusin',
|
|
138
|
+
'focusout'
|
|
123
139
|
];
|
|
124
140
|
|
|
125
141
|
const CLICK_EVENTS = [
|
|
@@ -141,19 +157,22 @@ class DragSession {
|
|
|
141
157
|
currentDropItem: DroppableItem;
|
|
142
158
|
dropOperation: DropOperation;
|
|
143
159
|
private mutationObserver: MutationObserver;
|
|
144
|
-
private mutationImmediate: NodeJS.Immediate;
|
|
145
160
|
private restoreAriaHidden: () => void;
|
|
146
|
-
private
|
|
161
|
+
private stringFormatter: LocalizedStringFormatter;
|
|
162
|
+
private isVirtualClick: boolean;
|
|
163
|
+
private initialFocused: boolean;
|
|
147
164
|
|
|
148
|
-
constructor(target: DragTarget,
|
|
165
|
+
constructor(target: DragTarget, stringFormatter: LocalizedStringFormatter) {
|
|
149
166
|
this.dragTarget = target;
|
|
150
|
-
this.
|
|
167
|
+
this.stringFormatter = stringFormatter;
|
|
151
168
|
|
|
152
169
|
this.onKeyDown = this.onKeyDown.bind(this);
|
|
153
170
|
this.onFocus = this.onFocus.bind(this);
|
|
154
171
|
this.onBlur = this.onBlur.bind(this);
|
|
155
172
|
this.onClick = this.onClick.bind(this);
|
|
173
|
+
this.onPointerDown = this.onPointerDown.bind(this);
|
|
156
174
|
this.cancelEvent = this.cancelEvent.bind(this);
|
|
175
|
+
this.initialFocused = false;
|
|
157
176
|
}
|
|
158
177
|
|
|
159
178
|
setup() {
|
|
@@ -161,25 +180,18 @@ class DragSession {
|
|
|
161
180
|
window.addEventListener('focus', this.onFocus, true);
|
|
162
181
|
window.addEventListener('blur', this.onBlur, true);
|
|
163
182
|
document.addEventListener('click', this.onClick, true);
|
|
183
|
+
document.addEventListener('pointerdown', this.onPointerDown, true);
|
|
164
184
|
|
|
165
185
|
for (let event of CANCELED_EVENTS) {
|
|
166
186
|
document.addEventListener(event, this.cancelEvent, true);
|
|
167
187
|
}
|
|
168
188
|
|
|
169
|
-
this.mutationObserver = new MutationObserver(() =>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// the next tick to update valid drop targets.
|
|
173
|
-
// See https://github.com/jsdom/jsdom/issues/3096
|
|
174
|
-
if (typeof setImmediate === 'function') {
|
|
175
|
-
this.mutationImmediate = setImmediate(() => this.updateValidDropTargets());
|
|
176
|
-
} else {
|
|
177
|
-
this.updateValidDropTargets();
|
|
178
|
-
}
|
|
179
|
-
});
|
|
189
|
+
this.mutationObserver = new MutationObserver(() =>
|
|
190
|
+
this.updateValidDropTargets()
|
|
191
|
+
);
|
|
180
192
|
this.updateValidDropTargets();
|
|
181
193
|
|
|
182
|
-
announce(this.
|
|
194
|
+
announce(this.stringFormatter.format(MESSAGES[getDragModality()]));
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
teardown() {
|
|
@@ -187,6 +199,7 @@ class DragSession {
|
|
|
187
199
|
window.removeEventListener('focus', this.onFocus, true);
|
|
188
200
|
window.removeEventListener('blur', this.onBlur, true);
|
|
189
201
|
document.removeEventListener('click', this.onClick, true);
|
|
202
|
+
document.removeEventListener('pointerdown', this.onPointerDown, true);
|
|
190
203
|
|
|
191
204
|
for (let event of CANCELED_EVENTS) {
|
|
192
205
|
document.removeEventListener(event, this.cancelEvent, true);
|
|
@@ -194,9 +207,6 @@ class DragSession {
|
|
|
194
207
|
|
|
195
208
|
this.mutationObserver.disconnect();
|
|
196
209
|
this.restoreAriaHidden();
|
|
197
|
-
if (this.mutationImmediate) {
|
|
198
|
-
clearImmediate(this.mutationImmediate);
|
|
199
|
-
}
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
onKeyDown(e: KeyboardEvent) {
|
|
@@ -272,25 +282,34 @@ class DragSession {
|
|
|
272
282
|
|
|
273
283
|
onClick(e: MouseEvent) {
|
|
274
284
|
this.cancelEvent(e);
|
|
285
|
+
if (e.detail === 0 || this.isVirtualClick) {
|
|
286
|
+
if (e.target === this.dragTarget.element) {
|
|
287
|
+
this.cancel();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
275
290
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return;
|
|
291
|
+
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
|
|
292
|
+
if (dropTarget) {
|
|
293
|
+
let item = dropItems.get(e.target as HTMLElement);
|
|
294
|
+
this.setCurrentDropTarget(dropTarget, item);
|
|
295
|
+
this.drop(item);
|
|
296
|
+
}
|
|
283
297
|
}
|
|
298
|
+
}
|
|
284
299
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
300
|
+
onPointerDown(e: PointerEvent) {
|
|
301
|
+
// Android Talkback double tap has e.detail = 1 for onClick. Detect the virtual click in onPointerDown before onClick fires
|
|
302
|
+
// so we can properly perform cancel and drop operations.
|
|
303
|
+
this.cancelEvent(e);
|
|
304
|
+
this.isVirtualClick = isVirtualPointerEvent(e);
|
|
291
305
|
}
|
|
292
306
|
|
|
293
307
|
cancelEvent(e: Event) {
|
|
308
|
+
// Allow focusin and focusout on the drag target so focus ring works properly.
|
|
309
|
+
if ((e.type === 'focusin' || e.type === 'focusout') && e.target === this.dragTarget?.element) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
294
313
|
// Allow default for events that might cancel a click event
|
|
295
314
|
if (!CLICK_EVENTS.includes(e.type)) {
|
|
296
315
|
e.preventDefault();
|
|
@@ -429,6 +448,13 @@ class DragSession {
|
|
|
429
448
|
|
|
430
449
|
item?.element.focus();
|
|
431
450
|
this.currentDropItem = item;
|
|
451
|
+
|
|
452
|
+
// Annouce first drop target after drag start announcement finishes.
|
|
453
|
+
// Otherwise, it will never get announced because drag start announcement is assertive.
|
|
454
|
+
if (!this.initialFocused) {
|
|
455
|
+
announce(item?.element.getAttribute('aria-label'), 'polite');
|
|
456
|
+
this.initialFocused = true;
|
|
457
|
+
}
|
|
432
458
|
}
|
|
433
459
|
}
|
|
434
460
|
|
|
@@ -446,6 +472,12 @@ class DragSession {
|
|
|
446
472
|
});
|
|
447
473
|
}
|
|
448
474
|
|
|
475
|
+
// Blur and re-focus the drop target so that the focus ring appears.
|
|
476
|
+
if (this.currentDropTarget) {
|
|
477
|
+
this.currentDropTarget.element.blur();
|
|
478
|
+
this.currentDropTarget.element.focus();
|
|
479
|
+
}
|
|
480
|
+
|
|
449
481
|
this.setCurrentDropTarget(null);
|
|
450
482
|
endDragging();
|
|
451
483
|
}
|
|
@@ -456,7 +488,7 @@ class DragSession {
|
|
|
456
488
|
this.dragTarget.element.focus();
|
|
457
489
|
}
|
|
458
490
|
|
|
459
|
-
announce(this.
|
|
491
|
+
announce(this.stringFormatter.format('dropCanceled'));
|
|
460
492
|
}
|
|
461
493
|
|
|
462
494
|
drop(item?: DroppableItem) {
|
|
@@ -494,7 +526,7 @@ class DragSession {
|
|
|
494
526
|
}
|
|
495
527
|
|
|
496
528
|
this.end();
|
|
497
|
-
announce(this.
|
|
529
|
+
announce(this.stringFormatter.format('dropComplete'));
|
|
498
530
|
}
|
|
499
531
|
|
|
500
532
|
activate() {
|
|
@@ -523,3 +555,19 @@ function findValidDropTargets(options: DragTarget) {
|
|
|
523
555
|
return true;
|
|
524
556
|
});
|
|
525
557
|
}
|
|
558
|
+
|
|
559
|
+
function isVirtualPointerEvent(event: PointerEvent) {
|
|
560
|
+
// If the pointer size is zero, then we assume it's from a screen reader.
|
|
561
|
+
// Android TalkBack double tap will sometimes return a event with width and height of 1
|
|
562
|
+
// and pointerType === 'mouse' so we need to check for a specific combination of event attributes.
|
|
563
|
+
// Cannot use "event.pressure === 0" as the sole check due to Safari pointer events always returning pressure === 0
|
|
564
|
+
// instead of .5, see https://bugs.webkit.org/show_bug.cgi?id=206216
|
|
565
|
+
return (
|
|
566
|
+
(event.width === 0 && event.height === 0) ||
|
|
567
|
+
(event.width === 1 &&
|
|
568
|
+
event.height === 1 &&
|
|
569
|
+
event.pressure === 0 &&
|
|
570
|
+
event.detail === 0
|
|
571
|
+
)
|
|
572
|
+
);
|
|
573
|
+
}
|
|
@@ -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};
|
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',
|
package/src/index.ts
CHANGED
|
@@ -10,10 +10,20 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
export
|
|
15
|
-
export
|
|
16
|
-
export
|
|
17
|
-
export
|
|
18
|
-
export
|
|
19
|
-
export
|
|
13
|
+
export type {DroppableCollectionOptions, DroppableCollectionResult} from './useDroppableCollection';
|
|
14
|
+
export type {DroppableItemOptions, DroppableItemResult} from './useDroppableItem';
|
|
15
|
+
export type {DropIndicatorProps, DropIndicatorAria} from './useDropIndicator';
|
|
16
|
+
export type {DraggableItemProps, DraggableItemResult} from './useDraggableItem';
|
|
17
|
+
export type {DragPreviewProps} from './DragPreview';
|
|
18
|
+
export type {DragOptions, DragResult} from './useDrag';
|
|
19
|
+
export type {DropOptions, DropResult} from './useDrop';
|
|
20
|
+
export type {ClipboardProps, ClipboardResult} from './useClipboard';
|
|
21
|
+
|
|
22
|
+
export {useDrag} from './useDrag';
|
|
23
|
+
export {useDrop} from './useDrop';
|
|
24
|
+
export {useDroppableCollection} from './useDroppableCollection';
|
|
25
|
+
export {useDroppableItem} from './useDroppableItem';
|
|
26
|
+
export {useDropIndicator} from './useDropIndicator';
|
|
27
|
+
export {useDraggableItem} from './useDraggableItem';
|
|
28
|
+
export {useClipboard} from './useClipboard';
|
|
29
|
+
export {DragPreview} from './DragPreview';
|
package/src/useClipboard.ts
CHANGED
|
@@ -11,20 +11,20 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {chain} from '@react-aria/utils';
|
|
14
|
-
import {DragItem, DropItem} from '@react-types/shared';
|
|
15
|
-
import {HTMLAttributes, useEffect, useRef} from 'react';
|
|
14
|
+
import {DOMAttributes, DragItem, DropItem} from '@react-types/shared';
|
|
16
15
|
import {readFromDataTransfer, writeToDataTransfer} from './utils';
|
|
16
|
+
import {useEffect, useRef} from 'react';
|
|
17
17
|
import {useFocus} from '@react-aria/interactions';
|
|
18
18
|
|
|
19
|
-
interface ClipboardProps {
|
|
19
|
+
export interface ClipboardProps {
|
|
20
20
|
getItems?: () => DragItem[],
|
|
21
21
|
onCopy?: () => void,
|
|
22
22
|
onCut?: () => void,
|
|
23
23
|
onPaste?: (items: DropItem[]) => void
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
interface ClipboardResult {
|
|
27
|
-
clipboardProps:
|
|
26
|
+
export interface ClipboardResult {
|
|
27
|
+
clipboardProps: DOMAttributes
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const globalEvents = new Map();
|