import * as React from "react" ; import { InvertedButton , BorderlessButton , buttonSizes } from "../Button" ; import { Modal } from "../Modal" ; import { colors , fonts } from "../styleConstants" ; import styled from "styled-components" ; import Dropzone from "react-dropzone" ; export interface ImageFileToDataUriState { modalOpen : boolean ; error ? : string ; } export interface ImageFileToDataUriProps { buttonText ? : string ; maxBytes ? : number ; maxSizeText ? : string ; info ? : string ; validMimeTypes ? : string [ ] ; onChange ( dataUri ) : void ; } export interface ImageFileToDataUriDefaultProps { maxBytes : number ; maxSizeText : string ; info : string ; validMimeTypes : string [ ] ; } const DropArea = styled . div `
  cursor: pointer;
  outline: none;
  width: 100%;
  box-sizing: border-box;
  height: 200px;
  border: 3px dashed ${ colors . primary . CIVIL_GRAY_2 };
  color: ${ colors . primary . CIVIL_GRAY_2 };
  border-radius: 5px;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 30px;
  &:hover {
    border-color: ${ colors . primary . CIVIL_BLUE_1 };
  }
  p {
    font-size: 18px;
    font-family: ${ fonts . SANS_SERIF };
  }
` ; const ButtonContainer = styled . div `
  display: flex;
  justify-content: flex-end;
  margin-top: 25px;
` ; const Error = styled . p `
  color: ${ colors . accent . CIVIL_RED };
` ; export class ImageFileToDataUri extends React . Component < ImageFileToDataUriProps & ImageFileToDataUriDefaultProps , ImageFileToDataUriState > { public static defaultProps : ImageFileToDataUriDefaultProps = { maxBytes : 256 * 1024 , maxSizeText : "250KB" , info : "Recommended dimensions 260x260px, maximum file size 250KB. Image will be displayed constrained to a square." , validMimeTypes : [ "image/gif" , "image/png" , "image/jpeg" ] , } ; constructor ( props ) { super ( props ) ; this . state = { modalOpen : false , } ; } public renderModal ( ) { if ( ! this . state . modalOpen ) { return null ; } return ( < Modal > < p > { this . props . info } < /p> < Dropzone onDrop = { this . createDataUrlFromFile } > { ( { getRootProps , getInputProps } ) => ( < section > < DropArea { ... getRootProps ( ) } > < input { ... getInputProps ( ) } / > < p > Drop files here , or click to select files < /p> < /DropArea> < /section>)} < /Dropzone> { this . state . error && < Error > { this . state . error } < /Error>} < ButtonContainer > < BorderlessButton size = { buttonSizes . SMALL } onClick = { ( ) => this . setState ( { modalOpen : false } ) } > Close < /BorderlessButton> < /ButtonContainer> < /Modal>); } public render ( ) { return ( < > < InvertedButton size = { buttonSizes . SMALL } onClick = { ( ) => this . setState ( { modalOpen : true } ) } > { " " } { this . props . buttonText || "Add Image" } { " " } < /InvertedButton> { this . renderModal ( ) } < />); } private createDataUrlFromFile = ( acceptedFiles ) => { const reader = new FileReader ( ) ; reader . onload = ( event ) => { const dataUri = event . target ! . result ; if ( ! dataUri || ! dataUri . length ) { this . setState ( { error : "Invalid image, please try another file." } ) ; return ; } const mimeMatch = dataUri . match ( /data:([^;]*);/ ) ; const mimeType = mimeMatch && mimeMatch [ 1 ] ; if ( this . props . validMimeTypes . indexOf ( mimeType ) === - 1 ) { this . setState ( { error : `Invalid file type "${ mimeType }", please try another file. Valid file types: ${ this . props . validMimeTypes . join ( ", " ) }.` , } ) ; return ; } const fileSize = dataUri . length / ( 4 / 3 ) ; if ( fileSize > this . props . maxBytes ) { this . setState ( { error : `Uploaded image is ${ Math . round ( fileSize / 1024 ) }KB, which is too large. Max size: ${ this . props . maxSizeText }` , } ) ; return ; } this . props . onChange ( dataUri ) ; this . setState ( { modalOpen : false , error : undefined } ) ; } ; reader . readAsDataURL ( acceptedFiles [ 0 ] ) ; } ; }