react를 다루면서 react-dnd와 같은 다양한 드래그-앤-드랍 라이브러리들이 존재하지만 아주 간단하게 핵심 로직만 직접 React + typesciprt로 구현해 보겠습니다!
DnD만들 구현하더라도 이를 구현하기 위한 데이터가 필요하기 때문에 데이터의 타입을 지정하겠습니다
export type IDragData = {
id: number;
title: string;
type: 'doing' | 'done';
};
이를 기반으로 IDragData[]
의 형태를 가지는 데이터를 만들어서 조작하면 될 거 같습니다.!
이름을 어떻게 정할까 고민히다가 useDragData()라고 정했지만 다른 이름으로 사용하셔도 문제가 없습니다..!
구현을 하면서 필요한 {filteredData,changeType, addItem}
를 구현하겠습니다.
import { useState } from 'react';
//초기 데이터로 사용 할 값 입니다.
const initialState: IDragData[] = [
{ id: 1, title: '진행중 테스트1', type: 'doing' },
{ id: 2, title: '진행중 테스트2', type: 'doing' },
{ id: 3, title: '진행중 테스트3', type: 'doing' },
{ id: 4, title: '진행중 테스트4', type: 'doing' },
{ id: 5, title: '진행중 테스트5', type: 'doing' },
{ id: 6, title: '완료 테스트1', type: 'done' },
{ id: 7, title: '완료 테스트2', type: 'done' },
];
const useDragData = () => {
const [dragData, setDragData] = useState<IDragData[]>(initialState);
const filteredData = () =>
dragData.reduce(
(acc, cur) => {
acc[cur.type].push(cur);
return acc;
},
{ doing: [] as IDragData[], done: [] as IDragData[] }
);
const changeType = ({
id,
type,
}: {
id: IDragData['id'];
type: IDragData['type'];
}) => setDragData(dragData.map(v => (v.id === id ? { ...v, type } : v)));
const addItem = ({
title,
type,
}: {
title: IDragData['title'];
type: IDragData['type'];
}) =>
setDragData([
...dragData,
{ id: dragData[dragData.length - 1].id + 1, title, type },
]);
return { filteredData, changeType, addItem };
};
export default useDragData;
ItemList.tsx
type IItemList = {
title: string;
data: IDragData[];
onDragEnter: (event: React.DragEvent<HTMLDivElement>) => void;
};
const ItemList = ({ title, data }: IItemList) => (
<div
className={'border-2 p-3 w-64 border-black category'}
data-category={title}
onDragEnter={onDragEnter}
>
<h2 className={'border-b-2 text-xl text-center font-bold'}>
{title.toUpperCase()}
</h2>
<ul>
{data.map(v => (
<ItemCard {...v} key={`item-card-${v.id}`} />
))}
</ul>
</div>
);
export default ItemList;
ItemCard.tsx
해당 요소는 드래그가 가능해야 하기 때문에 draggable을 주었습니다!
import { IDragData } from '../hooks/useDragData.ts';
const ItemCard = ({ title, id }: IDragData) => (
<li
className={`px-2 py-1 mt-2 border rounded bg-white`}
data-id={id}
draggable
>
<p className={'flex justify-between items-center'}>
<span>{title}</span>
<span className={'text-xs text-gray-400'}>Id:{id}</span>
</p>
</li>
);
export default ItemCard;
이번의 목표는 DnD를 간단하게 확인하려하기 때문에 DragTestComponent.tsx에 모든 로직을 다 집어넣었지만 실제로 적용을 하려면 ref를 통한 useDrag() hooks를 통한 구현을 추천드립니다...!
DragTestComponents.tsx
import { useCallback, useState } from 'react';
import useDragData from '../hooks/useDragData.ts';
import ItemList from './ItemList.tsx';
const DragTestComponent = () => {
const { filteredData, changeType } = useDragData();
const data = filteredData();
const [currentDragItem, setCurrentDragItem] = useState(-1);
const onDragStart = useCallback(
(event: React.DragEvent<HTMLDivElement>) => {
const { id } = (event.target as HTMLElement).dataset;
currentDragItem !== +(id || -1) && setCurrentDragItem(+(id || -1));
},
[currentDragItem]
);
const onDragEnd = useCallback(() => {
setCurrentDragItem(-1);
}, []);
const onDragEnter = useCallback(
(event: React.DragEvent<HTMLDivElement>) => {
const target = (
event.target === event.currentTarget
? event.target
: (event.target as HTMLElement).closest('div.category')
) as HTMLElement;
const { category } = target.dataset;
changeType({ id: currentDragItem, type: category as 'doing' | 'done' });
},
[currentDragItem]
);
const onDragOver = useCallback(
(event: React.DragEvent<HTMLDivElement>) => event.preventDefault(),
[]
);
return (
<div
className={'flex gap-20'}
onDragOver={onDragOver}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
{Object.keys(data).map(type => (
<ItemList
title={type}
data={data[type as keyof typeof data]}
key={`dnd-category-${type}`}
onDragEnter={onDragEnter}
/>
))}
</div>
);
};
export default DragTestComponent;
최종적으로 만들어진 결과물은 다음과 같습니다!!
구현을 진행하며 인덱스의 위치도 변경시킬 수 있게 할까 고민했지만 이는 제외하고 칸반을 이동하는 형태로 만들었습니다.!
https://melonplaymods.com/2023/06/10/ppg-miscellaneous-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/tank-m4-sherman-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/laboratory-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/star-wars-pack-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/flowers-in-pots-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/ghost-face-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/cudly-and-mutant-cudly-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/bunker-2-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/anti-personnel-gear-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/e-100-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/god-of-war-35-items-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/doors-mod-for-melon-playground-2/
https://melonplaymods.com/2023/06/10/m134-minigun-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/serro-ultraman-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/dark-background-with-stars-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/jotaro-star-platinum-v3-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/characters-in-metal-gear-rising-revengeance-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/3selfpropelled-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/maxim-machine-gun-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/yyds-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/bowl-with-fruits-and-vegetables-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/f4-phantom-attack-jet-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/panzerkampfwagen-viii-maus-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/pak-on-the-police-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/police-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/railway-car-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/two-storey-residential-building-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/opila-bird-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/naruto-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/inside-a-titan-shifters-nape-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/half-life-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/flamethrower-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/brawl-stars-pack-characters-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/super-heroes-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/swords-from-terraria-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/grave-stone-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/pirate-cannon-pirate-cannon-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/motorcycle-mod-for-melon-playground-3/
https://melonplaymods.com/2023/06/10/gta-protagonists-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/project-playtime-mod-for-melon-playground-2/