1717## What is DragSwag?
1818
1919** DragSwag** is primarily a drag-and-drop library for React. Frustrated with the bulky APIs offered by other libraries,
20- I developed DragSwag to focus on ergonomics and simplicity while maintaining flexibility and customization. It’ s built
20+ I developed DragSwag to focus on ergonomics and simplicity while maintaining flexibility and customization. It' s built
2121on top of dragswag/core, a universal building block suitable for any framework and vanilla JavaScript.
2222
2323## Key Features
2424
25- - ** Extremely** simple—just use the useDraggable and useDroppable hooks, and the Overlay component to get started
25+ - ** Extremely** simple—just use the useDraggable and useDroppable hooks, and the DragOverlayProvider to get started
2626- ** Highly** ergonomic—no need to memoize callbacks or configurations
2727- ** Fully** customizable—rich event system
2828- ** Two-way** data exchange between draggable and droppable components
@@ -72,12 +72,12 @@ bun add dragswag
7272Show Me the Code!
7373
7474Here is the simplest example involving two squares. The draggable square carries a color in its data. The droppable
75- square reacts to the drag interaction by setting its color according to the draggable’ s color. When dropped, the text of
75+ square reacts to the drag interaction by setting its color according to the draggable' s color. When dropped, the text of
7676the droppable square is updated.
7777
7878<img width =" 400 " alt =" Simple drag-and-drop squares " src =" https://raw.githubusercontent.com/pizzajsdev/dragswag/main/resources/drag-and-drop-squares.avif " />
7979
80- The ` DraggableSquare ` component uses the ` useDraggable ` hook to make it draggable. The hook’ s configuration defines the
80+ The ` DraggableSquare ` component uses the ` useDraggable ` hook to make it draggable. The hook' s configuration defines the
8181kind and the data of the draggable. The draggable wrapper is used to make the component actually draggable:
8282
8383``` tsx
@@ -102,7 +102,7 @@ export const DraggableSquare = ({ color }: { color: string }) => {
102102
103103The ` DroppableSquare ` component uses the ` useDroppable ` hook to make it droppable. The configuration defines the
104104accepted kind and the callback for the ` onDrop ` event. The droppable wrapper is used to make the component droppable.
105- The ` hovered ` property is used to get the data of the draggable when it’ s hovered over:
105+ The ` hovered ` property is used to get the data of the draggable when it' s hovered over:
106106
107107``` tsx
108108import { useDroppable } from ' dragswag'
@@ -128,14 +128,14 @@ export const DroppableSquare = ({ color }: { color: string }) => {
128128```
129129
130130The ` App ` component renders the draggable and droppable squares. The draggable square is wrapped in an absolute wrapper
131- to position it on the page. The ` Overlay ` component is rendered to show the dragged component:
131+ to position it on the page. The ` DragOverlayProvider ` component is needed to show and the dragged component:
132132
133133``` tsx
134- import { Overlay } from ' dragswag'
134+ import { DragOverlayProvider } from ' dragswag'
135135
136136export default function App() {
137137 return (
138- <>
138+ <DragOverlayProvider >
139139 { /* Render squares with absolute wrappers for positioning */ }
140140 <div style = { { position: ' relative' }} >
141141 <div style = { { position: ' absolute' , top: 100 , left: 100 }} >
@@ -145,9 +145,7 @@ export default function App() {
145145 <DroppableSquare color = " green" />
146146 </div >
147147 </div >
148- { /* Render overlay to show the dragged component */ }
149- <Overlay />
150- </>
148+ </DragOverlayProvider >
151149 )
152150}
153151```
@@ -160,12 +158,12 @@ See more examples in the `examples` folder and in the [Examples](examples) secti
160158
161159## How t works
162160
163- Under the hood, DragSwag attaches a pointerdown event listener to draggable elements. After it’ s triggered, it tracks
161+ Under the hood, DragSwag attaches a pointerdown event listener to draggable elements. After it' s triggered, it tracks
164162` pointermove ` events on the document until ` pointerup ` occurs. On every ` pointermove ` event, it checks elements under
165163the cursor using ` document.elementsFromPoint() ` , and then handles the logic of tracking current and new droppables at
166164that point.
167165
168- Draggables aren’ t bound to the initial configuration, so it can be changed at any time, making it very flexible to use
166+ Draggables aren' t bound to the initial configuration, so it can be changed at any time, making it very flexible to use
169167new closures, settings, etc. The React bindings wrap this core logic and adapt some arguments to be more convenient.
170168
171169An important point for React is the draggable/droppable wrappers—they keep the original ref to the React element and
@@ -236,7 +234,7 @@ There are three of them: `onDragStart`, `onDragMove`, and `onDragEnd`.
236234### ` onDragStart `
237235
238236The callback is called when the drag interaction starts — this means the user clicked and started to move the element.
239- In more detail, it’ s called after the ` shouldDrag ` function returns ` true ` . Here’ s how it looks in the code:
237+ In more detail, it' s called after the ` shouldDrag ` function returns ` true ` . Here' s how it looks in the code:
240238
241239``` tsx
242240const Square = () => {
@@ -250,7 +248,7 @@ const Square = () => {
250248}
251249```
252250
253- The ` props ` here contain data related to the interaction: ` data ` , ` event ` , ` dragStartEvent ` , and ` element ` . It’ s quite
251+ The ` props ` here contain data related to the interaction: ` data ` , ` event ` , ` dragStartEvent ` , and ` element ` . It' s quite
254252intuitive — data is the ` data ` field in the configuration (or the result of the data factory function if specified),
255253` event ` is the PointerEvent from the ` pointermove ` handler, and the ` dragStartEvent ` is the ` PointerEvent ` from the
256254` pointerdown ` event.
@@ -264,7 +262,7 @@ This callback is also [described later](#ondragstart-1) in the configuration doc
264262
265263### ` onDragMove `
266264
267- This callback is executed on every pointermove event. As you can imagine, it’ s time-sensitive, so try to avoid putting
265+ This callback is executed on every pointermove event. As you can imagine, it' s time-sensitive, so try to avoid putting
268266expensive logic here.
269267
270268Example:
@@ -283,7 +281,7 @@ const Square = () => {
283281
284282Props contain all the same data as in the onDragStart callback, but with some additions:
285283
286- - ` dropTargets ` is an array containing data about current drop targets under the cursor. It’ s an array, so if the
284+ - ` dropTargets ` is an array containing data about current drop targets under the cursor. It' s an array, so if the
287285 current draggable is over multiple of them, they will be here. Each drop target is represented as an object with data
288286 and element fields. The ` data ` is the data field from the ` useDroppable ` configuration, allowing data exchange between
289287 draggable and droppable components. The ` element ` is the droppable element.
@@ -294,7 +292,7 @@ To get more of the idea of dropTargets and using their data, see the Data Transf
294292
295293## Example - Data Transfer from Droppable to Draggable
296294
297- Let’ s modify the draggable squares example to show how data can be transferred from the droppable to the draggable in
295+ Let' s modify the draggable squares example to show how data can be transferred from the droppable to the draggable in
298296the ` onDragMove ` callback.
299297
300298First, we will change the` DraggableSquare.tsx ` to the following:
@@ -336,7 +334,7 @@ export const DraggableSquare = ({ color: initialColor }: { color: string }) => {
336334</details >
337335
338336Here we have ` onDragMove ` and ` onDragEnd ` callbacks that control the color of the draggable square. When there is a drop
339- target under the cursor (so ` dropTargets.length > 0 ` ), we get the color from the first drop target’ s data field.
337+ target under the cursor (so ` dropTargets.length > 0 ` ), we get the color from the first drop target' s data field.
340338
341339The ` DroppableSquare.tsx ` is mostly the same, but we add the data there and remove color matching logic for convenience:
342340
@@ -409,7 +407,7 @@ export const DroppableSquare = ({ color }: { color: string }) => {
409407}
410408```
411409
412- When the droppable is hovered by the draggable, `hovered returns its data and kind; otherwise, it’ s null.
410+ When the droppable is hovered by the draggable, `hovered returns its data and kind; otherwise, it' s null.
413411
414412Like the ` draggable ` wrapper, the component can be wrapped both in ` draggable ` and ` droppable ` ; the order doesn't
415413matter.
@@ -421,13 +419,13 @@ matter.
421419The configuration of ` useDroppable ` can have the following callbacks: ` onDragIn ` , ` onDragOut ` , ` onDragMove ` , and
422420` onDrop ` .
423421
424- Let’ s take a look at each of them.
422+ Let' s take a look at each of them.
425423
426424<hr />
427425
428426### ` onDragIn `
429427
430- This callback is called when a draggable item enters the area of the drop target. It’ s executed once and can be used for
428+ This callback is called when a draggable item enters the area of the drop target. It' s executed once and can be used for
431429different interactions like changing color, setting some state, etc.
432430
433431Here's an example:
@@ -459,11 +457,11 @@ area.
459457### ` onDragMove `
460458
461459` onDragMove ` is called on every ` pointermove ` event over the drop target. It can be used for customization of the drop
462- target’ s look during the drag interaction.
460+ target' s look during the drag interaction.
463461
464462### Example - Dynamic Border on DroppableSquare
465463
466- Let’ s modify the squares example to render a border on ` DroppableSquare ` depending on the position of the draggable.
464+ Let' s modify the squares example to render a border on ` DroppableSquare ` depending on the position of the draggable.
467465
468466The ` DraggableSquare ` will remain the same; the only changes will be in the `DroppableSquare component.
469467
@@ -540,10 +538,10 @@ will be showing the border. On `onDragOut` and `onDrop` events we remove the bor
540538
541539### ` onDragOut `
542540
543- This callback is called when the draggable leaves the drop target area. It’ s often used in conjunction with ` onDragIn `
541+ This callback is called when the draggable leaves the drop target area. It' s often used in conjunction with ` onDragIn `
544542to perform opposite actions, like restoring initial state, color, or text.
545543
546- You can see its usage in the previous example with dynamic borders — it’ s used there to remove borders after the cursor
544+ You can see its usage in the previous example with dynamic borders — it' s used there to remove borders after the cursor
547545leaves the droppable square.
548546
549547Arguments are mostly the same as in the previous callbacks; more details are in the [ config docs] ( #ondragout-1 ) .
@@ -707,7 +705,7 @@ const { draggable } = useDraggable({
707705If ` move ` is false or not defined, the draggable component is cloned to the overlay layer, and the original component is
708706shown as is.
709707
710- Also, it’ s important to note that the original component will not receive prop updates during the drag interaction —
708+ Also, it' s important to note that the original component will not receive prop updates during the drag interaction —
711709they are all applied to the dragging component.
712710
713711` move ` is ignored when the ` placeholder ` option is specified.
@@ -766,8 +764,8 @@ const { draggable } = useDraggable({
766764})
767765```
768766
769- Note: Offset is calculated once when the drag interaction starts. It’ s the distance between the cursor position and the
770- top-left corner of the dragging component. If not specified, it’ s computed so that the component’ s position matches its
767+ Note: Offset is calculated once when the drag interaction starts. It' s the distance between the cursor position and the
768+ top-left corner of the dragging component. If not specified, it' s computed so that the component' s position matches its
771769rendered position before the drag.
772770
773771### Callbacks
@@ -786,7 +784,7 @@ const { draggable } = useDraggable({
786784```
787785
788786` shouldDrag ` is called on every mouse move during the drag interaction until it returns ` true ` or the drag interaction
789- ends. It’ s useful for adding a threshold or some other condition to start the drag interaction.
787+ ends. It' s useful for adding a threshold or some other condition to start the drag interaction.
790788
791789Arguments:
792790
@@ -833,8 +831,8 @@ const { draggable } = useDraggable({
833831})
834832```
835833
836- The callback is called on every mouse move during the drag interaction. It’ s not recommended to put expensive logic here
837- because it’ s called frequently.
834+ The callback is called on every mouse move during the drag interaction. It' s not recommended to put expensive logic here
835+ because it' s called frequently.
838836
839837Arguments are the same as in ` onDragStart ` with some additions:
840838
@@ -845,7 +843,7 @@ Arguments are the same as in `onDragStart` with some additions:
845843
846844#### ` onDragEnd `
847845
848- Called when the drag interaction ends. ` dropTargets ` will be an empty array if the draggable wasn’ t dropped on any drop
846+ Called when the drag interaction ends. ` dropTargets ` will be an empty array if the draggable wasn' t dropped on any drop
849847target.
850848
851849``` tsx
@@ -861,7 +859,7 @@ const { draggable } = useDraggable({
861859
862860### Full Example
863861
864- Here’ s a complete example demonstrating the use of all the configuration options:
862+ Here' s a complete example demonstrating the use of all the configuration options:
865863
866864``` tsx
867865import { useDraggable } from ' dragswag'
@@ -1009,7 +1007,7 @@ Arguments are the same as in `onDragIn`.
10091007
10101008#### ` onDragMove `
10111009
1012- Called when a draggable item moves within the droppable area. It’ s called on every mouse move during the drag
1010+ Called when a draggable item moves within the droppable area. It' s called on every mouse move during the drag
10131011interaction, so avoid putting expensive logic here.
10141012
10151013``` tsx
@@ -1042,7 +1040,7 @@ Arguments are the same as in `onDragIn`.
10421040
10431041### Full Example
10441042
1045- Here’ s a complete example demonstrating the use of all the configuration options:
1043+ Here' s a complete example demonstrating the use of all the configuration options:
10461044
10471045``` tsx
10481046import { useDroppable } from ' dragswag'
@@ -1072,6 +1070,44 @@ const DroppableSquare = () => {
10721070}
10731071```
10741072
1073+ ### The ` DragOverlayProvider `
1074+
1075+ The ` DragOverlayProvider ` handles the visual representation of dragged elements, and must wrap any part of your
1076+ application that uses drag and drop functionality:
1077+
1078+ ``` tsx
1079+ import { DragOverlayProvider , Overlay } from ' dragswag'
1080+
1081+ export default function App() {
1082+ return (
1083+ <DragOverlayProvider >
1084+ { /* Your draggable and droppable components */ }
1085+ <div style = { { position: ' relative' }} >
1086+ <div style = { { position: ' absolute' , top: 100 , left: 100 }} >
1087+ <DraggableSquare color = " red" />
1088+ </div >
1089+ <div style = { { position: ' absolute' , top: 100 , left: 300 }} >
1090+ <DroppableSquare color = " green" />
1091+ </div >
1092+ </div >
1093+ </DragOverlayProvider >
1094+ )
1095+ }
1096+ ```
1097+
1098+ The overlay can be customized with style and className props:
1099+
1100+ ``` tsx
1101+ <DragOverlayProvider
1102+ style = {
1103+ {
1104+ /* your custom styles */
1105+ }
1106+ }
1107+ className = " your-custom-class"
1108+ />
1109+ ```
1110+
10751111## Author
10761112
10771113Javier Aguilar
0 commit comments