#746 4173087
Thanks @clauderic! - Accessibility related changes.
Accessibility-related props have been regrouped under the accessibility
prop of <DndContext>
:
<DndContext
- announcements={customAnnouncements}
- screenReaderInstructions={customScreenReaderInstructions}
+ accessibility={{
+ announcements: customAnnouncements,
+ screenReaderInstructions: customScreenReaderInstructions,
+ }}
This is a breaking change that will allow easier addition of new accessibility-related features without overloading the props namespace of <DndContext>
.
Arguments object for announcements
The arguments passed to announcement callbacks have changed. They now receive an object that contains the active
and over
properties that match the signature of those passed to the DragEvent handlers (onDragStart
, onDragMove
, etc.). This change allows consumers to read the data
property of the active
and over
node to customize the announcements based on the data.
Example migration steps:
export const announcements: Announcements = {
- onDragStart(id) {
+ onDragStart({active}) {
- return `Picked up draggable item ${id}.`;
+ return `Picked up draggable item ${active.id}.`;
},
- onDragOver(id, overId) {
+ onDragOver({active, over}) {
- if (overId) {
+ if (over) {
- return `Draggable item ${id} was moved over droppable area ${overId}.`;
+ return `Draggable item ${active.id} was moved over droppable area ${over.id}.`;
}
- return `Draggable item ${id} is no longer over a droppable area.`;
+ return `Draggable item ${active.id} is no longer over a droppable area.`;
},
};
The DOM nodes for the screen reader instructions and announcements are no longer portaled into the document.body
element by default.
This change is motivated by the fact that screen readers do not always announce ARIA live regions that are rendered on the document.body
. Common examples of this include when rendering a <DndContext>
within a <dialog>
element or an element that has role="dialog"
, only ARIA live regions rendered within the dialog will be announced.
Consumers can now opt to render announcements in the portal container of their choice using the container
property of the accessibility
prop:
<DndContext
+ accessibility={{
+ container: document.body,
+ }}
#733 035021a
Thanks @clauderic! - The <DragOverlay>
component's drop animation has been refactored, which fixes a number of bugs with the existing implementation and introduces new functionality.
What's new?
The drop animation now ensures that the the draggable node that we are animating to is in the viewport before performing the drop animation and scrolls it into view if needed.
Changes to the dropAnimation
prop
The dropAnimation
prop of <DragOverlay>
now accepts either a configuration object or a custom drop animation function.
The configuration object adheres to the following shape:
interface DropAnimationOptions {
duration?: number;
easing?: string;
keyframes?: DropAnimationKeyframeResolver;
sideEffects?: DropAnimationSideEffects;
}
The default drop animation options are:
const defaultDropAnimationConfiguration: DropAnimationOptions = {
duration: 250,
easing: 'ease',
keyframes: defaultDropAnimationKeyframes,
sideEffects: defaultDropAnimationSideEffects({
styles: {
active: {
opacity: '0',
},
},
}),
};
The keyframes
option allows consumers to override the keyframes of the drop animation. For example, here is how you would add a fade out transition to the drop animation using keyframes:
import {CSS} from '@dnd-kit/utilities';
const customDropAnimation = {
keyframes({transform}) {
return [
{opacity: 1, transform: CSS.Transform.toString(transform.initial)},
{opacity: 0, transform: CSS.Transform.toString(transform.final)},
];
},
};
The dragSourceOpacity
option has been deprecated in favour of letting consumers define arbitrary side effects that should run before the animation starts. Side effects may return a cleanup function that should run when the drop animation has completed.
type CleanupFunction = () => void;
export type DropAnimationSideEffects = (
parameters: DropAnimationSideEffectsParameters
) => CleanupFunction | void;
Drop animation side effects are a powerful abstraction that provide a lot of flexibility. The defaultDropAnimationSideEffects
function is exported by @dnd-kit/core
and aims to facilitate the types of side-effects we anticipate most consumers will want to use out of the box:
interface DefaultDropAnimationSideEffectsOptions {
className?: {
active?: string;
dragOverlay?: string;
};
styles?: {
active?: Styles;
dragOverlay?: Styles;
};
}
For advanced side-effects, consumers may define a custom sideEffects
function that may optionally return a cleanup function that will be executed when the drop animation completes:
const customDropAnimation = {
sideEffects({active}) {
active.node.classList.add('dropAnimationInProgress');
active.node.animate([{opacity: 0}, {opacity: 1}], {
easing: 'ease-in',
duration: 250,
});
return () => {
active.node.classList.remove('dropAnimationInProgress');
};
},
};
For even more advanced use-cases, consumers may also provide a function to the dropAnimation
prop, which adheres to the following shape:
interface DropAnimationFunctionArguments {
active: {
id: UniqueIdentifier;
data: DataRef;
node: HTMLElement;
rect: ClientRect;
};
draggableNodes: DraggableNodes;
dragOverlay: {
node: HTMLElement;
rect: ClientRect;
};
droppableContainers: DroppableContainers;
measuringConfiguration: DeepRequired<MeasuringConfiguration>;
transform: Transform;
}
type DropAnimationFunction = (
args: DropAnimationFunctionArguments
) => Promise<void> | void;
Bug fixes
- The
<DragOverlay>
now respects the measuringConfiguration
specified for the dragOverlay
and draggable
properties when measuring the rects to animate to and from.
- The
<DragOverlay>
component now supports rendering children while performing the drop animation. Previously, the drag overlay would be in a broken state when trying to pick up an item while a drop animation was in progress.
Migration steps
For consumers that were relying on the dragSourceOpacity
property in their dropAnimation
configuration:
+ import {defaultDropAnimationSideEffects} from '@dnd-kit/core';
const dropAnimation = {
- dragSourceOpacity: 0.5,
+ sideEffects: defaultDropAnimationSideEffects({
+ styles : {
+ active: {
+ opacity: '0.5',
+ },
+ },
+ ),
};
#755 33e6dd2
Thanks @clauderic! - The UniqueIdentifier
type has been updated to now accept either string
or number
identifiers. As a result, the id
property of useDraggable
, useDroppable
and useSortable
and the items
prop of <SortableContext>
now all accept either string
or number
identifiers.
Migration steps
For consumers that are using TypeScript, import the UniqueIdentifier
type to have strongly typed local state:
+ import type {UniqueIdentifier} from '@dnd-kit/core';
function MyComponent() {
- const [items, setItems] = useState(['A', 'B', 'C']);
+ const [items, setItems] = useState<UniqueIdentifier>(['A', 'B', 'C']);
}
Alternatively, consumers can cast or convert the id
property to a string
when reading the id
property of interfaces such as Active
, Over
, DroppableContainer
and DraggableNode
.
The draggableNodes
object has also been converted to a map. Consumers that were reading from the draggableNodes
property that is available on the public context of <DndContext>
should follow these migration steps:
- draggableNodes[someId];
+ draggableNodes.get(someId);