Skip to content

Commit 04fb726

Browse files
committed
feat: 드래그 앤 드롭 기능 추가
1 parent af25749 commit 04fb726

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

apps/web/src/components/(with-side-bar)/library/content-item.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,28 @@ const contentType = {
2525
export default function ContentItem({ item, ...props }: ContentItemProps) {
2626
const { className, ...restProps } = props;
2727

28+
const onDragStart = (e: React.DragEvent<HTMLButtonElement>) => {
29+
e.dataTransfer.setData("application/json", JSON.stringify(item));
30+
e.dataTransfer.effectAllowed = "copy";
31+
32+
// 드래그 중인 요소에 시각적 피드백 추가
33+
e.currentTarget.style.opacity = "0.5";
34+
};
35+
36+
const onDragEnd = (e: React.DragEvent<HTMLButtonElement>) => {
37+
e.currentTarget.style.opacity = "1";
38+
};
39+
2840
return (
2941
<button
3042
className={cn(
3143
"bg-card border-border hover:border-primary group cursor-pointer rounded-lg border p-6 text-start transition-all duration-300 hover:-translate-y-1 hover:transform hover:shadow-lg",
3244
className
3345
)}
3446
aria-label={`${item.title} 콘텐츠 상세보기`}
47+
draggable
48+
onDragStart={onDragStart}
49+
onDragEnd={onDragEnd}
3550
{...restProps}
3651
>
3752
<div className="mb-4 flex items-center gap-4">

apps/web/src/page/topic/index.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import { Button } from "@/components/ui/button";
1212
import { Input } from "@/components/ui/input";
1313
import type { ContentTypeOptions } from "@/constants/content";
1414
import { useCreateConnection, useRemoveConnection } from "@/lib/tanstack/mutation/connection";
15+
import { useCreateContent } from "@/lib/tanstack/mutation/topic-content";
1516
import { useGetTopicById } from "@/lib/tanstack/query/topic";
1617
import { useMobileMenuStore } from "@/lib/zustand/mobile-menu-store";
18+
import type { CategoryContentDTO } from "@/models/content";
1719
import { infoToast } from "@/utils/toast";
1820
import {
1921
addEdge,
@@ -67,6 +69,7 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
6769
} = useGetTopicById(id);
6870
const { mutateAsync: createConnection } = useCreateConnection();
6971
const { mutateAsync: removeConnection } = useRemoveConnection();
72+
const { mutateAsync: createContent } = useCreateContent(id);
7073

7174
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(initialNodes);
7275
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
@@ -139,6 +142,24 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
139142
}
140143
};
141144

145+
// 드래그 앤 드롭 핸들러
146+
const onDragOver = (e: React.DragEvent) => {
147+
e.preventDefault();
148+
e.dataTransfer.dropEffect = "copy";
149+
};
150+
151+
const onDrop = async (e: React.DragEvent) => {
152+
e.preventDefault();
153+
154+
const contentData: CategoryContentDTO = JSON.parse(e.dataTransfer.getData("application/json"));
155+
156+
// 서버에 콘텐츠 추가
157+
await createContent({
158+
topicId: id,
159+
contentId: contentData.id,
160+
});
161+
};
162+
142163
const onSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
143164
setSearchQuery(event.target.value);
144165
};
@@ -261,7 +282,11 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
261282
</div>
262283

263284
{/* Canvas */}
264-
<div className="relative flex-1 overflow-hidden rounded-r-lg border border-l-0">
285+
<div
286+
className="relative flex-1 overflow-hidden rounded-r-lg border border-l-0"
287+
onDragOver={onDragOver}
288+
onDrop={onDrop}
289+
>
265290
{isLoading ? (
266291
<div className="flex h-full flex-col items-center justify-center gap-2">
267292
<Loader2 className="text-muted-foreground size-16 animate-spin" />

0 commit comments

Comments
 (0)