├── .gitbook └── assets │ ├── axis-aligned-rectangle.png │ ├── closest-center-1-.png │ ├── closest-center-2-.png │ ├── closest-center-kanban.png │ ├── closest-center.png │ ├── closest-corners-kanban.png │ ├── closest-corners.png │ ├── concepts-illustration (1).svg │ ├── concepts-illustration-large.svg │ ├── concepts-illustration.svg │ ├── custom-collision-detection-intersection.png │ ├── custom-collision-detection.png │ ├── draggable-large.svg │ ├── draggable.png │ ├── draggable.svg │ ├── dragoverlay (1).png │ ├── dragoverlay.png │ ├── droppable-1-.png │ ├── droppable-large.svg │ ├── droppable.png │ ├── droppable.svg │ ├── example.png │ ├── multiple-containers.png │ ├── rect-intersection-1-.png │ ├── rect-intersection.png │ ├── robot-illustration-concepts.svg │ ├── sortable-1-.png │ ├── sortable-multiple-containers-empty-column-1-.png │ ├── sortable-multiple-containers-empty-column.png │ ├── sortable-multiple-containers-example.png │ ├── sortable-multiple-containers.png │ ├── sortable.png │ ├── usesortable-1-.png │ ├── usesortable-3-.png │ └── usesortable.png ├── README.md ├── SUMMARY.md ├── api-documentation ├── constraints.md ├── context-provider │ ├── README.md │ ├── collision-detection-algorithms.md │ ├── use-dnd-monitor.md │ └── usedndcontext.md ├── draggable │ ├── README.md │ ├── clone.md │ ├── drag-overlay.md │ └── usedraggable.md ├── droppable │ ├── README.md │ └── usedroppable.md ├── modifiers.md └── sensors │ ├── README.md │ ├── keyboard.md │ ├── mouse.md │ ├── pointer.md │ └── touch.md ├── concepts ├── context-provider.md ├── draggable │ ├── README.md │ ├── clone.md │ └── usedraggable.md ├── droppable │ ├── README.md │ └── usedroppable.md └── sensors │ ├── README.md │ ├── keyboard.md │ ├── mouse.md │ └── touch.md ├── guides ├── accessibility.md ├── getting-started.md └── installation.md ├── introduction ├── getting-started.md └── installation.md └── presets ├── sortable.md └── sortable ├── README.md ├── sortable-context.md └── usesortable.md /.gitbook/assets/axis-aligned-rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/axis-aligned-rectangle.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-center-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-center-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-center-2-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-center-2-.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-center-kanban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-center-kanban.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-center.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-corners-kanban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-corners-kanban.png -------------------------------------------------------------------------------- /.gitbook/assets/closest-corners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/closest-corners.png -------------------------------------------------------------------------------- /.gitbook/assets/custom-collision-detection-intersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/custom-collision-detection-intersection.png -------------------------------------------------------------------------------- /.gitbook/assets/custom-collision-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/custom-collision-detection.png -------------------------------------------------------------------------------- /.gitbook/assets/draggable-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | draggable 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitbook/assets/draggable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/draggable.png -------------------------------------------------------------------------------- /.gitbook/assets/draggable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | draggable 5 | Created with Sketch. 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /.gitbook/assets/dragoverlay (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/dragoverlay (1).png -------------------------------------------------------------------------------- /.gitbook/assets/dragoverlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/dragoverlay.png -------------------------------------------------------------------------------- /.gitbook/assets/droppable-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/droppable-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/droppable-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | draggable 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitbook/assets/droppable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/droppable.png -------------------------------------------------------------------------------- /.gitbook/assets/droppable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Alternative logo 5 | Created with Sketch. 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /.gitbook/assets/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/example.png -------------------------------------------------------------------------------- /.gitbook/assets/multiple-containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/multiple-containers.png -------------------------------------------------------------------------------- /.gitbook/assets/rect-intersection-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/rect-intersection-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/rect-intersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/rect-intersection.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable-multiple-containers-empty-column-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable-multiple-containers-empty-column-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable-multiple-containers-empty-column.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable-multiple-containers-empty-column.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable-multiple-containers-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable-multiple-containers-example.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable-multiple-containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable-multiple-containers.png -------------------------------------------------------------------------------- /.gitbook/assets/sortable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/sortable.png -------------------------------------------------------------------------------- /.gitbook/assets/usesortable-1-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/usesortable-1-.png -------------------------------------------------------------------------------- /.gitbook/assets/usesortable-3-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/usesortable-3-.png -------------------------------------------------------------------------------- /.gitbook/assets/usesortable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dnd-kit/docs/2449bcf9b0e9a727d16573880b0253cbfa777d4f/.gitbook/assets/usesortable.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | @dnd-kit – A lightweight, modular, performant, accessible and extensible drag 4 | & drop toolkit for React. 5 | --- 6 | 7 | # Overview 8 | 9 | * **Feature packed:** customizable collision detection algorithms, multiple activators, draggable overlay, drag handles, auto-scrolling, constraints, and so much more. 10 | * **Built for React:** exposes hooks such as [`useDraggable`](api-documentation/draggable/usedraggable.md) and [`useDroppable`](api-documentation/droppable/usedroppable.md), and won't require you to re-architect your app or create additional wrapper DOM nodes. 11 | * **Supports a wide range of use cases:** lists, grids, multiple containers, nested contexts, variable sized items, virtualized lists, 2D Games, and more. 12 | * **Zero dependencies and modular:** the core of the library weighs around 10kb minified and has no external dependencies. It's built around built-in React state management and context, which keeps the library lean. 13 | * **Built-in support for multiple input methods:** Pointer, mouse, touch and keyboard sensors. 14 | * **Fully customizable & extensible:** Customize every detail – animations, transitions, behaviours, styles. Build your own [sensors](api-documentation/sensors/), [collision detection algorithms](api-documentation/context-provider/collision-detection-algorithms.md), customize key bindings and so much more. 15 | * **Accessibility:** Keyboard support, sensible default aria attributes, customizable screen reader instructions and live regions built-in. 16 | * **Performance:** It was built with performance in mind in order to support silky smooth animations. 17 | * **Presets:** Need to build a sortable interface? Check out [`@dnd-kit/sortable`](presets/sortable/), which is a thin layer built on top of `@dnd-kit/core`. More presets coming in the future. 18 | 19 | ![](.gitbook/assets/concepts-illustration-large.svg) 20 | 21 | The core library of **dnd kit** exposes two main concepts: 22 | 23 | * [Draggable elements](api-documentation/draggable/) 24 | * [Droppable areas](api-documentation/droppable/) 25 | 26 | Augment your existing components using the [`useDraggable`](api-documentation/draggable/usedraggable.md) and [`useDroppable`](api-documentation/droppable/usedroppable.md) hooks, or combine both to create components that can both be dragged and dropped over. 27 | 28 | Handle events and customize the behaviour of your draggable elements and droppable areas using the [``](api-documentation/context-provider/) provider. Configure [sensors](api-documentation/sensors/) to handle different input methods. 29 | 30 | Use the [``](api-documentation/draggable/drag-overlay.md) component to render a draggable overlay that is removed from the normal document flow and is positioned relative to the viewport. 31 | 32 | Check out our quick start guide to learn how get started: 33 | 34 | {% content-ref url="introduction/getting-started.md" %} 35 | [getting-started.md](introduction/getting-started.md) 36 | {% endcontent-ref %} 37 | 38 | ### Extensibility 39 | 40 | Extensibility is at the core of **dnd kit**. It was built to be lean and extensible. It ships with the features we believe most people will want most of the time, and provides extension points to build the rest on top of `@dnd-kit/core`. 41 | 42 | A prime example of the level of extensibility of **dnd kit** is the[ Sortable preset](presets/sortable/), which is built using the extension points that are exposed by `@dnd-kit/core`. 43 | 44 | The primary extension points are: 45 | 46 | * [Sensors](api-documentation/sensors/) 47 | * [Modifiers](api-documentation/modifiers.md) 48 | * [Custom collision detection algorithms](api-documentation/context-provider/collision-detection-algorithms.md#custom-collision-detection-strategies) 49 | 50 | ### Accessibility 51 | 52 | Building drag and drop interfaces that are accessible to everyone isn't easy, and requires thoughtful consideration. 53 | 54 | The `@dnd-kit/core` library provides a number of starting points to help you make your drag and drop interfaces accessible: 55 | 56 | * [Keyboard support ](api-documentation/sensors/keyboard.md)out of the box 57 | * [Customizable screen reader instructions](guides/accessibility.md#screen-reader-instructions) for how to interact with draggable items 58 | * [Customizable live region updates](guides/accessibility.md#screen-reader-announcements-using-live-regions) to provide screen reader announcements in real-time of what is currently happening with draggable and droppable elements. 59 | * [Sensible defaults for attributes](api-documentation/draggable/usedraggable.md#attributes) that should be passed to draggable elements 60 | 61 | Check out our Accessibility guide to learn more about how you can help make your drag and drop interface accessible for everyone: 62 | 63 | {% content-ref url="guides/accessibility.md" %} 64 | [accessibility.md](guides/accessibility.md) 65 | {% endcontent-ref %} 66 | 67 | ### Architecture 68 | 69 | Unlike many drag and drop libraries, **dnd kit** is **** intentionally **not** built on top of the [HTML5 Drag and drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML\_Drag\_and\_Drop\_API). This was a deliberate architectural decision, that does come with tradeoffs that you should be aware of before deciding to use it. For most web applications, we believe the benefits outweigh the tradeoffs. 70 | 71 | The HTML5 Drag and drop API has some severe **limitations**. It does not support touch devices, which means that the libraries that are built on top of it need to expose an entirely different implementation to support touch devices. This typically increases the complexity of the codebase and the overall bundle size of the library. Further, it requires workarounds to implement common use cases such as customizing the drag preview, locking dragging to a specific axis or to the bounds of a container, or animating the dragged item as it is picked up. 72 | 73 | The main **tradeoff** with not using the HTML5 Drag and drop API is that you won't be able to drag from the desktop or between windows. If the drag and drop use-case you have in mind involves this kind of functionality, you'll definitely want to use a library that's built on top of the HTML 5 Drag and drop API. We highly recommend you check out [react-dnd](https://github.com/react-dnd/react-dnd/) for a React library that's has a native HTML 5 Drag and drop backend. 74 | 75 | ### Performance 76 | 77 | #### **Minimizing DOM mutations** 78 | 79 | **dnd kit** lets you build drag and drop interfaces without having to mutate the DOM every time an item needs to shift position. 80 | 81 | This is possible because **dnd kit** lazily calculates and stores the initial positions and client rects of your droppable containers when a drag operation is initiated. These positions are passed down to your components that use `useDraggable` and `useDroppable` so that you can compute the new positions of your items while a drag operation is underway, and move them to their new positions using performant CSS properties that do not trigger a repaint such as `translate3d` and `scale`. For an example of how this can be achieved, check out the implementation of the sorting strategies that are exposed by the [`@dnd-kit/sortable`](presets/sortable/) library. 82 | 83 | This isn't to say that you can't shift the position of the items in the DOM while dragging, this is something that **is supported** and sometimes inevitable. In some cases, it won't be possible to know in advance what the new position and layout of the item until you move it in the DOM. Just know that these kind of mutations to the DOM while dragging are much more expensive and will cause a repaint, so if possible, prefer computing the new positions using `translate3d` and `scale`. 84 | 85 | #### Synthetic events 86 | 87 | Sensors use [SyntheticEvent listeners](https://reactjs.org/docs/events.html) for the activator events of all sensors, which leads to improved performance over manually adding event listeners to each individual draggable node. 88 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Overview](README.md) 4 | 5 | ## Introduction 6 | 7 | * [Installation](introduction/installation.md) 8 | * [Quick start](introduction/getting-started.md) 9 | 10 | ## API Documentation 11 | 12 | * [DndContext](api-documentation/context-provider/README.md) 13 | * [Collision detection algorithms](api-documentation/context-provider/collision-detection-algorithms.md) 14 | * [useDndContext](api-documentation/context-provider/usedndcontext.md) 15 | * [useDndMonitor](api-documentation/context-provider/use-dnd-monitor.md) 16 | * [Droppable](api-documentation/droppable/README.md) 17 | * [useDroppable](api-documentation/droppable/usedroppable.md) 18 | * [Draggable](api-documentation/draggable/README.md) 19 | * [useDraggable](api-documentation/draggable/usedraggable.md) 20 | * [Drag Overlay](api-documentation/draggable/drag-overlay.md) 21 | * [Sensors](api-documentation/sensors/README.md) 22 | * [Pointer](api-documentation/sensors/pointer.md) 23 | * [Mouse](api-documentation/sensors/mouse.md) 24 | * [Touch](api-documentation/sensors/touch.md) 25 | * [Keyboard](api-documentation/sensors/keyboard.md) 26 | * [Modifiers](api-documentation/modifiers.md) 27 | 28 | ## Presets 29 | 30 | * [Sortable](presets/sortable/README.md) 31 | * [Sortable Context](presets/sortable/sortable-context.md) 32 | * [useSortable](presets/sortable/usesortable.md) 33 | 34 | ## Guides 35 | 36 | * [Accessibility](guides/accessibility.md) 37 | 38 | -------------------------------------------------------------------------------- /api-documentation/constraints.md: -------------------------------------------------------------------------------- 1 | # Constraints 2 | 3 | -------------------------------------------------------------------------------- /api-documentation/context-provider/README.md: -------------------------------------------------------------------------------- 1 | # DndContext 2 | 3 | ## Application structure 4 | 5 | ### Context provider 6 | 7 | In order for your your [Droppable](../droppable/) and [Draggable](../draggable/) components to interact with each other, you'll need to make sure that the part of your React tree that uses them is nested within a parent `` component. The `` provider makes use of the [React Context API](https://reactjs.org/docs/context.html) to share data between draggable and droppable components and hooks. 8 | 9 | > React context provides a way to pass data through the component tree without having to pass props down manually at every level. 10 | 11 | Therefore, components that use [`useDraggable`](../draggable/usedraggable.md), [`useDroppable`](../droppable/usedroppable.md) or [`DragOverlay`](../draggable/drag-overlay.md) will need to be nested within a `DndContext` provider. 12 | 13 | They don't need to be direct descendants, but, there does need to be a parent `` provider somewhere higher up in the tree. 14 | 15 | ```jsx 16 | import React from 'react'; 17 | import {DndContext} from '@dnd-kit/core'; 18 | 19 | function App() { 20 | return ( 21 | 22 | {/* Components that use `useDraggable`, `useDroppable` */} 23 | 24 | ); 25 | } 26 | ``` 27 | 28 | ### Nesting 29 | 30 | You may also nest `` providers within other `` providers to achieve nested draggable/droppable interfaces that are independent of one another. 31 | 32 | ```jsx 33 | import React from 'react'; 34 | import {DndContext} from '@dnd-kit/core'; 35 | 36 | function App() { 37 | return ( 38 | 39 | {/* Components that use `useDraggable`, `useDroppable` */} 40 | 41 | {/* ... */} 42 | 43 | {/* ... */} 44 | 45 | 46 | 47 | ); 48 | } 49 | ``` 50 | 51 | When nesting `DndContext` providers, keep in mind that the `useDroppable` and `useDraggable` hooks will only have access to the other draggable and droppable nodes within that context. 52 | 53 | If multiple `DndContext` providers are listening for the same event, events will be captured by the first `DndContext` that contains a [Sensor](../sensors/) that is activated by that event, similar to how [events bubble in the DOM](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). 54 | 55 | ## Props 56 | 57 | ```typescript 58 | interface Props { 59 | announcements?: Announcements; 60 | autoScroll?: boolean; 61 | cancelDrop?: CancelDrop; 62 | children?: React.ReactNode; 63 | collisionDetection?: CollisionDetection; 64 | layoutMeasuring?: Partial; 65 | modifiers?: Modifiers; 66 | screenReaderInstructions?: ScreenReaderInstructions; 67 | sensors?: SensorDescriptor[]; 68 | onDragStart?(event: DragStartEvent): void; 69 | onDragMove?(event: DragMoveEvent): void; 70 | onDragOver?(event: DragOverEvent): void; 71 | onDragEnd?(event: DragEndEvent): void; 72 | onDragCancel?(): void; 73 | } 74 | ``` 75 | 76 | ### Event handlers 77 | 78 | As you can see from the list of props above, there are a number of different events emitted by `` that you can listen to and decide how to handle. 79 | 80 | The main events you can listen to are: 81 | 82 | #### `onDragStart` 83 | 84 | Fires when a drag event that meets the [activation constraints](../sensors/#concepts) for that [sensor ](../sensors/)happens, along with the unique identifier of the draggable element that was picked up. 85 | 86 | #### `onDragMove` 87 | 88 | Fires anytime as the [draggable](../draggable/) item is moved. Depending on the activated [sensor](../sensors/#activators), this could for example be as the [Pointer](../sensors/pointer.md) is moved or the [Keyboard](../sensors/keyboard.md) movement keys are pressed. 89 | 90 | #### `onDragOver` 91 | 92 | Fires when a [draggable](../draggable/) item is moved over a [droppable](../droppable/) container, along with the unique identifier of that droppable container. 93 | 94 | #### `onDragEnd` 95 | 96 | Fires after a draggable item is dropped. 97 | 98 | This event contains information about the active draggable `id` along with information on whether the draggable item was dropped `over`. 99 | 100 | If there are no [collisions detected](collision-detection-algorithms.md) when the draggable item is dropped, the `over` property will be `null`. If a collision is detected, the `over` property will contain the `id` of the droppable over which it was dropped. 101 | 102 | {% hint style="info" %} 103 | It's important to understand that the `onDragEnd` event **does not move** [**draggable**](../draggable/) **items into** [**droppable**](../droppable/) **containers.** 104 | 105 | Rather, it provides **information** about which draggable item was dropped and whether it was over a droppable container when it was dropped. 106 | 107 | It is up to the **consumer** of `DndContext` to decide what to do with that information and how to react to it, for example, by updating \(or not\) its internal state in response to the event so that the items are declaratively rendered in a different parent droppable. 108 | {% endhint %} 109 | 110 | #### `onDragCancel` 111 | 112 | Fires if a drag operation is cancelled, for example, if the user presses `escape` while dragging a draggable item. 113 | 114 | ### Accessibility 115 | 116 | For more details and best practices around accessibility of draggable and droppable components, read the accessibility section: 117 | 118 | {% page-ref page="../../guides/accessibility.md" %} 119 | 120 | #### Announcements 121 | 122 | Use the `announcements` prop to customize the screen reader announcements that are announced in the live region when draggable items are picked up, moved over droppable regions, and dropped. 123 | 124 | The default announcements are: 125 | 126 | ```javascript 127 | const defaultAnnouncements = { 128 | onDragStart(id) { 129 | return `Picked up draggable item ${id}.`; 130 | }, 131 | onDragOver(id, overId) { 132 | if (overId) { 133 | return `Draggable item ${id} was moved over droppable area ${overId}.`; 134 | } 135 | 136 | return `Draggable item ${id} is no longer over a droppable area.`; 137 | }, 138 | onDragEnd(id, overId) { 139 | if (overId) { 140 | return `Draggable item was dropped over droppable area ${overId}`; 141 | } 142 | 143 | return `Draggable item ${id} was dropped.`; 144 | }, 145 | onDragCancel(id) { 146 | return `Dragging was cancelled. Draggable item ${id} was dropped.`; 147 | }, 148 | } 149 | ``` 150 | 151 | While these default announcements are sensible defaults that should cover most simple use cases, you know your application best, and we highly recommend that you customize these to provide a screen reader experience that is more tailored to the use case you are building. 152 | 153 | #### Screen reader instructions 154 | 155 | Use the `screenReaderInstructions` prop to customize the instructions that are read to screen readers when the focus is moved 156 | 157 | ### Autoscroll 158 | 159 | Use the optional `autoScroll` boolean prop to temporarily or permanently disable auto-scrolling for all sensors used within this `DndContext`. 160 | 161 | Auto-scrolling may also be disabled on an individual sensor basis using the static property `autoScrollEnabled` of the sensor. For example, the [Keyboard sensor](../sensors/keyboard.md) manages scrolling internally, and therefore has the static property `autoScrollEnabled` set to `false`. 162 | 163 | ### Collision detection 164 | 165 | Use the `collisionDetection` prop to customize the collision detection algorithm used to detect collisions between draggable nodes and droppable areas within the`DndContext` provider. 166 | 167 | The default collision detection algorithm is the [rectangle intersection](collision-detection-algorithms.md#rectangle-intersection) algorithm. 168 | 169 | The built-in collision detection algorithms are: 170 | 171 | * [Rectangle intersection](collision-detection-algorithms.md#rectangle-intersection) 172 | * [Closest center](collision-detection-algorithms.md#closest-center) 173 | * [Closest corners](collision-detection-algorithms.md#closest-corners) 174 | 175 | You may also build custom collision detection algorithms or compose existing ones. 176 | 177 | To learn more, read the collision detection guide: 178 | 179 | {% page-ref page="collision-detection-algorithms.md" %} 180 | 181 | ### Sensors 182 | 183 | Sensors are an abstraction to detect different input methods in order to initiate drag operations, respond to movement and end or cancel the operation. 184 | 185 | The default sensors used by `DndContext` are the [Pointer](../sensors/pointer.md) and [Keyboard](../sensors/keyboard.md) sensors. 186 | 187 | To learn how to customize sensors or how to pass different sensors to `DndContext`, read the Sensors guide: 188 | 189 | {% page-ref page="../sensors/" %} 190 | 191 | ### Modifiers 192 | 193 | Modifiers let you dynamically modify the movement coordinates that are detected by sensors. They can be used for a wide range of use cases, for example: 194 | 195 | * Restricting motion to a single axis 196 | * Restricting motion to the draggable node container's bounding rectangle 197 | * Restricting motion to the draggable node's scroll container bounding rectangle 198 | * Applying resistance or clamping the motion 199 | 200 | To learn more about how to use Modifiers, read the Modifiers guide: 201 | 202 | {% page-ref page="../modifiers.md" %} 203 | 204 | ### Layout measuring 205 | 206 | You can configure when and how often `DndContext` should measure its droppable elements by using the `layoutMeasuring` prop. 207 | 208 | The `frequency` argument controls how frequently layouts should be measured. By default, layout measuring is set to `optimized`, which only measures layouts based on the `strategy`. 209 | 210 | Specify one of the following strategies: 211 | 212 | * `LayoutMeasuringStrategy.WhileDragging`: Default behavior, only measure droppable elements right after dragging has begun. 213 | 214 | `LayoutMeasuringStrategy.BeforeDragging`: Measure droppable elements before dragging begins and right after it ends. 215 | 216 | * `LayoutMeasuringStrategy.Always`: Measure droppable elements before dragging begins, right after dragging has begun, and after it ends. 217 | 218 | Example usage: 219 | 220 | ```jsx 221 | import {DndContext, LayoutMeasuringStrategy} from '@dnd-kit/core'; 222 | 223 | 224 | ``` 225 | 226 | -------------------------------------------------------------------------------- /api-documentation/context-provider/collision-detection-algorithms.md: -------------------------------------------------------------------------------- 1 | # Collision detection algorithms 2 | 3 | If you're familiar with how 2D games are built, you may have come across the notion of collision detection algorithms. 4 | 5 | One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning rectangles that are not rotated. This form of collision detection is generally referred to as [Axis-Aligned Bounding Box](https://developer.mozilla.org/en-US/docs/Games/Techniques/2D\_collision\_detection#Axis-Aligned\_Bounding\_Box) (AABB). 6 | 7 | The built-in collision detection algorithms assume a rectangular bounding box. 8 | 9 | > The bounding box of an element is the smallest possible rectangle (aligned with the axes of that element's user coordinate system) that entirely encloses it and its descendants.\ 10 | > – Source: [MDN](https://developer.mozilla.org/en-US/docs/Glossary/bounding\_box) 11 | 12 | This means that even if the draggable or droppable nodes look round or triangular, their bounding boxes will still be rectangular: 13 | 14 | ![](../../.gitbook/assets/axis-aligned-rectangle.png) 15 | 16 | If you'd like to use other shapes than rectangles for detecting collisions, build your own [custom collision detection algorithm](collision-detection-algorithms.md#custom-collision-detection-strategies). 17 | 18 | ## Rectangle intersection 19 | 20 | By default, [`DndContext`](./) uses the **rectangle intersection** collision detection algorithm. 21 | 22 | The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist. 23 | 24 | This means that in order for a draggable item to be considered **over** a droppable area, there needs to be an intersection between both rectangles: 25 | 26 | ![](../../.gitbook/assets/rect-intersection-1-.png) 27 | 28 | ## Closest center 29 | 30 | While the rectangle intersection algorithm is well suited for most drag and drop use cases, it can be unforgiving, since it requires both the draggable and droppable bounding rectangles to come into direct contact and intersect. 31 | 32 | For some use cases, such as [sortable](../../presets/sortable/) lists, using a more forgiving collision detection algorithm is recommended. 33 | 34 | As its name suggests, the closest center algorithm finds the droppable container who's center is closest to the center of the bounding rectangle of the active draggable item: 35 | 36 | ![](../../.gitbook/assets/closest-center-2-.png) 37 | 38 | ## Closest corners 39 | 40 | Like to the closest center algorithm, the closest corner algorithm doesn't require the draggable and droppable rectangles to intersect. 41 | 42 | Rather, it measures the distance between all four corners of the active draggable item and the four corners of each droppable container to find the closest one. 43 | 44 | ![](../../.gitbook/assets/closest-corners.png) 45 | 46 | The distance is measured from the top left corner of the draggable item to the top left corner of the droppable bounding rectangle, top right to top right, bottom left to bottom left, and bottom right to bottom right. 47 | 48 | ### **When should I use the closest corners algorithm instead of closest center?** 49 | 50 | In most cases, the **closest center** algorithm works well, and is generally the recommended default for sortable lists because it provides a more forgiving experience than the **rectangle intersection algorithm**. 51 | 52 | In general, the closest center and closest corners algorithms will yield the same results. However, when building interfaces where droppable containers are stacked on top of one another, for example, when building a Kanban, the closest center algorithm can sometimes return the underlaying droppable of the entire Kanban column rather than the droppable areas within that column. 53 | 54 | ![Closest center is 'A', though the human eye would likely expect 'A2'](../../.gitbook/assets/closest-center-kanban.png) 55 | 56 | In those situations, the **closest corners** algorithm is preferred and will yield results that are more aligned with what the human eye would predict: 57 | 58 | ![Closest corners is 'A2', as the human eye would expect.](../../.gitbook/assets/closest-corners-kanban.png) 59 | 60 | ## Pointer within 61 | 62 | As its name suggests, the pointer within collision detection algorithm only registers collision when the pointer is contained within the bounding rectangle of other droppable containers. 63 | 64 | This collision detection algorithm is well suited for high precision drag and drop interfaces. 65 | 66 | {% hint style="info" %} 67 | As its name suggests, this collision detection algorithm **only works with pointer-based sensors**. For this reason, we suggest you use [composition of collision detection algorithms](collision-detection-algorithms.md#composition-of-existing-algorithms) if you intend to use the `pointerWithin` collision detection algorithm so that you can fall back to a different collision detection algorithm for the Keyboard sensor. 68 | {% endhint %} 69 | 70 | ## Custom collision detection algorithms 71 | 72 | In advanced use cases, you may want to build your own collision detection algorithms if the ones provided out of the box do not suit your use case. 73 | 74 | You can either write a new collision detection algorithm from scratch, or compose two or more existing collision detection algorithms. 75 | 76 | ### Composition of existing algorithms 77 | 78 | Sometimes, you don't need to build custom collision detection algorithms from scratch. Instead, you can compose existing collision algorithms to augment them. 79 | 80 | A common example of this is when using the `pointerWithin` collision detection algorithm. As its name suggest, this collision detection algorithm depends on pointer coordinates, and therefore does not work when using other sensors like the Keyboard sensor. It's also a very high precision collision detection algorithm, so it can sometimes be helpful to fall back to a more tolerant collision detection algorithm when the `pointerWithin` algorithm does not return any collisions. 81 | 82 | ```javascript 83 | import {pointerWithin, rectIntersection} from '@dnd-kit/core'; 84 | 85 | function customCollisionDetectionAlgorithm(args) { 86 | // First, let's see if there are any collisions with the pointer 87 | const pointerCollisions = pointerWithin(args); 88 | 89 | // Collision detection algorithms return an array of collisions 90 | if (pointerCollisions.length > 0) { 91 | return pointerCollisions; 92 | } 93 | 94 | // If there are no collisions with the pointer, return rectangle intersections 95 | return rectIntersection(args); 96 | }; 97 | ``` 98 | 99 | Another example where composition of existing algorithms can be useful is if you want some of your droppable containers to have a different collision detection algorithm than the others. 100 | 101 | For instance, if you were building a sortable list that also supported moving items to a trash bin, you may want to compose both the `closestCenter` and `rectangleIntersection` collision detection algorithms. 102 | 103 | ![Use the closest corners algorithm for all droppables except 'trash'.](../../.gitbook/assets/custom-collision-detection.png) 104 | 105 | ![Use the intersection detection algorithm for the 'trash' droppable.](../../.gitbook/assets/custom-collision-detection-intersection.png) 106 | 107 | From an implementation perspective, the custom intersection algorithm described in the example above would look like: 108 | 109 | ```javascript 110 | import {closestCorners, rectIntersection} from '@dnd-kit/core'; 111 | 112 | function customCollisionDetectionAlgorithm({ 113 | droppableContainers, 114 | ...args, 115 | }) { 116 | // First, let's see if the `trash` droppable rect is intersecting 117 | const rectIntersectionCollisions = rectIntersection({ 118 | ...args, 119 | droppableContainers: droppableContainers.filter(({id}) => id === 'trash') 120 | }); 121 | 122 | // Collision detection algorithms return an array of collisions 123 | if (rectIntersectionCollisions.length > 0) { 124 | // The trash is intersecting, return early 125 | return rectIntersectionCollisions; 126 | } 127 | 128 | // Compute other collisions 129 | return closestCorners({ 130 | ...args, 131 | droppableContainers: droppableContainers.filter(({id}) => id !== 'trash') 132 | }); 133 | }; 134 | ``` 135 | 136 | ### Building custom collision detection algorithms 137 | 138 | For advanced use cases or to detect collision between non-rectangular or non-axis aligned shapes, you'll want to build your own collision detection algorithms. 139 | 140 | Here's an example to [detect collisions between circles](https://developer.mozilla.org/en-US/docs/Games/Techniques/2D\_collision\_detection#Circle\_Collision) instead of rectangles: 141 | 142 | ```javascript 143 | /** 144 | * Sort collisions in descending order (from greatest to smallest value) 145 | */ 146 | export function sortCollisionsDesc( 147 | {data: {value: a}}, 148 | {data: {value: b}} 149 | ) { 150 | return b - a; 151 | } 152 | 153 | function getCircleIntersection(entry, target) { 154 | // Abstracted the logic to calculate the radius for simplicity 155 | var circle1 = {radius: 20, x: entry.offsetLeft, y: entry.offsetTop}; 156 | var circle2 = {radius: 12, x: target.offsetLeft, y: target.offsetTop}; 157 | 158 | var dx = circle1.x - circle2.x; 159 | var dy = circle1.y - circle2.y; 160 | var distance = Math.sqrt(dx * dx + dy * dy); 161 | 162 | if (distance < circle1.radius + circle2.radius) { 163 | return distance; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | /** 170 | * Returns the circle that has the greatest intersection area 171 | */ 172 | function circleIntersection({ 173 | collisionRect, 174 | droppableRects, 175 | droppableContainers, 176 | }) => { 177 | const collisions = []; 178 | 179 | for (const droppableContainer of droppableContainers) { 180 | const {id} = droppableContainer; 181 | const rect = droppableRects.get(id); 182 | 183 | if (rect) { 184 | const intersectionRatio = getCircleIntersection(rect, collisionRect); 185 | 186 | if (intersectionRatio > 0) { 187 | collisions.push({ 188 | id, 189 | data: {droppableContainer, value: intersectionRatio}, 190 | }); 191 | } 192 | } 193 | } 194 | 195 | return collisions.sort(sortCollisionsDesc); 196 | }; 197 | ``` 198 | 199 | To learn more, refer to the implementation of the built-in collision detection algorithms. 200 | -------------------------------------------------------------------------------- /api-documentation/context-provider/use-dnd-monitor.md: -------------------------------------------------------------------------------- 1 | # useDndMonitor 2 | 3 | The `useDndMonitor` hook can be used within components wrapped in a `DndContext` provider to monitor the different drag and drop events that happen for that `DndContext`. 4 | 5 | ```jsx 6 | import {DndContext, useDndMonitor} from '@dnd-kit/core'; 7 | 8 | function App() { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | function Component() { 17 | // Monitor drag and drop events that happen on the parent `DndContext` provider 18 | useDndMonitor({ 19 | onDragStart(event) {}, 20 | onDragMove(event) {}, 21 | onDragOver(event) {}, 22 | onDragEnd(event) {}, 23 | onDragCancel(event) {}, 24 | }); 25 | } 26 | ``` 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /api-documentation/context-provider/usedndcontext.md: -------------------------------------------------------------------------------- 1 | # useDndContext 2 | 3 | For advanced use-cases, for example, if you're building your own presets on top of `@dnd-kit/core`, you may want to have access to the internal context of `` that the `useDraggable` and `useDroppable` have access to. 4 | 5 | ```jsx 6 | import {useDndContext} from '@dnd-kit/core'; 7 | 8 | function CustomPreset() { 9 | const dndContext = useDndContext(); 10 | } 11 | ``` 12 | 13 | If you think the preset you're building could be useful to others, feel free to open up a PR for discussion in the `dnd-kit` repository. 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /api-documentation/draggable/README.md: -------------------------------------------------------------------------------- 1 | # Draggable 2 | 3 | ![](../../.gitbook/assets/draggable-large.svg) 4 | 5 | Use the `useDraggable` hook turn DOM nodes into draggable sources that can be picked up, moved and dropped over [droppable](../droppable/) containers. 6 | 7 | ## Usage 8 | 9 | The `useDraggable` hook isn't particularly opinionated about how your app should be structured. 10 | 11 | ### Node ref 12 | 13 | At minimum though, you need to pass the `setNodeRef` function that is returned by the `useDraggable` hook to a DOM element so that it can access the underlying DOM node and keep track of it to [detect collisions and intersections](../context-provider/collision-detection-algorithms.md) with other [droppable](../droppable/) elements. 14 | 15 | ```jsx 16 | import {useDraggable} from '@dnd-kit/core'; 17 | import {CSS} from '@dnd-kit/utilities'; 18 | 19 | 20 | function Draggable() { 21 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 22 | id: 'unique-id', 23 | }); 24 | const style = { 25 | transform: CSS.Translate.toString(transform), 26 | }; 27 | 28 | return ( 29 | 32 | ); 33 | } 34 | ``` 35 | 36 | {% hint style="info" %} 37 | Always try to use the DOM element that is most [semantic](https://developer.mozilla.org/en-US/docs/Glossary/Semantics) in the context of your app. \ 38 | Check out our [Accessibility guide](../../guides/accessibility.md) to learn more about how you can help provide a better experience for screen readers. 39 | {% endhint %} 40 | 41 | ### Identifier 42 | 43 | The `id` argument is a string that should be a unique identifier, meaning there should be no other **draggable** elements that share that same identifier within a given [`DndContext`](../context-provider/) provider. 44 | 45 | ### Listeners 46 | 47 | The `useDraggable` hook requires that you attach `listeners` to the DOM node that you would like to become the activator to start dragging. 48 | 49 | While we could have attached these listeners manually to the node provided to `setNodeRef`, there are actually a number of key advantages to forcing the consumer to manually attach the listeners. 50 | 51 | #### Flexibility 52 | 53 | While many drag and drop libraries need to expose the concept of "drag handles", creating a drag handle with the `useDraggable` hook is as simple as manually attaching the listeners to a different DOM element than the one that is set as the draggable source DOM node: 54 | 55 | ```jsx 56 | import {useDraggable} from '@dnd-kit/core'; 57 | 58 | 59 | function Draggable() { 60 | const {attributes, listeners, setNodeRef} = useDraggable({ 61 | id: 'unique-id', 62 | }); 63 | 64 | return ( 65 |
66 | /* Some other content that does not activate dragging */ 67 | 68 |
69 | ); 70 | } 71 | ``` 72 | 73 | {% hint style="info" %} 74 | When attaching the listeners to a different element than the node that is draggable, make sure you also attach the attributes to the same node that has the listeners attached so that it is still [accessible](../../guides/accessibility.md). 75 | {% endhint %} 76 | 77 | You can even have multiple drag handles if that makes sense in the context of your application: 78 | 79 | ```jsx 80 | import {useDraggable} from '@dnd-kit/core'; 81 | 82 | 83 | function Draggable() { 84 | const {attributes, listeners, setNodeRef} = useDraggable({ 85 | id: 'unique-id', 86 | }); 87 | 88 | return ( 89 |
90 | 91 | /* Some other content that does not activate dragging */ 92 | 93 |
94 | ); 95 | } 96 | ``` 97 | 98 | #### Performance 99 | 100 | This strategy also means that we're able to use [React synthetic events](https://reactjs.org/docs/events.html), which ultimately leads to improved performance over manually attaching event listeners to each individual node.\ 101 | \ 102 | Why? Because rather than having to attach individual event listeners for each draggable DOM node, React attaches a single event listener for every type of event we listen to on the `document`. Once click on one of the draggable nodes happens, React's listener on the document dispatches a SyntheticEvent back to the original handler. 103 | 104 | ### Transforms 105 | 106 | In order to actually see your draggable items move on screen, you'll need to move the item using CSS. You can use inline styles, CSS variables, or even CSS-in-JS libraries to pass the `transform` property as CSS to your draggable element. 107 | 108 | {% hint style="success" %} 109 | For performance reasons, we strongly recommend you use the **`transform`** CSS property to move your draggable item on the screen, as other positional properties such as **`top`**, **`left`** or **`margin`** can cause expensive repaints. Learn more about [CSS transforms](https://developer.mozilla.org/en-US/docs/Web/CSS/transform). 110 | {% endhint %} 111 | 112 | After an item starts being dragged, the `transform` property will be populated with the `translate` coordinates you'll need to move the item on the screen. The `transform` object adheres to the following shape: `{x: number, y: number, scaleX: number, scaleY: number}` 113 | 114 | The `x` and `y` coordinates represent the delta from the point of origin of your draggable element since it started being dragged. 115 | 116 | The `scaleX` and `scaleY` properties represent the difference in scale between the item that is dragged and the droppable container it is currently over. This is useful for building interfaces where the draggable item needs to adapt to the size of the droppable container it is currently over. 117 | 118 | The `CSS` helper is entirely optional; it's a convenient helper for generating [CSS transform ](https://developer.mozilla.org/en-US/docs/Web/CSS/transform)strings, and is equivalent to manually constructing the string as such: 119 | 120 | ```javascript 121 | CSS.Translate.toString(transform) === 122 | `translate3d(${translate.x}, ${translate.y}, 0)` 123 | ``` 124 | 125 | ### Attributes 126 | 127 | The `useDraggable` hook **** provides a set of sensible default attributes for draggable items. We recommend you attach these to the HTML element you are attaching the draggable listeners to. 128 | 129 | We encourage you to manually attach the attributes that you think make sense in the context of your application rather than using them all without considering whether it makes sense to do so. 130 | 131 | For example, if the HTML element you are attaching the `useDraggable` `listeners` to is already a semantic `button`, although it's harmless to do so, there's no need to add the `role="button"` attribute, since that is already the default role. 132 | 133 | | Attribute | Default value | Description | 134 | | ---------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 135 | | `role` | `"button"` |

If possible, we recommend you use a semantic <button> element for the DOM element you plan on attaching draggable listeners to.

In case that's not possible, make sure you include the role="button"attribute, which is the default value.

| 136 | | `tabIndex` | `"0"` | In order for your draggable elements to receive keyboard focus, they **need** to have the `tabindex` attribute set to `0` if they are not natively interactive elements (such as the HTML `button` element). For this reason, the `useDraggable` hook sets the `tabindex="0"` attribute by default. | 137 | | `aria-roledescription` | `"draggable"` | While `draggable` is a sensible default, we recommend you customize this value to something that is tailored to the use case you are building. | 138 | | `aria-describedby` | `"DndContext-[uniqueId]"` | Each draggable item is provided a unique `aria-describedby` ID that points to the [screen reader instructions](../context-provider/#screen-reader-instructions) to be read out when a draggable item receives focus. | 139 | 140 | To learn more about the best practices for making draggable interfaces accessible, read the full accessibility guide: 141 | 142 | {% content-ref url="../../guides/accessibility.md" %} 143 | [accessibility.md](../../guides/accessibility.md) 144 | {% endcontent-ref %} 145 | 146 | ### Recommendations 147 | 148 | #### `touch-action` 149 | 150 | We highly recommend you specify the `touch-action` CSS property for all of your draggable elements. 151 | 152 | > The **`touch-action`** CSS property sets how an element's region can be manipulated by a touchscreen user (for example, by zooming features built into the browser).\ 153 | > \ 154 | > Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) 155 | 156 | In general, we recommend you set the `touch-action` property to `none` for draggable elements in order to prevent scrolling on mobile devices. 157 | 158 | {% hint style="info" %} 159 | For [Pointer Events,](../sensors/pointer.md) there is no way to prevent the default behaviour of the browser on touch devices when interacting with a draggable element from the pointer event listeners. Using `touch-action: none;` is the only way to reliably prevent scrolling for pointer events. 160 | 161 | Further, using `touch-action: none;` is currently the only reliable way to prevent scrolling in iOS Safari for both Touch and Pointer events. 162 | {% endhint %} 163 | 164 | If your draggable item is part of a scrollable list, we recommend you use a drag handle and set `touch-action` to `none` only for the drag handle, so that the contents of the list can still be scrolled, but that initiating a drag from the drag handle does not scroll the page. 165 | 166 | Once a `pointerdown` or `touchstart` event has been initiated, any changes to the `touch-action` value will be ignored. Programmatically changing the `touch-action` value for an element from `auto` to `none` after a pointer or touch event has been initiated will not result in the user agent aborting or suppressing any default behavior for that event for as long as that pointer is active (for more details, refer to the [Pointer Events Level 2 Spec](https://www.w3.org/TR/pointerevents2/#determining-supported-touch-behavior)). 167 | 168 | ## Drag Overlay 169 | 170 | The `` component provides a way to render a draggable overlay that is removed from the normal document flow and is positioned relative to the viewport. 171 | 172 | ![](<../../.gitbook/assets/dragoverlay (1).png>) 173 | 174 | To learn more about how to use drag overlays, read the in-depth guide: 175 | 176 | {% content-ref url="drag-overlay.md" %} 177 | [drag-overlay.md](drag-overlay.md) 178 | {% endcontent-ref %} 179 | -------------------------------------------------------------------------------- /api-documentation/draggable/clone.md: -------------------------------------------------------------------------------- 1 | # DraggableClone 2 | 3 | -------------------------------------------------------------------------------- /api-documentation/draggable/drag-overlay.md: -------------------------------------------------------------------------------- 1 | # Drag Overlay 2 | 3 | The `` component provides a way to render a draggable overlay that is removed from the normal document flow and is positioned relative to the viewport. 4 | 5 | ![](../../.gitbook/assets/dragoverlay.png) 6 | 7 | ## When should I use a drag overlay? 8 | 9 | Depending on your use-case, you may want to use a drag overlay rather than transforming the original draggable source element that is connected to the [`useDraggable`](usedraggable.md) hook: 10 | 11 | * If you'd like to **show a preview** of where the draggable source will be when dropped, you can update the position of the draggable source while dragging without affecting the drag overlay. 12 | * If your item needs to **move from one container to another while dragging**, we strongly recommend you use the `` component so the draggable item can unmount from its original container while dragging and mount back into a different container without affecting the drag overlay. 13 | * If your draggable item is within a **scrollable container,** we also recommend you use a ``, otherwise you'll need to set the draggable element to `position: fixed` yourself so the item isn't restricted to the overflow and stacking context of its scroll container, and can move without being affected by the scroll position of its container. 14 | * If your `useDraggable` items are within a **virtualized list**, you will absolutely want to use a drag overlay, since the original drag source can unmount while dragging as the virtualized container is scrolled. 15 | * If you want **smooth drop animations** without the effort of building them yourself. 16 | 17 | ## Usage 18 | 19 | You may render any valid JSX within the children of the ``. 20 | 21 | The `` component should **remain mounted at all times** so that it can perform the drop animation. If you conditionally render the `` component, drop animations will not work. 22 | 23 | As a rule of thumb, try to render the `` outside of your draggable components, and follow the [presentational component pattern ](drag-overlay.md#presentational-components)to maintain a good separation of concerns. 24 | 25 | Instead, you should conditionally render the children passed to the ``: 26 | 27 | {% tabs %} 28 | {% tab title="App.jsx" %} 29 | ```jsx 30 | import React, {useState} from 'react'; 31 | import {DndContext, DragOverlay} from '@dnd-kit/core'; 32 | 33 | import {Draggable} from './Draggable'; 34 | 35 | /* The implementation details of and are not 36 | * relevant for this example and are therefore omitted. */ 37 | 38 | function App() { 39 | const [items] = useState(['1', '2', '3', '4', '5']); 40 | const [activeId, setActiveId] = useState(null); 41 | 42 | return ( 43 | 44 | 45 | {items.map(id => 46 | 47 | 48 | 49 | )} 50 | 51 | 52 | 53 | {activeId ? ( 54 | 55 | ): null} 56 | 57 | 58 | ); 59 | 60 | function handleDragStart(event) { 61 | setActiveId(event.active.id); 62 | } 63 | 64 | function handleDragEnd() { 65 | setActiveId(null); 66 | } 67 | } 68 | ``` 69 | {% endtab %} 70 | 71 | {% tab title="Draggable.jsx" %} 72 | ```jsx 73 | import React from 'react'; 74 | import {useDraggable} from '@dnd-kit/core'; 75 | 76 | function Draggable(props) { 77 | const {attributes, listeners, setNodeRef} = useDraggable({ 78 | id: props.id, 79 | }); 80 | 81 | return ( 82 |
  • 83 | {props.children} 84 |
  • 85 | ); 86 | } 87 | ``` 88 | {% endtab %} 89 | {% endtabs %} 90 | 91 | ## Patterns 92 | 93 | ### Presentational components 94 | 95 | While this is an optional pattern, we recommend that the components you intend to make draggable be [presentational components ](https://medium.com/@dan\_abramov/smart-and-dumb-components-7ca2f9a7c7d0)that are decoupled from `@dnd-kit`. 96 | 97 | Using this pattern, create a presentational version of your component that you intend on rendering within the drag overlay, and another version that is draggable and renders the presentational component. 98 | 99 | #### Wrapper nodes 100 | 101 | As you may have noticed from the example above, we can create small abstract components that render a wrapper node and make any children rendered within draggable: 102 | 103 | {% tabs %} 104 | {% tab title="Draggable.jsx" %} 105 | ```jsx 106 | import React from 'react'; 107 | import {useDraggable} from '@dnd-kit/core'; 108 | 109 | function Draggable(props) { 110 | const Element = props.element || 'div'; 111 | const {attributes, listeners, setNodeRef} = useDraggable({ 112 | id: props.id, 113 | }); 114 | 115 | return ( 116 | 117 | {props.children} 118 | 119 | ); 120 | } 121 | ``` 122 | {% endtab %} 123 | {% endtabs %} 124 | 125 | Using this pattern, we can then render our presentational components within `` and within ``: 126 | 127 | {% tabs %} 128 | {% tab title="App.jsx" %} 129 | ```jsx 130 | import React, {useState} from 'react'; 131 | import {DndContext, DragOverlay} from '@dnd-kit/core'; 132 | 133 | import {Draggable} from './Draggable'; 134 | 135 | /* The implementation details of is not 136 | * relevant for this example and therefore omitted. */ 137 | 138 | function App() { 139 | const [isDragging, setIsDragging] = useState(false); 140 | 141 | return ( 142 | 143 | 144 | 145 | 146 | 147 | 148 | {isDragging ? ( 149 | 150 | ): null} 151 | 152 | 153 | ); 154 | 155 | function handleDragStart() { 156 | setIsDragging(true); 157 | } 158 | 159 | function handleDragEnd() { 160 | setIsDragging(false); 161 | } 162 | } 163 | ``` 164 | {% endtab %} 165 | {% endtabs %} 166 | 167 | #### Ref forwarding 168 | 169 | Use the[ ref forwarding pattern](https://reactjs.org/docs/forwarding-refs.html) to connect your presentational components to the `useDraggable` hook: 170 | 171 | ```jsx 172 | import React, {forwardRef} from 'react'; 173 | 174 | const Item = forwardRef(({children, ...props}, ref) => { 175 | return ( 176 |
  • {children}
  • 177 | ) 178 | }); 179 | ``` 180 | 181 | This way, you can create two versions of your component, one that is presentational, and one that is draggable and renders the presentational component **without the need for additional wrapper elements**: 182 | 183 | ```jsx 184 | import React from 'react'; 185 | import {useDraggable} from '@dnd-kit/core'; 186 | 187 | function DraggableItem(props) { 188 | const {attributes, listeners, setNodeRef} = useDraggable({ 189 | id: props.id, 190 | }); 191 | 192 | return ( 193 | 194 | {value} 195 | 196 | ) 197 | }); 198 | ``` 199 | 200 | ### Portals 201 | 202 | The drag overlay is not rendered in a portal by default. Rather, it is rendered in the container where it is rendered. 203 | 204 | If you would like to render the `` in a different container than where it is rendered, import the [`createPortal`](https://reactjs.org/docs/portals.html) helper from `react-dom`: 205 | 206 | ```jsx 207 | import React, {useState} from 'react'; 208 | import {createPortal} from 'react-dom'; 209 | import {DndContext, DragOverlay} from '@dnd-kit/core'; 210 | 211 | function App() { 212 | return ( 213 | 214 | {createPortal( 215 | {/* ... */}, 216 | document.body, 217 | )} 218 | 219 | ); 220 | } 221 | ``` 222 | 223 | ## Props 224 | 225 | ```typescript 226 | { 227 | adjustScale?: boolean; 228 | children?: React.ReactNode; 229 | className?: string; 230 | dropAnimation?: DropAnimation | null; 231 | style?: React.CSSProperties; 232 | transition?: string | TransitionGetter; 233 | modifiers?: Modifiers; 234 | wrapperElement?: keyof JSX.IntrinsicElements; 235 | zIndex?: number; 236 | } 237 | ``` 238 | 239 | ### Children 240 | 241 | You may render any valid JSX within the children of the ``. However, **make sure that the components rendered within the drag overlay do not use the `useDraggable` hook**. 242 | 243 | Prefer conditionally rendering the `children` of `` rather than conditionally rendering ``, otherwise drop animations will not work. 244 | 245 | ### Class name and inline styles 246 | 247 | If you'd like to customize the[ wrapper element](drag-overlay.md#wrapper-element) that the `DragOverlay`'s children are rendered into, use the `className` and `style` props: 248 | 249 | ```jsx 250 | 256 | {/* ... */} 257 | 258 | ``` 259 | 260 | ### Drop animation 261 | 262 | Use the `dropAnimation` prop to configure the drop animation. 263 | 264 | ```typescript 265 | interface DropAnimation { 266 | duration: number; 267 | easing: string; 268 | } 269 | ``` 270 | 271 | The `duration` option should be a number, in `milliseconds`. The default value is `250` milliseconds. The `easing` option should be a string that represents a valid [CSS easing function](https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function). The default easing is `ease`. 272 | 273 | ```jsx 274 | 278 | {/* ... */} 279 | 280 | ``` 281 | 282 | To disable drop animations, set the `dropAnimation` prop to `null`. 283 | 284 | ```jsx 285 | 286 | {/* ... */} 287 | 288 | ``` 289 | 290 | {% hint style="warning" %} 291 | The `` component should **remain mounted at all times** so that it can perform the drop animation. If you conditionally render the `` component, drop animations will not work. 292 | {% endhint %} 293 | 294 | ### Modifiers 295 | 296 | Modifiers let you dynamically modify the movement coordinates that are detected by sensors. They can be used for a wide range of use-cases, which you can learn more about by reading the [Modifiers](../modifiers.md) documentation. 297 | 298 | For example, you can use modifiers to restrict the movement of the `` to the bounds of the window: 299 | 300 | ```jsx 301 | import {DndContext, DragOverlay} from '@dnd-kit'; 302 | import { 303 | restrictToWindowEdges, 304 | } from '@dnd-kit/modifiers'; 305 | 306 | function App() { 307 | return ( 308 | 309 | {/* ... */} 310 | 311 | {/* ... */} 312 | 313 | 314 | ) 315 | } 316 | ``` 317 | 318 | ### Transition 319 | 320 | By default, the `` component does not have any transitions, unless activated by the [`Keyboard` sensor](../sensors/keyboard.md). Use the `transition` prop to create a function that returns the transition based on the [activator event](../sensors/#activators). The default implementation is: 321 | 322 | ```javascript 323 | function defaultTransition(activatorEvent) { 324 | const isKeyboardActivator = activatorEvent instanceof KeyboardEvent; 325 | 326 | return isKeyboardActivator ? 'transform 250ms ease' : undefined; 327 | }; 328 | ``` 329 | 330 | ### Wrapper element 331 | 332 | By default, the `` component renders your elements within a `div` element. If your draggable elements are list items, you'll want to update the `` component to render a `ul` wrapper instead, since wrapping a `li` item without a parent `ul` is invalid HTML: 333 | 334 | ```jsx 335 | 336 | {/* ... */} 337 | 338 | ``` 339 | 340 | ### `z-index` 341 | 342 | The `zIndex` prop sets the [z-order](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) of the drag overlay. The default value is `999` for compatibility reasons, but we highly recommend you use a lower value. 343 | -------------------------------------------------------------------------------- /api-documentation/draggable/usedraggable.md: -------------------------------------------------------------------------------- 1 | # useDraggable 2 | 3 | ![](../../.gitbook/assets/draggable.png) 4 | 5 | ## Arguments 6 | 7 | ```typescript 8 | interface UseDraggableArguments { 9 | id: string | number; 10 | attributes?: { 11 | role?: string; 12 | roleDescription?: string; 13 | tabIndex?: number; 14 | }, 15 | data?: Record; 16 | disabled?: boolean; 17 | } 18 | ``` 19 | 20 | ### Identifier 21 | 22 | The `id` argument is a `string` or `number` that should be a unique identifier, meaning there should be no other **draggable** elements that share that same identifier within a given [`DndContext`](../context-provider/) provider. 23 | 24 | If you're building a component that uses both the `useDraggable` and `useDroppable` hooks, they can both share the same identifier since draggable elements are stored in a different key-value store than droppable elements. 25 | 26 | ### Data 27 | 28 | The `data` argument is for advanced use-cases where you may need access to additional data about the draggable element in event handlers, modifiers or custom sensors. 29 | 30 | For example, if you were building a sortable preset, you could use the `data` attribute to store the index of the draggable element within a sortable list to access it within a custom sensor. 31 | 32 | ```jsx 33 | const {setNodeRef} = useDraggable({ 34 | id: props.id, 35 | data: { 36 | index: props.index, 37 | }, 38 | }); 39 | ``` 40 | 41 | Another more advanced example where the `data` argument can be useful is create relationships between draggable nodes and droppable areas, for example, to specify which types of droppable nodes your draggable node can be dropped on: 42 | 43 | ```jsx 44 | import {DndContext, useDraggable, useDroppable} from '@dnd-kit/core'; 45 | 46 | function Droppable() { 47 | const {setNodeRef} = useDroppable({ 48 | id: 'droppable', 49 | data: { 50 | type: 'type1', 51 | }, 52 | }); 53 | 54 | /* ... */ 55 | } 56 | 57 | function Draggable() { 58 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 59 | id: 'draggable', 60 | data: { 61 | supports: ['type1', 'type2'], 62 | }, 63 | }); 64 | 65 | /* ... */ 66 | } 67 | 68 | function App() { 69 | return ( 70 | 71 | /* ... */ 72 | 73 | ); 74 | 75 | function handleDragEnd(event) { 76 | const {active, over} = event; 77 | 78 | if (over && active.data.current.supports.includes(over.data.current.type)) { 79 | // do stuff 80 | } 81 | } 82 | } 83 | ``` 84 | 85 | ### Disabled 86 | 87 | Since [hooks cannot be conditionally invoked](https://reactjs.org/docs/hooks-rules.html), use the `disabled` argument and set it to `true` if you need to temporarily disable a `draggable` element. 88 | 89 | ### Attributes 90 | 91 | The default values for the `attributes` property are sensible defaults that should cover a wide range of use cases, but there is no one-size-fits-all solution. 92 | 93 | You know your application best, and we encourage you to manually attach only the attributes that you think make sense in the context of your application rather than using them all without considering whether it makes sense to do so. 94 | 95 | For example, if the HTML element you are attaching the `useDraggable` `listeners` to is already a native HTML `button` element, although it's harmless to do so, there's no need to add the `role="button"` attribute, since that is already the default role of `button`. 96 | 97 | #### Role 98 | 99 | The ARIA `"role"` attribute lets you explicitly define the role for an element, which communicates its purpose to assistive technologies. 100 | 101 | The default value for the `"role"` attribute is `"button"`. 102 | 103 | {% hint style="info" %} 104 | If it makes sense in the context of what you are building, we recommend that you leverage the native HTML ` 185 | 186 | ); 187 | } 188 | ``` 189 | 190 | {% hint style="info" %} 191 | When attaching the listeners to a different element than the node that is draggable, make sure you also attach the attributes to the same node that has the listeners attached so that it is still [accessible](../../guides/accessibility.md). 192 | {% endhint %} 193 | 194 | You can even have multiple drag handles if that makes sense in the context of your application: 195 | 196 | ```jsx 197 | import {useDraggable} from '@dnd-kit/core'; 198 | 199 | 200 | function Draggable() { 201 | const {attributes, listeners, setNodeRef} = useDraggable({ 202 | id: 'unique-id', 203 | }); 204 | 205 | return ( 206 |
    207 | 208 | /* Some other content that does not activate dragging */ 209 | 210 |
    211 | ); 212 | } 213 | ``` 214 | 215 | #### Performance 216 | 217 | This strategy also means that we're able to use [React synthetic events](https://reactjs.org/docs/events.html), which ultimately leads to improved performance over manually attaching event listeners to each individual node.\ 218 | \ 219 | Why? Because rather than having to attach individual event listeners for each draggable DOM node, React attaches a single event listener for every type of event we listen to on the `document`. Once click on one of the draggable nodes happens, React's listener on the document dispatches a SyntheticEvent back to the original handler. 220 | 221 | ### Node 222 | 223 | **`setNodeRef`** 224 | 225 | In order for the `useDraggable` hook to function properly, it needs the `setNodeRef` property to be attached to the HTML element you intend on turning into a draggable element so that @dnd-kit can measure that element to compute collisions: 226 | 227 | ```jsx 228 | function Draggable(props) { 229 | const {setNodeRef} = useDraggable({ 230 | id: props.id, 231 | }); 232 | 233 | return ( 234 | 237 | ); 238 | } 239 | ``` 240 | 241 | Keep in mind that the `ref` should be assigned to the outer container that you want to become draggable, but this doesn't necessarily need to coincide with the container that the listeners are attached to. 242 | 243 | #### **`node`** 244 | 245 | A [ref](https://reactjs.org/docs/refs-and-the-dom.html) to the current node that is passed to `setNodeRef` 246 | 247 | ### Activator 248 | 249 | **`setActivatorNodeRef`** 250 | 251 | It's possible for the listeners to be attached to a different node than the one that `setNodeRef` is attached to. 252 | 253 | A common example of this is when implementing a drag handle and attaching the listeners to the drag handle: 254 | 255 | ```jsx 256 | function Draggable(props) { 257 | const {listeners, setNodeRef} = useDraggable({ 258 | id: props.id, 259 | }); 260 | 261 | return ( 262 |
    263 | {/* ... */} 264 | 265 |
    266 | ); 267 | } 268 | ``` 269 | 270 | When the activator node differs from the draggable node, we recommend setting the activator node ref on the activator node: 271 | 272 | ```jsx 273 | function Draggable(props) { 274 | const {listeners, setNodeRef, setActivatorNodeRef} = useDraggable({ 275 | id: props.id, 276 | }); 277 | 278 | return ( 279 |
    280 | {/* ... */} 281 | 282 |
    283 | ); 284 | } 285 | ``` 286 | 287 | This helps @dnd-kit more accurately handle automatic focus management and can also be accessed by sensors for enhanced activation constraints. 288 | 289 | {% hint style="info" %} 290 | Focus management is automatically handled by [@dnd-kit](https://github.com/dnd-kit). When the activator event is a Keyboard event, focus will automatically be restored back to the first focusable node of the activator node. 291 | 292 | If no activator node is set via `setActivatorNodeRef`, focus will automatically be restored on the first focusable node of the draggable node registered via `setNodeRef.` 293 | {% endhint %} 294 | 295 | ### Over 296 | 297 | #### **`over`** 298 | 299 | If you'd like to change the appearance of the draggable element in response to it being dragged over a different droppable container, check whether the `over` value is defined. 300 | 301 | If a draggable element is moved over a droppable area, the `over` property will be defined for all draggable elements, regardless of whether or not those draggable elements are active or not. 302 | 303 | If you'd like to make changes to only the active draggable element in response to it being moved over a droppable area, check whether the `isDragging` property is `true`. 304 | 305 | ### Transform 306 | 307 | After a draggable item is picked up, the `transform` property will be populated with the `translate` coordinates you'll need to move the item on the screen. 308 | 309 | The `transform` object adheres to the following shape: 310 | 311 | ```typescript 312 | { 313 | x: number; 314 | y: number; 315 | scaleX: number; 316 | scaleY: number; 317 | } 318 | ``` 319 | 320 | The `x` and `y` coordinates represent the delta from the point of origin of your draggable element since it started being dragged. 321 | 322 | The `scaleX` and `scaleY` properties represent the difference in scale between the element that is currently being dragged and the droppable it is currently over, which can be useful if the draggable item needs to be dynamically resized to the size of the droppable it is over. 323 | -------------------------------------------------------------------------------- /api-documentation/droppable/README.md: -------------------------------------------------------------------------------- 1 | # Droppable 2 | 3 | ![](../../.gitbook/assets/droppable-large.svg) 4 | 5 | Use the `useDroppable` hook to set up DOM nodes as droppable areas that [draggable](../draggable/) elements can be dropped over. 6 | 7 | ## Usage 8 | 9 | The `useDroppable` hook isn't opinionated about how you should structure your application. 10 | 11 | At minimum though, you need to pass the `setNodeRef` function that is returned by the `useDroppable` hook to a DOM element so that it can register the underlying DOM node and keep track of it to detect collisions and intersections with other draggable elements. 12 | 13 | {% hint style="info" %} 14 | If the concept of `ref` is new to you, we recommend you first check out the [Refs and the DOM article](https://reactjs.org/docs/refs-and-the-dom.html#adding-a-ref-to-a-dom-element) on the React documentation website. 15 | {% endhint %} 16 | 17 | ```jsx 18 | import {useDroppable} from '@dnd-kit/core'; 19 | 20 | 21 | function Droppable() { 22 | const {setNodeRef} = useDroppable({ 23 | id: 'unique-id', 24 | }); 25 | 26 | return ( 27 |
    28 | /* Render whatever you like within */ 29 |
    30 | ); 31 | } 32 | ``` 33 | 34 | You can set up as many droppable containers as you want, just make sure they all have a unique `id` so that they can be differentiated. Each droppable needs to have its own unique node though, so make sure you don't try to connect a single droppable to multiple refs. 35 | 36 | To set up multiple droppable targets, simply use the `useDroppable` hook as many times as needed. 37 | 38 | ```jsx 39 | function MultipleDroppables() { 40 | const {setNodeRef: setFirstDroppableRef} = useDroppable({ 41 | id: 'droppable-1', 42 | }); 43 | const {setNodeRef: setsecondDroppableRef} = useDroppable({ 44 | id: 'droppable-2', 45 | }); 46 | 47 | return ( 48 |
    49 |
    50 | /* Render whatever you like within */ 51 |
    52 |
    53 | /* Render whatever you like within */ 54 |
    55 |
    56 | ); 57 | } 58 | ``` 59 | 60 | If you need to dynamically render a list of droppable containers, we recommend you create a re-usable Droppable component and render that component as many times as needed: 61 | 62 | ```jsx 63 | function Droppable(props) { 64 | const {setNodeRef} = useDroppable({ 65 | id: props.id, 66 | }); 67 | 68 | return ( 69 |
    70 | {props.children} 71 |
    72 | ); 73 | } 74 | 75 | function MultipleDroppables() { 76 | const droppables = ['1', '2', '3', '4']; 77 | 78 | return ( 79 |
    80 | {droppables.map((id) => ( 81 | 82 | Droppable container id: ${id} 83 | 84 | ))} 85 |
    86 | ); 87 | } 88 | ``` 89 | 90 | For more details usage of the `useDroppable` hook, refer to the API documentation section: 91 | 92 | {% page-ref page="usedroppable.md" %} 93 | 94 | -------------------------------------------------------------------------------- /api-documentation/droppable/usedroppable.md: -------------------------------------------------------------------------------- 1 | # useDroppable 2 | 3 | ![](../../.gitbook/assets/droppable-1-.png) 4 | 5 | ## Arguments 6 | 7 | ```typescript 8 | interface UseDroppableArguments { 9 | id: string | number; 10 | disabled?: boolean; 11 | data?: Record; 12 | } 13 | ``` 14 | 15 | ### Identifier 16 | 17 | The `id` argument is a `string` or `number` that should be a unique identifier, meaning there should be no other **droppable** elements that share that same identifier within a given [`DndContext`](../context-provider/) provider. 18 | 19 | If you're building a component that uses both the `useDroppable` and `useDraggable` hooks, they can both share the same identifier since droppable elements are stored in a different key-value store than draggable elements. 20 | 21 | ### Disabled 22 | 23 | Since [hooks cannot be conditionally invoked](https://reactjs.org/docs/hooks-rules.html), use the `disabled` argument and set it to `true` if you need to temporarily disable a `droppable` area. 24 | 25 | ### Data 26 | 27 | The `data` argument is for advanced use-cases where you may need access to additional data about the droppable element in event handlers, modifiers or custom sensors. 28 | 29 | For example, if you were building a sortable preset, you could use the `data` attribute to store the index of the droppable element within a sortable list to access it within a custom sensor. 30 | 31 | ```jsx 32 | const {setNodeRef} = useDroppable({ 33 | id: props.id, 34 | data: { 35 | index: props.index, 36 | }, 37 | }); 38 | ``` 39 | 40 | Another more advanced example where the `data` argument can be useful is create relationships between draggable nodes and droppable areas, for example, to specify which types of draggable nodes your droppable accepts: 41 | 42 | ```jsx 43 | import {DndContext, useDraggable, useDroppable} from '@dnd-kit/core'; 44 | 45 | function Droppable() { 46 | const {setNodeRef} = useDroppable({ 47 | id: 'droppable', 48 | data: { 49 | accepts: ['type1', 'type2'], 50 | }, 51 | }); 52 | 53 | /* ... */ 54 | } 55 | 56 | function Draggable() { 57 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 58 | id: 'draggable', 59 | data: { 60 | type: 'type1', 61 | }, 62 | }); 63 | 64 | /* ... */ 65 | } 66 | 67 | function App() { 68 | return ( 69 | 70 | /* ... */ 71 | 72 | ); 73 | 74 | function handleDragEnd(event) { 75 | const {active, over} = event; 76 | 77 | if (over && over.data.current.accepts.includes(active.data.current.type)) { 78 | // do stuff 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | ## Properties 85 | 86 | ```typescript 87 | { 88 | rect: React.MutableRefObject; 89 | isOver: boolean; 90 | node: React.RefObject; 91 | over: {id: UniqueIdentifier} | null; 92 | setNodeRef(element: HTMLElement | null): void; 93 | } 94 | ``` 95 | 96 | ### Node 97 | 98 | #### `setNodeRef` 99 | 100 | In order for the `useDroppable` hook to function properly, it needs the `setNodeRef` property to be attached to the HTML element you intend on turning into a droppable area: 101 | 102 | ```jsx 103 | function Droppable(props) { 104 | const {setNodeRef} = useDroppable({ 105 | id: props.id, 106 | }); 107 | 108 | return ( 109 |
    110 | {/* ... */} 111 |
    112 | ); 113 | } 114 | ``` 115 | 116 | #### `node` 117 | 118 | A [ref](https://reactjs.org/docs/refs-and-the-dom.html) to the current node that is passed to `setNodeRef` 119 | 120 | #### `rect` 121 | 122 | For advanced use cases, if you need the bounding rect measurement of the droppable area. 123 | 124 | ### Over 125 | 126 | #### `isOver` 127 | 128 | Use the `isOver` boolean returned by the `useDroppable` hook to change the appearance or content displayed when a `draggable` element is dragged over your droppable container. 129 | 130 | #### `over` 131 | 132 | If you'd like to change the appearance of the droppable in response to a draggable being dragged over a different droppable container, check whether the `over` value is defined. Depending on your use-case, you can also read the `id` of the other droppable that the draggable item to make changes to the render output of your droppable component. 133 | 134 | #### 135 | -------------------------------------------------------------------------------- /api-documentation/modifiers.md: -------------------------------------------------------------------------------- 1 | # Modifiers 2 | 3 | Modifiers let you dynamically modify the movement coordinates that are detected by sensors. They can be used for a wide range of use cases, for example: 4 | 5 | * Restricting motion to a single axis 6 | * Restricting motion to the draggable node container's bounding rectangle 7 | * Restricting motion to the draggable node's scroll container bounding rectangle 8 | * Applying resistance or clamping the motion 9 | 10 | ## Installation 11 | 12 | To start using modifiers, install the modifiers package via yarn or npm: 13 | 14 | ``` 15 | npm install @dnd-kit/modifiers 16 | ``` 17 | 18 | ## Usage 19 | 20 | The modifiers repository contains a number of useful modifiers that can be applied on [`DndContext`](context-provider/) as well as [`DragOverlay`](draggable/drag-overlay.md). 21 | 22 | ```jsx 23 | import {DndContext, DragOverlay} from '@dnd-kit'; 24 | import { 25 | restrictToVerticalAxis, 26 | restrictToWindowEdges, 27 | } from '@dnd-kit/modifiers'; 28 | 29 | function App() { 30 | return ( 31 | 32 | {/* ... */} 33 | 34 | {/* ... */} 35 | 36 | 37 | ) 38 | } 39 | ``` 40 | 41 | As you can see from the example above, `DndContext` and `DragOverlay` can both have different modifiers. 42 | 43 | ## Built-in modifiers 44 | 45 | ### Restricting motion to an axis 46 | 47 | #### `restrictToHorizontalAxis` 48 | 49 | Restrict movement to only the horizontal axis. 50 | 51 | #### `restrictToVerticalAxis` 52 | 53 | Restrict movement to only the vertical axis. 54 | 55 | ### Restrict motion to a container's bounding rectangle 56 | 57 | #### `restrictToWindowEdges` 58 | 59 | Restrict movement to the edges of the window. This modifier can be useful to prevent the `DragOverlay` from being moved outside of the bounds of the window. 60 | 61 | #### `restrictToParentElement` 62 | 63 | Restrict movement to the parent element of the draggable item that is picked up. 64 | 65 | #### `restrictToFirstScrollableAncestor` 66 | 67 | Restrict movement to the first scrollable ancestor of the draggable item that is picked up. 68 | 69 | ### Snap to grid 70 | 71 | #### `createSnapModifier` 72 | 73 | Function to create modifiers to snap to a given grid size. 74 | 75 | ```javascript 76 | import {createSnapModifier} from '@dnd-kit/modifiers'; 77 | 78 | const gridSize = 20; // pixels 79 | const snapToGridModifier = createSnapModifier(gridSize); 80 | ``` 81 | 82 | ## Building custom modifiers 83 | 84 | To build your own custom modifiers, refer to the implementation of the built-in modifiers of `@dnd-kit/modifiers`: [https://github.com/clauderic/dnd-kit/tree/master/packages/modifiers/src](https://github.com/clauderic/dnd-kit/tree/master/packages/modifiers/src) 85 | 86 | For example, here is an implementation to create a modifier to snap to grid: 87 | 88 | ```javascript 89 | const gridSize = 20; 90 | 91 | function snapToGrid(args) { 92 | const {transform} = args; 93 | 94 | return { 95 | ...transform, 96 | x: Math.ceil(transform.x / gridSize) * gridSize, 97 | y: Math.ceil(transform.y / gridSize) * gridSize, 98 | }; 99 | } 100 | ``` 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /api-documentation/sensors/README.md: -------------------------------------------------------------------------------- 1 | # Sensors 2 | 3 | ## Concepts 4 | 5 | Sensors are an abstraction to detect different input methods in order to initiate drag operations, respond to movement and end or cancel the operation. 6 | 7 | ### Activators 8 | 9 | Sensors may define one or multiple **activator events**. Activator events use React [SyntheticEvent listeners](https://reactjs.org/docs/events.html), which leads to improved performance over manually adding event listeners to each individual draggable node. 10 | 11 | Sensors are initialized once one of the activator events is detected. 12 | 13 | ### Built-in sensors 14 | 15 | The built-in sensors are: 16 | 17 | * [Pointer](pointer.md) 18 | * [Mouse](mouse.md) 19 | * [Touch](touch.md) 20 | * [Keyboard](keyboard.md) 21 | 22 | ### Custom sensors 23 | 24 | If necessary, you may also implement custom sensors to respond to other inputs or if the built-in sensors do not suit your needs. If you build a custom sensor and you think others could benefit, don't hesitate to open an RFC pull request. 25 | 26 | ## Lifecycle 27 | 28 | The lifecycle of a sensor is as follows: 29 | 30 | * Activator event detected, if the event is qualified, sensor class is initialized. 31 | * Sensor manually attaches new listeners to input methods upon initialization. 32 | * Sensor dispatches drag start event once constraints are met. 33 | * Sensor dispatches drag move events in response to input. 34 | * Sensor dispatches drag end or drag cancel event. 35 | * Sensor is torn down and cleans up manually attached event listeners. 36 | 37 | From an implementation perspective, Sensors are [classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). 38 | 39 | They are class-based rather than hooks because they need to be instantiated synchronously to respond to user interactions immediately, and it must be possible for them to be conditionally invoked. 40 | 41 | ## Hooks 42 | 43 | ### useSensor 44 | 45 | By default, `DndContext` uses the [Pointer](pointer.md) and [Keyboard](keyboard.md) sensors. 46 | 47 | If you'd like to use other sensors, such as the Mouse and Touch sensors instead, initialize those sensors separately with the options you'd like to use using the `useSensor` hook 48 | 49 | ```jsx 50 | import {MouseSensor, TouchSensor, useSensor} from '@dnd-kit/core'; 51 | 52 | function App() { 53 | const mouseSensor = useSensor(MouseSensor, { 54 | // Require the mouse to move by 10 pixels before activating 55 | activationConstraint: { 56 | distance: 10, 57 | }, 58 | }); 59 | const touchSensor = useSensor(TouchSensor, { 60 | // Press delay of 250ms, with tolerance of 5px of movement 61 | activationConstraint: { 62 | delay: 250, 63 | tolerance: 5, 64 | }, 65 | }); 66 | } 67 | ``` 68 | 69 | ### useSensors 70 | 71 | When initializing sensors with `useSensor`, make sure you pass the sensors to `useSensors` before passing them to `DndContext`: 72 | 73 | ```jsx 74 | import { 75 | DndContext, 76 | KeyboardSensor, 77 | MouseSensor, 78 | TouchSensor, 79 | useSensor, 80 | useSensors, 81 | } from '@dnd-kit/core'; 82 | 83 | function App() { 84 | const mouseSensor = useSensor(MouseSensor); 85 | const touchSensor = useSensor(TouchSensor); 86 | const keyboardSensor = useSensor(KeyboardSensor); 87 | 88 | const sensors = useSensors( 89 | mouseSensor, 90 | touchSensor, 91 | keyboardSensor, 92 | ); 93 | 94 | return ( 95 | 96 | {/* ... */} 97 | 98 | ) 99 | } 100 | ``` 101 | 102 | In other examples across the documentation, you may also see sensors initialized without intermediate variables, which is equivalent to the syntax above: 103 | 104 | ```jsx 105 | import { 106 | DndContext, 107 | KeyboardSensor, 108 | MouseSensor, 109 | TouchSensor, 110 | useSensor, 111 | useSensors, 112 | } from '@dnd-kit/core'; 113 | 114 | function App() { 115 | const sensors = useSensors( 116 | useSensor(MouseSensor), 117 | useSensor(TouchSensor), 118 | useSensor(KeyboardSensor), 119 | ); 120 | 121 | return ( 122 | 123 | {/* ... */} 124 | 125 | ) 126 | } 127 | ``` 128 | 129 | -------------------------------------------------------------------------------- /api-documentation/sensors/keyboard.md: -------------------------------------------------------------------------------- 1 | # Keyboard 2 | 3 | The Keyboard sensor responds to [Keyboard events](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent). It is one of the default sensors used by the [DndContext](../context-provider/) provider if none are defined. 4 | 5 | {% hint style="warning" %} 6 | In order for the Keyboard sensor to function properly, the activator element that receives the `useDraggable` [listeners](../draggable/usedraggable.md#listeners) **must** be able to receive focus. To learn more, read the in-depth [Accessibility guide](../../guides/accessibility.md). 7 | {% endhint %} 8 | 9 | ### Activator 10 | 11 | The keyboard activator is the `onKeyDown` event handler. The Keyboard sensor is initialized if the [`event.code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) property matches one of the `start` keys passed to `keyboardCodes` option of the Keyboard sensor. 12 | 13 | By default, the keys that activate the Keyboard sensor are `Space` and `Enter`. 14 | 15 | ### Options 16 | 17 | #### Keyboard codes 18 | 19 | This option represents the keys that are associated with the drag `start`, `cancel` and `end` events. The `keyboardCodes` options adheres to the following interface: 20 | 21 | ```typescript 22 | type KeyboardCode = KeyboardEvent['code']; 23 | 24 | interface KeyboardCodes { 25 | start: KeyboardCode[]; 26 | cancel: KeyboardCode[]; 27 | end: KeyboardCode[]; 28 | }; 29 | ``` 30 | 31 | The default values are: 32 | 33 | ```javascript 34 | const defaultKeyboardCodes = { 35 | start: ['Space', 'Enter'], 36 | cancel: ['Escape'], 37 | end: ['Space', 'Enter'], 38 | }; 39 | ``` 40 | 41 | You can customize these values, but keep in mind that the [third rule of ARIA ](https://www.w3.org/TR/using-aria/#3rdrule)requires that a user **must** be able to activate the action associated with a draggable widget using **both** the `enter` \(on Windows\) or `return` \(on macOS\) and the `space` key. To learn more, read the in-depth [accessibility guide](../../guides/accessibility.md). 42 | 43 | Keep in mind that you should also customize the screen reader instructions using the `screenReaderInstructions` prop of [``](../context-provider/) if you update these values, as the screen reader instructions assume that the Keyboard sensor is initialized with the default keyboard shortcuts. 44 | 45 | The `move` keyboard codes are not a customizable option, because those are handled by the [coordinate getter function](keyboard.md#coordinates-getter). To customize them, write a custom coordinate getter function. 46 | 47 | #### Coordinates getter 48 | 49 | By default, the Keyboard sensor moves in any given direction by `25` pixels when any of the arrow keys are pressed while dragging. 50 | 51 | This is an arbitrary sensible default that may or may not be suited to the use case you are building. 52 | 53 | The `getNextCoordinates` option can be used to define a custom coordinate getter function that is passed the latest keyboard `event` along with the current coordinates: 54 | 55 | ```javascript 56 | function customCoordinatesGetter(event, args) { 57 | const {currentCoordinates} = args; 58 | const delta = 50; 59 | 60 | switch (event.code) { 61 | case 'Right': 62 | return { 63 | ...currentCoordinates, 64 | x: currentCoordinates.x + delta, 65 | }; 66 | case 'Left': 67 | return { 68 | ...currentCoordinates, 69 | x: currentCoordinates.x - delta, 70 | }; 71 | case 'Down': 72 | return { 73 | ...currentCoordinates, 74 | y: currentCoordinates.y + delta, 75 | }; 76 | case 'Up': 77 | return { 78 | ...currentCoordinates, 79 | y: currentCoordinates.y - delta, 80 | }; 81 | } 82 | 83 | return undefined; 84 | }; 85 | ``` 86 | 87 | While the example above is fairly simple, you can build complex coordinate getters to support advanced use cases. The [Sortable](../../presets/sortable/) preset uses the `getNextCoordinates` option to build on top of the Keyboard sensor and move the active sortable item to its new index depending on the arrow key that is pressed. 88 | 89 | #### Scroll behavior 90 | 91 | This option represents the [scroll behavior ](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo)that should be used when scrolling to new coordinates. The default value is `smooth`, which results in the scroll container being scrolled smoothly to the new coordinates. 92 | 93 | The other possible value is `auto`, which results in the scroll container being scrolled directly to the new coordinates without any animation. 94 | 95 | -------------------------------------------------------------------------------- /api-documentation/sensors/mouse.md: -------------------------------------------------------------------------------- 1 | # Mouse 2 | 3 | The Mouse sensor responds to [Mouse events](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent). Mouse events represent events that occur due to the user interacting with a pointing device \(such as a mouse\). 4 | 5 | ### Activator 6 | 7 | The mouse activator is the `onMouseDown` event handler. The Mouse sensor is initialized if the mouse down event was triggered by the left mouse button. 8 | 9 | ### Activation constraints 10 | 11 | Like the [Pointer](pointer.md) sensor, the Mouse sensor has two activation constraints: 12 | 13 | * Distance constraint 14 | * Delay constraint 15 | 16 | These activation constraints are mutually exclusive and may not be used simultaneously. 17 | 18 | #### Distance 19 | 20 | The distance constraint subscribes to the following interface: 21 | 22 | ```typescript 23 | interface DistanceConstraint { 24 | distance: number; 25 | } 26 | ``` 27 | 28 | The `distance` property represents the distance, in _pixels_, by which the mouse needs to be moved before a drag start event is emitted. 29 | 30 | #### Delay 31 | 32 | The delay constraint subscribe to the following interface: 33 | 34 | ```typescript 35 | interface DelayConstraint { 36 | delay: number; 37 | tolerance: number; 38 | } 39 | ``` 40 | 41 | The `delay` property represents the duration, in _milliseconds_, that a draggable item needs to be held by the mouse for before a drag start event is emitted. 42 | 43 | The `tolerance` property represents the distance, in _pixels_, of motion that is tolerated before the drag operation is aborted. If the mouse is moved during the delay duration and the tolerance is set to zero, the drag operation will be immediately aborted. If a higher tolerance is set, for example, a tolerance of `5` pixels, the operation will only be aborted if the mouse is moved by more than 5 pixels during the delay. 44 | 45 | -------------------------------------------------------------------------------- /api-documentation/sensors/pointer.md: -------------------------------------------------------------------------------- 1 | # Pointer 2 | 3 | The Pointer sensor responds to [Pointer events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer\_events). It is one of the default sensors used by the [DndContext](../context-provider/) provider if none are defined. 4 | 5 | > Pointer events are DOM events that are fired for a pointing device. They are designed to create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus or touch (such as one or more fingers). 6 | > 7 | > The pointer is a hardware-agnostic device that can target a specific set of screen coordinates. Having a single event model for pointers can simplify creating Web sites and applications and provide a good user experience regardless of the user's hardware. 8 | > 9 | > – Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Pointer\_events) 10 | 11 | ### Activator 12 | 13 | The pointer activator is the `onPointerDown` event handler. The Pointer sensor is initialized if the pointer event was triggered by the [primary pointer](https://developer.mozilla.org/en-US/docs/Web/API/Pointer\_events#Determining\_the\_Primary\_Pointer). 14 | 15 | For mouse there is only one pointer, so it will always be the primary pointer. For touch input, a pointer is considered primary if the user touched the screen when there were no other active touches. For pen and stylus input, a pointer is considered primary if the user's pen initially contacted the screen when there were no other active pens contacting the screen. 16 | 17 | ### Activation constraints 18 | 19 | The Pointer sensor has two activation constraints: 20 | 21 | * Distance constraint 22 | * Delay constraint 23 | 24 | These activation constraints are mutually exclusive and may not be used simultaneously. 25 | 26 | #### Distance 27 | 28 | The distance constraint subscribes to the following interface: 29 | 30 | ```typescript 31 | interface DistanceConstraint { 32 | distance: number; 33 | } 34 | ``` 35 | 36 | The `distance` property represents the distance, in _pixels_, by which the pointer needs to be moved before a drag start event is emitted. 37 | 38 | #### Delay 39 | 40 | The delay constraint subscribe to the following interface: 41 | 42 | ```typescript 43 | interface DelayConstraint { 44 | delay: number; 45 | tolerance: number; 46 | } 47 | ``` 48 | 49 | The `delay` property represents the duration, in _milliseconds_, that a draggable item needs to be held by the primary pointer for before a drag start event is emitted. 50 | 51 | The `tolerance` property represents the distance, in _pixels_, of motion that is tolerated before the drag operation is aborted. If the pointer is moved during the delay duration and the tolerance is set to zero, the drag operation will be immediately aborted. If a higher tolerance is set, for example, a tolerance of `5` pixels, the operation will only be aborted if the pointer is moved by more than 5 pixels during the delay. 52 | 53 | This property is particularly useful for touch input, where some tolerance should be accounted for when using a delay constraint, as touch input is less precise than mouse input. 54 | 55 | ### Recommendations 56 | 57 | #### `touch-action` 58 | 59 | We highly recommend you specify the `touch-action` CSS property for all of your draggable elements. 60 | 61 | > The **`touch-action`** CSS property sets how an element's region can be manipulated by a touchscreen user (for example, by zooming features built into the browser).\ 62 | > \ 63 | > Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) 64 | 65 | In general, we recommend you set the `touch-action` property to `none` for draggable elements in order to prevent scrolling on mobile devices. 66 | 67 | {% hint style="warning" %} 68 | For [Pointer Events,](pointer.md) there is no way to prevent the default behaviour of the browser on touch devices when interacting with a draggable element from the pointer event listeners. Using `touch-action: none;` is the only way to reliably prevent scrolling for pointer events. 69 | {% endhint %} 70 | 71 | If your draggable item is part of a scrollable list, we recommend you use a drag handle and set `touch-action` to `none` only for the drag handle, so that the contents of the list can still be scrolled, but that initiating a drag from the drag handle does not scroll the page. 72 | 73 | {% hint style="info" %} 74 | If the above recommendations are not suitable for your use-case, we recommend that you use both the [Mouse](mouse.md) and [Touch](touch.md) sensors instead, as Touch events do not suffer the same limitations as Pointer events, and it is possible to prevent the page from scrolling in `touchmove` events. 75 | {% endhint %} 76 | 77 | Once a `pointerdown` or `touchstart` event has been initiated, any changes to the `touch-action` value will be ignored. Programmatically changing the `touch-action` value for an element from `auto` to `none` after a pointer or touch event has been initiated will not result in the user agent aborting or suppressing any default behavior for that event for as long as that pointer is active (for more details, refer to the [Pointer Events Level 2 Spec](https://www.w3.org/TR/pointerevents2/#determining-supported-touch-behavior)). 78 | -------------------------------------------------------------------------------- /api-documentation/sensors/touch.md: -------------------------------------------------------------------------------- 1 | # Touch 2 | 3 | The Touch sensor responds to [Touch events](https://developer.mozilla.org/en-US/docs/Web/API/Touch\_events). Touch events offer the ability to interpret finger or stylus activity on touch screens or trackpads. 4 | 5 | ### Activator 6 | 7 | The touch activator is the `onTouchStart` event handler. The Touch sensor is initialized if the there is no more than a single touch on the [`event.touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches) property. 8 | 9 | ### Activation constraints 10 | 11 | Like the [Pointer](pointer.md) sensor, the Touch sensor has two activation constraints: 12 | 13 | * Distance constraint 14 | * Delay constraint 15 | 16 | These activation constraints are mutually exclusive and may not be used simultaneously. 17 | 18 | #### Distance 19 | 20 | The distance constraint subscribes to the following interface: 21 | 22 | ```typescript 23 | interface DistanceConstraint { 24 | distance: number; 25 | } 26 | ``` 27 | 28 | The `distance` property represents the distance, in _pixels_, by which the touch input needs to be moved before a drag start event is emitted. 29 | 30 | #### Delay 31 | 32 | The delay constraint subscribe to the following interface: 33 | 34 | ```typescript 35 | interface DelayConstraint { 36 | delay: number; 37 | tolerance: number; 38 | } 39 | ``` 40 | 41 | The `delay` property represents the duration, in _milliseconds_, that a draggable item needs to be held by the touch input before a drag start event is emitted. 42 | 43 | The `tolerance` property represents the distance, in _pixels_, of motion that is tolerated before the drag operation is aborted. If the finger or stylus is moved during the delay duration and the tolerance is set to zero, the drag operation will be immediately aborted. If a higher tolerance is set, for example, a tolerance of `5` pixels, the operation will only be aborted if the finger is moved by more than 5 pixels during the delay. 44 | 45 | This property is particularly useful for touch input, where some tolerance should be accounted for when using a delay constraint, as touch input is less precise than mouse input. 46 | 47 | ### Recommendations 48 | 49 | #### `touch-action` 50 | 51 | We highly recommend you specify the `touch-action` CSS property for all of your draggable elements. 52 | 53 | > The **`touch-action`** CSS property sets how an element's region can be manipulated by a touchscreen user (for example, by zooming features built into the browser).\ 54 | > \ 55 | > Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) 56 | 57 | In general, we recommend you set the `touch-action` property to `manipulation` for draggable elements when using the Touch sensor. 58 | 59 | {% hint style="info" %} 60 | Touch events do not suffer the same limitations as Pointer events, and it is possible to prevent the page from scrolling in `touchmove` events. 61 | {% endhint %} 62 | -------------------------------------------------------------------------------- /concepts/context-provider.md: -------------------------------------------------------------------------------- 1 | # DndContext 2 | 3 | In order for your your [Droppable](droppable/) and [Draggable](draggable/) components to interact with each other, you'll need to make sure that the part of your React tree that uses them is nested within a parent `` component, which makes use of the [React Context API](https://reactjs.org/docs/context.html) to share data between draggable and droppable components and hooks. 4 | 5 | > React context provides a way to pass data through the component tree without having to pass props down manually at every level. 6 | 7 | ## Setup 8 | 9 | ```jsx 10 | import React from 'react'; 11 | import {DndContext} from '@dnd-kit/core'; 12 | 13 | function App() { 14 | return ( 15 | 16 | /* Your components that use `useDraggable`, 17 | * `useDroppable` or `DraggableClone` will need 18 | * to be nested within here. 19 | * 20 | * They don't need to be direct descendants, but 21 | * there does need to be a parent 22 | * somewhere higher up in the tree. 23 | * 24 | * You can also nest providers within 25 | * other to achieve nested draggable 26 | * UIs that are independent of one another. 27 | */ 28 | 29 | ); 30 | } 31 | ``` 32 | 33 | ## Props 34 | 35 | ```typescript 36 | interface Props { 37 | autoScroll?: boolean; 38 | announcements?: Announcements; 39 | children?: React.ReactNode; 40 | collisionDetection?: CollisionDetection; 41 | screenReaderInstructions?: ScreenReaderInstructions; 42 | sensors?: SensorDescriptor[]; 43 | translateModifiers?: Modifiers; 44 | onDragStart?(event: DragStartEvent): void; 45 | onDragMove?(event: DragMoveEvent): void; 46 | onDragOver?(event: DragOverEvent): void; 47 | onDragEnd?(event: DragEndEvent): void; 48 | onDragCancel?(): void; 49 | } 50 | ``` 51 | 52 | ## Usage 53 | 54 | As you can see from the list of props above, there's a number of different events you can subscribe to and different customizations you can pass to ``. 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /concepts/draggable/README.md: -------------------------------------------------------------------------------- 1 | # Draggable 2 | 3 | ![](../../.gitbook/assets/draggable-large.svg) 4 | 5 | -------------------------------------------------------------------------------- /concepts/draggable/clone.md: -------------------------------------------------------------------------------- 1 | # DraggableClone 2 | 3 | -------------------------------------------------------------------------------- /concepts/draggable/usedraggable.md: -------------------------------------------------------------------------------- 1 | # useDraggable 2 | 3 | -------------------------------------------------------------------------------- /concepts/droppable/README.md: -------------------------------------------------------------------------------- 1 | # Droppable 2 | 3 | 4 | 5 | ![](../../.gitbook/assets/droppable-large.svg) 6 | 7 | -------------------------------------------------------------------------------- /concepts/droppable/usedroppable.md: -------------------------------------------------------------------------------- 1 | # useDroppable 2 | 3 | -------------------------------------------------------------------------------- /concepts/sensors/README.md: -------------------------------------------------------------------------------- 1 | # Sensors 2 | 3 | -------------------------------------------------------------------------------- /concepts/sensors/keyboard.md: -------------------------------------------------------------------------------- 1 | # Keyboard 2 | 3 | -------------------------------------------------------------------------------- /concepts/sensors/mouse.md: -------------------------------------------------------------------------------- 1 | # Mouse 2 | 3 | -------------------------------------------------------------------------------- /concepts/sensors/touch.md: -------------------------------------------------------------------------------- 1 | # Touch 2 | 3 | -------------------------------------------------------------------------------- /guides/accessibility.md: -------------------------------------------------------------------------------- 1 | # Accessibility 2 | 3 | ## Introduction 4 | 5 | If you're new to accessibility for the web, the _Web Almanac by HTTP Archive_ has an excellent primer on the subject and the state of web accessibility that you should read before diving into this guide: [https://almanac.httparchive.org/en/2021/accessibility](https://almanac.httparchive.org/en/2021/accessibility) 6 | 7 | > Web accessibility is about achieving feature and information parity and giving complete access to all aspects of an interface to disabled people. 8 | > 9 | > A digital product or website is simply not complete if it is not usable by everyone. If it excludes certain disabled populations, this is discrimination and potentially grounds for fines and/or lawsuits. 10 | > 11 | > – Source: [Web Almanac by HTTP Archive](https://almanac.httparchive.org/en/2020/accessibility#screen-reader-only-text) 12 | 13 | People with varying disabilities use different assistive technologies to help them experience the web. 14 | 15 | The [Tools and Techniques](https://www.w3.org/WAI/people-use-web/tools-techniques/) article from the Web Accessibility Initiative (WAI) of the W3C covers how users can perceive, understand and interact with the web using different assistive technologies. 16 | 17 | Some assistive technologies for the web include: 18 | 19 | * Screen readers 20 | * Voice control 21 | * Screen magnifiers 22 | * Input devices (such as the keyboard, pointers and switch devices) 23 | 24 | When building accessible interfaces for the web, it's important to keep the three following questions in mind: 25 | 26 | 1. **Identity:** What element is the user interacting with? 27 | 2. **Operation:** How can the user interact with the element? 28 | 3. **State:** What is the current state of the element? 29 | 30 | In this guide, we'll focus on how to make drag and drop interfaces that are keyboard accessible and provide identity, operation instructions and live state updates for screen readers. 31 | 32 | ## Building accessible drag and drop interfaces 33 | 34 | Building drag and drop interfaces that are accessible to everyone isn't easy, and requires thoughtful consideration. 35 | 36 | The `@dnd-kit/core` library provides a number of sensible defaults to help you make your drag and drop interfaces accessible. 37 | 38 | These sensible defaults should be seen as _starting points_ rather than something you can set and forget; there is no one-size-fits-all solution to web accessibility. 39 | 40 | You know your application best, and while these sensible defaults will go a long way to help making your application more accessible, in most cases you'll want to customize these so that they are tailored to the context of your application. 41 | 42 | The three main areas of focus for this guide to help you make your drag and drop interface more accessible are: 43 | 44 | * [Keyboard support](accessibility.md#keyboard-support) 45 | * [Screen reader instructions](accessibility.md#screen-reader-instructions) 46 | * [Live regions to provide screen reader announcements](accessibility.md#screen-reader-announcements-using-live-regions) 47 | 48 | ### Keyboard support 49 | 50 | One of the[ five rules of ARIA](https://www.w3.org/TR/using-aria/#rule3) is that all interactive ARIA controls must be usable with the keyboard. 51 | 52 | When creating widgets that a user can click or tap, drag, and drop, a user must also be able to **navigate to the widget** and **perform an equivalent action using the keyboard**. 53 | 54 | For drag and drop interfaces, this means that the activator element that initiates the drag action must: 55 | 56 | * Be able to receive focus 57 | * A user must be able to activate the action associated with the element using **both** the `enter` (on Windows) or `return` (on macOS) and the `space` key. 58 | 59 | Both these guidelines should be respected to comply with the [third rule of ARIA](https://www.w3.org/TR/using-aria/#3rdrule). 60 | 61 | The `@dnd-kit/core` library ships with a [Keyboard sensor ](../api-documentation/sensors/keyboard.md)that adheres to these guidelines. The keyboard sensor is one of the two sensors that are enabled by default on the [``](../api-documentation/context-provider/) provider component. 62 | 63 | #### Focus 64 | 65 | In order for the Keyboard sensor to function properly, the activator element that receives the `useDraggable` [listeners](../api-documentation/draggable/usedraggable.md#listeners) **must** be able to receive focus. 66 | 67 | The `tabindex` attribute dictates the order in which focus moves throughout the document. 68 | 69 | * Natively interactive elements such as [buttons](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button), [anchor tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) and[ form controls ](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection)have a default `tabindex` value of `0`. 70 | * Custom elements that are intended to be interactive and receive keyboard focus need to have an explicitly assigned `tabindex="0"`(for example, `div` and `li` elements) 71 | 72 | In other words, in order for your draggable activator elements to be able to receive keyboard focus, they _need_ to have the `tabindex` attribute set to `0` **if** they are not natively interactive elements (such as the HTML `button` element). 73 | 74 | For this reason, the `useDraggable` hook sets the `tabindex="0"` attribute by default. 75 | 76 | #### Keyboard shortcuts 77 | 78 | Once a draggable activator element receives focus, the `enter` (on Windows) or `return` (on macOS) and the `space` keys can be used to initiate a drag operation and pick up the draggable item. 79 | 80 | The arrow keys are used to move the draggable item in any given direction. 81 | 82 | After an item is picked up, it can be dropped using the `enter` (on Windows) or `return` (on macOS) and the `space` keys. 83 | 84 | A drag operation can be cancelled using the `escape` key. It is recommended to allow users to cancel the drag operation using the `escape` key for all sensors, not just the Keyboard sensor. 85 | 86 | The keyboard shortcuts of the Keyboard sensor can be [customized](../api-documentation/sensors/keyboard.md#keyboard-codes), but we discourage you to do so unless you maintain support for the `enter`, `return` and `space` keys to follow the guidelines set by the third rule of ARIA. 87 | 88 | By default, the [Keyboard sensor](../api-documentation/sensors/keyboard.md) moves in any given direction by `25` pixels when the arrow keys are pressed while dragging. 89 | 90 | This is an arbitrary default that is likely not suited for all use-cases. We encourage you to customize this behaviour and tailor it to the context of your application using the [`getNextCoordinates` option ](../api-documentation/sensors/keyboard.md#coordinates-getter)of the Keyboard sensor. 91 | 92 | For example, the `useSortable` hook ships with an augmented version of the Keyboard sensor that uses the `getNextCoordinates` option behind the scenes to find the coordinates of the next sortable element in any given direction when an arrow key is pressed. 93 | 94 | In order to let users learn how to interact with draggable elements using these keyboard shortcuts, it's important to provide screen reader instructions. 95 | 96 | ### Screen reader instructions 97 | 98 | In order to users know how to interact with draggable items using only the keyboard, it's important to provide information to the user that their focus is currently on a draggable item, along with clear instruction on how to pick up a a draggable item, how to move it, how to drop it and how to cancel the operation. 99 | 100 | #### Role 101 | 102 | To let users know that their focus is currently on a draggable item, the [`useDraggable`](../api-documentation/draggable/usedraggable.md) hook provides the [`role`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles) and [`aria-roledescription`](https://www.digitala11y.com/aria-roledescriptionproperties/), and [`aria-describedby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA\_Techniques/Using\_the\_aria-describedby\_attribute) attributes by default: 103 | 104 | | Attribute | Default value | Description | 105 | | ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 106 | | `role` | `"button"` |

    The ARIA "role" attribute lets you explicitly define the role for an element, which communicates its purpose to assistive technologies.

    If possible, we recommend you use a semantic <button> element for the DOM element you plan on attaching draggable listeners to.

    In case that's not possible, make sure you include the role="button"attribute, which is the default value.

    | 107 | | `aria-roledescription` | `"draggable"` |

    Defines a human-readable, localized description for the role of an element that is read by screen readers.

    While draggable is a sensible default, we recommend you customize this value to something that is tailored to your use-case.

    | 108 | | `aria-describedby` | `"DndContext-[uniqueId]"` | Each draggable item is provided a unique `aria-describedby` ID that points to the voiceover instructions to be read out when a draggable item receives focus | 109 | 110 | The `role` and `aria-roledescription` attributes can be customized via the [options passed to the `useDraggable` hook](../api-documentation/draggable/usedraggable.md#arguments). 111 | 112 | To customize the `aria-describedby` instructions, refer to the section below. 113 | 114 | #### Instructions 115 | 116 | By default, each [``](../api-documentation/context-provider/) component renders a unique HTML element that is rendered off-screen to be used to provide these instructions to screen readers. 117 | 118 | The default instructions are: 119 | 120 | > To pick up a draggable item, press space or enter. \ 121 | > While dragging, use the arrow keys to move the item in any given direction.\ 122 | > Press space or enter again to drop the item in its new position, or press escape to cancel. 123 | 124 | We recommend you customize and localize these instructions to your application and use-case using the `screenReaderInstructions` prop of [``](../api-documentation/context-provider/). 125 | 126 | For example, if you were building a sortable grocery shopping list, you may want to tailor the instructions like so: 127 | 128 | > To pick up a grocery list item, press space or enter. \ 129 | > Use the up and down arrow keys to update the position of the item in the grocery list.\ 130 | > Press space or enter again to drop the item in its new position, or press escape to cancel. 131 | 132 | If your application supports multiple languages, make sure you also translate these instructions. The `` component only ships with instructions in English due to bundle size concerns. 133 | 134 | ### Screen reader announcements using live regions 135 | 136 | [Live regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA\_Live\_Regions) are used to notify screen readers of content changes. 137 | 138 | When building accessible drag and drop interfaces, live regions should be used to provide screen reader announcements in real-time of time-sensitive information of what is currently happening with draggable and droppable elements without having to move focus . 139 | 140 | By default, each [``](../api-documentation/context-provider/) component renders a unique HTML element that is rendered off-screen to be used for live screen-reader announcements of events like when a drag operation has started, when a draggable item has been dragged over a droppable container, when a drag operation has ended, and when a drag operation has been cancelled. 141 | 142 | These instructions can be customized using the `announcements` prop of `DndContext`. 143 | 144 | The default announcements are: 145 | 146 | ```javascript 147 | const defaultAnnouncements = { 148 | onDragStart({active}) { 149 | return `Picked up draggable item ${active.id}.`; 150 | }, 151 | onDragOver({active, over}) { 152 | if (over) { 153 | return `Draggable item ${active.id} was moved over droppable area ${over.id}.`; 154 | } 155 | 156 | return `Draggable item ${active.id} is no longer over a droppable area.`; 157 | }, 158 | onDragEnd({active, over}) { 159 | if (over) { 160 | return `Draggable item ${active.id} was dropped over droppable area ${over.id}`; 161 | } 162 | 163 | return `Draggable item ${active.id} was dropped.`; 164 | }, 165 | onDragCancel({active}) { 166 | return `Dragging was cancelled. Draggable item ${active.id} was dropped.`; 167 | }, 168 | } 169 | ``` 170 | 171 | While these default announcements are sensible defaults that should cover most simple use cases, you know your application best, and we highly recommend that you customize these to provide a screen reader experience that is more tailored to the use case you are building. 172 | 173 | {% hint style="info" %} 174 | When authoring screen reader announcements that rely on an element's position (index) in a list, use positions rather than indices to describe the element's current position. 175 | 176 | Here's an example of index based announcements and why you should avoid them: 177 | 178 | > Item with index 0 was picked up. Item was moved to index 1 of 4. 179 | 180 | Position based announcements are much more intuitive and natural: 181 | 182 | > Item at position 1 was picked up. Item was moved to position 2 of 5. 183 | {% endhint %} 184 | 185 | For example, when building a sortable list, you could write custom announcements that are tailored to that use-case using position based announcements: 186 | 187 | ```javascript 188 | function App() { 189 | const items = useState(['Apple', 'Orange', 'Strawberries', 'Raspberries']); 190 | const getPosition = (id) => items.indexOf(id) + 1; // prefer position over index 191 | const itemCount = items.length; 192 | 193 | const announcements = { 194 | onDragStart({active}) { 195 | return `Picked up sortable item ${active.id}. Sortable item ${active.id} is in position ${getPosition(id)} of ${itemCount}`; 196 | }, 197 | onDragOver({active, over}) { 198 | if (over) { 199 | return `Sortable item ${active.id} was moved into position ${getPosition(over.id)} of ${itemCount}`; 200 | } 201 | }, 202 | onDragEnd({active, over}) { 203 | if (over) { 204 | return `Sortable item ${active.id} was dropped at position ${getPosition(over.id)} of ${itemCount}`; 205 | } 206 | }, 207 | onDragCancel({active}) { 208 | return `Dragging was cancelled. Sortable item ${active.id} was dropped.`; 209 | }, 210 | }; 211 | 212 | return ( 213 | 218 | ``` 219 | 220 | The example above assumes that the [`closestCenter` collision detection strategy](../api-documentation/context-provider/collision-detection-algorithms.md#closest-center) is used, so the `over` property should always be defined. 221 | 222 | If your application supports multiple languages, make sure you also translate these announcements. The `` component only ships with announcements in English due to bundle size concerns. 223 | -------------------------------------------------------------------------------- /guides/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Eager to get started? This quick start guide will help you familiarize 4 | yourself with the core concepts of dnd kit. 5 | --- 6 | 7 | # Quick start 8 | 9 | {% hint style="info" %} 10 | Before getting started, make sure you have followed the installation steps outlined in the [Installation guide](installation.md). 11 | {% endhint %} 12 | 13 | ### Context provider 14 | 15 | First, we'll set up the general structure of the app. In order for the [`useDraggable`]() and [`useDroppable`]() hooks to function correctly, you'll need to ensure that the components where they are used are wrapped within a [``](../api-documentation/context-provider/) component: 16 | 17 | {% tabs %} 18 | {% tab title="App.jsx" %} 19 | ```jsx 20 | import React from 'react'; 21 | import {DndContext} from '@dnd-kit/core'; 22 | 23 | function App() { 24 | return ( 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | ``` 32 | {% endtab %} 33 | {% endtabs %} 34 | 35 | ### Droppable 36 | 37 | ![](../.gitbook/assets/droppable-large.svg) 38 | 39 | Next, let's set up your first **Droppable** component. To do so, we'll be using the `useDroppable` hook. 40 | 41 | The `useDroppable` hook isn't opinionated about how your app should be structured. At minimum, they requires you pass a ref to the DOM element that you would like to become droppable. You'll also need to provide a unique `id` attribute to all your droppable components. 42 | 43 | When a **Draggable** item is over your droppable element, the `isOver` property will become true. 44 | 45 | {% tabs %} 46 | {% tab title="Droppable.jsx" %} 47 | ```jsx 48 | import React from 'react'; 49 | import {useDroppable} from '@dnd-kit/core'; 50 | 51 | function Droppable(props) { 52 | const {isOver, setNodeRef} = useDroppable({ 53 | id: 'droppable', 54 | }); 55 | const style = { 56 | color: isOver ? 'green' : undefined, 57 | }; 58 | 59 | 60 | return ( 61 |
    62 | {props.children} 63 |
    64 | ); 65 | } 66 | ``` 67 | {% endtab %} 68 | {% endtabs %} 69 | 70 | As you can see, it really only takes just a few lines to transform your existing components into droppable containers. 71 | 72 | ### Draggable 73 | 74 | ![](../.gitbook/assets/draggable-large.svg) 75 | 76 | Finally, let's take a look at implementing our first **Draggable** component. To do so, we'll be using the `useDraggable` hook. 77 | 78 | The `useDraggable` ****hook isn't opinionated about how your app should be structured. At minimum, it requires you to be able to attach listeners and a ref to the DOM element that you would like to become draggable. You'll also need to provide a unique `id` attribute to all your draggable components. 79 | 80 | After an item starts being dragged, the `transform` property will be populated with the `translate` coordinates you'll need to move the item on the screen. The `transform` object adheres to the following shape: `{x: number, y: number, scaleX: number, scaleY: number}` 81 | 82 | {% tabs %} 83 | {% tab title="Draggable.jsx" %} 84 | ```jsx 85 | import React from 'react'; 86 | import {useDraggable} from '@dnd-kit/core'; 87 | 88 | function Draggable(props) { 89 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 90 | id: 'draggable', 91 | }); 92 | const style = transform ? { 93 | transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, 94 | } : undefined; 95 | 96 | 97 | return ( 98 | 101 | ); 102 | } 103 | ``` 104 | {% endtab %} 105 | {% endtabs %} 106 | 107 | As you can see from the example above, it really only takes just a few lines to transform your existing components into draggable components. 108 | 109 | {% hint style="success" %} 110 | **Tips:** 111 | 112 | * For performance reasons, we recommend you use **`transform`** over other positional CSS properties to move the dragged element. 113 | * You'll likely want to alter the **`z-index`** of your Draggable component to ensure it appears on top of other elements. 114 | * If your item needs to move from one container to another, we recommend you use the [``](../api-documentation/draggable/clone.md) component so the item isn't constrained to its parent's stacking context and overflow constraints. 115 | {% endhint %} 116 | 117 | Converting the `transform` object to a string can feel tedious. Fear not, you can avoid having to do this by hand by importing the `CSS` utility from the `@dnd-kit/utilities` package: 118 | 119 | ```jsx 120 | import {CSS} from '@dnd-kit/utilities'; 121 | 122 | // Within your component that receives `transform` from `useDraggable`: 123 | const style = { 124 | transform: CSS.Translate.toString(transform), 125 | } 126 | ``` 127 | 128 | ### Assembling all the pieces 129 | 130 | Once you've set up your **Droppable** and **Draggable** components, you'll want to come back to where you set up your [``](../api-documentation/context-provider/) component so you can add event listeners to be able to respond to the different events that are fired. 131 | 132 | In this example, we'll assume you want to move your `` component from outside into your `` component: 133 | 134 | ![](../.gitbook/assets/example.png) 135 | 136 | To do so, you'll want to listen to the `onDragEnd` event of the `` to see if your draggable item was dropped over your droppable: 137 | 138 | {% tabs %} 139 | {% tab title="App.jsx" %} 140 | ```jsx 141 | import React from 'react'; 142 | import {DndContext} from '@dnd-kit/core'; 143 | 144 | import {Droppable} from './Droppable'; 145 | import {Draggable} from './Draggable'; 146 | 147 | function App() { 148 | const [isDropped, setIsDropped] = useState(false); 149 | const draggableMarkup = ( 150 | Drag me 151 | ); 152 | 153 | return ( 154 | 155 | {!isDropped ? draggableMarkup : null} 156 | 157 | {isDropped ? draggableMarkup : 'Drop here'} 158 | 159 | 160 | ); 161 | 162 | function handleDragEnd(event) { 163 | if (event.over && event.over.id === 'droppable') { 164 | setIsDropped(true); 165 | } 166 | } 167 | } 168 | ``` 169 | {% endtab %} 170 | 171 | {% tab title="Droppable.jsx" %} 172 | ```jsx 173 | import {useDroppable} from '@dnd-kit/core'; 174 | 175 | export function Droppable(props) { 176 | const {isOver, setNodeRef} = useDroppable({ 177 | id: 'droppable', 178 | }); 179 | const style = { 180 | color: isOver ? 'green' : undefined, 181 | }; 182 | 183 | 184 | return ( 185 |
    186 | {props.children} 187 |
    188 | ); 189 | } 190 | ``` 191 | {% endtab %} 192 | 193 | {% tab title="Draggable.jsx" %} 194 | ```jsx 195 | import React from 'react'; 196 | import {useDraggable} from '@dnd-kit/core'; 197 | 198 | export function Draggable(props) { 199 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 200 | id: 'draggable', 201 | }); 202 | const style = transform ? { 203 | transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, 204 | } : undefined; 205 | 206 | 207 | return ( 208 | 211 | ); 212 | } 213 | ``` 214 | {% endtab %} 215 | {% endtabs %} 216 | 217 | That's it! You've set up your first [**Droppable**](../api-documentation/droppable/) ****and [**Draggable**](../api-documentation/draggable/) components. 218 | 219 | ### Pushing things a bit further 220 | 221 | The example we've set up above is a bit simplistic. In the real world, you may have multiple droppable containers, and you may also want to be able to drag your items back out of the droppable containers once they've been dragged within them. 222 | 223 | Here's a slightly more complex example that contains multiple **Droppable** containers: 224 | 225 | ```jsx 226 | import React from 'react'; 227 | import {DndContext} from '@dnd-kit/core'; 228 | 229 | import {Droppable} from './Droppable'; 230 | import {Draggable} from './Draggable'; 231 | 232 | function App() { 233 | const containers = ['A', 'B', 'C']; 234 | const [parent, setParent] = useState(null); 235 | const draggableMarkup = ( 236 | Drag me 237 | ); 238 | 239 | return ( 240 | 241 | {parent === null ? draggableMarkup : null} 242 | 243 | {containers.map((id) => ( 244 | // We updated the Droppable component so it would accept an `id` 245 | // prop and pass it to `useDroppable` 246 | 247 | {parent === id ? draggableMarkup : 'Drop here'} 248 | 249 | ))} 250 | 251 | ); 252 | 253 | function handleDragEnd(event) { 254 | const {over} = event; 255 | 256 | // If the item is dropped over a container, set it as the parent 257 | // otherwise reset the parent to `null` 258 | setParent(over ? over.id : null); 259 | } 260 | }; 261 | ``` 262 | 263 | We hope this quick start guide has given you a glimpse of the simplicity and power of **dnd kit**. There's much more to learn, and we encourage you to keep reading about all of the different options you can pass to the `` in the next section. 264 | 265 | -------------------------------------------------------------------------------- /guides/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | To get started with **dnd kit**, install the core library via `npm` or `yarn`: 4 | 5 | ``` 6 | npm install @dnd-kit/core 7 | ``` 8 | 9 | You'll also need to be make sure you have **dnd kit**'s peer dependencies installed. Chances are you already have `react` and `react-dom` installed in your project, but if not, make sure to ins: 10 | 11 | ```bash 12 | npm install react react-dom 13 | ``` 14 | 15 | ## Sub-packages 16 | 17 | {% hint style="info" %} 18 | **dnd kit** is a [monorepo](https://en.wikipedia.org/wiki/Monorepo). Depending on your needs, you may also want to install other sub-packages that are available under the `@dnd-kit` namespace. 19 | {% endhint %} 20 | 21 | In order to keep the core of the library small, **dnd kit** only ships with the core building blocks that the majority of users will need most of the time for building drag and drop experiences: 22 | 23 | * [Context provider](../api-documentation/context-provider/) 24 | * Hooks for: 25 | * [Draggable](../api-documentation/draggable/) 26 | * [Droppable](../api-documentation/droppable/) 27 | * Sensors for: 28 | * [Mouse](../api-documentation/sensors/mouse.md) 29 | * [Touch](../api-documentation/sensors/touch.md) 30 | * [Keyboard](../api-documentation/sensors/keyboard.md) 31 | * Accessibility features 32 | 33 | If you don't need any other features, you can skip right ahead to the [Quick start](getting-started.md) guide. 34 | 35 | ### [Sortable](../presets/sortable/) 36 | 37 | The `@dnd-kit/core` package provides all the building blocks you would need to build a sortable interface from scratch should you choose to, but thankfully you don't need to. 38 | 39 | If you plan on building a sortable interface, we highly recommend you try out `@dnd-kit/sortable`, which is a small layer built on top of `@dnd-kit/core` and optimized for building silky smooth, flexible, and accessible sortable interfaces. 40 | 41 | ``` 42 | npm install @dnd-kit/sortable 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /introduction/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Eager to get started? This quick start guide will help you familiarize 4 | yourself with the core concepts of dnd kit. 5 | --- 6 | 7 | # Quick start 8 | 9 | {% hint style="info" %} 10 | Before getting started, make sure you have followed the installation steps outlined in the [Installation guide](installation.md). 11 | {% endhint %} 12 | 13 | ### Context provider 14 | 15 | First, we'll set up the general structure of the app. In order for the [`useDraggable`](broken-reference) and [`useDroppable`](broken-reference) hooks to function correctly, you'll need to ensure that the components where they are used are wrapped within a [``](../api-documentation/context-provider/) component: 16 | 17 | {% tabs %} 18 | {% tab title="App.jsx" %} 19 | ```jsx 20 | import React from 'react'; 21 | import {DndContext} from '@dnd-kit/core'; 22 | 23 | import {Draggable} from './Draggable'; 24 | import {Droppable} from './Droppable'; 25 | 26 | function App() { 27 | return ( 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | ``` 35 | {% endtab %} 36 | {% endtabs %} 37 | 38 | ### Droppable 39 | 40 | ![](../.gitbook/assets/droppable-large.svg) 41 | 42 | Next, let's set up your first **Droppable** component. To do so, we'll be using the `useDroppable` hook.\ 43 | \ 44 | The `useDroppable` hook isn't opinionated about how your app should be structured. At minimum though, it requires you pass a [ref](https://reactjs.org/docs/refs-and-the-dom.html) to the DOM element that you would like to become droppable. You'll also need to provide a unique `id` attribute to all your droppable components. 45 | 46 | When a **draggable** element is moved over your droppable element, the `isOver` property will become true. 47 | 48 | {% tabs %} 49 | {% tab title="Droppable.jsx" %} 50 | ```jsx 51 | import React from 'react'; 52 | import {useDroppable} from '@dnd-kit/core'; 53 | 54 | function Droppable(props) { 55 | const {isOver, setNodeRef} = useDroppable({ 56 | id: 'droppable', 57 | }); 58 | const style = { 59 | color: isOver ? 'green' : undefined, 60 | }; 61 | 62 | 63 | return ( 64 |
    65 | {props.children} 66 |
    67 | ); 68 | } 69 | ``` 70 | {% endtab %} 71 | {% endtabs %} 72 | 73 | ### Draggable 74 | 75 | ![](../.gitbook/assets/draggable-large.svg) 76 | 77 | Next, let's take a look at implementing our first **Draggable** component. To do so, we'll be using the `useDraggable` hook. 78 | 79 | The `useDraggable` **** hook isn't opinionated about how your app should be structured. It does however require you to be able to attach listeners and a ref to the DOM element that you would like to become draggable. You'll also need to provide a unique `id` attribute to all your draggable components. 80 | 81 | After a draggable item is picked up, the `transform` property will be populated with the `translate` coordinates you'll need to move the item on the screen. 82 | 83 | The `transform` object adheres to the following shape: `{x: number, y: number, scaleX: number, scaleY: number}` 84 | 85 | {% tabs %} 86 | {% tab title="Draggable.jsx" %} 87 | ```jsx 88 | import React from 'react'; 89 | import {useDraggable} from '@dnd-kit/core'; 90 | 91 | function Draggable(props) { 92 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 93 | id: 'draggable', 94 | }); 95 | const style = transform ? { 96 | transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, 97 | } : undefined; 98 | 99 | 100 | return ( 101 | 104 | ); 105 | } 106 | ``` 107 | {% endtab %} 108 | {% endtabs %} 109 | 110 | As you can see from the example above, it really only takes just a few lines to transform your existing components into draggable components. 111 | 112 | {% hint style="success" %} 113 | **Tips:** 114 | 115 | * For performance reasons, we recommend you use **`transform`** over other positional CSS properties to move the dragged element. 116 | * You'll likely want to alter the **`z-index`** of your Draggable component to ensure it appears on top of other elements. 117 | * If your item needs to move from one container to another, we recommend you use the [``](../api-documentation/draggable/drag-overlay.md) component. 118 | {% endhint %} 119 | 120 | Converting the `transform` object to a string can feel tedious. Fear not, you can avoid having to do this by hand by importing the `CSS` utility from the `@dnd-kit/utilities` package: 121 | 122 | ```jsx 123 | import {CSS} from '@dnd-kit/utilities'; 124 | 125 | // Within your component that receives `transform` from `useDraggable`: 126 | const style = { 127 | transform: CSS.Translate.toString(transform), 128 | } 129 | ``` 130 | 131 | ### Assembling all the pieces 132 | 133 | Once you've set up your **Droppable** and **Draggable** components, you'll want to come back to where you set up your [``](../api-documentation/context-provider/) component so you can add event listeners to be able to respond to the different events that are fired. 134 | 135 | In this example, we'll assume you want to move your `` component from outside into your `` component: 136 | 137 | ![](../.gitbook/assets/example.png) 138 | 139 | To do so, you'll want to listen to the `onDragEnd` event of the `` to see if your draggable item was dropped over your droppable: 140 | 141 | {% tabs %} 142 | {% tab title="App.jsx" %} 143 | ```jsx 144 | import React, {useState} from 'react'; 145 | import {DndContext} from '@dnd-kit/core'; 146 | 147 | import {Droppable} from './Droppable'; 148 | import {Draggable} from './Draggable'; 149 | 150 | function App() { 151 | const [isDropped, setIsDropped] = useState(false); 152 | const draggableMarkup = ( 153 | Drag me 154 | ); 155 | 156 | return ( 157 | 158 | {!isDropped ? draggableMarkup : null} 159 | 160 | {isDropped ? draggableMarkup : 'Drop here'} 161 | 162 | 163 | ); 164 | 165 | function handleDragEnd(event) { 166 | if (event.over && event.over.id === 'droppable') { 167 | setIsDropped(true); 168 | } 169 | } 170 | } 171 | ``` 172 | {% endtab %} 173 | 174 | {% tab title="Droppable.jsx" %} 175 | ```jsx 176 | import React from 'react'; 177 | import {useDroppable} from '@dnd-kit/core'; 178 | 179 | export function Droppable(props) { 180 | const {isOver, setNodeRef} = useDroppable({ 181 | id: 'droppable', 182 | }); 183 | const style = { 184 | color: isOver ? 'green' : undefined, 185 | }; 186 | 187 | 188 | return ( 189 |
    190 | {props.children} 191 |
    192 | ); 193 | } 194 | ``` 195 | {% endtab %} 196 | 197 | {% tab title="Draggable.jsx" %} 198 | ```jsx 199 | import React from 'react'; 200 | import {useDraggable} from '@dnd-kit/core'; 201 | 202 | export function Draggable(props) { 203 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 204 | id: 'draggable', 205 | }); 206 | const style = transform ? { 207 | transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, 208 | } : undefined; 209 | 210 | 211 | return ( 212 | 215 | ); 216 | } 217 | ``` 218 | {% endtab %} 219 | {% endtabs %} 220 | 221 | That's it! You've set up your first [**Droppable**](../api-documentation/droppable/) **** and [**Draggable**](../api-documentation/draggable/) components. 222 | 223 | ### Pushing things a bit further 224 | 225 | The example we've set up above is a bit simplistic. In a real world example, you may have multiple droppable containers, and you may also want to be able to drag your items back out of the droppable containers once they've been dragged within them. 226 | 227 | Here's a slightly more complex example that contains multiple **Droppable** containers: 228 | 229 | {% tabs %} 230 | {% tab title="App.jsx" %} 231 | ```jsx 232 | import React, {useState} from 'react'; 233 | import {DndContext} from '@dnd-kit/core'; 234 | 235 | import {Droppable} from './Droppable'; 236 | import {Draggable} from './Draggable'; 237 | 238 | function App() { 239 | const containers = ['A', 'B', 'C']; 240 | const [parent, setParent] = useState(null); 241 | const draggableMarkup = ( 242 | Drag me 243 | ); 244 | 245 | return ( 246 | 247 | {parent === null ? draggableMarkup : null} 248 | 249 | {containers.map((id) => ( 250 | // We updated the Droppable component so it would accept an `id` 251 | // prop and pass it to `useDroppable` 252 | 253 | {parent === id ? draggableMarkup : 'Drop here'} 254 | 255 | ))} 256 | 257 | ); 258 | 259 | function handleDragEnd(event) { 260 | const {over} = event; 261 | 262 | // If the item is dropped over a container, set it as the parent 263 | // otherwise reset the parent to `null` 264 | setParent(over ? over.id : null); 265 | } 266 | }; 267 | ``` 268 | {% endtab %} 269 | 270 | {% tab title="Droppable.jsx" %} 271 | ```jsx 272 | import React from 'react'; 273 | import {useDroppable} from '@dnd-kit/core'; 274 | 275 | export function Droppable(props) { 276 | const {isOver, setNodeRef} = useDroppable({ 277 | id: props.id, 278 | }); 279 | const style = { 280 | color: isOver ? 'green' : undefined, 281 | }; 282 | 283 | 284 | return ( 285 |
    286 | {props.children} 287 |
    288 | ); 289 | } 290 | ``` 291 | {% endtab %} 292 | 293 | {% tab title="Draggable.jsx" %} 294 | ```jsx 295 | import React from 'react'; 296 | import {useDraggable} from '@dnd-kit/core'; 297 | 298 | export function Draggable(props) { 299 | const {attributes, listeners, setNodeRef, transform} = useDraggable({ 300 | id: props.id, 301 | }); 302 | const style = transform ? { 303 | transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, 304 | } : undefined; 305 | 306 | 307 | return ( 308 | 311 | ); 312 | } 313 | ``` 314 | {% endtab %} 315 | {% endtabs %} 316 | 317 | We hope this quick start guide has given you a glimpse of the simplicity and power of @dnd-kit. There's much more to learn, and we encourage you to keep reading about all of the different options you can pass to `` , `useDroppable` and `useDraggable` by reading their respective API documentation. 318 | -------------------------------------------------------------------------------- /introduction/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | To get started with **@dnd-kit**, install the core library via `npm` or `yarn`: 4 | 5 | ``` 6 | npm install @dnd-kit/core 7 | ``` 8 | 9 | You'll also need to be make sure you have peer dependencies installed. Chances are you already have `react` and `react-dom` installed in your project, but if not, make sure to install them: 10 | 11 | ```bash 12 | npm install react react-dom 13 | ``` 14 | 15 | ## Packages 16 | 17 | {% hint style="info" %} 18 | **@dnd-kit** is a [monorepo](https://en.wikipedia.org/wiki/Monorepo). Depending on your needs, you may also want to install other sub-packages that are available under the `@dnd-kit` namespace. 19 | {% endhint %} 20 | 21 | ### Core library 22 | 23 | In order to keep the core of the library small, `@dnd-kit/core` only ships with the main building blocks that the majority of users will need most of the time for building drag and drop experiences: 24 | 25 | * [Context provider](../api-documentation/context-provider/) 26 | * Hooks for: 27 | * [Draggable](../api-documentation/draggable/) 28 | * [Droppable](../api-documentation/droppable/) 29 | * [Drag Overlay](../api-documentation/draggable/drag-overlay.md) 30 | * Sensors for: 31 | * [Pointer](../api-documentation/sensors/pointer.md) 32 | * [Mouse](../api-documentation/sensors/mouse.md) 33 | * [Touch](../api-documentation/sensors/touch.md) 34 | * [Keyboard](../api-documentation/sensors/keyboard.md) 35 | * [Accessibility features](../guides/accessibility.md) 36 | 37 | ### Modifiers 38 | 39 | Modifiers let you dynamically modify the movement coordinates that are detected by sensors. They can be used for a wide range of use cases, for example: 40 | 41 | * Restricting motion to a single axis 42 | * Restricting motion to the draggable node container's bounding rectangle 43 | * Restricting motion to the draggable node's scroll container bounding rectangle 44 | * Applying resistance or clamping the motion 45 | 46 | The modifiers repository contains a number of useful modifiers that can be applied on [`DndContext`](../api-documentation/context-provider/) as well as [`DraggableClone`](../api-documentation/draggable/drag-overlay.md). 47 | 48 | To start using modifiers, install the modifiers package via yarn or npm: 49 | 50 | ``` 51 | npm install @dnd-kit/modifiers 52 | ``` 53 | 54 | ### Presets 55 | 56 | #### [Sortable](../presets/sortable/) 57 | 58 | The `@dnd-kit/core` package provides all the building blocks you would need to build a sortable interface from scratch should you choose to, but thankfully you don't need to. 59 | 60 | If you plan on building a sortable interface, we highly recommend you try out `@dnd-kit/sortable`, which is a small layer built on top of `@dnd-kit/core` and optimized for building silky smooth, flexible, and accessible sortable interfaces. 61 | 62 | ``` 63 | npm install @dnd-kit/sortable 64 | ``` 65 | 66 | ## Development releases 67 | 68 | Each commit merged into the @dnd-kit main branch will trigger a development build to be released to npm under the `next` tag. 69 | 70 | To try a development release before the official release, install each @dnd-kit package you intend to use with the `@next`tag 71 | 72 | ``` 73 | npm install @dnd-kit/core@next @dnd-kit/sortable@next 74 | ``` 75 | 76 | {% hint style="info" %} 77 | Development releases can be unstable, we recommend you lock to a specific development release if you intend to use them in production. 78 | {% endhint %} 79 | -------------------------------------------------------------------------------- /presets/sortable.md: -------------------------------------------------------------------------------- 1 | # Sortable 2 | 3 | -------------------------------------------------------------------------------- /presets/sortable/sortable-context.md: -------------------------------------------------------------------------------- 1 | # Sortable Context 2 | 3 | The `SortableContext` provides information via context that is consumed by the [`useSortable`](usesortable.md) hook. 4 | 5 | ## Props 6 | 7 | ### Items 8 | 9 | It requires that you pass it a sorted array of the unique identifiers associated with the elements that use the `useSortable` hook within it. 10 | 11 | ```jsx 12 | import React, {useState} from 'react'; 13 | import {DndContext} from '@dnd-kit/core'; 14 | import {SortableContext} from '@dnd-kit/sortable'; 15 | 16 | function App() { 17 | const [items] = useState([1, 2, 3]); 18 | 19 | return ( 20 | 21 | 22 | {/* ... */} 23 | 24 | 25 | ); 26 | } 27 | ``` 28 | 29 | {% hint style="info" %} 30 | It's important that the `items` prop passed to `SortableContext` be sorted in the same order in which the items are rendered, otherwise you may see unexpected results. 31 | {% endhint %} 32 | 33 | ### Strategy 34 | 35 | The `SortableContext` component also accepts different [sorting strategies](./#sorting-strategies) to compute transforms for the `useSortable` hook. The built in strategies include: 36 | 37 | * `rectSortingStrategy`: This is the default value, and is suitable for most use cases. This strategy does not support virtualized lists. 38 | * `verticalListSortingStrategy`: This strategy is optimized for vertical lists, and supports virtualized lists. 39 | * `horizontalListSortingStrategy`: This strategy is optimized for horizontal lists, and supports virtualized lists. 40 | * `rectSwappingStrategy`: Use this strategy to achieve swappable functionality. 41 | 42 | Make sure to use the sorting strategy that is the most adapted to the use case you are building for. 43 | 44 | For advanced use cases, you may also build custom sorting strategies. To do so, make sure that the custom strategy you are building accepts the arguments that are passed to a sorting strategy and adheres to the return values that are expected. For more details on this, refer to the implementation of the built-in sorting strategies. 45 | 46 | ### Identifier 47 | 48 | The `SortableContext` component also optionally accepts an `id` prop. If an `id` is not provided, one will be auto-generated for you. The `id` prop is for advanced use cases. If you're building custom sensors, you'll have access to each sortable element's `data` prop, which will contain the `containerId` associated to that sortable context. 49 | 50 | ## Usage 51 | 52 | {% hint style="info" %} 53 | In order for the `SortableContext` component to function properly, make sure it is a descendant of a `DndContext` provider. 54 | {% endhint %} 55 | 56 | You may nest multiple `SortableContext` providers within the same parent `DndContext` provider. 57 | 58 | You may also nest `SortableContext` providers within other `SortableContext` providers, either all under the same `DndContext` provider or each with their own individual `DndContext` providers if you would like to configure them with different options: 59 | 60 | ```jsx 61 | // Bad, missing parent 62 | 63 | {/* ... */} 64 | 65 | 66 | // Good, basic setup 67 | 68 | 69 | {/* ... */} 70 | 71 | 72 | 73 | // Good, multiple sibling Sortable contexts 74 | 75 | 76 | {/* ... */} 77 | 78 | 79 | {/* ... */} 80 | 81 | 82 | 83 | // Good, nested DndContexts 84 | 85 | 86 | 87 | 88 | {/* ... */} 89 | 90 | 91 | 92 | 93 | 94 | // Bad, nested Sortable contexts with `id` collisions 95 | 96 | 97 | 98 | {/* ... */} 99 | 100 | 101 | 102 | 103 | // Good, nested Sortable contexts with unique `id`s 104 | 105 | 106 | 107 | {/* ... */} 108 | 109 | 110 | 111 | 112 | ``` 113 | 114 | ## 115 | -------------------------------------------------------------------------------- /presets/sortable/usesortable.md: -------------------------------------------------------------------------------- 1 | # useSortable 2 | 3 | The `useSortable` hook is an abstraction that composes the [`useDroppable`](../../api-documentation/droppable/) and [`useDraggable`](../../api-documentation/draggable/) hooks. 4 | 5 | ![](../../.gitbook/assets/usesortable-3-.png) 6 | 7 | {% hint style="info" %} 8 | To function properly, the `useSortable` hook needs to be used within a descendant of a [`SortableContext`](sortable-context.md) provider higher up in the tree. 9 | {% endhint %} 10 | 11 | ## Usage 12 | 13 | If you're already familiar with the [`useDraggable`](../../api-documentation/draggable/) hook, the `useSortable` hook should look very familiar, since, it is an abstraction on top of it. 14 | 15 | In addition to the `attributes`, `listeners`,`transform` and `setNodeRef` arguments, which you should already be familiar with if you've used the `useDraggable` hook before, you'll notice that the `useSortable` hook also provides a [`transition`](usesortable.md#transform) argument. 16 | 17 | ```jsx 18 | import React from 'react'; 19 | import {useSortable} from '@dnd-kit/sortable'; 20 | import {CSS} from '@dnd-kit/utilities'; 21 | 22 | function SortableItem(props) { 23 | const { 24 | attributes, 25 | listeners, 26 | setNodeRef, 27 | transform, 28 | transition, 29 | } = useSortable({id: props.id}); 30 | 31 | const style = { 32 | transform: CSS.Transform.toString(transform), 33 | transition, 34 | }; 35 | 36 | return ( 37 |
  • 38 | {/* ... */} 39 |
  • 40 | ); 41 | } 42 | ``` 43 | 44 | ## Properties 45 | 46 | ### Listeners 47 | 48 | The `listeners` property contains the [activator event handlers](../../api-documentation/sensors/#activators) for each [Sensor](../../api-documentation/sensors/) that is defined on the parent [`DndContext`](../../api-documentation/context-provider/#props) provider. 49 | 50 | It should be attached to the node(s) that you wish to use as the activator to begin a sort event. In most cases, that will be the same node as the one passed to `setNodeRef`, though not necessarily. For instance, when implementing a sortable element with a "drag handle", the ref should be attached to the parent node that should be sortable, but the listeners can be attached to the handle node instead. 51 | 52 | For additional details on the [`listeners`](../../api-documentation/draggable/#listeners) property, refer to the [`useDraggable`](../../api-documentation/draggable/) documentation. 53 | 54 | ### Attributes 55 | 56 | The `useSortable` hook provides a set of sensible default attributes for draggable items. We recommend you attach these to your draggable elements, though nothing will break if you don't. 57 | 58 | For additional details on the [`attributes`](../../api-documentation/draggable/#attributes) property, refer to the [`useDraggable`](../../api-documentation/draggable/) documentation. 59 | 60 | ### Transform 61 | 62 | The `transform` property represents the displacement and change of scale transformation that a sortable item needs to apply to transition to its new position without needing to update the DOM order. 63 | 64 | The `transform` property for the `useSortable` hook behaves similarly to the [`transform`](../../api-documentation/draggable/#transforms) property of the [`useDraggable`](../../api-documentation/draggable/) hook for the active sortable item, when there is no [`DragOverlay`](../../api-documentation/draggable/drag-overlay.md) being used. 65 | 66 | ### Node ref 67 | 68 | In order for the `useSortable` hook to function properly, it needs the `setNodeRef` property to be attached to the HTML element you intend on turning into a sortable element: 69 | 70 | ```jsx 71 | function SortableItem(props) { 72 | const {setNodeRef} = useDraggable({ 73 | id: props.id, 74 | }); 75 | 76 | return ( 77 |
  • 78 | {/* ... */} 79 |
  • 80 | ); 81 | } 82 | ``` 83 | 84 | Keep in mind that the `ref` should be assigned to the outer container that you want to become draggable, but this doesn't necessarily need to coincide with the container that the listeners are attached to: 85 | 86 | ```jsx 87 | function SortableItem(props) { 88 | const {arguments, listeners, setNodeRef} = useDraggable({ 89 | id: props.id, 90 | }); 91 | 92 | return ( 93 |
  • 94 | {/* ... */} 95 | 96 |
  • 97 | ); 98 | } 99 | ``` 100 | 101 | Since the `useSortable` hook is simply an abstraction on top of the [`useDraggable`](../../api-documentation/draggable/usedraggable.md) and [`useDroppable`](../../api-documentation/droppable/usedroppable.md) hooks, in some advanced use cases, you may also use the `setDroppableNodeRef` and `setDraggableNodeRef` properties to connect them to different nodes. For example, if you want the draggable element to have a different dimension than the droppable element that will be sortable: 102 | 103 | ```jsx 104 | function SortableItem(props) { 105 | const {setDraggableNodeRef, setDroppableNodeRef} = useDraggable({ 106 | id: props.id, 107 | }); 108 | 109 | return ( 110 |
  • 111 | {/* ... */} 112 | 113 |
  • 114 | ); 115 | } 116 | ``` 117 | 118 | 119 | 120 | ### Activator 121 | 122 | **`setActivatorNodeRef`** 123 | 124 | It's possible for the listeners to be attached to a different node than the one that `setNodeRef` is attached to. 125 | 126 | A common example of this is when implementing a drag handle and attaching the listeners to the drag handle: 127 | 128 | ```jsx 129 | function SortableItem(props) { 130 | const {listeners, setNodeRef} = useSortable({ 131 | id: props.id, 132 | }); 133 | 134 | return ( 135 |
  • 136 | {/* ... */} 137 | 138 |
  • 139 | ); 140 | } 141 | ``` 142 | 143 | When the activator node differs from the draggable node, we recommend setting the activator node ref on the activator node: 144 | 145 | ```jsx 146 | function SortableItem(props) { 147 | const {listeners, setNodeRef, setActivatorNodeRef} = useSortable({ 148 | id: props.id, 149 | }); 150 | 151 | return ( 152 |
  • 153 | {/* ... */} 154 | 155 |
  • 156 | ); 157 | } 158 | ``` 159 | 160 | This helps @dnd-kit more accurately handle automatic focus management and can also be accessed by sensors for enhanced activation constraints. 161 | 162 | {% hint style="info" %} 163 | Focus management is automatically handled by [@dnd-kit](https://github.com/dnd-kit). When the activator event is a Keyboard event, focus will automatically be restored back to the first focusable node of the activator node. 164 | 165 | If no activator node is set via `setActivatorNodeRef`, focus will automatically be restored on the first focusable node of the draggable node registered via `setNodeRef.` 166 | {% endhint %} 167 | 168 | ### Transition 169 | 170 | Refer to the [`transition` argument](usesortable.md#transition-1) documentation below. 171 | 172 | ## Arguments 173 | 174 | ### Identifier 175 | 176 | The `id` argument is a `string` or `number` that should be unique. 177 | 178 | Since the `useSortable` is an abstraction on top of the `useDroppable` and `useDraggable` hooks, which both require a unique identifier, the `useSortable` hook also requires a unique identifier. 179 | 180 | The argument passed to the `id` argument of `useSortable` should match the `id` passed in the `items` array of the parent [`SortableContext`](sortable-context.md) provider. 181 | 182 | ### Disabled 183 | 184 | If you'd like to temporarily disable a sortable item from being interactive, set the `disabled` argument to `true`. 185 | 186 | ### Transition 187 | 188 | The transition argument controls the value of the `transition` property for you. It conveniently disables transform transitions while not dragging, but ensures that items transition back to their final positions when the drag operation is ended or cancelled. 189 | 190 | It also disables transitions for the active sortable element that is being dragged, unless there is a [`DragOverlay`](../../api-documentation/draggable/drag-overlay.md) being used. 191 | 192 | The default transition is `250` milliseconds, with an easing function set to `ease`, but you can customize this and pass any valid [CSS transition timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function). 193 | 194 | ```javascript 195 | const { 196 | transition, 197 | } = useSortable({ 198 | transition: { 199 | duration: 150, // milliseconds 200 | easing: 'cubic-bezier(0.25, 1, 0.5, 1)', 201 | }, 202 | }); 203 | ``` 204 | 205 | Make sure you pass the `transition` style property to the same node that has the `transform` property applied: 206 | 207 | ```jsx 208 | import React from 'react'; 209 | import {useSortable} from '@dnd-kit/sortable'; 210 | import {CSS} from '@dnd-kit/utilities'; 211 | 212 | function SortableItem(props) { 213 | const { 214 | transform, 215 | transition, 216 | } = useSortable({id: props.id}); 217 | 218 | const style = { 219 | transform: CSS.Transform.toString(transform), 220 | transition, 221 | }; 222 | 223 | return ( 224 |
  • 225 | {/* ... */} 226 |
  • 227 | ); 228 | } 229 | ``` 230 | 231 | If you prefer, you may also use CSS variables to manage the `transform` and `transition` properties: 232 | 233 | ```jsx 234 | import React from 'react'; 235 | import {useSortable} from '@dnd-kit/sortable'; 236 | import {CSS} from '@dnd-kit/utilities'; 237 | 238 | function SortableItem(props) { 239 | const { 240 | transform, 241 | transition, 242 | } = useSortable({id: props.id}); 243 | 244 | const style = { 245 | '--translate-x': transform ? transform.x : 0, 246 | '--translate-y': transform ? transform.y : 0, 247 | '--transition': transition, 248 | }; 249 | 250 | return ( 251 |
  • 252 | {/* ... */} 253 |
  • 254 | ); 255 | } 256 | ``` 257 | 258 | To disable transitions entirely, set the `transition` argument to `null`: 259 | 260 | ```javascript 261 | const { 262 | transition, 263 | } = useSortable({ 264 | transition: null, 265 | }); 266 | ``` 267 | 268 | If you prefer to manage transitions yourself, you may also choose to do so, but this isn't something we recommend. 269 | 270 | ### Sorting strategy 271 | 272 | Optionally, you can pass a local sorting strategy that differs from the [global sorting strategy](sortable-context.md#strategy) passed to the parent `SortableContext` provider. 273 | --------------------------------------------------------------------------------