@supunlakmal/hooks
Version:
A collection of reusable React hooks
86 lines • 4.33 kB
JavaScript
import { useRef, useEffect, useState, useCallback } from 'react';
/**
* Provides basic drag-and-drop event handling for an element.
* Attaches necessary listeners and manages drag state.
*
* @param ref React ref object attached to the draggable element.
* @param options Configuration options for the drag behavior.
* @returns State indicating if the element is currently being dragged.
*/
export const useDrag = (ref, options = {}) => {
const { setDraggableAttribute = true } = options;
const [isDragging, setIsDragging] = useState(false);
// Store options in refs to avoid re-running effects when they change
const optionsRef = useRef(options);
useEffect(() => {
optionsRef.current = options;
}, [options]);
const handleDragStart = useCallback((event) => {
var _a, _b, _c;
const currentOptions = optionsRef.current;
setIsDragging(true);
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = currentOptions.dragEffect || 'move';
if (currentOptions.transferData !== undefined) {
let dataString = null;
try {
if (typeof currentOptions.transferData === 'string') {
dataString = currentOptions.transferData;
}
else {
dataString = JSON.stringify(currentOptions.transferData);
}
event.dataTransfer.setData(currentOptions.dataFormat || 'text/plain', dataString);
}
catch (e) {
console.error("useDrag: Failed to stringify transferData. Ensure it's serializable.", e);
event.dataTransfer.setData(currentOptions.dataFormat || 'text/plain', '[Serialization Error]');
}
}
if (currentOptions.dragImage && event.dataTransfer.setDragImage) {
event.dataTransfer.setDragImage(currentOptions.dragImage, ((_a = currentOptions.dragImageOffset) === null || _a === void 0 ? void 0 : _a.x) || 0, ((_b = currentOptions.dragImageOffset) === null || _b === void 0 ? void 0 : _b.y) || 0);
}
else if (currentOptions.dragImage === null) {
// If explicitly null, try to use a minimal transparent image (browser support varies)
const emptyImage = new Image();
emptyImage.src =
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
event.dataTransfer.setDragImage(emptyImage, 0, 0);
}
// Otherwise, use browser default drag image
}
(_c = currentOptions.onDragStart) === null || _c === void 0 ? void 0 : _c.call(currentOptions, event);
}, []); // No dependencies, relies on optionsRef
const handleDrag = useCallback((event) => {
var _a, _b;
(_b = (_a = optionsRef.current).onDrag) === null || _b === void 0 ? void 0 : _b.call(_a, event);
// isDragging state is managed by dragstart/dragend
}, []); // No dependencies, relies on optionsRef
const handleDragEnd = useCallback((event) => {
var _a, _b;
setIsDragging(false);
(_b = (_a = optionsRef.current).onDragEnd) === null || _b === void 0 ? void 0 : _b.call(_a, event);
}, []); // No dependencies, relies on optionsRef
useEffect(() => {
const element = ref.current;
if (!element)
return;
if (setDraggableAttribute) {
element.setAttribute('draggable', 'true');
}
element.addEventListener('dragstart', handleDragStart);
element.addEventListener('drag', handleDrag);
element.addEventListener('dragend', handleDragEnd);
return () => {
element.removeEventListener('dragstart', handleDragStart);
element.removeEventListener('drag', handleDrag);
element.removeEventListener('dragend', handleDragEnd);
if (setDraggableAttribute) {
// Reset draggable attribute on cleanup if we set it
element.removeAttribute('draggable');
}
};
}, [ref, setDraggableAttribute, handleDragStart, handleDrag, handleDragEnd]);
return { isDragging };
};
//# sourceMappingURL=useDrag.js.map