Делаю простой калькулятор на React. Для реализации drag-and-drop использую библиотеку dnd-kit. Проблему можно увидеть на гифке снизу: при перетаскивании элемента калькулятора из левой области в область для дропа отсутствует анимация перетаскивания, хотя элемент может быть дропнут в область для дропа. В самой же области для дропа анимация перетаскивания прекрасно работает и элементы могут быть поменяны местами.
Мне необходимо чтобы работала анимация перетаскивания, когда я перетаскиваю элемент из левой области в область для дропа.
Код для компонента App:
const App: FC = () => {
const [selected, setSelected] = useState('Constructor')
const [droppedElems, setDroppedElems] = useState<CalcElemListInterface[]>([])
const handleActiveSwitcher = (id: string) => {
setSelected(id)
}
const deleteDroppedElem = (item: CalcElemListInterface) => {
const filtered = [...droppedElems].filter(elem => elem.id !== item.id)
setDroppedElems(filtered)
}
const leftFieldStyles = cn(styles.left, {
[styles.hidden]: selected === 'Runtime'
})
const calcElementsList = calcElemListArray.map((item) => {
const index = droppedElems.findIndex(elem => elem.id === item.id)
const layoutDisabledStyle = index !== -1
return (
<CalcElemLayout
key={item.id}
id={item.id}
item={item}
layoutDisabledStyle={layoutDisabledStyle}
/>
)
})
const handleDragEnd = (event: DragEndEvent) => {
const { id, list } = event.active.data.current as CalcElemListInterface
const elem = {id, list}
if (event.over && event.over.id === 'droppable') {
setDroppedElems((prev) => {
return [...prev, elem]
})
}
}
return (
<div className={styles.layout}>
<div className={styles.top}>
<Switcher
selected={selected}
handleActiveSwitcher={handleActiveSwitcher}
/>
</div>
<DndContext
onDragEnd={handleDragEnd}
>
<div className={styles.content}>
<div className={leftFieldStyles}>
{calcElementsList}
</div>
<DropElemLayout
deleteDroppedElem={deleteDroppedElem}
selected={selected}
droppedElems={droppedElems}
setDroppedElems={setDroppedElems}
/>
</div>
</DndContext>
</div>
)
}
Код для области дропа:
const DropElemLayout: FC<DropElemLayoutInterface> = ({ selected, droppedElems, deleteDroppedElem, setDroppedElems }) => {
const { isOver, setNodeRef } = useDroppable({
id: 'droppable'
})
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
)
const style = {
backgroundColor: (isOver && !droppedElems.length) ? '#F0F9FF' : undefined,
}
const droppedRuntimeElemList = droppedElems.map((item) => {
const layoutEnabledStyle = droppedElems.length ? true : false
return (
<CalcElemLayout
key={item.id}
id={item.id}
item={item}
deleteDroppedElem={deleteDroppedElem}
selected={selected}
layoutEnabledStyle={layoutEnabledStyle}
/>
)
})
const droppedElemList = !droppedElems.length
?
<div className={styles.rightContent}>
<Icon name="#drop"/>
<p>Перетащите сюда</p>
<span>любой элемент</span>
<span>из левой панели</span>
</div>
:
droppedRuntimeElemList
const className = !droppedElems.length ? styles.right : styles.left
const handleDragEnd = (event: DragEndEvent) => {
if (event.active.id !== event.over?.id) {
setDroppedElems((items: CalcElemListInterface[]) => {
const oldIndex = items.findIndex(item => item.id === event.active?.id)
const newIndex = items.findIndex(item => item.id === event.over?.id)
return arrayMove(items, oldIndex, newIndex)
})
}
}
return (
<DndContext
onDragEnd={handleDragEnd}
sensors={sensors}
collisionDetection={closestCenter}
>
<div
ref={setNodeRef}
className={className}
style={style}
>
<SortableContext
items={droppedElems}
strategy={verticalListSortingStrategy}
>
{droppedElemList}
</SortableContext>
</div>
</DndContext>
)
}
Код для перетаскиваемого элемента:
const CalcElemLayout: FC<CalcElemLayoutInterface> = ({ item, id, deleteDroppedElem, selected, layoutDisabledStyle, layoutEnabledStyle }) => {
const { current } = useAppSelector(state => state.calculator)
// const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
// id: id,
// data: {...item},
// disabled: selected === 'Runtime'
// })
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging
} = useSortable({
id: id,
data: {...item},
disabled: selected === 'Runtime'
})
const style = {
transform: CSS.Translate.toString(transform),
transition: transition
}
const handleDeleteDroppedElem = () => {
deleteDroppedElem?.(item)
}
const doubleClickCondition = selected === 'Constructor' ? handleDeleteDroppedElem : undefined
const layoutStyle = cn(styles.elemLayout, {
[styles.operators]: item.id === 'operators',
[styles.digits]: item.id === 'digits',
[styles.equal]: item.id === 'equal',
[styles.disabled]: layoutDisabledStyle,
[styles.enabled]: layoutEnabledStyle,
})
const buttonList = item.list?.map(elem => (
<Button
key={elem.name}
elem={elem.name}
selected={selected!}
/>
))
const resultStyle = cn(styles.result, {
[styles.minified]: current.length >= 10
})
const elemList = item.id === 'result'
?
<div className={resultStyle}>{current}</div>
:
buttonList
const overlayStyle = {
opacity: '0.5',
}
return (
<>
<div
ref={setNodeRef}
className={layoutStyle}
onDoubleClick={doubleClickCondition}
style={style}
{...attributes}
{...listeners}
>
{elemList}
</div>
</>
)
}