UNPKG

5.96 kBJavaScriptView Raw
1import React, { Component, createRef } from 'react';
2import PropTypes from 'prop-types';
3import UploadCore from '@availity/upload-core';
4import { FormFeedback } from 'reactstrap';
5import Dropzone from 'react-dropzone';
6import map from 'lodash.map';
7import uuid from 'uuid/v4';
8import FilePickerBtn from './FilePickerBtn';
9import FileList from './FileList';
10import './styles.scss';
11
12const validationAttrs = ['min', 'max', 'required'];
13
14class Upload extends Component {
15 constructor(props) {
16 super(props);
17
18 this.state = {
19 files: [],
20 };
21 }
22
23 input = createRef();
24
25 files = [];
26
27 error = null;
28
29 removeFile = fileId => {
30 this.error = null;
31 this.setState(({ files }) => {
32 const newFiles = files.filter(file => file.id !== fileId);
33 if (newFiles.length !== files.length) {
34 this.files = newFiles;
35
36 if (this.props.onFileRemove)
37 this.props.onFileRemove(this.files, fileId);
38 return {
39 files: newFiles,
40 };
41 }
42 return null;
43 });
44 };
45
46 setFiles = files => {
47 let selectedFiles = [];
48 for (let i = 0; i < files.length; i++) {
49 selectedFiles[i] = files[i];
50 }
51 if (
52 this.props.max &&
53 selectedFiles.length + this.state.files.length > this.props.max
54 ) {
55 selectedFiles = selectedFiles.slice(
56 0,
57 Math.max(0, this.props.max - this.state.files.length)
58 );
59 }
60 this.files = this.files.concat(
61 selectedFiles.map(file => {
62 const upload = new UploadCore(file, {
63 bucketId: this.props.bucketId,
64 customerId: this.props.customerId,
65 clientId: this.props.clientId,
66 fileTypes: this.props.allowedFileTypes,
67 maxSize: this.props.maxSize,
68 allowedFileNameCharacters: this.props.allowedFileNameCharacters,
69 });
70 upload.id = `${upload.id}-${uuid()}`;
71 upload.start();
72 if (this.props.onFileUpload) this.props.onFileUpload(upload);
73 return upload;
74 })
75 );
76 this.error = null;
77 this.setState({ files: this.files });
78 };
79
80 handleFileInputChange = event => {
81 this.setFiles(event.target.files);
82 };
83
84 onDrop = (acceptedFiles, rejectedFiles) => {
85 if (rejectedFiles && rejectedFiles.length > 0) {
86 const fileNames = map(rejectedFiles, 'name');
87 this.error = `Could not attach ${fileNames.slice().join(', ')}`;
88 }
89
90 this.setFiles(acceptedFiles);
91 };
92
93 reset = () => {
94 this.files = [];
95 this.setState({ files: [] });
96 this.error = null;
97 };
98
99 componentDidMount() {
100 if (this.context.FormCtrl && this.props.name) {
101 this.updateValidations();
102 }
103 }
104
105 updateValidations(props = this.props) {
106 this.validations = { ...props.validate };
107
108 Object.keys(props)
109 .filter(val => validationAttrs.indexOf(val) > -1)
110 .forEach(attr => {
111 if (props[attr]) {
112 this.validations[attr] = this.validations[attr] || {
113 value: props[attr],
114 };
115 } else {
116 delete this.validations[attr];
117 }
118 });
119
120 this.context.FormCtrl.register(this);
121 this.validate();
122 }
123
124 validate() {
125 if (this.context.FormCtrl && this.props.name) {
126 this.context.FormCtrl.validate(this.props.name);
127 }
128 }
129
130 componentWillUnmount() {
131 if (this.context.FormCtrl && this.props.name)
132 this.context.FormCtrl.unregister(this);
133 }
134
135 getValue() {
136 if (!this.files) return [];
137 return this.files;
138 }
139
140 render() {
141 const {
142 btnText,
143 max,
144 multiple,
145 allowedFileTypes,
146 maxSize,
147 children,
148 showFileDrop,
149 } = this.props;
150 const { files } = this.state;
151
152 let fileAddArea;
153 const text = btnText || (
154 <>
155 <i className="icon icon-plus-circle" title="Add File Icon" />
156 {files.length === 0 ? 'Add File' : 'Add Another File Attachment'}
157 </>
158 );
159
160 if (!max || files.length < max) {
161 if (showFileDrop) {
162 fileAddArea = (
163 <div>
164 <Dropzone
165 onDrop={this.onDrop}
166 multiple={multiple}
167 maxSize={maxSize}
168 className="file-drop"
169 activeClassName="file-drop-active"
170 >
171 {({ getRootProps, getInputProps }) => (
172 <section>
173 <div {...getRootProps()}>
174 <input data-testid="file-picker" {...getInputProps()} />
175 <p>
176 <strong>Drag and Drop</strong>
177 </p>
178 {text}
179 </div>
180 </section>
181 )}
182 </Dropzone>
183 <FormFeedback valid={!this.error} className="d-block">
184 {this.error}
185 </FormFeedback>
186 </div>
187 );
188 } else {
189 fileAddArea = (
190 <FilePickerBtn
191 data-testid="file-picker"
192 onChange={this.handleFileInputChange}
193 color={files.length === 0 ? 'light' : 'link'}
194 multiple={multiple}
195 allowedFileTypes={allowedFileTypes}
196 maxSize={maxSize}
197 >
198 {text}
199 </FilePickerBtn>
200 );
201 }
202 }
203
204 return (
205 <>
206 <FileList files={files} onRemoveFile={this.removeFile}>
207 {children}
208 </FileList>
209 {fileAddArea}
210 </>
211 );
212 }
213}
214
215Upload.propTypes = {
216 btnText: PropTypes.node,
217 bucketId: PropTypes.string.isRequired,
218 customerId: PropTypes.string.isRequired,
219 clientId: PropTypes.string.isRequired,
220 allowedFileNameCharacters: PropTypes.string,
221 allowedFileTypes: PropTypes.arrayOf(PropTypes.string),
222 onFileUpload: PropTypes.func,
223 onFileRemove: PropTypes.func,
224 maxSize: PropTypes.number,
225 max: PropTypes.number,
226 multiple: PropTypes.bool,
227 children: PropTypes.func,
228 name: PropTypes.string,
229 showFileDrop: PropTypes.bool,
230};
231
232Upload.defaultProps = {
233 multiple: true,
234 showFileDrop: false,
235};
236
237export default Upload;