-
-
Save qborreda/0d52ee2157615ad425e7bbc8bdccd495 to your computer and use it in GitHub Desktop.
HTML5 Drag and Drop File Upload React Component
This file contains hidden or 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; | |
} |
This file contains hidden or 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, {PropTypes} from 'react'; | |
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, | |
} | |
constructor() { | |
super(); | |
this.state = {}; | |
this.handleDragOver = this.handleDragOver.bind(this); | |
this.handleDrop = this.handleDrop.bind(this); | |
this.handleDragLeave = this.handleDragLeave.bind(this); | |
this.handleClick = this.handleClick.bind(this); | |
} | |
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") { | |
let 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.refs.fileInput.click(); | |
} | |
handleFilesFromInput(e) { | |
let 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() { | |
let 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: 'fileInput', | |
type: 'file', | |
onChange: this.handleFilesFromInput.bind(this), | |
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 hidden or 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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment