-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update simplified DnD handler api and memoize useDnDHooks #3533
Changes from all commits
15bd874
9e96fae
f831552
60993aa
bd6bef7
3bde69a
d282671
5f803cc
9e0e4dc
5df8143
b5f21c0
0f23e7c
d322b70
094aa06
4dd87af
a9f5e5a
53b3360
604cc03
63961c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,11 +10,11 @@ | |
* governing permissions and limitations under the License. | ||
*/ | ||
|
||
import {clearGlobalDnDState, globalDndState, setDropCollectionRef, useDroppableCollectionId} from './utils'; | ||
import {clearGlobalDnDState, globalDndState, isInternalDropOperation, setDropCollectionRef, useDroppableCollectionId} from './utils'; | ||
import {Collection, DropEvent, DropOperation, DroppableCollectionDropEvent, DroppableCollectionProps, DropPosition, DropTarget, DropTargetDelegate, KeyboardDelegate, Node} from '@react-types/shared'; | ||
import {DIRECTORY_DRAG_TYPE, getTypes} from './utils'; | ||
import * as DragManager from './DragManager'; | ||
import {DroppableCollectionState} from '@react-stately/dnd'; | ||
import {getTypes} from './utils'; | ||
import {HTMLAttributes, Key, RefObject, useCallback, useEffect, useRef} from 'react'; | ||
import {mergeProps, useLayoutEffect} from '@react-aria/utils'; | ||
import {setInteractionModality} from '@react-aria/interactions'; | ||
|
@@ -55,24 +55,24 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
onRootDrop, | ||
onItemDrop, | ||
onReorder, | ||
acceptedDragTypes, | ||
acceptedDragTypes = 'all', | ||
shouldAcceptItemDrop | ||
} = localState.props; | ||
|
||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
let {draggingKeys} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bunch of these |
||
let { | ||
target, | ||
dropOperation, | ||
items | ||
} = e; | ||
|
||
let filteredItems = items; | ||
if (acceptedDragTypes) { | ||
if (acceptedDragTypes !== 'all' || shouldAcceptItemDrop) { | ||
filteredItems = items.filter(item => { | ||
let itemTypes: Set<string>; | ||
let itemTypes: Set<string | symbol>; | ||
if (item.kind === 'directory') { | ||
itemTypes = new Set(['directory']); | ||
itemTypes = new Set([DIRECTORY_DRAG_TYPE]); | ||
} else { | ||
itemTypes = item.kind === 'file' ? new Set([item.type]) : item.types; | ||
} | ||
|
@@ -90,22 +90,24 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
}); | ||
} | ||
|
||
if (target.type === 'root' && onRootDrop) { | ||
await onRootDrop({items: filteredItems, dropOperation}); | ||
} | ||
|
||
if (target.type === 'item') { | ||
if (target.dropPosition === 'on' && onItemDrop) { | ||
await onItemDrop({items: filteredItems, dropOperation, isInternalDrop, target: {key: target.key, dropPosition: 'on'}}); | ||
if (filteredItems.length > 0) { | ||
if (target.type === 'root' && onRootDrop) { | ||
await onRootDrop({items: filteredItems, dropOperation}); | ||
} | ||
|
||
if (target.dropPosition !== 'on') { | ||
if (!isInternalDrop && onInsert) { | ||
await onInsert({items: filteredItems, dropOperation, target: {key: target.key, dropPosition: target.dropPosition}}); | ||
if (target.type === 'item') { | ||
if (target.dropPosition === 'on' && onItemDrop) { | ||
await onItemDrop({items: filteredItems, dropOperation, isInternal, target}); | ||
} | ||
|
||
if (isInternalDrop && onReorder) { | ||
await onReorder({keys: draggingKeys, dropOperation, target: {key: target.key, dropPosition: target.dropPosition}}); | ||
if (target.dropPosition !== 'on') { | ||
if (!isInternal && onInsert) { | ||
await onInsert({items: filteredItems, dropOperation, target}); | ||
} | ||
|
||
if (isInternal && onReorder) { | ||
await onReorder({keys: draggingKeys, dropOperation, target}); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -122,22 +124,22 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
autoScroll.move(e.x, e.y); | ||
}, | ||
getDropOperationForPoint(types, allowedOperations, x, y) { | ||
let {draggingCollectionRef, draggingKeys, dropCollectionRef} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
let isValidDropTarget = (target) => state.getDropOperation({target, types, allowedOperations, isInternalDrop, draggingKeys}) !== 'cancel'; | ||
let {draggingKeys, dropCollectionRef} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
let isValidDropTarget = (target) => state.getDropOperation({target, types, allowedOperations, isInternal, draggingKeys}) !== 'cancel'; | ||
let target = props.dropTargetDelegate.getDropTargetFromPoint(x, y, isValidDropTarget); | ||
if (!target) { | ||
localState.dropOperation = 'cancel'; | ||
localState.nextTarget = null; | ||
return 'cancel'; | ||
} | ||
|
||
localState.dropOperation = state.getDropOperation({target, types, allowedOperations, isInternalDrop, draggingKeys}); | ||
localState.dropOperation = state.getDropOperation({target, types, allowedOperations, isInternal, draggingKeys}); | ||
|
||
// If the target doesn't accept the drop, see if the root accepts it instead. | ||
if (localState.dropOperation === 'cancel') { | ||
let rootTarget: DropTarget = {type: 'root'}; | ||
let dropOperation = state.getDropOperation({target: rootTarget, types, allowedOperations, isInternalDrop, draggingKeys}); | ||
let dropOperation = state.getDropOperation({target: rootTarget, types, allowedOperations, isInternal, draggingKeys}); | ||
if (dropOperation !== 'cancel') { | ||
target = rootTarget; | ||
localState.dropOperation = dropOperation; | ||
|
@@ -375,15 +377,15 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
): DropTarget => { | ||
let seenRoot = 0; | ||
let operation: DropOperation; | ||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
let {draggingKeys} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
do { | ||
let nextTarget = getNextTarget(target, wrap); | ||
if (!nextTarget) { | ||
return null; | ||
} | ||
target = nextTarget; | ||
operation = localState.state.getDropOperation({target: nextTarget, types, allowedOperations: allowedDropOperations, isInternalDrop, draggingKeys}); | ||
operation = localState.state.getDropOperation({target: nextTarget, types, allowedOperations: allowedDropOperations, isInternal, draggingKeys}); | ||
if (target.type === 'root') { | ||
seenRoot++; | ||
} | ||
|
@@ -404,9 +406,9 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
element: ref.current, | ||
getDropOperation(types, allowedOperations) { | ||
if (localState.state.target) { | ||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
return localState.state.getDropOperation({target: localState.state.target, types, allowedOperations, isInternalDrop, draggingKeys}); | ||
let {draggingKeys} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
return localState.state.getDropOperation({target: localState.state.target, types, allowedOperations, isInternal, draggingKeys}); | ||
} | ||
|
||
// Check if any of the targets accept the drop. | ||
|
@@ -418,7 +420,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
let types = getTypes(drag.items); | ||
let selectionManager = localState.state.selectionManager; | ||
let target: DropTarget; | ||
// Update the drop collection ref tracker for useDroppableItem's getDropOperation isInternalDrop check | ||
// Update the drop collection ref tracker for useDroppableItem's getDropOperation isInternal check | ||
setDropCollectionRef(ref); | ||
|
||
// When entering the droppable collection for the first time, the default drop target | ||
|
@@ -452,10 +454,10 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
dropPosition | ||
}; | ||
|
||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
let {draggingKeys} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
// If the default target is not valid, find the next one that is. | ||
if (localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternalDrop, draggingKeys}) === 'cancel') { | ||
if (localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternal, draggingKeys}) === 'cancel') { | ||
target = nextValidTarget(target, types, drag.allowedDropOperations, getNextTarget, false) | ||
?? nextValidTarget(target, types, drag.allowedDropOperations, getPreviousTarget, false); | ||
} | ||
|
@@ -556,8 +558,8 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
// If the target does not accept the drop, find the next valid target. | ||
// If no next valid target, find the previous valid target. | ||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current === ref?.current; | ||
let operation = localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternalDrop, draggingKeys}); | ||
let isInternal = draggingCollectionRef?.current === ref?.current; | ||
let operation = localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternal, draggingKeys}); | ||
if (operation === 'cancel') { | ||
target = nextValidTarget(target, types, drag.allowedDropOperations, getNextTarget, false) | ||
?? nextValidTarget(target, types, drag.allowedDropOperations, getPreviousTarget, false); | ||
|
@@ -599,9 +601,9 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: | |
|
||
// If the target does not accept the drop, find the previous valid target. | ||
// If no next valid target, find the next valid target. | ||
let {draggingCollectionRef, draggingKeys} = globalDndState; | ||
let isInternalDrop = draggingCollectionRef?.current != null && draggingCollectionRef?.current === ref?.current; | ||
let operation = localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternalDrop, draggingKeys}); | ||
let {draggingKeys} = globalDndState; | ||
let isInternal = isInternalDropOperation(ref); | ||
let operation = localState.state.getDropOperation({target, types, allowedOperations: drag.allowedDropOperations, isInternal, draggingKeys}); | ||
if (operation === 'cancel') { | ||
target = nextValidTarget(target, types, drag.allowedDropOperations, getPreviousTarget, false) | ||
?? nextValidTarget(target, types, drag.allowedDropOperations, getNextTarget, false); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was doing some auditing and have a clarifying question: should things like
@react-stately/dnd
/@react-types/*
be in devDeps instead if they are only used to import types/interfaces? I was under the impression that we could do aimport type
for things like that and thus avoid needing to include them within the dependencies because they only matter during dev (aka typechecking)