@glaudsonsilva/react-images-upload
Version:
It's a fork project from https://github.com/JakeHartnell/react-images-upload. I make it to wait for the PR response. Check that repository for documentation and oficial commits.
332 lines (293 loc) • 9.48 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import './index.css';
import FlipMove from 'react-flip-move';
import UploadIcon from './UploadIcon.svg';
const styles = {
display: "flex",
alignItems: "center",
justifyContent: "center",
flexWrap: "wrap",
width: "100%"
};
const ERROR = {
NOT_SUPPORTED_EXTENSION: 'NOT_SUPPORTED_EXTENSION',
FILESIZE_TOO_LARGE: 'FILESIZE_TOO_LARGE'
}
class ReactImageUploadComponent extends React.Component {
constructor(props) {
super(props);
this.defaultState = {
pictures: [...props.defaultImages],
files: [...props.defaultImages.map(() => { })],
fileErrors: []
};
this.state = this.defaultState;
this.inputElement = '';
this.onDropFile = this.onDropFile.bind(this);
this.onUploadClick = this.onUploadClick.bind(this);
this.triggerFileUpload = this.triggerFileUpload.bind(this);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevState.files !== this.state.files) {
// Skip blank changes because its not keeping state (clearing)
if (this.props.keepState !== true && this.state.files.length < 1)
return;
this.notifyImageChange(this.state.files, this.state.pictures);
if (this.props.keepState !== true) {
this.setState(this.defaultState);
}
}
}
/*
Load image at the beggining if defaultImage prop exists
*/
componentWillReceiveProps(nextProps) {
if (nextProps.defaultImages !== this.props.defaultImages) {
this.setState({ pictures: nextProps.defaultImages, files: nextProps.defaultImages.map(() => { }) });
}
}
/*
Check file extension (onDropFile)
*/
hasExtension(fileName) {
const pattern = '(' + this.props.imgExtension.join('|').replace(/\./g, '\\.') + ')$';
return new RegExp(pattern, 'i').test(fileName);
}
/*
Handle file validation
*/
onDropFile(e) {
const files = e.target.files;
const allFilePromises = [];
const fileErrors = [];
// Iterate over all uploaded files
var length = Math.min(this.props.maxFiles, files.length);
const filesLoaded = this.state.files.filter(x => x).length + this.state.pictures.length;
if (files.length + filesLoaded > this.props.maxFiles) {
length = this.props.maxFiles - filesLoaded;
}
for (let i = 0; i < length; i++) {
let file = files[i];
let fileError = {
name: file.name,
};
// Check for file extension
if (!this.hasExtension(file.name)) {
fileError = Object.assign(fileError, {
type: ERROR.NOT_SUPPORTED_EXTENSION
});
fileErrors.push(fileError);
continue;
}
// Check for file size
if (file.size > this.props.maxFileSize) {
fileError = Object.assign(fileError, {
type: ERROR.FILESIZE_TOO_LARGE
});
fileErrors.push(fileError);
continue;
}
allFilePromises.push(this.readFile(file));
}
this.setState({
fileErrors
});
const { singleImage } = this.props;
Promise.all(allFilePromises).then(newFilesData => {
const dataURLs = singleImage ? [] : this.state.pictures.slice();
const files = singleImage ? [] : this.state.files.slice();
newFilesData.forEach(newFileData => {
dataURLs.push(newFileData.dataURL);
files.push(newFileData.file);
});
this.setState({ pictures: dataURLs, files: files });
});
}
onUploadClick(e) {
// Fixes https://github.com/JakeHartnell/react-images-upload/issues/55
e.target.value = null;
}
/*
Read a file and return a promise that when resolved gives the file itself and the data URL
*/
readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
// Read the image via FileReader API and save image result in state.
reader.onload = function (e) {
// Add the file name to the data URL
let dataURL = e.target.result;
dataURL = dataURL.replace(";base64", `;name=${file.name};base64`);
resolve({ file, dataURL });
};
reader.readAsDataURL(file);
});
}
/*
Remove the image from state
*/
removeImage(picture) {
const removeIndex = this.state.pictures.findIndex(e => e === picture);
const filteredPictures = this.state.pictures.filter((e, index) => index !== removeIndex);
const filteredFiles = this.state.files.filter((e, index) => index !== removeIndex);
this.setState({ pictures: filteredPictures, files: filteredFiles }, () => {
this.notifyImageChange(this.state.files, this.state.pictures);
});
}
/*
Notifies image change without undefined files
*/
notifyImageChange(files, pictures) {
this.props.onChange(files.filter(e => e), pictures);
}
/*
Check if any errors && render
*/
renderErrors() {
const { fileErrors } = this.state;
return fileErrors.map((fileError, index) => {
return (
<div className={'errorMessage ' + this.props.errorClass} key={index} style={this.props.errorStyle}>
* {fileError.name} {fileError.type === ERROR.FILESIZE_TOO_LARGE ? this.props.fileSizeError : this.props.fileTypeError}
</div>
);
});
}
/*
Render the upload icon
*/
renderIcon() {
if (this.props.withIcon) {
return <img src={this.props.customIcon || UploadIcon} className="uploadIcon" alt="Upload Icon" />;
}
}
/*
Render label
*/
renderLabel() {
if (this.props.withLabel) {
return <p className={this.props.labelClass} style={this.props.labelStyles}>{this.props.label}</p>
}
}
/*
Render preview images
*/
renderPreview() {
return (
<div className="uploadPicturesWrapper">
<FlipMove enterAnimation="fade" leaveAnimation="fade" style={styles}>
{this.renderPreviewPictures()}
</FlipMove>
</div>
);
}
renderPreviewPictures() {
return this.state.pictures.map((picture, index) => {
return (
<div key={index} className="uploadPictureContainer">
<div className="deleteImage" onClick={() => this.removeImage(picture)}>X</div>
<img src={picture} className="uploadPicture" alt="preview" />
</div>
);
});
}
/*
On button click, trigger input file to open
*/
triggerFileUpload() {
this.inputElement.click();
}
clearPictures() {
this.setState({ pictures: [] })
}
render() {
return (
<div className={"fileUploader " + this.props.className} style={this.props.style}>
<div className="fileContainer" style={this.props.fileContainerStyle}>
{this.props.withPreview ? this.renderPreview() : null}
{this.renderIcon()}
{this.renderLabel()}
<div className="errorsContainer">
{this.renderErrors()}
</div>
<button
type={this.props.buttonType}
className={"chooseFileButton " + this.props.buttonClassName}
style={this.props.buttonStyles}
onClick={this.triggerFileUpload}
>
{this.props.buttonText}
</button>
<input
type="file"
ref={input => this.inputElement = input}
name={this.props.name}
multiple={!this.props.singleImage}
onChange={this.onDropFile}
onClick={this.onUploadClick}
accept={this.props.accept}
/>
</div>
</div>
)
}
}
ReactImageUploadComponent.defaultProps = {
className: '',
fileContainerStyle: {},
buttonClassName: "",
buttonStyles: {},
withPreview: false,
accept: "image/*",
name: "",
withIcon: true,
buttonText: "Choose images",
buttonType: "button",
withLabel: true,
label: "Max file size: 5mb, accepted: jpg|gif|png",
labelStyles: {},
labelClass: "",
imgExtension: ['.jpg', '.jpeg', '.gif', '.png'],
maxFileSize: 5242880,
maxFiles: 99,
fileSizeError: " file size is too big",
fileTypeError: " is not a supported file extension",
errorClass: "",
style: {},
errorStyle: {},
singleImage: false,
onChange: () => { },
defaultImages: [],
keepState: true
};
ReactImageUploadComponent.propTypes = {
style: PropTypes.object,
fileContainerStyle: PropTypes.object,
className: PropTypes.string,
onChange: PropTypes.func,
onDelete: PropTypes.func,
buttonClassName: PropTypes.string,
buttonStyles: PropTypes.object,
buttonType: PropTypes.string,
withPreview: PropTypes.bool,
accept: PropTypes.string,
name: PropTypes.string,
withIcon: PropTypes.bool,
buttonText: PropTypes.string,
withLabel: PropTypes.bool,
label: PropTypes.string,
labelStyles: PropTypes.object,
labelClass: PropTypes.string,
imgExtension: PropTypes.array,
maxFileSize: PropTypes.number,
maxFiles: PropTypes.number,
fileSizeError: PropTypes.string,
fileTypeError: PropTypes.string,
errorClass: PropTypes.string,
errorStyle: PropTypes.object,
singleImage: PropTypes.bool,
defaultImages: PropTypes.array,
keepState: PropTypes.bool
};
export default ReactImageUploadComponent;