Home Reference Source

src/views/form/image.field.js

import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon as FA } from '@fortawesome/react-fontawesome';
import { faUpload } from '@fortawesome/free-solid-svg-icons';
import { ImageUtils } from '../../utils';
import Field from './field.view';

/**
 * Controls an input file element that shows a preview of the selected image
 *
 * #### SCSS import:
 * ```
 * @import "~@orloxx/ui-core/scss/form/image-field";
 * ```
 *
 * @extends {Field}
 *
 * @example
 * <ImageField id='image' name='image' label='Choose from computer' />
 */
class ImageField extends Field {
  /**
   * @type {Object}
   * @property {String} [src] - Default image path when nothing is selected
   * @property {number} [fileSize=256Kb] - Maximum file size (Kb) accepted
   */
  static propTypes = Object.assign({}, Field.propTypes, {
    src: PropTypes.string,
    fileSize: PropTypes.number,
  });

  /**
   * @ignore
   */
  static defaultProps = Object.assign({}, Field.defaultProps, {
    src: '',
    fileSize: 256,
  });

  /**
   * @ignore
   */
  constructor(props) {
    super(props);

    /**
     * @ignore
     */
    this.state = Object.assign({}, this.state, {
      imageData: null,
    });
  }

  /**
   * @ignore
   */
  get hasImageClassName() {
    return this.imageSrc ? 'imageField--hasImage' : '';
  }

  /**
   * @ignore
   */
  get imageSrc() {
    const { imageData } = this.state;
    const { src } = this.props;
    return imageData || src;
  }

  /**
   * @ignore
   */
  setImage(e) {
    const file = e.target.files[0];
    const maxKb = this.props.fileSize;
    const maxSize = maxKb * 1024;

    if (file && file.size > maxSize) {
      alert(`File cannot be greater than ${maxKb}Kb. Image will be removed.`);
      e.target.value = null;
      this.setState({ imageData: null });
      return;
    }
    this.setImageData(file);
  }

  /**
   * @ignore
   */
  setImageData(file) {
    if (file) {
      ImageUtils.getFileData(file)
        .then((imageData) => {
          this.setState({ imageData });
          this.validate();
        });
    }
  }

  /**
   * @ignore
   */
  render() {
    const { id, name, label } = this.props;
    return (
      <div className={`field imageField ${this.hasImageClassName}`}>
        <div className='field__inputWrapper'>
          <input
            className='field__input imageField__input hideAccessible'
            id={id} name={name}
            type='file'
            required={this.props.required}
            accept='image/*'
            onBlur={() => this.validate()}
            onChange={e => this.setImage(e)}
          />
          <label className='button field__label imageField__label' htmlFor={id}>
            <img
              className='imageField__image'
              src={this.imageSrc}
              alt='Upload'
            />
            <input
              type='hidden' id={`${id}-value`} name={`${name}-value`}
              ref={this.input} value={this.imageSrc} />
            <FA className='imageField__noImageIcon' icon={faUpload} />
            <span className='field__labelSpan imageField__labelSpan'>
              {label} {this.requiredLabel}
            </span>
          </label>
        </div>
        {this.renderValidationMessages()}
      </div>
    );
  }
}

export default ImageField;