diff --git a/public/icons/camera.svg b/public/icons/camera.svg new file mode 100644 index 00000000..cc6a70ac --- /dev/null +++ b/public/icons/camera.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/features/study/group/ui/group-study-thumbnail-input.tsx b/src/features/study/group/ui/group-study-thumbnail-input.tsx new file mode 100644 index 00000000..319193e2 --- /dev/null +++ b/src/features/study/group/ui/group-study-thumbnail-input.tsx @@ -0,0 +1,136 @@ +'use client'; + +import Image from 'next/image'; +import { useState, DragEvent, ChangeEvent, useRef } from 'react'; +import Button from '@/shared/ui/button'; + +const inputStyles = { + base: 'rounded-100 flex w-full flex-col items-center justify-center rounded-lg border-2 p-500', + dragging: 'border-border-brand bg-fill-brand-subtle-hover', + notDragging: 'border-gray-300 border-gray-300 border-dashed', +}; + +export default function GroupStudyThumbnailInput({ + image, + onChangeImage, +}: { + image?: string; + onChangeImage: (image?: string) => void; +}) { + const fileInputRef = useRef(null); + + const handleOpenFileDialog = () => { + fileInputRef.current?.click(); + }; + + const [isDragging, setIsDragging] = useState(false); + + // 영역 안에 드래그 들어왔을 때 + const handleDragEnter = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(true); + }; + // 영역 밖으로 드래그 나갈 때 + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + console.log('drag leave'); + setIsDragging(false); + }; + // 영역 안에서 드래그 중일 때 + const handleDragOver = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + console.log('drag over'); + + if (e.dataTransfer.files) { + setIsDragging(true); + } + }; + // 영역 안에서 drop 했을 때 + const handleDrop = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + console.log('drop'); + setIsDragging(false); + + const file = e.dataTransfer.files[0]; // 1장만 허용 + + if (file && file.type.startsWith('image/')) { + onChangeImage(URL.createObjectURL(file)); + } + }; + + // 파일 업로드 버튼으로 파일 선택했을 때 preview 설정 + const handleFileChange = (e: ChangeEvent) => { + const file = e.target.files?.[0]; + + if (file && file.type.startsWith('image/')) { + onChangeImage(URL.createObjectURL(file)); + } + }; + + const handleRemove = () => { + onChangeImage(undefined); + }; + + return ( +
+ {!image ? ( +
+
+ 파일 업로드 + + 드래그하여 파일 업로드 + +
+ + +
+ ) : ( +
+ preview + +
+ )} +
+ ); +} diff --git a/src/stories/ui/group-study-thumbnail-input.stories.tsx b/src/stories/ui/group-study-thumbnail-input.stories.tsx new file mode 100644 index 00000000..948fd35d --- /dev/null +++ b/src/stories/ui/group-study-thumbnail-input.stories.tsx @@ -0,0 +1,29 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { useState } from 'react'; +import GroupStudyThumbnailInput from '@/features/study/group/ui/group-study-thumbnail-input'; + +const meta: Meta = { + component: GroupStudyThumbnailInput, + argTypes: { + image: { + description: + '이미지 URL 상태 입니다. (undefined인 경우 이미지가 없는 상태를 의미합니다.)', + }, + onChangeImage: { + description: + '이미지 URL 상태를 변경하는 함수입니다. 파라미터는 이미지 URL 또는 undefined 입니다. (undefined인 경우 이미지가 제거된 상태를 의미합니다.)', + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => { + const [image, setImage] = useState(undefined); + + return ; + }, +};