Skip to content

Instantly share code, notes, and snippets.

@dmytro-y-dev
Created August 6, 2017 12:41
Show Gist options
  • Save dmytro-y-dev/285fc877e2712dea6aedd9a9d4184e89 to your computer and use it in GitHub Desktop.
Save dmytro-y-dev/285fc877e2712dea6aedd9a9d4184e89 to your computer and use it in GitHub Desktop.
Simple React file input for admin-on-rest that supports uploading of exactly one file and automatically encodes uploads to base64
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Dropzone from 'react-dropzone';
import {translate} from 'admin-on-rest';
import FileInputPreview from "admin-on-rest/lib/mui/input/FileInputPreview";
const defaultStyle = {
dropZone: {
background: '#efefef',
cursor: 'pointer',
padding: '1rem',
textAlign: 'center',
color: '#999',
},
preview: {
float: 'left',
},
};
export class SingleFileInput extends Component {
static propTypes = {
accept: PropTypes.string,
children: PropTypes.element,
disableClick: PropTypes.bool,
elStyle: PropTypes.object,
input: PropTypes.object,
itemStyle: PropTypes.object,
labelSingle: PropTypes.string,
maxSize: PropTypes.number,
minSize: PropTypes.number,
removeStyle: PropTypes.object,
style: PropTypes.object,
placeholder: PropTypes.node,
};
static defaultProps = {
addLabel: true,
addField: true,
itemStyle: {},
labelSingle: 'aor.input.file.upload_single',
onUpload: () => {},
removeStyle: { display: 'inline-block' },
};
constructor(props) {
super(props);
let file = props.input.value;
this.state = {
file: this.transformFile(file),
};
}
componentWillReceiveProps(nextProps) {
let file = nextProps.input.value;
this.setState({ file: this.transformFile(file) });
}
onDrop = async (files) => {
const updatedFile = await this.setUploadedFile(files[0]);
this.setState({ file: updatedFile });
this.props.input.onChange(updatedFile);
}
onRemove = file => () => {
this.setState({ file: null });
this.props.input.onChange(null);
}
setUploadedFile = (file) => new Promise(async (resolve, reject) => {
const convertFileToBase64 = file => new Promise((resolve, reject) => {
if (!(file instanceof File)) {
resolve(undefined);
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
let transformedFile = this.transformFile(file);
transformedFile.content = await convertFileToBase64(file);
resolve(transformedFile);
})
// turn a browser dropped file structure into expected structure
transformFile = (file) => {
if (!(file instanceof File)) {
return file;
}
const { title } = React.Children.toArray(this.props.children)[0].props;
const transformedFile = {
preview: file.preview,
name: file.name,
mimetype: file.type,
size: file.size,
lastModified: file.lastModified
};
if (title) {
transformedFile[title] = file.name;
}
return transformedFile;
}
label() {
const { translate, placeholder, labelSingle } = this.props;
if (placeholder) {
return placeholder;
}
return (
<p>{translate(labelSingle)}</p>
);
}
render() {
const {
accept,
children,
disableClick,
elStyle,
itemStyle,
maxSize,
minSize,
style,
removeStyle,
} = this.props;
const finalStyle = {
...defaultStyle,
...style,
};
return (
<div style={elStyle}>
<Dropzone
onDrop={this.onDrop}
accept={accept}
disableClick={disableClick}
maxSize={maxSize}
minSize={minSize}
multiple={false}
style={finalStyle.dropZone}
>
{this.label()}
</Dropzone>
{ children && this.state.file && (
<div className="previews">
<FileInputPreview
file={this.state.file}
itemStyle={itemStyle}
onRemove={this.onRemove(this.state.file)}
removeStyle={removeStyle}
>
{React.cloneElement(children, {
record: this.state.file,
style: defaultStyle.preview,
})}
</FileInputPreview>
</div>
) }
</div>
);
}
}
export default translate(SingleFileInput);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment