3

I am using React-dnd, and trying to create a table with sortable rows, which can only be dragged from a handle, which is a small span on the right hand side.

The example they give here https://react-dnd.github.io/react-dnd/examples/customize/handles-and-previews

does not show any sorting. I can get the Handle to work, with a different preview, the problem is if I use the code they provide, the dropzone for sorting the table only works when I hover it directly over the span, not over the row like it should.

my code:

 const dragRef  = useRef<any>(null)


const [{ isDragging }, drag, preview] = useDrag({
    type: DragTypeEnum.ENTRY,
    item: () => {
        return { id: props.id, index: props.sequence }
    },
    collect: (monitor: any) => ({
        isDragging: monitor.isDragging(),
    }),
});


const [{ handlerId }, drop] = useDrop({
    accept: DragTypeEnum.ENTRY,
    collect(monitor) {
        return {
            handlerId: monitor.getHandlerId(),
        }
    },
    hover(item: DragItem, monitor: DropTargetMonitor) {
        if (!dragRef.current) {
            return
        }
        const dragId = item.id
        const hoverId = props.id;

        // Don't replace items with themselves
        if (dragId === hoverId) {
            return
        }

        // Determine rectangle on screen
        const hoverBoundingRect = dragRef.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 (dragId < hoverId && hoverClientY < hoverMiddleY) {
            console.log("downwards");
            return
        }

        // Dragging upwards
        if (dragId > hoverId && hoverClientY > hoverMiddleY) {
            console.log("upwards");
            return
        }

        // Time to actually perform the action
        props.moveEntry(dragId, hoverId)      //ONLY FIRING WHEN I GO OVER THE HANDLE :(

        // 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.id = hoverId
    },
});


const opacity = isDragging ? 0 : 1;
drag(drop(dragRef))

And the table tsx is:

    <tr
        style={{ opacity }}
        data-handler-id={handlerId}
        ref={preview}   //THIS SHOULD BE THE PREVIEW HTML AND ALSO WHERE I CAN DRAG INTO
    >
        <td>
        //emptied for demo purpose
        </td>

        <td>
        //emptied for demo purpose
        </td>



        <td>
                    <span ref={dragRef}>                 // THIS IS MY DRAG HANDLE
                        <FontAwesomeIcon 
                            icon="bars"
                            className="pointer"
                        ></FontAwesomeIcon>
                    </span>
                </>
            }
        </td>

    </tr>
Michael
  • 8,229
  • 20
  • 61
  • 113

1 Answers1

18

Create 2 refs for handler and for preview for example like this

const dragRef = useRef<HTMLDivElement>(null)
const previewRef = useRef<HTMLDivElement>(null)

Then you need wrap your refs with handlers

drag(dragRef)
drop(preview(previewRef))

With your code it will be look like:

const dragRef = useRef<HTMLDivElement>(null)
const previewRef = useRef<HTMLDivElement>(null)

const [{ isDragging }, drag, preview] = useDrag({
    type: DragTypeEnum.ENTRY,
    item: () => {
        return { id: props.id, index: props.sequence }
    },
    collect: (monitor: any) => ({
        isDragging: monitor.isDragging(),
    }),
});

const [{ handlerId }, drop] = useDrop({
    accept: DragTypeEnum.ENTRY,
    collect(monitor) {
        return {
            handlerId: monitor.getHandlerId(),
        }
    },
    hover(item: DragItem, monitor: DropTargetMonitor) {
        if (!previewRef.current) {
            return
        }
        const dragId = item.id
        const hoverId = props.id;

        // Don't replace items with themselves
        if (dragId === hoverId) {
            return
        }

        // Determine rectangle on screen
        const hoverBoundingRect = previewRef.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 (dragId < hoverId && hoverClientY < hoverMiddleY) {
            console.log("downwards");
            return
        }

        // Dragging upwards
        if (dragId > hoverId && hoverClientY > hoverMiddleY) {
            console.log("upwards");
            return
        }

        // Time to actually perform the action
        props.moveEntry(dragId, hoverId)      //ONLY FIRING WHEN I GO OVER THE HANDLE :(

        // 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.id = hoverId
    },
});


const opacity = isDragging ? 0 : 1;

drag(dragRef)
drop(preview(previewRef))

table

<tr
        style={{ opacity }}
        data-handler-id={handlerId}
        ref={previewRef}   //THIS SHOULD BE THE PREVIEW HTML AND ALSO WHERE I CAN DRAG INTO
    >
        <td>
        //emptied for demo purpose
        </td>

        <td>
        //emptied for demo purpose
        </td>



        <td>
                    <span ref={dragRef}>                 // THIS IS MY DRAG HANDLE
                        <FontAwesomeIcon 
                            icon="bars"
                            className="pointer"
                        ></FontAwesomeIcon>
                    </span>
                </>
            }
        </td>

    </tr>
Muhammedogz
  • 774
  • 8
  • 21