Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/assets/image/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/image/upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions src/pages/FileUploadPage/FileUpload.style.ts
Original file line number Diff line number Diff line change
@@ -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;
}
`;
157 changes: 157 additions & 0 deletions src/pages/FileUploadPage/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -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<FileFormat[]>([]);
const [isDragging, setIsDragging] = useState<boolean>(false);
const dragRef = useRef<HTMLLabelElement | null>(null);
const fileId = useRef<number>(0);

const onChangeFiles = useCallback(
(e: ChangeEvent<HTMLInputElement> | 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 (
<S.UploadWrapper>
<div
className={`${isDragging ? "UploadWrapperActive" : "UploadWrapper"}`}
>
<label
className="UploadArea"
htmlFor="fileUpload"
ref={dragRef}
onClick={handleLabelClick}
>
{files.length === 0 ? (
<div className="desdiv">
<img src={upload} alt="upload" />
<span className="text">파일을 드래그 하여 업로드</span>
</div>
) : (
""
)}
</label>
</div>

<div className="FileContainer">
{files.length > 0 &&
files.map((file: FileFormat) => {
const {
id,
content: { name },
} = file;

return (
<div className="UploadFile" key={id}>
<div className="Filename">{name} </div>
<div className="DeleteBtn" onClick={() => handleDeleteFile(id)}>
<img src={remove} alt="remove" />
</div>
</div>
);
})}
</div>
<input
type="file"
id="fileUpload"
style={{ display: "none" }}
onChange={onChangeFiles} // 하나의 파일만 추가
/>
</S.UploadWrapper>
);
};

export default Upload;
Loading