I am trying to create a drag-and-drop sortable list using react-dnd as shown in one of their examples - example codesandbox
But my card items are not getting dragged. Here is the code snippet -
const {
video: { title, subtitle, thumb },
handleClick,
isSelected,
handleDragEnd,
index,
} = props;
const ref = useRef<HTMLDivElement>(null)
const [{ isDragging }, drag] = useDrag({
type: "Card",
item: () => {
return { index };
},
collect: (monitor: any) => ({
isDragging: monitor.isDragging(),
}),
});
const [{ handlerId }, drop] = useDrop({
accept: "Card",
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
};
},
hover(item: any, monitor) {
console.log(item, index);
if (!ref.current) {
return;
}
const dragIndex = item.index;
const hoverIndex = index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
const hoverBoundingRect = ref.current?.getBoundingClientRect();
// Get vertical middle
const hoverMiddleY =
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
// Determine mouse position
const clientOffset = monitor.getClientOffset();
// Get pixels to the top
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
// Only perform the move when the mouse has crossed half of the items height
// When dragging downwards, only move when the cursor is below 50%
// When dragging upwards, only move when the cursor is above 50%
// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Time to actually perform the action
handleDragEnd(dragIndex, hoverIndex);
// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
item.index = hoverIndex;
},
});
return (
<div
className="px-3 py-2 border border-[#e3e3e3] rounded-md flex items-center gap-4 shadow-playlist-card cursor-pointer draggable-node mb-3"
ref={() => drag(drop(ref))}
data-handler-id={handlerId}
// onClick={handleClick}
>
And here is where I am using the Provider -
const handleDragEnd = useCallback(
(fromIndex: number, toIndex: number) => {
const modifiedPlaylist = [...playlist];
const draggedVideo = modifiedPlaylist.splice(fromIndex, 1)[0];
modifiedPlaylist.splice(toIndex, 0, draggedVideo);
setPlaylist(modifiedPlaylist);
setOriginalPlaylist(modifiedPlaylist);
setSelectedVideo(toIndex);
},
[playlist]
);
return (
<main className="min-h-screen w-screen">
<VideoPlayer
source={
selectedVideo !== null ? playlist[selectedVideo].sources[0] : null
}
playNext={() => handleChangeVideo(1)}
playPrevious={() => handleChangeVideo(-1)}
currentVideoIndex={selectedVideo}
/>
<section className="flex flex-col mx-3 mb-20 overflow-auto">
<Searchbar handleChange={handleSearch} />
<DndProvider backend={TouchBackend}>
{playlist.map((video, index) => (
<VideoCard
key={video.sources[0]}
index={index}
video={video}
handleClick={() => setSelectedVideo(index)}
isSelected={selectedVideo === index}
handleDragEnd={handleDragEnd}
/>
))}
</DndProvider>
</section>
</main>
Spent some good 3-4 hours debugging this issue but unable to resolve it. Any help would be appreciated. Thanks!