@react-aria/dnd
Advanced tools
Comparing version 3.0.0-nightly.2674 to 3.0.0-nightly.2677
{ | ||
"name": "@react-aria/dnd", | ||
"version": "3.0.0-nightly.2674+faa93480", | ||
"version": "3.0.0-nightly.2677+1e2b7f28", | ||
"description": "Spectrum UI components in React", | ||
@@ -21,12 +21,12 @@ "license": "Apache-2.0", | ||
"@babel/runtime": "^7.6.2", | ||
"@react-aria/i18n": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/interactions": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/live-announcer": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/overlays": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/utils": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/visually-hidden": "3.0.0-nightly.994+faa93480", | ||
"@react-stately/dnd": "3.0.0-nightly.2674+faa93480", | ||
"@react-stately/selection": "3.0.0-nightly.994+faa93480", | ||
"@react-types/button": "3.3.2-nightly.2674+faa93480", | ||
"@react-types/shared": "3.0.0-nightly.994+faa93480" | ||
"@react-aria/i18n": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-aria/interactions": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-aria/live-announcer": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-aria/overlays": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-aria/utils": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-aria/visually-hidden": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-stately/dnd": "3.0.0-nightly.2677+1e2b7f28", | ||
"@react-stately/selection": "3.0.0-nightly.997+1e2b7f28", | ||
"@react-types/button": "3.3.2-nightly.2677+1e2b7f28", | ||
"@react-types/shared": "3.0.0-nightly.997+1e2b7f28" | ||
}, | ||
@@ -40,3 +40,3 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "faa9348097bf62403e37e32daf897cdad5512e09" | ||
"gitHead": "1e2b7f280a04ead83e21b27fa580eb189e5f4186" | ||
} |
@@ -13,8 +13,9 @@ /* | ||
import {Collection, DropEvent, DropOperation, DroppableCollectionProps, DropPosition, DropTarget, KeyboardDelegate, Node} from '@react-types/shared'; | ||
import * as DragManager from './DragManager'; | ||
import {DropOperation, DroppableCollectionProps, DropPosition, DropTarget, KeyboardDelegate} from '@react-types/shared'; | ||
import {DroppableCollectionState} from '@react-stately/dnd'; | ||
import {getTypes} from './utils'; | ||
import {HTMLAttributes, RefObject, useEffect, useRef} from 'react'; | ||
import {HTMLAttributes, Key, RefObject, useCallback, useEffect, useLayoutEffect, useRef} from 'react'; | ||
import {mergeProps} from '@react-aria/utils'; | ||
import {setInteractionModality} from '@react-aria/interactions'; | ||
import {useAutoScroll} from './useAutoScroll'; | ||
@@ -33,2 +34,9 @@ import {useDrop} from './useDrop'; | ||
interface DroppingState { | ||
collection: Collection<Node<unknown>>, | ||
focusedKey: Key, | ||
selectedKeys: Set<Key>, | ||
timeout: NodeJS.Timeout | ||
} | ||
const DROP_POSITIONS: DropPosition[] = ['before', 'on', 'after']; | ||
@@ -101,10 +109,3 @@ | ||
if (state.target && typeof props.onDrop === 'function') { | ||
props.onDrop({ | ||
type: 'drop', | ||
x: e.x, // todo | ||
y: e.y, | ||
target: state.target, | ||
items: e.items, | ||
dropOperation: e.dropOperation | ||
}); | ||
onDrop(e, state.target); | ||
} | ||
@@ -114,3 +115,95 @@ } | ||
let droppingState = useRef<DroppingState>(null); | ||
let onDrop = useCallback((e: DropEvent, target: DropTarget) => { | ||
let {state} = localState; | ||
// Focus the collection. | ||
state.selectionManager.setFocused(true); | ||
// Save some state of the collection/selection before the drop occurs so we can compare later. | ||
let focusedKey = state.selectionManager.focusedKey; | ||
droppingState.current = { | ||
timeout: null, | ||
focusedKey, | ||
collection: state.collection, | ||
selectedKeys: state.selectionManager.selectedKeys | ||
}; | ||
localState.props.onDrop({ | ||
type: 'drop', | ||
x: e.x, // todo | ||
y: e.y, | ||
target, | ||
items: e.items, | ||
dropOperation: e.dropOperation | ||
}); | ||
// Wait for a short time period after the onDrop is called to allow the data to be read asynchronously | ||
// and for React to re-render. If an insert occurs during this time, it will be selected/focused below. | ||
// If items are not "immediately" inserted by the onDrop handler, the application will need to handle | ||
// selecting and focusing those items themselves. | ||
droppingState.current.timeout = setTimeout(() => { | ||
// If focus didn't move already (e.g. due to an insert), and the user dropped on an item, | ||
// focus that item and show the focus ring to give the user feedback that the drop occurred. | ||
// Also show the focus ring if the focused key is not selected, e.g. in case of a reorder. | ||
let {state} = localState; | ||
if (state.selectionManager.focusedKey === focusedKey) { | ||
if (target.type === 'item' && target.dropPosition === 'on' && state.collection.getItem(target.key) != null) { | ||
state.selectionManager.setFocusedKey(target.key); | ||
state.selectionManager.setFocused(true); | ||
setInteractionModality('keyboard'); | ||
} else if (!state.selectionManager.isSelected(focusedKey)) { | ||
setInteractionModality('keyboard'); | ||
} | ||
} | ||
droppingState.current = null; | ||
}, 50); | ||
}, [localState]); | ||
// eslint-disable-next-line arrow-body-style | ||
useEffect(() => { | ||
return () => { | ||
if (droppingState.current) { | ||
clearTimeout(droppingState.current.timeout); | ||
} | ||
}; | ||
}, []); | ||
useLayoutEffect(() => { | ||
// If an insert occurs during a drop, we want to immediately select these items to give | ||
// feedback to the user that a drop occurred. Only do this if the selection didn't change | ||
// since the drop started so we don't override if the user or application did something. | ||
if ( | ||
droppingState.current && | ||
state.selectionManager.isFocused && | ||
state.collection.size > droppingState.current.collection.size && | ||
state.selectionManager.isSelectionEqual(droppingState.current.selectedKeys) | ||
) { | ||
let newKeys = new Set<Key>(); | ||
for (let key of state.collection.getKeys()) { | ||
if (!droppingState.current.collection.getItem(key)) { | ||
newKeys.add(key); | ||
} | ||
} | ||
state.selectionManager.setSelectedKeys(newKeys); | ||
// If the focused item didn't change since the drop occurred, also focus the first | ||
// inserted item. If selection is disabled, then also show the focus ring so there | ||
// is some indication that items were added. | ||
if (state.selectionManager.focusedKey === droppingState.current.focusedKey) { | ||
let first = newKeys.keys().next().value; | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') { | ||
setInteractionModality('keyboard'); | ||
} | ||
} | ||
droppingState.current = null; | ||
} | ||
}); | ||
useEffect(() => { | ||
let getNextTarget = (target: DropTarget, wrap = true): DropTarget => { | ||
@@ -322,10 +415,3 @@ if (!target) { | ||
if (localState.state.target && typeof localState.props.onDrop === 'function') { | ||
localState.props.onDrop({ | ||
type: 'drop', | ||
x: e.x, // todo | ||
y: e.y, | ||
target: target || localState.state.target, | ||
items: e.items, | ||
dropOperation: e.dropOperation | ||
}); | ||
onDrop(e, target || localState.state.target); | ||
} | ||
@@ -448,3 +534,3 @@ }, | ||
}); | ||
}, [localState, ref]); | ||
}, [localState, ref, onDrop]); | ||
@@ -451,0 +537,0 @@ let id = useDroppableCollectionId(state); |
@@ -157,3 +157,3 @@ /* | ||
// Unfortunately, this doesn't tell us what types of files the user is dragging, so we need to assume that any | ||
// type the user checks for is included. | ||
// type the user checks for is included. See https://bugs.webkit.org/show_bug.cgi?id=223517. | ||
this.includesUnknownTypes = !hasFiles && dataTransfer.types.includes('Files'); | ||
@@ -212,6 +212,7 @@ } | ||
if (!entry) { | ||
// For some reason, Chrome and Firefox include an item with type image/png when copy | ||
// and pasting any file or directory (no matter the type), but return `null` for both | ||
// For some reason, Firefox includes an item with type image/png when copy | ||
// and pasting any file or directory (no matter the type), but returns `null` for both | ||
// item.getAsFile() and item.webkitGetAsEntry(). Safari works as expected. Ignore this | ||
// item if this happens. | ||
// item if this happens. See https://bugzilla.mozilla.org/show_bug.cgi?id=1699743. | ||
// This was recently fixed in Chrome Canary: https://bugs.chromium.org/p/chromium/issues/detail?id=1175483. | ||
continue; | ||
@@ -218,0 +219,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
544546
5759