1

I have a drag-and-drop application that reorders an array of cards. When start dragging, I'm removing the dragged item from the array and then putting back on drop based on the new position. Everything works as expected, but because I'm removing the dragged element from the array on dragStart, the ghost image shows as the next item (e.g. when dragging card1, the ghost image shows as card2).

I would like to remove the dragged card from the page but also show the ghost image as that dragged card.

import styles from '../../styles/test.module.css'
import { useState } from 'react'

export default function TestDragDrop() {
    const [tiles, setTiles] = useState([
        { APPID: 1, APPLICATIONNAME: 'App 1' },
        { APPID: 2, APPLICATIONNAME: 'App 2' },
        { APPID: 3, APPLICATIONNAME: 'App 3' },
        { APPID: 4, APPLICATIONNAME: 'App 4' },
        { APPID: 5, APPLICATIONNAME: 'App 5' },
        { APPID: 6, APPLICATIONNAME: 'App 6' },
        { APPID: 7, APPLICATIONNAME: 'App 7' },
        { APPID: 8, APPLICATIONNAME: 'App 8' },
        { APPID: 9, APPLICATIONNAME: 'App 9' },
    ])
    const [appBeingDragged, setAppBeingDragged] = useState(null)
    const [endIndex, setEndIndex] = useState(null)

    const handleDragStart = (e) => {
        e.dataTransfer.setData('id', e.target.id)
        const thisAppId = parseInt(e.target.id)
        setAppBeingDragged(tiles.find(tile => tile.APPID === thisAppId))
        setTiles(tiles.filter(tile => tile.APPID !== thisAppId))
    }

    const handleDragEnter = (i) => {
        setEndIndex(i)
    }

    const handleDragLeave = () => {
        setEndIndex(null)
    }

    const handleDrop = (e) => {
        e.preventDefault()
        reorderTiles(e.dataTransfer.getData('index'), endIndex)
    }

    const reorderTiles = (startIndex, endIndex) => {
        const newTiles = [...tiles]
        newTiles.splice(endIndex, 0, appBeingDragged)
        setTiles(newTiles)
    }

    const cards = tiles.map((app, index) => {
        return (
            <>
                <div
                    key={index}
                    id={app.APPID}
                    className={styles.card}
                    draggable
                    onDragStart={(e) => handleDragStart(e, index)}
                    onDragOver={(e) => e.preventDefault()}
                    onDragEnter={() => handleDragEnter(index)}
                    onDragLeave={handleDragLeave}
                    onDrop={(e) => handleDrop(e)}
                >
                    {app.APPLICATIONNAME}
                </div>
            </>
        )
    })


    return (
        <div style={{ display: 'flex', flexFlow: 'row wrap', margin: '1rem 5rem' }}>
            {cards}
        </div>
    )
}

I've tried hiding the element being dragged or adjusting the opacity, instead of removing the element from the array, but those approaches did not give the desired ui.

0

Browse other questions tagged or ask your own question.