Repository URL to install this package:
|
Version:
7.13.0-rc.6 ▾
|
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Resizer from 'react-image-file-resizer';
import Button from '../../Button/Button';
import ButtonLabel from '../../Button/ButtonLabel';
import LoadingButton from '../../Button/LoadingButton';
import InputFeedback from '../../Input/InputFeedback';
import Modal from '../../../structure/Modal';
import ImageCropper from './ImageCropper';
import { getCroppedImg, readImageFile, validateFileSizeLimit, validateImageDimensions } from './imageHelper';
import { maxImageDimensions, aspectRatio, errorKeys } from './config';
class ImageUpload extends Component {
static propTypes = {
buttonLabel: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func,
isLoading: PropTypes.bool.isRequired,
imageType: PropTypes.string,
note: PropTypes.string,
buttonCancelText: PropTypes.string.isRequired,
buttonApplyText: PropTypes.string.isRequired,
onError: PropTypes.func,
};
static defaultProps = {
onChange: () => {},
note: '',
imageType: '',
onError: () => {},
};
constructor(props) {
super(props);
this.state = {
imageSrc: '',
fileType: '',
};
this.handleImageUploadButtonClick = this.handleImageUploadButtonClick.bind(this);
this.handleChange = this.handleChange.bind(this);
this.setInputRef = this.setInputRef.bind(this);
this.handleSaveCroppedImage = this.handleSaveCroppedImage.bind(this);
this.compressFile = this.compressFile.bind(this);
this.handleCancelUploadFile = this.handleCancelUploadFile.bind(this);
this.handleChangeFile = this.handleChangeFile.bind(this);
this.resetState = this.resetState.bind(this);
this.handleCropper = this.handleCropper.bind(this);
}
setInputRef(node) {
this.inputFile = node;
}
/**
* Validates file size does not exceed limit in kilobytes
* Call onChange with file if no imageType prop has been provided, else handle the image cropper
*/
handleChange(event) {
const { target: { files } } = event;
const { imageType } = this.props;
if (files && files.length > 0) {
const file = files[0];
const isBelowFileSize = validateFileSizeLimit(file.size, 1000);
if (isBelowFileSize) {
if (!imageType) this.props.onChange(file);
this.handleCropper(file);
} else {
this.props.onError({ errorKey: errorKeys.fileSizeError });
}
if (this.inputFile) {
this.inputFile.value = '';
}
}
}
/**
* Reads the file to obtain the image source required for the ImageCropper
* imageSrc state triggers the modal with cropper
* @param {File} file - the image file
*/
async handleCropper(file) {
try {
const { imageSrc } = await readImageFile(file);
this.setState({ imageSrc, fileType: file.type });
} catch (error) {
this.props.onError({ errorKey: errorKeys.readImageError });
}
}
/**
* Returns a new blob of the cropped image from getCroppedImg helper.
* A second file size check is performed as in some edge cases the produced cropped image can have a slightly higher size than the original
* (typically this only happens if the file is near 1MB already and the zoom range is below 1)
* If the image is too large (dimensions and size) use react-image-resizer to resize and compress the image
* @param {object} croppedAreaPixels - used in the helper to draw the cropped image on a new canvas
*/
async handleSaveCroppedImage(croppedAreaPixels) {
try {
const { imageSrc, fileType } = this.state;
const { imageType } = this.props;
const croppedImg = await getCroppedImg(imageSrc, croppedAreaPixels, fileType);
const isBelowDimensionsLimit = validateImageDimensions(croppedImg, imageType);
const isBelowFileSize = validateFileSizeLimit(croppedImg.size, 1000);
if (isBelowDimensionsLimit && isBelowFileSize) {
this.handleChangeFile(croppedImg);
} else {
this.compressFile(croppedImg);
}
} catch (error) {
this.props.onError({ errorKey: errorKeys.cropImageError });
}
}
/**
* Calls react-image-resizer to resize and compress the image
* handleChangeFile callback is called with the new image URI as params
* @param {File} file - the new cropped image blob returned
*/
compressFile(file) {
const { imageType } = this.props;
const { fileType } = this.state;
const { maxWidth, maxHeight } = maxImageDimensions[imageType];
Resizer.imageFileResizer(
file, // e.g event.target.files[0]
maxWidth, // is the maxWidth of the new image
maxHeight, // is the maxHeight of the new image
fileType.match(/[^/]+$/g), // is the compressFormat of the new image
70, // is the quality of the new image - A number between 0 and 100. Used for the JPEG compression.(if no compress is needed, just set it to 100)
0, // is the rotatoion of the new image
async uri => this.handleChangeFile(uri), // is the callBack function of the new image URI
'blob' // is the output type of the new image
);
}
resetState() {
this.setState({ imageSrc: '', fileType: '' });
}
handleImageUploadButtonClick() {
this.inputFile.click();
}
handleCancelUploadFile() {
this.resetState();
}
handleChangeFile(file) {
this.resetState();
this.props.onChange(file);
}
render() {
const { buttonLabel, name, isLoading, imageType, note, buttonCancelText, buttonApplyText } = this.props;
const { imageSrc } = this.state;
return (
<React.Fragment>
<Modal show={Boolean(imageSrc)} title="Modal for image cropping tool" variant="ink">
<ImageCropper
imageSrc={imageSrc}
type={imageType}
onSaveCroppedImage={this.handleSaveCroppedImage}
onCancelCroppedImage={this.handleCancelUploadFile}
buttonCancelText={buttonCancelText}
buttonApplyText={buttonApplyText}
aspect={aspectRatio[imageType]}
/>
</Modal>
<div className="ImageUpload">
<input
className="ImageUpload-input"
name={name}
type="file"
accept="image/jpeg,image/png,image/gif"
onChange={this.handleChange}
ref={this.setInputRef}
/>
<Button
variant="whiteWithBorder"
dimension="compact"
className="ImageUpload-uploadButton"
type="button"
onClick={this.handleImageUploadButtonClick}
disabled={isLoading}
inputButtonClass={isLoading ? 'loading' : null}
>
<LoadingButton />
<ButtonLabel>{buttonLabel}</ButtonLabel>
</Button>
{note && (
<div className="ImageUpload-note Input--dark">
<InputFeedback>{note}</InputFeedback>
</div>
)}
</div>
</React.Fragment>
);
}
}
export default ImageUpload;