Welcome to the Next-Light-DND

New

1

Test Card 1

In Progress

1

Test Card 2

Done

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