Repository URL to install this package:
|
Version:
3.14.0-rc.2 ▾
|
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Resizer from 'react-image-file-resizer';
import classnames from 'classnames';
import Button from '../../Button/Button';
import IconButton from '../../Button/IconButton';
import ButtonLabel from '../../Button/ButtonLabel';
import InputFeedback from '../../Input/InputFeedback';
import Modal from '../../../structure/Modal';
import DeleteIcon from '../../../visuals/Icon/svg/ic_delete.svg';
import ImageCropper from './ImageCropper';
import { getCroppedImg, readImageFile, validateFileSizeLimit, validateImageDimensions } from './imageHelper';
import { maxImageDimensions, aspectRatio } from './config';
class ImageUpload extends Component {
static propTypes = {
previewIcon: PropTypes.node.isRequired,
imageUrl: PropTypes.string.isRequired,
buttonLabel: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
semantic: PropTypes.string,
onChange: PropTypes.func,
isLoading: PropTypes.bool.isRequired,
imageType: PropTypes.string.isRequired,
note: PropTypes.string,
onFileSizeError: PropTypes.func,
errorKey: PropTypes.string,
buttonCancelText: PropTypes.string.isRequired,
buttonApplyText: PropTypes.string.isRequired,
};
static defaultProps = {
semantic: '',
onChange: () => {},
note: '',
onFileSizeError: () => {},
errorKey: '',
};
constructor(props) {
super(props);
this.state = {
imageSrc: '',
fileType: '',
fileName: '',
};
this.handleImageUploadButtonClick = this.handleImageUploadButtonClick.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleDeleteFile = this.handleDeleteFile.bind(this);
this.setInputRef = this.setInputRef.bind(this);
this.handleSaveCroppedImage = this.handleSaveCroppedImage.bind(this);
this.checkFileSize = this.checkFileSize.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);
}
setInputRef(node) {
this.inputFile = node;
}
handleChange(event) {
const { target: { files } } = event;
if (files && files.length > 0) {
const file = files[0];
this.checkFileSize(file);
if (this.inputFile) {
this.inputFile.value = '';
}
}
}
async checkFileSize(file) {
try {
const { imageSrc } = await readImageFile(file);
const isBelowFileSize = validateFileSizeLimit(file.size, 1000);
if (isBelowFileSize) {
this.setState({ imageSrc, fileType: file.type, fileName: file.name });
} else {
const { onFileSizeError, errorKey } = this.props;
onFileSizeError(errorKey);
}
} catch (error) {
console.error(error);
}
}
async handleSaveCroppedImage(croppedAreaPixels) {
try {
const { imageSrc, fileType, fileName } = this.state;
const { imageType } = this.props;
const croppedImg = await getCroppedImg(imageSrc, croppedAreaPixels, fileType, fileName);
const isBelowDimensionsLimit = validateImageDimensions(croppedImg, imageType);
const isBelowFileSize = validateFileSizeLimit(croppedImg.size, 1000);
if (isBelowDimensionsLimit && isBelowFileSize) {
this.handleChangeFile(croppedImg);
} else {
this.compressFile(croppedImg);
}
} catch (error) {
console.error(error);
}
}
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: '', fileName: '' });
}
handleImageUploadButtonClick() {
this.inputFile.click();
}
handleCancelUploadFile() {
this.resetState();
}
handleDeleteFile(event) {
event.preventDefault();
this.props.onChange();
}
handleChangeFile(file) {
this.resetState();
this.props.onChange(file);
}
render() {
const {
buttonLabel,
name,
semantic,
previewIcon,
imageUrl,
isLoading,
imageType,
note,
buttonCancelText,
buttonApplyText,
} = this.props;
const { imageSrc } = this.state;
const previewBackgroundImgStyle = {
backgroundImage: imageUrl ? `url(${imageUrl})` : '',
};
const previewClassNames = classnames('ImageUpload-preview', {
'ImageUpload-preview--background': imageType === 'background',
'ImageUpload-preview--logo': imageType === 'logo',
});
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">
<div className="ImageUpload-row">
<input
className="ImageUpload-input"
name={name}
type="file"
accept="image/jpeg,image/png,image/gif"
onChange={this.handleChange}
ref={this.setInputRef}
/>
{!imageUrl ? (
<Button className={previewClassNames} variant="white" onClick={this.handleImageUploadButtonClick}>
{previewIcon}
</Button>
) : (
<div className={previewClassNames} style={previewBackgroundImgStyle} />
)}
{imageUrl && (
<IconButton
className="ImageUpload-deleteIcon"
icon={DeleteIcon}
semantic={semantic}
variant="whiteWithBorder"
dimension="small"
onClick={this.handleDeleteFile}
disabled={isLoading}
/>
)}
</div>
<Button
variant="whiteWithBorder"
className="ImageUpload-uploadButton"
type="button"
onClick={this.handleImageUploadButtonClick}
disabled={isLoading}
>
<ButtonLabel>{buttonLabel}</ButtonLabel>
</Button>
{note && (
<div className="ImageUpload-note Input--dark">
<InputFeedback>{note}</InputFeedback>
</div>
)}
</div>
</React.Fragment>
);
}
}
export default ImageUpload;