I am using dnd-kit and facing an issue! When I swap draggable with any first element from the list (either above or below) it works fine, but IF I CONTINUE dragging and swapping with the following elements, the sorting behaves strangely.
Let me show you the GIF: Link
Here's the code for the list item:
import React, { useState } from "react";
import "./FieldTokenComponent.scss";
import { TokenData } from "../../../classes/TokenClasses/TokenCreator";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
export interface FieldTokenData {
tokenData: TokenData;
}
export default function FieldTokenComponent(props: FieldTokenData) {
const { tokenData } = props;
const {
tokenLabelColor,
tokenAlias,
tokenDefense,
tokenHP,
tokenInitiative,
tokenSpeed,
tokenType,
id,
} = tokenData;
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: id });
const style = {
transition,
transform: CSS.Transform.toString(transform),
backgroundColor: `${tokenEliminated ? "gray" : tokenLabelColor}`,
};
return (
<div>
<div
id="battlefieldToken"
data-type={tokenType}
style={style}
ref={setNodeRef}
{...attributes}
{...listeners}
>
<span className="tokenInitiative">{tokenInitiative}</span>
<i className={`fa-solid fa-skull deathStatusIcon`}></i>
</span>
<span className="tokenName">{tokenAlias}</span>
<button onClick={expandToken} className="tokenExpandButton">
<i className="fa-solid fa-angle-down expandArrow"></i>
</button>
</div>
</div>
);
}
Here's the code for the list container:
import "./BattlefieldComponent.scss";
import TokenModal from "../../UIComponents/AddTokenModal/TokenModal";
import { useState } from "react";
import { DndContext, DragEndEvent } from "@dnd-kit/core";
import { closestCenter } from "@dnd-kit/core";
import { TokenData } from "../../../classes/TokenClasses/TokenCreator";
import FieldTokenComponent from "../../TokenComponents/FieldTokenComponent/FieldTokenComponent";
import {
SortableContext,
arrayMove,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
export function BattlefieldComponent() {
const [creaturesTokens, setCreaturesToken] = useState<
{ data: TokenData; id: number }[]
>([]);
function onDragEnd(event: DragEndEvent) {
const { active, over } = event;
console.log(event);
if (active.id !== over!.id) {
setCreaturesToken((tokens) => {
const newIndex = tokens.findIndex((token) => token.id === active.id);
const oldIndex = tokens.findIndex((token) => token.id === over!.id);
return arrayMove(tokens, oldIndex, newIndex);
});
}
}
return (
<div className="battlefieldZone">
<div className="battlefieldCol" id="creaturesCol">
<header>
<h3>CREATURES</h3>
</header>
<main className="battlefieldRow">
<DndContext collisionDetection={closestCenter} onDragEnd={onDragEnd}>
<SortableContext
items={creaturesTokens}
strategy={verticalListSortingStrategy}
>
{creaturesTokens.map((tokenData) => (
<FieldTokenComponent
tokenData={tokenData.data}
key={tokenData.data.tokenId}
/>
))}
</SortableContext>
</DndContext>
</main>
<TokenModal
tokenType="creaturesToken"
addToken={setCreaturesToken}
key="creaturesTokenModal"
/>
</div>
</div>
);
}
TokenModal is with what I add draggable elements, I am not sure if I need to add this code here, so please, tell me if you think it is relevant.
You should use the same id as key in FieldTokenComponent and in useSortable({ id: id })
Parent:
Child: