384 lines
No EOL
9.1 KiB
Markdown
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;
|
|
```
|
|
|
|
|
|
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;
|
|
}
|
|
```
|
|
---
|
|
|
|
- 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;
|
|
|
|
```
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
```
|
|
|
|
--- |