joplin-notes/joplin notion extension/Web Development/React/Snippets/Drag and Drop File Upload.md
2026-11-04 00:33:23 +05:30

384 lines
No EOL
9.1 KiB
Markdown

Drag and Drop File Upload
[react-drop-file-input-main.zip](../../../../_resources/react-drop-file-input-main.zip)
- JSX
```JSX
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import './drop-file-input.css';
import { ImageConfig } from '../../config/ImageConfig';
import uploadImg from '../../assets/cloud-upload-regular-240.png';
const DropFileInput = props => {
const wrapperRef = useRef(null);
const [fileList, setFileList] = useState([]);
const onDragEnter = () => wrapperRef.current.classList.add('dragover');
const onDragLeave = () => wrapperRef.current.classList.remove('dragover');
const onDrop = () => wrapperRef.current.classList.remove('dragover');
const onFileDrop = (e) => {
const newFile = e.target.files[0];
if (newFile) {
const updatedList = [...fileList, newFile];
setFileList(updatedList);
props.onFileChange(updatedList);
}
}
const fileRemove = (file) => {
const updatedList = [...fileList];
updatedList.splice(fileList.indexOf(file), 1);
setFileList(updatedList);
props.onFileChange(updatedList);
}
return (
<>
<div
ref={wrapperRef}
className="drop-file-input"
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDrop={onDrop}
>
<div className="drop-file-input__label">
<img src={uploadImg} alt="" />
<p>Drag & Drop your files here</p>
</div>
<input type="file" value="" onChange={onFileDrop}/>
</div>
{
fileList.length > 0 ? (
<div className="drop-file-preview">
<p className="drop-file-preview__title">
Ready to upload
</p>
{
fileList.map((item, index) => (
<div key={index} className="drop-file-preview__item">
<img src={ImageConfig[item.type.split('/')[1]] || ImageConfig['default']} alt="" />
<div className="drop-file-preview__item__info">
<p>{item.name}</p>
<p>{item.size}B</p>
</div>
<span className="drop-file-preview__item__del" onClick={() => fileRemove(item)}>x</span>
</div>
))
}
</div>
) : null
}
</>
);
}
DropFileInput.propTypes = {
onFileChange: PropTypes.func
}
export default DropFileInput;
```
&nbsp;
CSS
```css
.drop-file-input {
position: relative;
width: 400px;
height: 200px;
border: 2px dashed var(--border-color);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--input-bg);
}
.drop-file-input input {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.drop-file-input:hover,
.drop-file-input.dragover {
opacity: 0.6;
}
.drop-file-input__label {
text-align: center;
color: var(--txt-second-color);
font-weight: 600;
padding: 10px;
}
.drop-file-input__label img {
width: 100px;
}
.drop-file-preview {
margin-top: 30px;
}
.drop-file-preview p {
font-weight: 500;
}
.drop-file-preview__title {
margin-bottom: 20px;
}
.drop-file-preview__item {
position: relative;
display: flex;
margin-bottom: 10px;
background-color: var(--input-bg);
padding: 15px;
border-radius: 20px;
}
.drop-file-preview__item img {
width: 50px;
margin-right: 20px;
}
.drop-file-preview__item__info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.drop-file-preview__item__del {
background-color: var(--box-bg);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
box-shadow: var(--box-shadow);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
}
.drop-file-preview__item:hover .drop-file-preview__item__del {
opacity: 1;
}
```
---
&nbsp;
- For multiple file uploading use this and to limit the height of preview file list:
```jsx
import React, { useRef, useState } from "react";
import PropTypes from "prop-types";
import "./drop-file-input.css";
import { ImageConfig } from "../../config/ImageConfig";
import uploadImg from "../../assets/cloud-upload-regular-240.png";
const DropFileInput = (props) => {
const wrapperRef = useRef(null);
const [fileList, setFileList] = useState([]);
const onDragEnter = () => wrapperRef.current.classList.add("dragover");
const onDragLeave = () => wrapperRef.current.classList.remove("dragover");
const onDrop = () => wrapperRef.current.classList.remove("dragover");
const onFileDrop = (e) => {
const newFiles = e.target.files;
if (newFiles.length !== 0) {
const updatedList = [...fileList, ...newFiles];
setFileList(updatedList);
props.onFileChange(updatedList);
}
};
const fileRemove = (file) => {
const updatedList = [...fileList];
updatedList.splice(fileList.indexOf(file), 1);
setFileList(updatedList);
props.onFileChange(updatedList);
};
return (
<>
<div
ref={wrapperRef}
className="drop-file-input"
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDrop={onDrop}
>
<div className="drop-file-input__label">
<img src={uploadImg} alt="" />
<p>Drag & Drop your files here</p>
</div>
<input type="file" value="" multiple onChange={onFileDrop} />
</div>
{fileList.length > 0 ? (
<div className="drop-file-preview">
<p className="drop-file-preview__title">Ready to upload</p>
{fileList.map((item, index) => (
<div key={index} className="drop-file-preview__item">
<img
src={
ImageConfig[item.type.split("/")[1]] || ImageConfig["default"]
}
alt=""
/>
<div className="drop-file-preview__item__info">
<p>{item.name}</p>
<p>{item.size}B</p>
</div>
<span
className="drop-file-preview__item__del"
onClick={() => fileRemove(item)}
>
x
</span>
</div>
))}
</div>
) : null}
</>
);
};
DropFileInput.propTypes = {
onFileChange: PropTypes.func,
};
export default DropFileInput;
```
&nbsp;
CSS
```css
.drop-file-input {
position: relative;
width: 400px;
height: 200px;
border: 2px dashed var(--border-color);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--input-bg);
}
.drop-file-input input {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.drop-file-input:hover,
.drop-file-input.dragover {
opacity: 0.6;
}
.drop-file-input__label {
text-align: center;
color: var(--txt-second-color);
font-weight: 600;
padding: 10px;
}
.drop-file-input__label img {
width: 100px;
}
.drop-file-preview {
margin-top: 30px;
max-height: 70%;
overflow-y: auto;
}
.drop-file-preview p {
font-weight: 500;
}
.drop-file-preview__title {
margin-bottom: 20px;
}
.drop-file-preview__item {
position: relative;
display: flex;
margin-bottom: 10px;
background-color: var(--input-bg);
padding: 15px;
border-radius: 20px;
}
.drop-file-preview__item img {
width: 50px;
margin-right: 20px;
}
.drop-file-preview__item__info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.drop-file-preview__item__del {
background-color: var(--box-bg);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
box-shadow: var(--box-shadow);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
}
.drop-file-preview__item:hover .drop-file-preview__item__del {
opacity: 1;
}
```
---