Last active
July 12, 2024 07:05
-
-
Save pizzarob/cefe1727533619f340bb2aee5e1339af to your computer and use it in GitHub Desktop.
HTML5 Drag and Drop File Upload React Component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import PropTypes from 'prop-types'; | |
import classNames from 'classnames'; | |
const ANIMATION_DURATION = 1000; | |
class BatchDropZone extends React.Component { | |
static propTypes = { | |
// function that recieves an array of files | |
receiveFiles: PropTypes.func.isRequired, | |
// will be passed as second argument of receiveFiles function if present | |
id: PropTypes.string, | |
// if single is set then file input will NOT be multiple | |
single: PropTypes.bool, | |
// text message that will appear in drop zone | |
copy: PropTypes.string, | |
// additional classes that will be added to the drop zone wrapper | |
className: PropTypes.string, | |
} | |
state = {} | |
handleDragOver = e => { | |
e.preventDefault(); | |
if (this.state.inDropZone) { | |
return; | |
} | |
this.setState({ inDropZone: true }); | |
} | |
handleDrop = e => { | |
e.preventDefault(); | |
const { dataTransfer } = e; | |
const files = []; | |
if (dataTransfer.items) { | |
for (let i = this.props.single ? dataTransfer.items.length - 1 : 0; i < dataTransfer.items.length; i++) { | |
if (dataTransfer.items[i].kind == "file") { | |
const file = dataTransfer.items[i].getAsFile(); | |
files.push(file); | |
} | |
} | |
} else { | |
for (let i = this.props.single ? dataTransfer.files.length - 1 : 0; i < dataTransfer.files.length; i++) { | |
files.push(dataTransfer.files[i]); | |
} | |
} | |
this.setState({ | |
uploading: true, | |
}); | |
setTimeout(() => { | |
this.setState({ | |
uploading: false, | |
inDropZone: false, | |
}); | |
this.props.receiveFiles(files, this.props.id); | |
}, ANIMATION_DURATION); | |
} | |
handleDragLeave = () => { | |
if (!this.state.inDropZone) return; | |
this.setState({ inDropZone: false }); | |
} | |
handleClick = () => this.fileInput && this.fileInput.click(); | |
handleFilesFromInput = (e) => { | |
const files = []; | |
Array.from(e.currentTarget.files).forEach(file => { | |
files.push(file); | |
}); | |
this.setState({ | |
uploading: true, | |
}); | |
setTimeout(() => { | |
this.setState({ | |
uploading: false, | |
inDropZone: false, | |
}); | |
this.props.receiveFiles(files, this.props.id); | |
}, ANIMATION_DURATION); | |
} | |
render() { | |
const classes = classNames(`drop-it-wrap batch ${this.props.className || ''}`, { | |
active: this.state.inDropZone, | |
uploading: this.state.uploading, | |
}); | |
const dropEvents = { | |
onDrop: this.handleDrop, | |
onDragOver: this.handleDragOver, | |
onDragLeave: this.handleDragLeave, | |
onClick: this.handleClick, | |
}; | |
let err; | |
if (this.state.error) { | |
err = ( | |
<div className="notification danger"> | |
{this.state.error} | |
</div> | |
); | |
} | |
const fileInputAttrs = { | |
ref: (c) => { this.fileInput = c; }, | |
type: 'file', | |
onChange: this.handleFilesFromInput, | |
style: { position: 'absolute', left: -99999999 }, | |
}; | |
if (!this.props.single) { | |
fileInputAttrs.multiple = true; | |
} | |
return ( | |
<div style={{ width: '100%', height: '100%' }}> | |
{err} | |
<div className={classes} {...dropEvents}> | |
<div className="loader"> | |
Processing ... | |
</div> | |
<input {...fileInputAttrs} /> | |
<div className="default"> | |
{this.props.copy || 'Drop image files here or click'} | |
</div> | |
</div> | |
</div> | |
); | |
} | |
} | |
export default BatchDropZone; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import DropZone from './DropZone'; | |
export const readAsBase64 = (file) => { | |
return new Promise((resolve, reject) => { | |
let reader = new FileReader(); | |
reader.addEventListener('load', () => { | |
resolve({file, dataURL: reader.result}); | |
}); | |
reader.readAsDataURL(file); | |
}); | |
} | |
export default DropZone; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$primary: blue; | |
$secondary: red; | |
.drop-it-wrap{ | |
width: 100%; | |
border: 5px dashed #ececec; | |
height: 250px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
cursor: pointer; | |
.loader{ | |
display: none; | |
} | |
&.uploading{ | |
.loader{ | |
display: block; | |
} | |
.default{ | |
display: none; | |
} | |
} | |
&.active{ | |
border-color: $primary; | |
} | |
&:hover{ | |
border: 5px dashed darken(#ececec, 10%); | |
} | |
} | |
.drop-it-input{ | |
position: absolute; | |
top: -99999999px; | |
} | |
@keyframes disco { | |
0% { | |
background: $primary; | |
} | |
100% { | |
background: $secondary; | |
} | |
} | |
.batch { | |
cursor: crosshair; | |
&.uploading { | |
animation: disco .1s infinite alternate; | |
} | |
background: #ffffff; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment