Skip to content

Commit fd3a9ad

Browse files
committed
wip: 드래그앤드롭 기능 구현 중 중간 저장
1 parent f6d84c8 commit fd3a9ad

File tree

3 files changed

+101
-51
lines changed

3 files changed

+101
-51
lines changed

design-system/ui/DragArea.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,31 @@ interface DragAreaProps {
2424

2525
const DragArea = ({ data, setData, droppableId }: DragAreaProps) => {
2626
const column = data.columns[droppableId];
27+
const isOptionsArea = droppableId === 'options';
2728

2829
return (
29-
<div className="p-2 m-8 border-2 border-gray-300 rounded-lg">
30-
<p className="text-sm font-bold p-4">{column.title}</p>
31-
<Droppable droppableId={droppableId}>
32-
{provided => (
33-
<div ref={provided.innerRef} {...provided.droppableProps}>
30+
<div className="w-full py-2">
31+
{/* <p className="text-sm font-bold p-4">{column.title}</p> */}
32+
<Droppable droppableId={droppableId} isDropDisabled={isOptionsArea}>
33+
{(provided, snapshot) => (
34+
<div
35+
ref={provided.innerRef}
36+
{...provided.droppableProps}
37+
className={`grid grid-cols-2 gap-4 h-36 ${
38+
!isOptionsArea && snapshot.isDraggingOver ? 'bg-gray-200' : 'bg-white'
39+
}`}
40+
>
3441
{column.taskIds.map((taskId, index) => {
3542
const task = data.tasks[taskId];
36-
return <DraggableList key={task.id} id={task.id} content={task.content} index={index} />;
43+
return (
44+
<DraggableList
45+
key={task.id}
46+
id={task.id}
47+
content={task.content}
48+
index={index}
49+
isDragDisabled={isOptionsArea}
50+
/>
51+
);
3752
})}
3853
{provided.placeholder}
3954
</div>

design-system/ui/DraggableList.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ interface DraggableListProps {
44
id: string; // task의 id
55
content: string; // task의 content
66
index: number; // 드래그앤드롭 위치 추적용
7+
isDragDisabled?: boolean;
78
}
89

9-
const DraggableList = ({ id, content, index }: DraggableListProps) => {
10+
const DraggableList = ({ id, content, index, isDragDisabled = false }: DraggableListProps) => {
1011
return (
11-
<Draggable draggableId={id} index={index}>
12-
{provided => (
12+
<Draggable draggableId={id} index={index} isDragDisabled={isDragDisabled}>
13+
{(provided, snapshot) => (
1314
<div
1415
ref={provided.innerRef}
1516
{...provided.draggableProps}
1617
{...provided.dragHandleProps}
17-
className="p-2 mb-2 border border-gray-300 rounded-lg bg-white"
18+
className={`p-4 mb-2 bg-white rounded border ${snapshot.isDragging ? 'shadow-lg' : ''}`}
1819
>
1920
{content}
2021
</div>

src/pages/dashboard/ui/ticket/TicketOptionPage.tsx

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,24 @@ const TicketOptionPage = () => {
3030
const navigate = useNavigate();
3131
const [data, setData] = useState<Data>({
3232
tasks: {
33-
'task-1': { id: 'task-1', content: 'Take out the garbage' },
34-
'task-2': { id: 'task-2', content: 'Watch my favorite show' },
35-
'task-3': { id: 'task-3', content: 'Charge my phone' },
36-
'task-4': { id: 'task-4', content: 'Cook dinner' },
33+
'task-1': { id: 'task-1', content: '티셔츠 사이즈' },
34+
'task-2': { id: 'task-2', content: '음식 선호도' },
3735
},
3836
columns: {
39-
'column-1': {
40-
id: 'column-1',
41-
title: 'To do',
42-
taskIds: ['task-1', 'task-2', 'task-3', 'task-4'],
37+
options: {
38+
// 옵션 영역
39+
id: 'options',
40+
title: '옵션',
41+
taskIds: ['task-1', 'task-2'],
42+
},
43+
ticket: {
44+
// 티켓 영역
45+
id: 'ticket',
46+
title: '티켓',
47+
taskIds: [],
4348
},
4449
},
45-
columnOrder: ['column-1'],
50+
columnOrder: ['options', 'ticket'],
4651
});
4752

4853
const onDragEnd = (result: DropResult) => {
@@ -52,57 +57,86 @@ const TicketOptionPage = () => {
5257
return;
5358
}
5459

55-
if (destination.droppableId === source.droppableId && destination.index === source.index) {
60+
if (destination.droppableId === source.droppableId) {
61+
// options 영역 내에서는 이동 불가
62+
if (source.droppableId === 'options') {
63+
return;
64+
}
65+
66+
// ticket 영역 내에서만 순서 변경 가능
67+
if (source.droppableId === 'ticket') {
68+
const column = data.columns[source.droppableId];
69+
const newTaskIds = Array.from(column.taskIds);
70+
newTaskIds.splice(source.index, 1);
71+
newTaskIds.splice(destination.index, 0, draggableId);
72+
73+
setData(prev => ({
74+
...prev,
75+
columns: {
76+
...prev.columns,
77+
[source.droppableId]: {
78+
...column,
79+
taskIds: newTaskIds,
80+
},
81+
},
82+
}));
83+
}
5684
return;
5785
}
5886

59-
const column = data.columns[source.droppableId];
60-
const newTaskIds = Array.from(column.taskIds);
87+
// options에서 ticket으로 이동할 때
88+
if (source.droppableId === 'options' && destination.droppableId === 'ticket') {
89+
const sourceColumn = data.columns[source.droppableId];
90+
const destColumn = data.columns[destination.droppableId];
6191

62-
newTaskIds.splice(source.index, 1);
63-
newTaskIds.splice(destination.index, 0, draggableId);
92+
// ticket 영역의 마지막에 추가
93+
const newDestTaskIds = [...destColumn.taskIds, draggableId];
6494

65-
setData(prev => ({
66-
...prev,
67-
columns: {
68-
...prev.columns,
69-
[source.droppableId]: { ...column, taskIds: newTaskIds },
70-
},
71-
}));
95+
setData(prev => ({
96+
...prev,
97+
columns: {
98+
...prev.columns,
99+
[destination.droppableId]: {
100+
...destColumn,
101+
taskIds: newDestTaskIds,
102+
},
103+
},
104+
}));
105+
}
72106
};
73107

74108
return (
75109
<DashboardLayout centerContent="WOOACON 2024">
76110
<div className="mt-8 px-7">
77-
<div className="text-center text-xl font-bold mb-5"> 티켓에 추가 옵션 부착 하기</div>
111+
<div className="text-center text-xl font-bold mb-5">티켓에 추가 옵션 부착 하기</div>
78112
<p className="text-gray-400 text-xs mb-5">
79113
티켓에 구매하기 전 설문에 응답받기 위해서는 다음 항목을 각 티켓으로 드래그 앤 드롭 해보세요.
80114
</p>
115+
81116
<DragDropContext onDragEnd={onDragEnd}>
82-
{/*티켓 옵션 생성 페이지 이동 버튼*/}
83-
<IconText iconPath={<img src={Tag} alt="추가 버튼" />} children="옵션" />
84-
<div>
85-
{data.columnOrder.map(columnId => (
86-
<DragArea key={columnId} data={data} setData={setData} droppableId={columnId} />
87-
))}
88-
</div>
89-
<div className="flex items-center bg-gray3 rounded-lg w-72 h-12 gap-5 mb-10">
90-
<HorizontalCardButton
91-
iconPath={<img src={AddButton} alt="추가 버튼" />}
92-
onClick={() => navigate('/dashboard/ticket/option/create')}
93-
label="티켓 새로 생성하기"
94-
className="text-sm mx-auto"
95-
/>
117+
{/* 옵션 영역 */}
118+
<div className="mb-8">
119+
<IconText iconPath={<img src={Tag} alt="추가 버튼" />} children="옵션" />
120+
<DragArea data={data} setData={setData} droppableId="options" />
121+
122+
<div className="flex items-center bg-gray-300 rounded-lg w-48 h-12 gap-5 mb-10">
123+
<HorizontalCardButton
124+
iconPath={<img src={AddButton} alt="추가 버튼" />}
125+
onClick={() => navigate('/dashboard/ticket/option/create')}
126+
label="티켓 새로 생성하기"
127+
className="text-sm mx-auto"
128+
/>
129+
</div>
96130
</div>
97131

98-
{/*티켓 옵션 렌더링 구역*/}
99-
<>
132+
{/* 티켓 영역 */}
133+
<div>
100134
<div className="flex items-center gap-2 mb-1">
101-
<img src={Ticket} />
135+
<img src={Ticket} alt="티켓" />
102136
<p className="font-bold text-base md:text-lg">티켓</p>
103137
</div>
104-
<div className="w-20 h-20 bg-red-500"></div>
105-
</>
138+
<DragArea data={data} setData={setData} droppableId="ticket" />
139+
</div>
106140
</DragDropContext>
107141
</div>
108142
</DashboardLayout>

0 commit comments

Comments
 (0)