useDragAndDrop
Hook for adding ability to drag and drop some element.
Usage
Here is an example of simple draggable element
import { type CSSProperties, useRef, useState } from 'react';
import { useDragAndDrop } from '@krutoo/utils/react';
function App() {
const ref = useRef<HTMLDivElement>(null);
const [grabbed, setGrabbed] = useState(false);
const [offset, setOffset] = useState({ x: 0, y: 0 });
useDragAndDrop(ref, {
onGrab(event) {
setGrabbed(true);
setOffset(event.offset);
},
onMove(event) {
setOffset(event.offset);
},
onDrop() {
setGrabbed(false);
},
});
const style: CSSProperties = grabbed
? {
position: 'absolute',
top: `${offset.y}px`,
left: `${offset.x}px`,
}
: {};
return <div ref={ref}>This block is draggable</div>;
}
This example uses state for style but you can set position to style directly
to element in event callbacks using ref or event.target.
useDragAndDrop(ref, {
onGrab(event) {
event.target.style.position = 'absolute';
event.target.style.left = `${event.offset.x}px`;
event.target.style.top = `${event.offset.y}px`;
},
onMove(event) {
event.target.style.left = `${event.offset.x}px`;
event.target.style.top = `${event.offset.y}px`;
},
onDrop(event) {
event.target.style.position = 'static';
},
});
Controlling drag start
By default hook start dragging immediately on pointerdown event.
To disable or change this behavior you can call preventDefault on grab event.
For example you can start drag only if some child element is pressed:
const needStartDrag = event => {
return event.nativeEvent.target.classList.contains('handle');
};
useDragAndDrop(ref, {
onGrab(event) {
if (needStartDrag(event)) {
return event.preventDefault();
}
// ... your state logic
},
});
Or you can implement dragging activation only after some shift threshold reached:
const threshold = 8;
const needStartDrag = event => {
return Math.abs(event.offset.x - event.startOffset.x) >= threshold;
};
Plugins
Hook provides ability to define plugins to extend basic drag-and-drop behavior.
useDragAndDrop(ref, {
plugins: [myPlugin, myOtherPlugin],
// ...other options
});
Plugin can add hooks for some events:
init- here you can add some listenersgrab- calls hook right after dispatchinggrabeventmove- calls hook right after dispatchingmoveeventdrop- calls hook right after dispatchingdropeventdestroy- here you can remove some listeners
By default hook uses some builtin plugins.
You can disable this plugins by passing array:
useDragAndDrop(ref, {
// no plugins and no builtins
plugins: [],
});
useDragAndDrop(ref, {
// only your plugin and no builtins
plugins: [myPlugin],
});
Builtin plugin cleanSelection
Plugin that cleans selection during drag.
Builtin plugin preventClick
Plugin that prevents click if target element was moved by drag-and-drop.
Builtin plugin touchScroll
Plugin that prevents page scroll and "pull-to-refresh" for draggable element.
Using builtins selectively
import { DragAndDropBuiltinPlugins, useDragAndDrop } from '@krutoo/utils/react';
useDragAndDrop(ref, {
// Using only `touchScroll` plugin from builtins
plugins: [DragAndDropBuiltinPlugins.touchScroll],
});
Optimization
By default observer uses same event object between callbacks, just change property values.
You can disable this behavior by passing option reuseEvent: false.