Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
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;