@react-aria/dnd
Advanced tools
Comparing version 3.6.2-nightly.4698 to 3.6.2-nightly.4700
var $4620ae0dc40f0031$exports = require("./utils.main.js"); | ||
var $hMqHl$reactarialiveannouncer = require("@react-aria/live-announcer"); | ||
var $hMqHl$reactariaoverlays = require("@react-aria/overlays"); | ||
var $hMqHl$reactdom = require("react-dom"); | ||
var $hMqHl$reactariautils = require("@react-aria/utils"); | ||
@@ -33,3 +32,2 @@ var $hMqHl$react = require("react"); | ||
let $28e10663603f5ea1$var$dropTargets = new Map(); | ||
@@ -355,18 +353,8 @@ let $28e10663603f5ea1$var$dropItems = new Map(); | ||
} | ||
// Blur and re-focus the drop target so that the focus ring appears. | ||
if (this.currentDropTarget) { | ||
// Since we cancel all focus events in drag sessions, refire blur to make sure state gets updated so drag target doesn't think it's still focused | ||
// i.e. When you from one list to another during a drag session, we need the blur to fire on the first list after the drag. | ||
if (!this.dragTarget.element.contains(this.currentDropTarget.element)) { | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('blur')); | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('focusout', { | ||
bubbles: true | ||
})); | ||
} | ||
// Re-focus the focusedKey upon reorder. This requires a React rerender between blurring and focusing. | ||
(0, $hMqHl$reactdom.flushSync)(()=>{ | ||
this.currentDropTarget.element.blur(); | ||
}); | ||
this.currentDropTarget.element.focus(); | ||
} | ||
if (this.currentDropTarget && !this.currentDropTarget.preventFocusOnDrop) // Re-trigger focus event on active element, since it will not have received it during dragging (see cancelEvent). | ||
// This corrects state such as whether focus ring should appear. | ||
// useDroppableCollection handles this itself, so this is only for standalone drop zones. | ||
document.activeElement.dispatchEvent(new FocusEvent('focusin', { | ||
bubbles: true | ||
})); | ||
this.setCurrentDropTarget(null); | ||
@@ -373,0 +361,0 @@ } |
import {getDragModality as $7252cd45fc48c07c$export$1fb2158d224b542c, getTypes as $7252cd45fc48c07c$export$e1d41611756c6326} from "./utils.module.js"; | ||
import {announce as $irqIb$announce} from "@react-aria/live-announcer"; | ||
import {ariaHideOutside as $irqIb$ariaHideOutside} from "@react-aria/overlays"; | ||
import {flushSync as $irqIb$flushSync} from "react-dom"; | ||
import {isVirtualClick as $irqIb$isVirtualClick, isVirtualPointerEvent as $irqIb$isVirtualPointerEvent} from "@react-aria/utils"; | ||
@@ -23,3 +22,2 @@ import {useState as $irqIb$useState, useEffect as $irqIb$useEffect} from "react"; | ||
let $67560de7c78cb232$var$dropTargets = new Map(); | ||
@@ -345,18 +343,8 @@ let $67560de7c78cb232$var$dropItems = new Map(); | ||
} | ||
// Blur and re-focus the drop target so that the focus ring appears. | ||
if (this.currentDropTarget) { | ||
// Since we cancel all focus events in drag sessions, refire blur to make sure state gets updated so drag target doesn't think it's still focused | ||
// i.e. When you from one list to another during a drag session, we need the blur to fire on the first list after the drag. | ||
if (!this.dragTarget.element.contains(this.currentDropTarget.element)) { | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('blur')); | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('focusout', { | ||
bubbles: true | ||
})); | ||
} | ||
// Re-focus the focusedKey upon reorder. This requires a React rerender between blurring and focusing. | ||
(0, $irqIb$flushSync)(()=>{ | ||
this.currentDropTarget.element.blur(); | ||
}); | ||
this.currentDropTarget.element.focus(); | ||
} | ||
if (this.currentDropTarget && !this.currentDropTarget.preventFocusOnDrop) // Re-trigger focus event on active element, since it will not have received it during dragging (see cancelEvent). | ||
// This corrects state such as whether focus ring should appear. | ||
// useDroppableCollection handles this itself, so this is only for standalone drop zones. | ||
document.activeElement.dispatchEvent(new FocusEvent('focusin', { | ||
bubbles: true | ||
})); | ||
this.setCurrentDropTarget(null); | ||
@@ -363,0 +351,0 @@ } |
@@ -185,19 +185,52 @@ var $4620ae0dc40f0031$exports = require("./utils.main.js"); | ||
let droppingState = (0, $foOxf$react.useRef)(null); | ||
let updateFocusAfterDrop = (0, $foOxf$react.useCallback)(()=>{ | ||
let { state: state } = localState; | ||
if (droppingState.current) { | ||
var _state_collection_getItem; | ||
let { target: target, collection: prevCollection, selectedKeys: prevSelectedKeys, focusedKey: prevFocusedKey, isInternal: isInternal, draggingKeys: draggingKeys } = droppingState.current; | ||
// 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 (state.collection.size > prevCollection.size && state.selectionManager.isSelectionEqual(prevSelectedKeys)) { | ||
let newKeys = new Set(); | ||
for (let key of state.collection.getKeys())if (!prevCollection.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 === prevFocusedKey) { | ||
let first = newKeys.keys().next().value; | ||
let item = state.collection.getItem(first); | ||
// If this is a cell, focus the parent row. | ||
if ((item === null || item === void 0 ? void 0 : item.type) === 'cell') first = item.parentKey; | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') (0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
} | ||
} else if (state.selectionManager.focusedKey === prevFocusedKey && isInternal && target.type === 'item' && target.dropPosition !== 'on' && draggingKeys.has((_state_collection_getItem = state.collection.getItem(prevFocusedKey)) === null || _state_collection_getItem === void 0 ? void 0 : _state_collection_getItem.parentKey)) { | ||
// Focus row instead of cell when reordering. | ||
state.selectionManager.setFocusedKey(state.collection.getItem(prevFocusedKey).parentKey); | ||
(0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
} else if (state.selectionManager.focusedKey === prevFocusedKey && target.type === 'item' && target.dropPosition === 'on' && state.collection.getItem(target.key) != null) { | ||
// 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. | ||
state.selectionManager.setFocusedKey(target.key); | ||
(0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
} else if (!state.selectionManager.isSelected(state.selectionManager.focusedKey)) (0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
state.selectionManager.setFocused(true); | ||
} | ||
}, [ | ||
localState | ||
]); | ||
let onDrop = (0, $foOxf$react.useCallback)((e, target)=>{ | ||
var _state_collection_getItem; | ||
let { state: 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; | ||
// If parent key was dragged, we want to use it instead (i.e. focus row instead of cell after dropping) | ||
if ((0, $4620ae0dc40f0031$exports.globalDndState).draggingKeys.has((_state_collection_getItem = state.collection.getItem(focusedKey)) === null || _state_collection_getItem === void 0 ? void 0 : _state_collection_getItem.parentKey)) { | ||
focusedKey = state.collection.getItem(focusedKey).parentKey; | ||
state.selectionManager.setFocusedKey(focusedKey); | ||
} | ||
droppingState.current = { | ||
timeout: null, | ||
focusedKey: focusedKey, | ||
focusedKey: state.selectionManager.focusedKey, | ||
collection: state.collection, | ||
selectedKeys: state.selectionManager.selectedKeys | ||
selectedKeys: state.selectionManager.selectedKeys, | ||
draggingKeys: (0, $4620ae0dc40f0031$exports.globalDndState).draggingKeys, | ||
isInternal: (0, $4620ae0dc40f0031$exports.isInternalDropOperation)(ref), | ||
target: target | ||
}; | ||
@@ -214,15 +247,6 @@ let onDropFn = localState.props.onDrop || defaultOnDrop; | ||
// 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. | ||
// and for React to re-render. If the collection didn't already change during this time (handled below), | ||
// update the focused key here. | ||
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: state } = localState; | ||
if (target.type === 'item' && target.dropPosition === 'on' && state.collection.getItem(target.key) != null) { | ||
state.selectionManager.setFocusedKey(target.key); | ||
state.selectionManager.setFocused(true); | ||
(0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
} else if (!state.selectionManager.isSelected(focusedKey)) (0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
updateFocusAfterDrop(); | ||
droppingState.current = null; | ||
@@ -232,3 +256,5 @@ }, 50); | ||
localState, | ||
defaultOnDrop | ||
defaultOnDrop, | ||
ref, | ||
updateFocusAfterDrop | ||
]); | ||
@@ -242,22 +268,4 @@ // eslint-disable-next-line arrow-body-style | ||
(0, $foOxf$reactariautils.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(); | ||
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; | ||
let item = state.collection.getItem(first); | ||
// If this is a cell, focus the parent row. | ||
if ((item === null || item === void 0 ? void 0 : item.type) === 'cell') first = item.parentKey; | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') (0, $foOxf$reactariainteractions.setInteractionModality)('keyboard'); | ||
} | ||
droppingState.current = null; | ||
} | ||
// If the collection changed after a drop, update the focused key. | ||
if (droppingState.current && state.collection !== droppingState.current.collection) updateFocusAfterDrop(); | ||
}); | ||
@@ -366,2 +374,3 @@ let { direction: direction } = (0, $foOxf$reactariai18n.useLocale)(); | ||
element: ref.current, | ||
preventFocusOnDrop: true, | ||
getDropOperation (types, allowedOperations) { | ||
@@ -368,0 +377,0 @@ if (localState.state.target) { |
@@ -179,19 +179,52 @@ import {clearGlobalDnDState as $7252cd45fc48c07c$export$70936501603e6c57, DIRECTORY_DRAG_TYPE as $7252cd45fc48c07c$export$990fced5dfac2637, droppableCollectionMap as $7252cd45fc48c07c$export$dfdf5deeaf27473f, getTypes as $7252cd45fc48c07c$export$e1d41611756c6326, globalDndState as $7252cd45fc48c07c$export$6ca6700462636d0b, isInternalDropOperation as $7252cd45fc48c07c$export$78bf638634500fa5, setDropCollectionRef as $7252cd45fc48c07c$export$dac8db29d42db9a1} from "./utils.module.js"; | ||
let droppingState = (0, $4ZR0C$useRef)(null); | ||
let updateFocusAfterDrop = (0, $4ZR0C$useCallback)(()=>{ | ||
let { state: state } = localState; | ||
if (droppingState.current) { | ||
var _state_collection_getItem; | ||
let { target: target, collection: prevCollection, selectedKeys: prevSelectedKeys, focusedKey: prevFocusedKey, isInternal: isInternal, draggingKeys: draggingKeys } = droppingState.current; | ||
// 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 (state.collection.size > prevCollection.size && state.selectionManager.isSelectionEqual(prevSelectedKeys)) { | ||
let newKeys = new Set(); | ||
for (let key of state.collection.getKeys())if (!prevCollection.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 === prevFocusedKey) { | ||
let first = newKeys.keys().next().value; | ||
let item = state.collection.getItem(first); | ||
// If this is a cell, focus the parent row. | ||
if ((item === null || item === void 0 ? void 0 : item.type) === 'cell') first = item.parentKey; | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') (0, $4ZR0C$setInteractionModality)('keyboard'); | ||
} | ||
} else if (state.selectionManager.focusedKey === prevFocusedKey && isInternal && target.type === 'item' && target.dropPosition !== 'on' && draggingKeys.has((_state_collection_getItem = state.collection.getItem(prevFocusedKey)) === null || _state_collection_getItem === void 0 ? void 0 : _state_collection_getItem.parentKey)) { | ||
// Focus row instead of cell when reordering. | ||
state.selectionManager.setFocusedKey(state.collection.getItem(prevFocusedKey).parentKey); | ||
(0, $4ZR0C$setInteractionModality)('keyboard'); | ||
} else if (state.selectionManager.focusedKey === prevFocusedKey && target.type === 'item' && target.dropPosition === 'on' && state.collection.getItem(target.key) != null) { | ||
// 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. | ||
state.selectionManager.setFocusedKey(target.key); | ||
(0, $4ZR0C$setInteractionModality)('keyboard'); | ||
} else if (!state.selectionManager.isSelected(state.selectionManager.focusedKey)) (0, $4ZR0C$setInteractionModality)('keyboard'); | ||
state.selectionManager.setFocused(true); | ||
} | ||
}, [ | ||
localState | ||
]); | ||
let onDrop = (0, $4ZR0C$useCallback)((e, target)=>{ | ||
var _state_collection_getItem; | ||
let { state: 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; | ||
// If parent key was dragged, we want to use it instead (i.e. focus row instead of cell after dropping) | ||
if ((0, $7252cd45fc48c07c$export$6ca6700462636d0b).draggingKeys.has((_state_collection_getItem = state.collection.getItem(focusedKey)) === null || _state_collection_getItem === void 0 ? void 0 : _state_collection_getItem.parentKey)) { | ||
focusedKey = state.collection.getItem(focusedKey).parentKey; | ||
state.selectionManager.setFocusedKey(focusedKey); | ||
} | ||
droppingState.current = { | ||
timeout: null, | ||
focusedKey: focusedKey, | ||
focusedKey: state.selectionManager.focusedKey, | ||
collection: state.collection, | ||
selectedKeys: state.selectionManager.selectedKeys | ||
selectedKeys: state.selectionManager.selectedKeys, | ||
draggingKeys: (0, $7252cd45fc48c07c$export$6ca6700462636d0b).draggingKeys, | ||
isInternal: (0, $7252cd45fc48c07c$export$78bf638634500fa5)(ref), | ||
target: target | ||
}; | ||
@@ -208,15 +241,6 @@ let onDropFn = localState.props.onDrop || defaultOnDrop; | ||
// 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. | ||
// and for React to re-render. If the collection didn't already change during this time (handled below), | ||
// update the focused key here. | ||
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: state } = localState; | ||
if (target.type === 'item' && target.dropPosition === 'on' && state.collection.getItem(target.key) != null) { | ||
state.selectionManager.setFocusedKey(target.key); | ||
state.selectionManager.setFocused(true); | ||
(0, $4ZR0C$setInteractionModality)('keyboard'); | ||
} else if (!state.selectionManager.isSelected(focusedKey)) (0, $4ZR0C$setInteractionModality)('keyboard'); | ||
updateFocusAfterDrop(); | ||
droppingState.current = null; | ||
@@ -226,3 +250,5 @@ }, 50); | ||
localState, | ||
defaultOnDrop | ||
defaultOnDrop, | ||
ref, | ||
updateFocusAfterDrop | ||
]); | ||
@@ -236,22 +262,4 @@ // eslint-disable-next-line arrow-body-style | ||
(0, $4ZR0C$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(); | ||
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; | ||
let item = state.collection.getItem(first); | ||
// If this is a cell, focus the parent row. | ||
if ((item === null || item === void 0 ? void 0 : item.type) === 'cell') first = item.parentKey; | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') (0, $4ZR0C$setInteractionModality)('keyboard'); | ||
} | ||
droppingState.current = null; | ||
} | ||
// If the collection changed after a drop, update the focused key. | ||
if (droppingState.current && state.collection !== droppingState.current.collection) updateFocusAfterDrop(); | ||
}); | ||
@@ -360,2 +368,3 @@ let { direction: direction } = (0, $4ZR0C$useLocale)(); | ||
element: ref.current, | ||
preventFocusOnDrop: true, | ||
getDropOperation (types, allowedOperations) { | ||
@@ -362,0 +371,0 @@ if (localState.state.target) { |
{ | ||
"name": "@react-aria/dnd", | ||
"version": "3.6.2-nightly.4698+b940126e4", | ||
"version": "3.6.2-nightly.4700+81abb2185", | ||
"description": "Spectrum UI components in React", | ||
@@ -25,11 +25,11 @@ "license": "Apache-2.0", | ||
"dependencies": { | ||
"@internationalized/string": "3.2.4-nightly.4698+b940126e4", | ||
"@react-aria/i18n": "3.0.0-nightly.2986+b940126e4", | ||
"@react-aria/interactions": "3.0.0-nightly.2986+b940126e4", | ||
"@react-aria/live-announcer": "3.0.0-nightly.2986+b940126e4", | ||
"@react-aria/overlays": "3.0.0-nightly.2986+b940126e4", | ||
"@react-aria/utils": "3.0.0-nightly.2986+b940126e4", | ||
"@react-stately/dnd": "3.3.2-nightly.4698+b940126e4", | ||
"@react-types/button": "3.9.5-nightly.4698+b940126e4", | ||
"@react-types/shared": "3.0.0-nightly.2986+b940126e4", | ||
"@internationalized/string": "3.2.4-nightly.4700+81abb2185", | ||
"@react-aria/i18n": "3.0.0-nightly.2988+81abb2185", | ||
"@react-aria/interactions": "3.0.0-nightly.2988+81abb2185", | ||
"@react-aria/live-announcer": "3.0.0-nightly.2988+81abb2185", | ||
"@react-aria/overlays": "3.0.0-nightly.2988+81abb2185", | ||
"@react-aria/utils": "3.0.0-nightly.2988+81abb2185", | ||
"@react-stately/dnd": "3.3.2-nightly.4700+81abb2185", | ||
"@react-types/button": "3.9.5-nightly.4700+81abb2185", | ||
"@react-types/shared": "3.0.0-nightly.2988+81abb2185", | ||
"@swc/helpers": "^0.5.0" | ||
@@ -44,3 +44,3 @@ }, | ||
}, | ||
"gitHead": "b940126e4d67a11d28b7c0b098eab23205598b6c" | ||
"gitHead": "81abb2185cb3a9c52a8feaf76a136c64852aea13" | ||
} |
@@ -16,3 +16,2 @@ /* | ||
import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared'; | ||
import {flushSync} from 'react-dom'; | ||
import {getDragModality, getTypes} from './utils'; | ||
@@ -30,2 +29,3 @@ import {isVirtualClick, isVirtualPointerEvent} from '@react-aria/utils'; | ||
element: FocusableElement, | ||
preventFocusOnDrop?: boolean, | ||
getDropOperation?: (types: Set<string>, allowedOperations: DropOperation[]) => DropOperation, | ||
@@ -518,15 +518,7 @@ onDropEnter?: (e: DropEnterEvent, dragTarget: DragTarget) => void, | ||
// Blur and re-focus the drop target so that the focus ring appears. | ||
if (this.currentDropTarget) { | ||
// Since we cancel all focus events in drag sessions, refire blur to make sure state gets updated so drag target doesn't think it's still focused | ||
// i.e. When you from one list to another during a drag session, we need the blur to fire on the first list after the drag. | ||
if (!this.dragTarget.element.contains(this.currentDropTarget.element)) { | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('blur')); | ||
this.dragTarget.element.dispatchEvent(new FocusEvent('focusout', {bubbles: true})); | ||
} | ||
// Re-focus the focusedKey upon reorder. This requires a React rerender between blurring and focusing. | ||
flushSync(() => { | ||
this.currentDropTarget.element.blur(); | ||
}); | ||
this.currentDropTarget.element.focus(); | ||
if (this.currentDropTarget && !this.currentDropTarget.preventFocusOnDrop) { | ||
// Re-trigger focus event on active element, since it will not have received it during dragging (see cancelEvent). | ||
// This corrects state such as whether focus ring should appear. | ||
// useDroppableCollection handles this itself, so this is only for standalone drop zones. | ||
document.activeElement.dispatchEvent(new FocusEvent('focusin', {bubbles: true})); | ||
} | ||
@@ -533,0 +525,0 @@ |
@@ -60,2 +60,5 @@ /* | ||
selectedKeys: Set<Key>, | ||
target: DropTarget, | ||
draggingKeys: Set<Key>, | ||
isInternal: boolean, | ||
timeout: ReturnType<typeof setTimeout> | ||
@@ -217,22 +220,89 @@ } | ||
let droppingState = useRef<DroppingState>(null); | ||
let onDrop = useCallback((e: DropEvent, target: DropTarget) => { | ||
let updateFocusAfterDrop = useCallback(() => { | ||
let {state} = localState; | ||
if (droppingState.current) { | ||
let { | ||
target, | ||
collection: prevCollection, | ||
selectedKeys: prevSelectedKeys, | ||
focusedKey: prevFocusedKey, | ||
isInternal, | ||
draggingKeys | ||
} = droppingState.current; | ||
// 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 ( | ||
state.collection.size > prevCollection.size && | ||
state.selectionManager.isSelectionEqual(prevSelectedKeys) | ||
) { | ||
let newKeys = new Set<Key>(); | ||
for (let key of state.collection.getKeys()) { | ||
if (!prevCollection.getItem(key)) { | ||
newKeys.add(key); | ||
} | ||
} | ||
// Focus the collection. | ||
state.selectionManager.setFocused(true); | ||
state.selectionManager.setSelectedKeys(newKeys); | ||
// Save some state of the collection/selection before the drop occurs so we can compare later. | ||
let focusedKey = state.selectionManager.focusedKey; | ||
// 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 === prevFocusedKey) { | ||
let first = newKeys.keys().next().value; | ||
let item = state.collection.getItem(first); | ||
// If parent key was dragged, we want to use it instead (i.e. focus row instead of cell after dropping) | ||
if (globalDndState.draggingKeys.has(state.collection.getItem(focusedKey)?.parentKey)) { | ||
focusedKey = state.collection.getItem(focusedKey).parentKey; | ||
state.selectionManager.setFocusedKey(focusedKey); | ||
// If this is a cell, focus the parent row. | ||
if (item?.type === 'cell') { | ||
first = item.parentKey; | ||
} | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') { | ||
setInteractionModality('keyboard'); | ||
} | ||
} | ||
} else if ( | ||
state.selectionManager.focusedKey === prevFocusedKey && | ||
isInternal && | ||
target.type === 'item' && | ||
target.dropPosition !== 'on' && | ||
draggingKeys.has(state.collection.getItem(prevFocusedKey)?.parentKey) | ||
) { | ||
// Focus row instead of cell when reordering. | ||
state.selectionManager.setFocusedKey(state.collection.getItem(prevFocusedKey).parentKey); | ||
setInteractionModality('keyboard'); | ||
} else if ( | ||
state.selectionManager.focusedKey === prevFocusedKey && | ||
target.type === 'item' && | ||
target.dropPosition === 'on' && | ||
state.collection.getItem(target.key) != null | ||
) { | ||
// 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. | ||
state.selectionManager.setFocusedKey(target.key); | ||
setInteractionModality('keyboard'); | ||
} else if (!state.selectionManager.isSelected(state.selectionManager.focusedKey)) { | ||
setInteractionModality('keyboard'); | ||
} | ||
state.selectionManager.setFocused(true); | ||
} | ||
}, [localState]); | ||
let onDrop = useCallback((e: DropEvent, target: DropTarget) => { | ||
let {state} = localState; | ||
// Save some state of the collection/selection before the drop occurs so we can compare later. | ||
droppingState.current = { | ||
timeout: null, | ||
focusedKey, | ||
focusedKey: state.selectionManager.focusedKey, | ||
collection: state.collection, | ||
selectedKeys: state.selectionManager.selectedKeys | ||
selectedKeys: state.selectionManager.selectedKeys, | ||
draggingKeys: globalDndState.draggingKeys, | ||
isInternal: isInternalDropOperation(ref), | ||
target | ||
}; | ||
@@ -251,22 +321,9 @@ | ||
// 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. | ||
// and for React to re-render. If the collection didn't already change during this time (handled below), | ||
// update the focused key here. | ||
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 (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'); | ||
} | ||
updateFocusAfterDrop(); | ||
droppingState.current = null; | ||
}, 50); | ||
}, [localState, defaultOnDrop]); | ||
}, [localState, defaultOnDrop, ref, updateFocusAfterDrop]); | ||
@@ -283,40 +340,5 @@ // eslint-disable-next-line arrow-body-style | ||
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; | ||
let item = state.collection.getItem(first); | ||
// If this is a cell, focus the parent row. | ||
if (item?.type === 'cell') { | ||
first = item.parentKey; | ||
} | ||
state.selectionManager.setFocusedKey(first); | ||
if (state.selectionManager.selectionMode === 'none') { | ||
setInteractionModality('keyboard'); | ||
} | ||
} | ||
droppingState.current = null; | ||
// If the collection changed after a drop, update the focused key. | ||
if (droppingState.current && state.collection !== droppingState.current.collection) { | ||
updateFocusAfterDrop(); | ||
} | ||
@@ -477,2 +499,3 @@ }); | ||
element: ref.current, | ||
preventFocusOnDrop: true, | ||
getDropOperation(types, allowedOperations) { | ||
@@ -479,0 +502,0 @@ if (localState.state.target) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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
1711729
15989