diff --git a/src/assets/image/remove.svg b/src/assets/image/remove.svg new file mode 100644 index 0000000..f7f227e --- /dev/null +++ b/src/assets/image/remove.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/assets/image/upload.svg b/src/assets/image/upload.svg new file mode 100644 index 0000000..e10b989 --- /dev/null +++ b/src/assets/image/upload.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/pages/FileUploadPage/FileUpload.style.ts b/src/pages/FileUploadPage/FileUpload.style.ts new file mode 100644 index 0000000..990c1be --- /dev/null +++ b/src/pages/FileUploadPage/FileUpload.style.ts @@ -0,0 +1,92 @@ +import styled from "styled-components"; + +export const UploadWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 55vh; + height: 330px; + flex-shrink: 0; + border-radius: 16px; + border: 0px solid #9095a1; + background: #fff; + box-shadow: 0px 0px 2px 0px rgba(23, 26, 31, 0.12), + 0px 4px 9px 0px rgba(23, 26, 31, 0.11); + + .desdiv { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 16px; + height: 200px; + gap: 20%; + } + + .text { + color: #379ae6; + text-align: center; + font-family: Archivo; + font-size: 18px; + font-style: normal; + font-weight: 500; + line-height: 28px; /* 155.556% */ + } + + .UploadWrapperActive { + background: #0000000f; + width: 400px; + height: 200px; + border: 2px dotted black; + opacity: 0.7; + } + + .UploadArea { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + } + + .FileContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: start; + margin-top: 10px; + gap: 2px; + } + + .UploadFile { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 5px; + background-color: #0000000f; + border-radius: 5px; + } + + .Filename { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .DeleteBtn { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + text-align: center; + vertical-align: center; + } + + .DeleteBtn:hover { + cursor: pointer; + } +`; diff --git a/src/pages/FileUploadPage/FileUpload.tsx b/src/pages/FileUploadPage/FileUpload.tsx new file mode 100644 index 0000000..0a90d76 --- /dev/null +++ b/src/pages/FileUploadPage/FileUpload.tsx @@ -0,0 +1,157 @@ +import * as S from "@/pages/FileUploadPage/FileUpload.style"; +import { ChangeEvent, useCallback, useRef, useState, useEffect } from "react"; +import upload from "@/assets/image/upload.svg"; +import remove from "@/assets/image/remove.svg"; + +interface FileFormat { + id: number; + content: File; +} + +const Upload = () => { + const [files, setFiles] = useState([]); + const [isDragging, setIsDragging] = useState(false); + const dragRef = useRef(null); + const fileId = useRef(0); + + const onChangeFiles = useCallback( + (e: ChangeEvent | any): void => { + let selectFiles: File[] = []; + let tempFiles: FileFormat[] = []; + + e.type === "drop" + ? (selectFiles = e.dataTransfer.files) + : (selectFiles = e.target.files); + + // 새로운 파일을 하나만 추가 + const file = selectFiles[0]; + if (file) { + tempFiles = [ + { + id: fileId.current++, + content: file, + }, + ]; + setFiles(tempFiles); + } + }, + [] + ); + + const handleLabelClick = (e: any) => { + e.preventDefault(); + }; + + const handleDeleteFile = useCallback( + (id: number): void => { + setFiles(files.filter((file: FileFormat) => file.id !== id)); + }, + [files] + ); + + const handleDragIn = useCallback((e: DragEvent): void => { + e.preventDefault(); + e.stopPropagation(); + }, []); + + const handleDragOut = useCallback((e: DragEvent): void => { + e.preventDefault(); + e.stopPropagation(); + + setIsDragging(false); + }, []); + + const handleDragOver = useCallback((e: DragEvent): void => { + e.preventDefault(); + e.stopPropagation(); + + if (e.dataTransfer!.files) { + setIsDragging(true); + } + }, []); + + const handleDrop = useCallback( + (e: DragEvent): void => { + e.preventDefault(); + e.stopPropagation(); + + onChangeFiles(e); + setIsDragging(false); + }, + [onChangeFiles] + ); + + const initDragEvents = useCallback((): void => { + if (dragRef.current !== null) { + dragRef.current.addEventListener("dragenter", handleDragIn); + dragRef.current.addEventListener("dragleave", handleDragOut); + dragRef.current.addEventListener("dragover", handleDragOver); + dragRef.current.addEventListener("drop", handleDrop); + } + }, [handleDragIn, handleDragOut, handleDragOver, handleDrop]); + + const resetDragEvents = useCallback((): void => { + if (dragRef.current !== null) { + dragRef.current.removeEventListener("dragenter", handleDragIn); + dragRef.current.removeEventListener("dragleave", handleDragOut); + dragRef.current.removeEventListener("dragover", handleDragOver); + dragRef.current.removeEventListener("drop", handleDrop); + } + }, [handleDragIn, handleDragOut, handleDragOver, handleDrop]); + + useEffect(() => { + initDragEvents(); + return () => resetDragEvents(); + }, [initDragEvents, resetDragEvents]); + + return ( + + + + {files.length === 0 ? ( + + + 파일을 드래그 하여 업로드 + + ) : ( + "" + )} + + + + + {files.length > 0 && + files.map((file: FileFormat) => { + const { + id, + content: { name }, + } = file; + + return ( + + {name} + handleDeleteFile(id)}> + + + + ); + })} + + + + ); +}; + +export default Upload; diff --git a/src/pages/FileUploadPage/FileUploadPage.style.ts b/src/pages/FileUploadPage/FileUploadPage.style.ts index e69de29..b09803c 100644 --- a/src/pages/FileUploadPage/FileUploadPage.style.ts +++ b/src/pages/FileUploadPage/FileUploadPage.style.ts @@ -0,0 +1,160 @@ +import styled from "styled-components"; + +export const StyledFileUploadPage = styled.div` + margin-top: 25px; + display: flex; + flex-direction: column; + align-items: start; + gap: 20px; + .title { + color: #171a1f; + font-family: Archivo; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 48px; /* 150% */ + text-align: start; + } +`; + +export const FileUploadContent = styled.div` + display: flex; + justify-content: center; + align-items: start; + gap: 30px; +`; + +export const FileUploadDescription = styled.div` + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; + gap: 16px; + margin-left: 16px; + + .ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .li { + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; + margin-bottom: 20px; + } + + .title { + color: #424856; + font-family: Inter; + font-size: 18px; + font-style: normal; + font-weight: 700; + line-height: 28px; /* 155.556% */ + margin-bottom: 5px; + } + + .name_input { + padding: 10px; + width: 40vw; + border-radius: 6px 6px 0px 0px; + border: 0px solid rgba(0, 0, 0, 0); + background: #fff; + color: #171a1f; + font-family: Inter; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 26px; /* 162.5% */ + } + + .price_input { + padding: 10px; + width: 20vw; + border-radius: 6px 6px 0px 0px; + border: 0px solid rgba(0, 0, 0, 0); + background: #fff; + color: #171a1f; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 26px; /* 162.5% */ + } + + .div { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + } + + .textarea { + color: #171a1f; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 26px; /* 162.5% */ + padding: 10px; + width: 40vw; + border-radius: 6px 6px 0px 0px; + border: 0px solid rgba(0, 0, 0, 0); + background: #fff; + } + + .select { + color: #171a1f; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 26px; /* 162.5% */ + padding: 10px; + width: 40vw; + border-radius: 6px 6px 0px 0px; + border: 0px solid rgba(0, 0, 0, 0); + background: #fff; + } + + .description { + color: #323743; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + + line-height: 22px; + margin-top: 8px; + margin-bottom: 30px; + } + + hr { + stroke-width: 1px; + stroke: #bdc1ca; + margin-top: 20px; + margin-bottom: 20px; + } + + .button { + display: flex; + width: 513px; + padding: 12px 216px 12px 209px; + align-items: center; + border-radius: 6px; + border: 1px solid rgba(0, 0, 0, 0); + background: #636ae8; + color: #fff; + font-family: Inter; + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 28px; /* 155.556% */ + } + + .button:hover { + background: #4f55c1; + } +`; diff --git a/src/pages/FileUploadPage/FileUploadPage.tsx b/src/pages/FileUploadPage/FileUploadPage.tsx index 1e546fe..f6cf330 100644 --- a/src/pages/FileUploadPage/FileUploadPage.tsx +++ b/src/pages/FileUploadPage/FileUploadPage.tsx @@ -1,5 +1,102 @@ +import NavBar from "@/components/NavBar/NavBar"; +import * as S from "@/pages/FileUploadPage/FileUploadPage.style"; +import Upload from "./FileUpload"; +import { useState } from "react"; + const FileUploadPage = () => { - return FileUploadPage; + // 상태 선언 + const [fileName, setFileName] = useState(""); + const [fileCategory, setFileCategory] = useState("0"); + const [filePrice, setFilePrice] = useState(""); + const [fileDescription, setFileDescription] = useState(""); + + // form 제출 시 데이터 출력 + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); // 기본 폼 제출 동작 방지 + console.log("자료명:", fileName); + console.log("카테고리:", fileCategory); + console.log("가격:", filePrice); + console.log("자료 설명:", fileDescription); + }; + + return ( + <> + + + 새 자료 업로드 + + + + + + + + 자료명 + + setFileName(e.target.value)} + /> + + + + 카테고리 + + setFileCategory(e.target.value)} + > + 카테고리 선택 + 시험 자료 + 자소서 + 이력서 + 레포트 + 악보 + 기타 + + + + + 가격 + + + setFilePrice(e.target.value)} + /> + XRP + + + + + 자료 설명 + + setFileDescription(e.target.value)} + /> + + + + + 자료 업로드 + + + + + + > + ); }; export default FileUploadPage;