Welcome to the Next-Light-DND
Test Card 1
Test Card 2
Test Card 3
Test Card 4
1
2
3 const Board = ({ initialCards }: { initialCards: initialCards[] }) => {
4 const [cards, setCards] = useState(initialCards);
5 return (
6 <div className="flex min-h-[40vh] mx-auto w-3/4 gap-3 p-12">
7
8 <Column
9 title="New"
10 column="NEW"
11 headingColor="text-neutral-500"
12 cards={cards}
13 setCards={setCards}
14 />
15
16 <Column
17 title="In Progress"
18 column="IN_PROGRESS"
19 headingColor="text-blue-200"
20 cards={cards}
21 setCards={setCards}
22 />
23 <Column
24 title="Done"
25 column="DONE"
26 headingColor="text-emerald-200"
27 cards={cards}
28 setCards={setCards}
29 />
30 </div>
31 );
32};
33
Add your mock data now
const testItems = [
{ id: "1", title: "Test Card 1", category: Category.NEW },
{ id: "2", title: "Test Card 2", category: Category.IN_PROGRESS },
{ id: "3", title: "Test Card 3", category: Category.DONE },
{ id: "4", title: "Test Card 4", category: Category.DONE },
];
1
2const Column = ({
3 title,
4 headingColor,
5 cards,
6 column,
7 setCards,
8}: ColumnProps) => {
9 const [active, setActive] = useState(false);
10
11 const handleDragStart = (e: DragEvent, card: initialCards) => {
12 e.dataTransfer.setData("cardId", card.id);
13 };
14
15 const handleDragEnd = async (e: DragEvent) => {
16 const cardId = e.dataTransfer.getData("cardId");
17 setActive(false);
18 clearHighlights();
19
20 const indicators = getIndicators();
21 const { element } = getNearestIndicator(e, indicators);
22 const before = element.dataset.before || "-1";
23
24 if (before !== cardId) {
25 let copy = [...cards];
26 let cardToTransfer = copy.find((c) => c.id === cardId);
27 if (!cardToTransfer) return;
28 cardToTransfer = { ...cardToTransfer, category: column as Category };
29
30 copy = copy.filter((c) => c.id !== cardId);
31 const moveToBack = before === "-1";
32
33 if (moveToBack) {
34 copy.push(cardToTransfer);
35 } else {
36 const insertAtIndex = copy.findIndex((el) => el.id === before);
37 if (insertAtIndex === undefined) return;
38 copy.splice(insertAtIndex, 0, cardToTransfer);
39 }
40
41 setCards(copy);
42 }
43 };
Lastly, we need to have types of our last component which is Cards (draggable items)
type CardProps = {
title: string;
id: string;
category: string;
handleDragStart: Function;
};
1
2 const Card = ({ title, id, category, handleDragStart }: CardProps) => {
3 return (
4 <div>
5 <DropIndicator beforeId={id} column={category} />
6 <motion.div
7 layout
8 layoutId={id}
9 draggable="true"
10 onDragStart={(e) => handleDragStart(e, { title, id, category })}
11 className="cursor-grab rounded border border-neutral-700 bg-neutral-800 p-3 py-7 active:cursor-grabbing"
12 >
13 <p className="text-sm text-neutral-100">{title}</p>
14 </motion.div>
15 </div>
16 );
17};
18
19
20Also, you can add drop-indicator component, which makes the component visually appealing.
21
1
2type DropIndicatorProps = {
3 beforeId: string | null;
4 column: string;
5};
6
7const DropIndicator = ({ beforeId, column }: DropIndicatorProps) => {
8 return (
9 <div
10 data-before={beforeId || "-1"}
11 data-column={column}
12 className="my-0.5 h-0.5 w-full bg-violet-400 opacity-0"
13 />
14 );
15};
16
17