react-dropzone
Version:
Simple HTML5 drag-drop zone with React.js
217 lines (178 loc) • 5.4 kB
JavaScript
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var React = require('react');
var accept = require('attr-accept');
var Dropzone = React.createClass({
displayName: 'Dropzone',
getDefaultProps: function getDefaultProps() {
return {
disableClick: false,
multiple: true
};
},
getInitialState: function getInitialState() {
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 componentDidMount() {
this.enterCounter = 0;
},
allFilesAccepted: function allFilesAccepted(files) {
var _this = this;
return files.every(function (file) {
return accept(file, _this.props.accept);
});
},
onDragEnter: function onDragEnter(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 onDragOver(e) {
e.preventDefault();
},
onDragLeave: function onDragLeave(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 onDrop(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 onClick() {
if (!this.props.disableClick) {
this.open();
}
},
open: function open() {
var fileInput = React.findDOMNode(this.refs.fileInput);
fileInput.value = null;
fileInput.click();
},
render: function render() {
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 = _extends({}, style, activeStyle);
} else {
appliedStyle = _extends({}, style);
};
return React.createElement(
'div',
{
className: className,
style: appliedStyle,
onClick: this.onClick,
onDragEnter: this.onDragEnter,
onDragOver: this.onDragOver,
onDragLeave: this.onDragLeave,
onDrop: this.onDrop
},
this.props.children,
React.createElement('input', {
type: 'file',
ref: 'fileInput',
style: { display: 'none' },
multiple: this.props.multiple,
accept: this.props.accept,
onChange: this.onDrop
})
);
}
});
module.exports = Dropzone;