react-dropzone
Version:
Simple HTML5 drag-drop zone with React.js
213 lines (177 loc) • 4.92 kB
JavaScript
var React = require('react');
var accept = require('attr-accept');
var Dropzone = React.createClass({
getDefaultProps: function() {
return {
disableClick: false,
multiple: true
};
},
getInitialState: function() {
return {
isDragActive: false
};
},
propTypes: {
onDrop: React.PropTypes.func,
onDropAccepted: React.PropTypes.func,
onDropRejected: React.PropTypes.func,
onDragEnter: React.PropTypes.func,
onDragLeave: React.PropTypes.func,
style: React.PropTypes.object,
activeStyle: React.PropTypes.object,
className: React.PropTypes.string,
activeClassName: React.PropTypes.string,
rejectClassName: React.PropTypes.string,
disableClick: React.PropTypes.bool,
multiple: React.PropTypes.bool,
accept: React.PropTypes.string,
},
componentDidMount: function() {
this.enterCounter = 0;
},
allFilesAccepted: function(files) {
return files.every(file => accept(file, this.props.accept))
},
onDragEnter: function(e) {
e.preventDefault();
// Count the dropzone and any children that are entered.
++this.enterCounter;
// This is tricky. During the drag even the dataTransfer.files is null
// But Chrome implements some drag store, which is accesible via dataTransfer.items
var dataTransferItems = e.dataTransfer && e.dataTransfer.items ? e.dataTransfer.items : [];
// Now we need to convert the DataTransferList to Array
var itemsArray = Array.prototype.slice.call(dataTransferItems);
var allFilesAccepted = this.allFilesAccepted(itemsArray);
this.setState({
isDragActive: allFilesAccepted,
isDragReject: !allFilesAccepted
});
if (this.props.onDragEnter) {
this.props.onDragEnter(e);
}
},
onDragOver: function (e) {
e.preventDefault();
},
onDragLeave: function(e) {
e.preventDefault();
// Only deactivate once the dropzone and all children was left.
if (--this.enterCounter > 0) {
return;
}
this.setState({
isDragActive: false,
isDragReject: false
});
if (this.props.onDragLeave) {
this.props.onDragLeave(e);
}
},
onDrop: function(e) {
e.preventDefault();
// Reset the counter along with the drag on a drop.
this.enterCounter = 0;
this.setState({
isDragActive: false,
isDragReject: false
});
var droppedFiles = e.dataTransfer ? e.dataTransfer.files : e.target.files;
var max = this.props.multiple ? droppedFiles.length : 1;
var files = [];
for (var i = 0; i < max; i++) {
var file = droppedFiles[i];
file.preview = URL.createObjectURL(file);
files.push(file);
}
if (this.props.onDrop) {
this.props.onDrop(files, e);
}
if (this.allFilesAccepted(files)) {
if (this.props.onDropAccepted) {
this.props.onDropAccepted(files, e);
}
} else {
if (this.props.onDropRejected) {
this.props.onDropRejected(files, e);
}
}
},
onClick: function () {
if (!this.props.disableClick) {
this.open();
}
},
open: function() {
var fileInput = React.findDOMNode(this.refs.fileInput);
fileInput.value = null;
fileInput.click();
},
render: function() {
var className;
if (this.props.className) {
className = this.props.className;
if (this.state.isDragActive) {
className += ' ' + this.props.activeClassName;
};
if (this.state.isDragReject) {
className += ' ' + this.props.rejectClassName;
};
};
var style, activeStyle;
if (this.props.style || this.props.activeStyle) {
if (this.props.style) {
style = this.props.style;
}
if (this.props.activeStyle) {
activeStyle = this.props.activeStyle;
}
} else if (!className) {
style = {
width: 200,
height: 200,
borderWidth: 2,
borderColor: '#666',
borderStyle: 'dashed',
borderRadius: 5,
};
activeStyle = {
borderStyle: 'solid',
backgroundColor: '#eee'
};
}
var appliedStyle;
if (activeStyle && this.state.isDragActive) {
appliedStyle = {
...style,
...activeStyle
};
} else {
appliedStyle = {
...style
};
};
return (
<div
className={className}
style={appliedStyle}
onClick={this.onClick}
onDragEnter={this.onDragEnter}
onDragOver={this.onDragOver}
onDragLeave={this.onDragLeave}
onDrop={this.onDrop}
>
{this.props.children}
<input
type='file'
ref='fileInput'
style={{ display: 'none' }}
multiple={this.props.multiple}
accept={this.props.accept}
onChange={this.onDrop}
/>
</div>
);
}
});
module.exports = Dropzone;