@plusscommunities/pluss-core-web 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/index.cjs.js +7349 -1360
  2. package/dist/index.esm.js +7341 -1358
  3. package/dist/index.umd.js +7346 -1364
  4. package/package.json +5 -3
  5. package/src/actions/AuthActions.js +83 -0
  6. package/src/actions/UsersActions.js +65 -0
  7. package/src/actions/index.js +2 -2
  8. package/src/actions/types.js +9 -9
  9. package/src/analytics.js +73 -0
  10. package/src/apis/analyticsActions.js +49 -0
  11. package/src/apis/authActions.js +58 -0
  12. package/src/apis/fileActions.js +92 -94
  13. package/src/apis/index.js +7 -0
  14. package/src/apis/profileActions.js +133 -0
  15. package/src/apis/stringActions.js +25 -0
  16. package/src/apis/typeActions.js +186 -0
  17. package/src/apis/userActions.js +128 -0
  18. package/src/apis/utilityActions.js +35 -0
  19. package/src/colours.js +16 -16
  20. package/src/components/AnalyticsFilter.js +110 -0
  21. package/src/components/AudienceIncluder.js +174 -0
  22. package/src/components/AudienceSelector.js +549 -0
  23. package/src/components/CheckBox.js +77 -0
  24. package/src/components/DatePicker.js +268 -0
  25. package/src/components/DropdownInput.js +223 -0
  26. package/src/components/FileInput.js +314 -0
  27. package/src/components/ImageInput.js +971 -0
  28. package/src/components/MakerPopup.js +300 -0
  29. package/src/components/OptionsSection.js +64 -0
  30. package/src/components/P60Icon.js +40 -0
  31. package/src/components/ProfilePic.js +35 -0
  32. package/src/components/Reactions.js +77 -0
  33. package/src/components/Tag.js +62 -0
  34. package/src/components/TextFormatPopup.js +54 -0
  35. package/src/components/TimePicker.js +205 -0
  36. package/src/components/UserListing.js +64 -0
  37. package/src/components/index.js +23 -7
  38. package/src/components/svg-icons.json +6 -0
  39. package/src/config.js +10 -0
  40. package/src/helper/HelpDeskWidget.js +52 -0
  41. package/src/helper/api/getUrl.js +15 -0
  42. package/src/helper/api/getUrlParams.js +9 -0
  43. package/src/helper/api/safeReadParams.js +6 -0
  44. package/src/helper/colours.js/getAppColourFromState.js +10 -0
  45. package/src/helper/files/canvasImageUploader.js +159 -0
  46. package/src/helper/files/get1400.js +28 -0
  47. package/src/helper/files/getExtension.js +9 -0
  48. package/src/helper/files/getFileName.js +13 -0
  49. package/src/helper/files/getThumb300.js +32 -0
  50. package/src/helper/files/isVideo.js +8 -0
  51. package/src/{helper.js → helper/helper.js} +19 -130
  52. package/src/helper/index.js +29 -0
  53. package/src/helper/site/getSiteName.js +16 -0
  54. package/src/helper/site/getSiteNameFromRoles.js +12 -0
  55. package/src/helper/storage/readJSONFromStorage.js +9 -0
  56. package/src/helper/storage/setLocalStorage.js +5 -0
  57. package/src/helper/strings/isEmail.js +11 -0
  58. package/src/helper/strings/onlyAlphanumeric.js +8 -0
  59. package/src/helper/strings/randomString.js +10 -0
  60. package/src/helper/strings/toParagraphed.js +17 -0
  61. package/src/index.js +2 -1
  62. package/src/session.js +107 -107
@@ -0,0 +1,314 @@
1
+ import React, { Component } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import _ from 'lodash';
4
+ import Dropzone from 'react-dropzone';
5
+ import FontAwesome from 'react-fontawesome';
6
+ import { Button } from './';
7
+ import { fileActions } from '../apis';
8
+ import { getFileName } from '../helper';
9
+
10
+ const DEFAULT_INPUT = {
11
+ uploadingFile: false,
12
+ value: '',
13
+ };
14
+
15
+ class FileInputComponent extends Component {
16
+ state = {
17
+ inputs: [
18
+ {
19
+ uploadingFile: false,
20
+ value: '',
21
+ },
22
+ ],
23
+ };
24
+
25
+ componentDidMount() {
26
+ setTimeout(() => {
27
+ if (this.props.hasDefault) {
28
+ this.setState({
29
+ inputs: [
30
+ {
31
+ uploadingFile: false,
32
+ value: this.props.hasDefault,
33
+ },
34
+ ],
35
+ });
36
+ if (this.props.refreshCallback) {
37
+ this.props.refreshCallback(this.props.hasDefault);
38
+ }
39
+ }
40
+ }, 100);
41
+ }
42
+
43
+ setValue(val) {
44
+ if (typeof val === 'string') {
45
+ this.setState({
46
+ inputs: [
47
+ {
48
+ uploadingImage: false,
49
+ value: val,
50
+ },
51
+ ],
52
+ });
53
+ } else {
54
+ const inputs = [];
55
+ _.forEach(val, (str) => {
56
+ inputs.push({
57
+ uploadingImage: false,
58
+ value: str,
59
+ });
60
+ });
61
+ if (!this.props.limit || this.props.limit > inputs.length) inputs.push(DEFAULT_INPUT);
62
+ this.setState({ inputs });
63
+ }
64
+ }
65
+
66
+ getClassNames(input, inputsLength) {
67
+ let classes = 'clearfix imageInput';
68
+ if (input.uploadingFile) {
69
+ classes += ' imageInput-uploading';
70
+ } else if (!_.isEmpty(input.value) && !this.props.onlyAllowUpload) {
71
+ classes += ' imageInput-hasImage';
72
+ }
73
+ if (inputsLength === 1) {
74
+ classes += ' imageInput-only';
75
+ }
76
+ if (this.props.style && this.props.style.height && this.props.style.height < 150) {
77
+ classes += ' imageInput-condensed';
78
+ }
79
+ return classes;
80
+ }
81
+
82
+ getErrorState() {
83
+ if (!_.isUndefined(this.props.showError) && this.props.showError()) {
84
+ return ' imageInput_upload--error';
85
+ }
86
+ return '';
87
+ }
88
+
89
+ checkRefreshCallback = (inputs) => {
90
+ if (_.isUndefined(this.props.refreshCallback)) {
91
+ return;
92
+ }
93
+ if (!this.props.multiple) {
94
+ const file = _.find(inputs, (i) => {
95
+ return !i.uploadingFile && !_.isEmpty(i.value);
96
+ });
97
+ this.props.refreshCallback(file ? file.value : undefined);
98
+ } else {
99
+ const files = _.filter(inputs, (i) => {
100
+ return !i.uploadingFile && !_.isEmpty(i.value);
101
+ }).map((i) => {
102
+ return i.value;
103
+ });
104
+ this.props.refreshCallback(files);
105
+ }
106
+ };
107
+
108
+ checkSetDisplayValue(input, value) {
109
+ input.value = value;
110
+ input.uploadingFile = false;
111
+ const newInputs = _.clone(this.state.inputs);
112
+ this.setState({ inputs: newInputs });
113
+ this.checkRefreshCallback(newInputs);
114
+ }
115
+
116
+ onDrop = (files) => {
117
+ if (this.props.multiple) {
118
+ const newInputs = _.clone(this.state.inputs);
119
+ files.forEach((file) => {
120
+ if (this.props.limit && newInputs.length === this.props.limit) {
121
+ // hit limit - replace last input if it's empty
122
+ if (_.isEmpty(newInputs[newInputs.length - 1].value)) {
123
+ const newInput = newInputs[newInputs.length - 1];
124
+ this.handleFile(newInput, file);
125
+ }
126
+ } else {
127
+ const newInput = {
128
+ ...DEFAULT_INPUT,
129
+ };
130
+ newInputs.splice(newInputs.length - 1, 0, newInput);
131
+ this.handleFile(newInput, file);
132
+ }
133
+ });
134
+ this.setState({
135
+ inputs: newInputs,
136
+ });
137
+ this.checkRefreshCallback(newInputs);
138
+ } else {
139
+ this.handleFile(this.state.inputs[0], files[0]);
140
+ }
141
+ };
142
+
143
+ handleFile = async (input, file) => {
144
+ if (!file || input.uploadingFile) {
145
+ return;
146
+ }
147
+ input.uploadingFile = true;
148
+ const newInputs = _.clone(this.state.inputs);
149
+ this.setState({
150
+ inputs: newInputs,
151
+ });
152
+ this.checkRefreshCallback(newInputs);
153
+ try {
154
+ let uploadFile = file;
155
+ const url = await fileActions.uploadMediaAsync(uploadFile, uploadFile.name);
156
+ this.checkSetDisplayValue(input, url);
157
+ } catch (error) {
158
+ console.log('handleFile error', error);
159
+ this.setState({ uploadingFile: false });
160
+ }
161
+ };
162
+
163
+ removeFile(input) {
164
+ const newState = {};
165
+
166
+ input.value = '';
167
+
168
+ const newInputs = _.clone(this.state.inputs);
169
+
170
+ if (newInputs.length > 1) {
171
+ newInputs.splice(newInputs.indexOf(input), 1);
172
+ if (newInputs.length === this.props.limit - 1 && !_.isEmpty(newInputs[newInputs.length - 1].value)) {
173
+ // was full
174
+ newInputs.push(DEFAULT_INPUT);
175
+ }
176
+ }
177
+
178
+ newState.inputs = newInputs;
179
+
180
+ this.setState(newState);
181
+ // for single item
182
+ if (!_.isUndefined(this.props.refreshCallback)) {
183
+ this.props.refreshCallback();
184
+ }
185
+ }
186
+
187
+ renderInput(input, index, inputsLength) {
188
+ const renderContent = () => {
189
+ if (this.props.onlyAllowUpload) {
190
+ return null;
191
+ }
192
+ if (!_.isEmpty(input.value) && !input.uploadingFile) {
193
+ return (
194
+ <div className="imageInput_simpleContainer">
195
+ <div
196
+ className="imageInput_image"
197
+ style={{
198
+ ...this.props.style,
199
+ height: this.props.style && this.props.style.height ? this.props.style.height - 62 : 80,
200
+ }}
201
+ >
202
+ <img
203
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
204
+ className="imageInput_icon"
205
+ alt="file"
206
+ />
207
+ <p className="imageInput_text">{getFileName(input.value)}</p>
208
+ </div>
209
+ <Dropzone accept={this.props.accept} onDrop={(files) => this.onDrop(files, true)} style={{ padding: 10 }}>
210
+ <Button buttonType="secondary" className="imageInput_button" disabled>
211
+ Change file
212
+ </Button>
213
+ </Dropzone>
214
+ </div>
215
+ );
216
+ }
217
+ return <div className="imageInput_image" style={{ ...this.props.style }}></div>;
218
+ };
219
+
220
+ const renderRemove = () => {
221
+ return (
222
+ !this.props.disableRemove &&
223
+ !this.props.disabled &&
224
+ (this.props.simpleStyle ? (
225
+ <FontAwesome name="remove" className="imageInput_removeIcon" onClick={this.removeFile.bind(this, input)} />
226
+ ) : (
227
+ <p className="imageInput_remove" onClick={this.removeFile.bind(this, input)}>
228
+ remove
229
+ </p>
230
+ ))
231
+ );
232
+ };
233
+
234
+ const renderDownload = () => {
235
+ if (this.props.noDownload) return null;
236
+ return (
237
+ <a href={input.value} target="_blank" className="imageInput_download">
238
+ <img
239
+ alt=""
240
+ className="dlicon"
241
+ src="https://pluss60-dev-uploads.s3.ap-southeast-2.amazonaws.com/uploads/users/ap-southeast-2:efbab8db-136a-446e-b14a-d00af0067841/public/025971e94153af280049fdf3e3/downloadicon.png"
242
+ ></img>
243
+ </a>
244
+ );
245
+ };
246
+
247
+ return (
248
+ <div key={index} className={this.getClassNames(input, inputsLength)} style={{ ...this.props.style }}>
249
+ <Dropzone
250
+ className={`imageInput_upload ${this.getErrorState()}`}
251
+ accept={this.props.accept}
252
+ onDrop={this.onDrop}
253
+ style={{ ...this.props.style }}
254
+ >
255
+ {({ isDragActive, isDragReject }) => {
256
+ return (
257
+ <div className={`${isDragActive ? 'imageInput_dropZoneActive' : ''}`}>
258
+ {!this.props.simpleStyle && (
259
+ <img
260
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
261
+ className="imageInput_icon"
262
+ alt="file"
263
+ />
264
+ )}
265
+ <p className="imageInput_helpText">{isDragActive ? `Drop file here` : `Drag and drop file or`}</p>
266
+ {!isDragActive && (
267
+ <Button buttonType="secondary" className="imageInput_button" disabled>
268
+ {this.props.multiple ? `Upload files` : `Upload a file`}
269
+ </Button>
270
+ )}
271
+ </div>
272
+ );
273
+ }}
274
+ </Dropzone>
275
+ <div className="imageInput_uploading" style={{ ...this.props.style }}>
276
+ <FontAwesome className="spinner imageInput_spinner" name="spinner fa-pulse fa-fw" />
277
+ </div>
278
+ {renderContent()}
279
+ {renderRemove()}
280
+ {renderDownload()}
281
+ </div>
282
+ );
283
+ }
284
+
285
+ renderUploadFiles(inputs) {
286
+ return (
287
+ <div
288
+ className={`imageInputContainer clearfix ${inputs.length === 1 ? 'imageInputContainer-noPadding' : ''} ${
289
+ this.props.simpleStyle ? 'imageInputContainer-simple' : ''
290
+ }`}
291
+ style={{ ...this.props.style }}
292
+ >
293
+ {inputs.map((input, index) => {
294
+ return this.renderInput(input, index, inputs.length);
295
+ })}
296
+ </div>
297
+ );
298
+ }
299
+
300
+ renderContent() {
301
+ return this.renderUploadFiles(this.state.inputs);
302
+ }
303
+
304
+ render() {
305
+ return <div style={this.props.containerStyle}>{this.renderContent()}</div>;
306
+ }
307
+ }
308
+
309
+ const mapStateToProps = () => {
310
+ return {};
311
+ };
312
+
313
+ const FileInput = connect(mapStateToProps, {}, null, { withRef: true })(FileInputComponent);
314
+ export { FileInput };
@@ -0,0 +1,971 @@
1
+ import React, { Component } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import _ from 'lodash';
4
+ import $ from 'jquery';
5
+ import moment from 'moment';
6
+ import Dropzone from 'react-dropzone';
7
+ import FontAwesome from 'react-fontawesome';
8
+ import { Button, GenericInput } from './';
9
+ import { get1400, getThumb300, isVideo } from '../helper';
10
+ import { fileActions } from '../apis';
11
+ import { validateAccess, getApiError } from '../session';
12
+ import { Popup } from './Popup';
13
+
14
+ const DEFAULT_INPUT = {
15
+ uploadingImage: false,
16
+ value: '',
17
+ displayValue: '',
18
+ };
19
+
20
+ class ImageInputComponent extends Component {
21
+ state = {
22
+ selectedTab: 'upload',
23
+ inputs: [
24
+ {
25
+ uploadingImage: false,
26
+ value: '',
27
+ displayValue: '',
28
+ },
29
+ ],
30
+ libraryLoadTriggered: false,
31
+ libraryLoaded: false,
32
+ imageLibrary: [],
33
+ loadingFolders: false,
34
+ addingFolder: false,
35
+ deletingFolder: {},
36
+ deletingImage: {},
37
+ folders: [],
38
+ addFolderOpen: false,
39
+ addFolderInput: '',
40
+ selectedFolder: null,
41
+ saveErrorMessage: '',
42
+ };
43
+
44
+ UNSAFE_componentWillMount() {
45
+ setTimeout(() => {
46
+ if (this.props.hasDefault) {
47
+ this.setState({
48
+ inputs: [
49
+ {
50
+ uploadingImage: false,
51
+ value: this.props.hasDefault,
52
+ displayValue: this.props.hasDefault,
53
+ },
54
+ ],
55
+ });
56
+ if (this.props.refreshCallback) {
57
+ this.props.refreshCallback(this.props.hasDefault);
58
+ }
59
+ }
60
+ }, 100);
61
+ }
62
+
63
+ componentDidMount() {
64
+ this.getFolders();
65
+ this.getLibrary();
66
+ }
67
+
68
+ getUploading(index) {
69
+ return this.state.inputs[index].uploadingImage;
70
+ }
71
+
72
+ getValue() {
73
+ if (this.props.multiple) {
74
+ return _.filter(
75
+ this.state.inputs.map((input) => {
76
+ return input.value;
77
+ }),
78
+ (url) => {
79
+ return !_.isEmpty(url);
80
+ },
81
+ );
82
+ }
83
+ return this.state.inputs[0].value;
84
+ }
85
+
86
+ setValue(val) {
87
+ if (typeof val === 'string') {
88
+ this.setState({
89
+ inputs: [
90
+ {
91
+ uploadingImage: false,
92
+ value: val,
93
+ displayValue: val,
94
+ },
95
+ ],
96
+ });
97
+ } else {
98
+ const inputs = [];
99
+ _.forEach(val, (str) => {
100
+ inputs.push({
101
+ uploadingImage: false,
102
+ value: str,
103
+ displayValue: str,
104
+ });
105
+ });
106
+ if (!this.props.limit || this.props.limit > inputs.length) inputs.push(DEFAULT_INPUT);
107
+ this.setState({ inputs });
108
+ }
109
+ }
110
+
111
+ getImageStyle(displayValue) {
112
+ if (_.isEmpty(displayValue)) {
113
+ return null;
114
+ }
115
+ const image = isVideo(displayValue) ? get1400(displayValue) : displayValue;
116
+ if (this.props.noCompress) {
117
+ return { backgroundImage: `url(${image})`, backgroundSize: 'contain' };
118
+ }
119
+ return { backgroundImage: `url(${image})` };
120
+ }
121
+
122
+ getContainerClasses() {
123
+ let result = this.props.className || '';
124
+ if (this.props.noMenu) {
125
+ result += ' imageInputOuter-noMenu';
126
+ }
127
+ if (!this.props.multiple) {
128
+ result += ' imageInputOuter-single';
129
+ }
130
+ if (this.props.grid) {
131
+ result += ' imageInputOuter-grid';
132
+ }
133
+ return result;
134
+ }
135
+
136
+ getClassNames(input, inputsLength) {
137
+ let classes = 'imageInput';
138
+ if (input.uploadingImage) {
139
+ classes += ' imageInput-uploading';
140
+ } else if (!_.isEmpty(input.value) && !this.props.onlyAllowUpload) {
141
+ classes += ' imageInput-hasImage';
142
+ }
143
+ if (inputsLength === 1) {
144
+ classes += ' imageInput-only';
145
+ }
146
+ return classes;
147
+ }
148
+
149
+ getErrorState() {
150
+ if (!_.isUndefined(this.props.showError) && this.props.showError()) {
151
+ return ' imageInput_upload--error';
152
+ }
153
+ return '';
154
+ }
155
+
156
+ refreshFolders = async () => {
157
+ const res = await fileActions.getMediaFolders(this.props.auth.site);
158
+ return res.data;
159
+ };
160
+
161
+ getFolders() {
162
+ this.setState({ loadingFolders: true }, async () => {
163
+ try {
164
+ const folders = await this.refreshFolders();
165
+ this.setState({ loadingFolders: false, folders });
166
+ } catch (error) {
167
+ console.log('getFolders - error', error);
168
+ this.setState({ loadingFolders: false });
169
+ }
170
+ });
171
+ }
172
+
173
+ checkRefreshCallback = (inputs) => {
174
+ if (_.isUndefined(this.props.refreshCallback)) {
175
+ return;
176
+ }
177
+ if (!this.props.multiple) {
178
+ const image = _.find(inputs, (i) => {
179
+ return !i.uploadingImage && !_.isEmpty(i.value);
180
+ });
181
+ this.props.refreshCallback(image ? image.value : undefined);
182
+ } else {
183
+ const images = _.filter(inputs, (i) => {
184
+ return !i.uploadingImage && !_.isEmpty(i.value);
185
+ }).map((i) => {
186
+ return i.value;
187
+ });
188
+ this.props.refreshCallback(images);
189
+ }
190
+ };
191
+
192
+ parseImagesArray = (images) => {
193
+ return _.map(images, (f) => {
194
+ const uri = typeof f === 'string' ? f : f.uri;
195
+ const image1400 = get1400(uri);
196
+ return {
197
+ Value: uri,
198
+ Thumb: getThumb300(uri),
199
+ 1400: image1400,
200
+ Selected: _.some(this.state.inputs, (input) => input.value === uri || input.value === image1400),
201
+ };
202
+ });
203
+ };
204
+
205
+ getLibrary() {
206
+ if (this.state.libraryLoadTriggered) return;
207
+
208
+ this.setState({ libraryLoadTriggered: true }, async () => {
209
+ try {
210
+ const res = await fileActions.getMediaLibrary();
211
+ // console.log('getLibrary', res.data);
212
+ this.setState({
213
+ libraryLoaded: true,
214
+ imageLibrary: this.parseImagesArray(res.data),
215
+ });
216
+ } catch (error) {
217
+ console.log('getLibrary - error', error);
218
+ }
219
+ });
220
+ }
221
+
222
+ toggleLibraryItem(image) {
223
+ const newInputs = _.clone(this.state.inputs);
224
+ if (image.Selected) {
225
+ // Unselelct selected image
226
+ const index = _.findIndex(newInputs, (input) => {
227
+ return input.value === image.Value || input.value === image[1400];
228
+ });
229
+ if (index !== -1) {
230
+ newInputs.splice(index, 1);
231
+ }
232
+ image.Selected = false;
233
+ } else {
234
+ // Select unselected image
235
+ const newFile = {
236
+ uploadingImage: false,
237
+ value: image.Value,
238
+ displayValue: image[1400],
239
+ fromLibrary: true,
240
+ };
241
+ if (!this.props.multiple) {
242
+ newInputs[newInputs.length - 1] = newFile;
243
+ // Single image selector -> Unselect others
244
+ this.state.imageLibrary.map((i) => (i.Selected = false));
245
+ if (this.state.selectedFolder && this.state.selectedFolder.parsedImages)
246
+ this.state.selectedFolder.parsedImages.map((i) => (i.Selected = false));
247
+ image.Selected = true;
248
+ } else if (!(this.props.limit && newInputs.length === this.props.limit)) {
249
+ newInputs.splice(newInputs.length - 1, 0, newFile);
250
+ image.Selected = true;
251
+ }
252
+ }
253
+
254
+ if (_.isEmpty(newInputs)) newInputs.push(DEFAULT_INPUT);
255
+ this.setState({
256
+ inputs: newInputs,
257
+ imageLibrary: _.clone(this.state.imageLibrary),
258
+ selectedFolder: this.state.selectedFolder ? _.clone(this.state.selectedFolder) : undefined,
259
+ });
260
+ this.checkRefreshCallback(newInputs);
261
+ }
262
+
263
+ addFolder = () => {
264
+ this.setState({ addFolderOpen: true }, () => {
265
+ const input = document.getElementById('addFolderInput');
266
+ if (input) input.focus();
267
+ });
268
+ };
269
+
270
+ cancelAddStates = { addFolderOpen: false, addFolderInput: '', saveErrorMessage: '' };
271
+ cancelAddFolder = () => {
272
+ this.setState(this.cancelAddStates);
273
+ };
274
+
275
+ saveFolder = () => {
276
+ this.setState({ addingFolder: true }, async () => {
277
+ try {
278
+ await fileActions.addMediaFolder(this.props.auth.site, this.state.addFolderInput);
279
+ const folders = await this.refreshFolders();
280
+ this.setState({
281
+ addingFolder: false,
282
+ folders,
283
+ ...this.cancelAddStates,
284
+ });
285
+ } catch (error) {
286
+ console.log('addFolder - error', error);
287
+ this.setState({ addingFolder: false, saveErrorMessage: getApiError(error).message });
288
+ }
289
+ });
290
+ };
291
+
292
+ isStockFolder = (folder) => {
293
+ if (!folder) folder = this.state.selectedFolder;
294
+ return folder && _.isNil(folder.RowId);
295
+ };
296
+
297
+ selectFolder = (selectedFolder) => {
298
+ if (!this.isStockFolder(selectedFolder)) {
299
+ selectedFolder.parsedImages = this.parseImagesArray(selectedFolder.Images);
300
+ }
301
+ this.setState({ selectedFolder });
302
+ };
303
+
304
+ selectRoot = () => {
305
+ this.getFolders();
306
+ this.setState({ selectedFolder: null });
307
+ };
308
+
309
+ checkSetDisplayValue(input, value) {
310
+ const displayValue = get1400(value);
311
+ const storeValue = isVideo(value) || this.props.noCompress ? value : get1400(value);
312
+ $('<img/>')
313
+ .attr('src', `${displayValue}?t=${moment().valueOf()}`)
314
+ .on('load', () => {
315
+ $(this).remove();
316
+ input.value = storeValue;
317
+ input.displayValue = storeValue;
318
+ input.uploadingImage = false;
319
+ const newInputs = _.clone(this.state.inputs);
320
+ this.setState({ inputs: newInputs }, () => {
321
+ const { selectedTab, selectedFolder } = this.state;
322
+ if (selectedTab === 'library' && selectedFolder) {
323
+ // Uploading to a user folder
324
+ const { RowId, Images } = selectedFolder;
325
+ const images = [...Images, value];
326
+ fileActions.addImagesToFolder(RowId, this.props.auth.site, [value]).catch((error) => {
327
+ console.log('addImagesToFolder error', error);
328
+ });
329
+ selectedFolder.Images = images;
330
+ selectedFolder.parsedImages = this.parseImagesArray(images);
331
+ this.setState({ selectedFolder: _.clone(selectedFolder) });
332
+ }
333
+ });
334
+ this.checkRefreshCallback(newInputs);
335
+ })
336
+ .on('error', () => {
337
+ $(this).remove();
338
+ setTimeout(() => {
339
+ this.checkSetDisplayValue(input, value);
340
+ }, 200);
341
+ });
342
+ }
343
+
344
+ onDrop = (files, fromLibrary = false) => {
345
+ if (this.props.multiple) {
346
+ const newInputs = _.clone(this.state.inputs);
347
+ files.forEach((file) => {
348
+ if (this.props.limit && newInputs.length === this.props.limit) {
349
+ // hit limit - replace last input if it's empty
350
+ if (_.isEmpty(newInputs[newInputs.length - 1].value)) {
351
+ const newInput = newInputs[newInputs.length - 1];
352
+ this.handleFile(newInput, file);
353
+ }
354
+ } else {
355
+ const newInput = {
356
+ ...DEFAULT_INPUT,
357
+ fromLibrary,
358
+ };
359
+ newInputs.splice(newInputs.length - 1, 0, newInput);
360
+ this.handleFile(newInput, file);
361
+ }
362
+ });
363
+ this.setState({
364
+ inputs: newInputs,
365
+ });
366
+ this.checkRefreshCallback(newInputs);
367
+ } else {
368
+ this.handleFile(this.state.inputs[0], files[0]);
369
+ }
370
+ };
371
+
372
+ handleFile = async (input, file) => {
373
+ if (!file || input.uploadingImage) {
374
+ return;
375
+ }
376
+ input.uploadingImage = true;
377
+ const newInputs = _.clone(this.state.inputs);
378
+ this.setState({
379
+ inputs: newInputs,
380
+ });
381
+ this.checkRefreshCallback(newInputs);
382
+ try {
383
+ let uploadFile = file;
384
+ if (!isVideo(file.name)) {
385
+ uploadFile = await fileActions.compressImage(
386
+ file,
387
+ this.props.maxSize || 1400,
388
+ this.props.quality || 0.8,
389
+ this.props.noCompress || false,
390
+ );
391
+ }
392
+ const url = await fileActions.uploadMediaAsync(uploadFile, uploadFile.name);
393
+ this.checkSetDisplayValue(input, url);
394
+ } catch (error) {
395
+ console.log('handleFile error', error);
396
+ this.setState({ uploadingImage: false });
397
+ }
398
+ };
399
+
400
+ removeImage(input) {
401
+ const newState = {};
402
+ if (input.fromLibrary) {
403
+ input.fromLibrary = undefined;
404
+ const selectedFolderImages = this.state.selectedFolder ? this.state.selectedFolder.parsedImages : [];
405
+ const imageList = this.isStockFolder() ? this.state.imageLibrary : selectedFolderImages;
406
+ const libraryFile = _.find(imageList, (f) => {
407
+ return f.Value === input.value || f[1400] === input.value;
408
+ });
409
+ if (libraryFile) {
410
+ libraryFile.Selected = false;
411
+ if (this.isStockFolder()) {
412
+ newState.imageLibrary = _.clone(this.state.imageLibrary);
413
+ } else {
414
+ newState.selectedFolder = this.state.selectedFolder ? _.clone(this.state.selectedFolder) : undefined;
415
+ }
416
+ }
417
+ }
418
+
419
+ input.value = '';
420
+ input.displayValue = '';
421
+
422
+ const newInputs = _.clone(this.state.inputs);
423
+
424
+ if (newInputs.length > 1) {
425
+ newInputs.splice(newInputs.indexOf(input), 1);
426
+ if (newInputs.length === this.props.limit - 1 && !_.isEmpty(newInputs[newInputs.length - 1].value)) {
427
+ // was full
428
+ newInputs.push(DEFAULT_INPUT);
429
+ }
430
+ }
431
+
432
+ newState.inputs = newInputs;
433
+
434
+ this.setState(newState);
435
+ // for single item
436
+ if (!_.isUndefined(this.props.refreshCallback)) {
437
+ this.props.refreshCallback();
438
+ }
439
+ }
440
+
441
+ selectTab(tab) {
442
+ this.setState({ selectedTab: tab });
443
+ }
444
+
445
+ handleChange = (event) => {
446
+ var stateChange = {};
447
+ stateChange[event.target.getAttribute('id')] = event.target.value;
448
+ this.setState(stateChange);
449
+ };
450
+
451
+ deleteFolder = (event, folder) => {
452
+ event.stopPropagation();
453
+
454
+ if (window.confirm(`Are you sure you want to delete ${folder.Name}?`)) {
455
+ const deletingFolder = { ...this.state.deletingFolder };
456
+ deletingFolder[folder.RowId] = true;
457
+ this.setState({ deletingFolder: _.clone(deletingFolder), ...this.cancelAddStates }, async () => {
458
+ try {
459
+ await fileActions.deleteMediaFolder(folder.RowId, this.props.auth.site);
460
+ const folders = await this.refreshFolders();
461
+ deletingFolder[folder.RowId] = false;
462
+ this.setState({
463
+ deletingFolder: _.clone(deletingFolder),
464
+ folders,
465
+ });
466
+ } catch (error) {
467
+ console.log('addFolder - error', getApiError(error));
468
+ deletingFolder[folder.RowId] = false;
469
+ this.setState({ deletingFolder: _.clone(deletingFolder) });
470
+ }
471
+ });
472
+ }
473
+ };
474
+
475
+ deleteImage = (event, image) => {
476
+ if (window.confirm('Are you sure you want to delete the image?')) {
477
+ // Unselect the image first if selected
478
+ if (image.Selected) this.toggleLibraryItem(image);
479
+
480
+ const deletingImage = { ...this.state.deletingImage };
481
+ deletingImage[image.Value] = true;
482
+ this.setState({ deletingImage: _.clone(deletingImage) }, async () => {
483
+ try {
484
+ const { selectedFolder } = this.state;
485
+ const { RowId, Images } = selectedFolder;
486
+ await fileActions.deleteImagesFromFolder(RowId, this.props.auth.site, [image.Value]);
487
+ const images = Images.filter((i) => i.uri !== image.Value);
488
+ selectedFolder.Images = images;
489
+ selectedFolder.parsedImages = this.parseImagesArray(images);
490
+ deletingImage[image.Value] = false;
491
+ this.setState({ deletingImage: _.clone(deletingImage), selectedFolder: _.clone(selectedFolder) });
492
+ } catch (error) {
493
+ console.log('deleteImage - error', getApiError(error));
494
+ deletingImage[image.Value] = false;
495
+ this.setState({ deletingImage: _.clone(deletingImage) });
496
+ }
497
+ });
498
+ }
499
+ };
500
+
501
+ canManageLibrary = () => {
502
+ return validateAccess(this.props.auth.site, 'manageImageLibrary', this.props.auth);
503
+ };
504
+
505
+ canAddImageToLibrary = () => {
506
+ return validateAccess(this.props.auth.site, 'addToImageLibrary', this.props.auth);
507
+ };
508
+
509
+ renderInput(input, index, inputsLength) {
510
+ const renderContent = () => {
511
+ if (this.props.onlyAllowUpload) {
512
+ return null;
513
+ }
514
+ if (isVideo(input.displayValue)) {
515
+ return <video className="imageInput_image" style={this.props.style} controls src={input.displayValue} />;
516
+ }
517
+ if (this.props.simpleStyle && !_.isEmpty(input.displayValue) && !input.uploadingImage) {
518
+ return (
519
+ <div className="imageInput_simpleContainer">
520
+ <div
521
+ className="imageInput_image"
522
+ style={{
523
+ ...this.getImageStyle(input.displayValue),
524
+ ...this.props.style,
525
+ // height: this.props.style && this.props.style.height ? this.props.style.height : 80,
526
+ }}
527
+ >
528
+ <Dropzone
529
+ accept={`image/jpeg, image/png${this.props.allowVideo ? ', video/*' : ''}`}
530
+ onDrop={(files) => this.onDrop(files, true)}
531
+ className="imageInput_buttonContainer-simple"
532
+ >
533
+ <Button buttonType="secondary" className="imageInput_button imageInput_button-simple" disabled>
534
+ Change image
535
+ </Button>
536
+ </Dropzone>
537
+ </div>
538
+ </div>
539
+ );
540
+ }
541
+ return <div className="imageInput_image" style={{ ...this.getImageStyle(input.displayValue), ...this.props.style }}></div>;
542
+ };
543
+
544
+ const renderRemove = () => {
545
+ return (
546
+ !this.props.disableRemove &&
547
+ !this.props.disabled &&
548
+ (this.props.simpleStyle ? (
549
+ <FontAwesome name="remove" className="imageInput_removeIcon" onClick={this.removeImage.bind(this, input)} />
550
+ ) : (
551
+ <p className="imageInput_remove" onClick={this.removeImage.bind(this, input)}>
552
+ remove
553
+ </p>
554
+ ))
555
+ );
556
+ };
557
+
558
+ const renderDownload = () => {
559
+ if (isVideo(input.displayValue)) return null;
560
+ if (this.props.noDownload) return null;
561
+ return (
562
+ <a href={input.value} target="_blank" className="imageInput_download">
563
+ <img
564
+ alt=""
565
+ className="dlicon"
566
+ src="https://pluss60-dev-uploads.s3.ap-southeast-2.amazonaws.com/uploads/users/ap-southeast-2:efbab8db-136a-446e-b14a-d00af0067841/public/025971e94153af280049fdf3e3/downloadicon.png"
567
+ ></img>
568
+ </a>
569
+ );
570
+ };
571
+
572
+ const mediaText = this.props.allowVideo ? 'media' : `image${this.props.multiple ? 's' : ''}`;
573
+ return (
574
+ <div key={index} className={this.getClassNames(input, inputsLength)} style={{ ...this.props.style }}>
575
+ <Dropzone
576
+ className={`imageInput_upload ${this.getErrorState()}`}
577
+ accept={`image/jpeg, image/png${this.props.allowVideo ? ', video/*' : ''}`}
578
+ onDrop={this.onDrop}
579
+ style={{ ...this.props.style }}
580
+ >
581
+ {({ isDragActive, isDragReject }) => {
582
+ return (
583
+ <div
584
+ className={`${isDragActive ? 'imageInput_dropZoneActive' : ''} ${
585
+ this.props.horizontalText ? ' imageInput_horizontalText' : ''
586
+ }`}
587
+ >
588
+ {!this.props.simpleStyle && (
589
+ <img
590
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
591
+ className="imageInput_icon"
592
+ alt="file"
593
+ />
594
+ )}
595
+ <p className="imageInput_helpText">{isDragActive ? `Drop ${mediaText} here` : `Drag and drop ${mediaText} or`}</p>
596
+ {!isDragActive && (
597
+ <Button buttonType="secondary" className="imageInput_button" disabled>
598
+ {this.props.multiple ? `Upload ${mediaText}` : `Upload an ${mediaText}`}
599
+ </Button>
600
+ )}
601
+ </div>
602
+ );
603
+ }}
604
+ </Dropzone>
605
+ <div className="imageInput_uploading" style={{ ...this.props.style }}>
606
+ <FontAwesome className="spinner imageInput_spinner" name="spinner fa-pulse fa-fw" />
607
+ </div>
608
+ {renderContent()}
609
+ {renderRemove()}
610
+ {renderDownload()}
611
+ </div>
612
+ );
613
+ }
614
+
615
+ renderLibraryImage(image, index, allowDelete) {
616
+ let classes = 'imageInput imageInput-hasImage imageInput-libraryImage';
617
+ if (image.Selected) {
618
+ classes += ' imageInput-librarySelected';
619
+ }
620
+ if (this.props.multiple && this.props.limit && this.state.inputs.length === this.props.limit) {
621
+ classes += ' imageInput-libraryDisabled';
622
+ }
623
+ return (
624
+ <div key={index} className={classes}>
625
+ <div
626
+ className="imageInput_image"
627
+ style={{ ...this.getImageStyle(image[1400]) }}
628
+ onClick={this.toggleLibraryItem.bind(this, image)}
629
+ ></div>
630
+ <div className="imageInput_selected">
631
+ <FontAwesome className="imageInput_selectedIcon" name={'check'} />
632
+ </div>
633
+ {allowDelete && (
634
+ <div className="imageInput_delete">
635
+ {this.state.deletingImage[image.Value] ? (
636
+ <FontAwesome style={styles.saveImageSpinner} name="spinner fa-pulse fa-fw" />
637
+ ) : (
638
+ <FontAwesome className="imageInput_deleteIcon" name="minus-circle" onClick={(event) => this.deleteImage(event, image)} />
639
+ )}
640
+ </div>
641
+ )}
642
+ </div>
643
+ );
644
+ }
645
+
646
+ renderError() {
647
+ if (!_.isUndefined(this.props.showError) && this.props.showError()) {
648
+ return <div className={'fieldLabel fieldLabel-warning'}>{this.props.errorMessage ? this.props.errorMessage : 'Required'}</div>;
649
+ }
650
+ return null;
651
+ }
652
+
653
+ renderMenu() {
654
+ if (this.props.noMenu || this.props.disabled) {
655
+ return null;
656
+ }
657
+ return (
658
+ <div className="imageInputMenu">
659
+ <p
660
+ className={`imageInputMenu__option ${this.state.selectedTab === 'upload' ? 'imageInputMenu__option--selected' : ''}`}
661
+ onClick={() => {
662
+ this.selectTab('upload');
663
+ }}
664
+ >
665
+ UPLOAD
666
+ </p>
667
+ <p
668
+ className={`imageInputMenu__option ${this.state.selectedTab === 'library' ? 'imageInputMenu__option--selected' : ''}`}
669
+ onClick={() => {
670
+ this.selectTab('library');
671
+ }}
672
+ >
673
+ LIBRARY
674
+ </p>
675
+ </div>
676
+ );
677
+ }
678
+
679
+ renderAddFolder() {
680
+ const isSaving = this.state.addingFolder;
681
+
682
+ if (this.state.addFolderOpen) {
683
+ return (
684
+ <div className={'imageFolderContainer'}>
685
+ <FontAwesome name={'folder'} className={'folderIcon'} />
686
+ <GenericInput
687
+ id="addFolderInput"
688
+ type="text"
689
+ placeholder="Enter folder name here"
690
+ value={this.state.addFolderInput}
691
+ onChange={this.handleChange}
692
+ style={styles.addFolderInputContainer}
693
+ inputStyle={styles.addFolderInputText}
694
+ readOnly={isSaving}
695
+ showError={() => !_.isEmpty(this.state.saveErrorMessage)}
696
+ errorMessage={this.state.saveErrorMessage}
697
+ />
698
+ {isSaving ? (
699
+ <div className={'addControlContainer'}>
700
+ <FontAwesome style={styles.saveFolderSpinner} name="spinner fa-pulse fa-fw" />
701
+ </div>
702
+ ) : (
703
+ <div className={'addControlContainer'}>
704
+ <Button inline buttonType="secondary" onClick={this.cancelAddFolder} style={styles.addFolderCancel}>
705
+ Cancel
706
+ </Button>
707
+ <Button
708
+ inline
709
+ buttonType="primary"
710
+ isActive={!_.isEmpty(this.state.addFolderInput)}
711
+ onClick={this.saveFolder}
712
+ style={styles.addFolderSave}
713
+ >
714
+ Save
715
+ </Button>
716
+ </div>
717
+ )}
718
+ </div>
719
+ );
720
+ }
721
+ return (
722
+ <div className={'imageFolderContainer'} onClick={this.addFolder}>
723
+ <FontAwesome name={'plus'} className={'addIcon'} />
724
+ <div className={'addText'}>Add New Folder</div>
725
+ </div>
726
+ );
727
+ }
728
+
729
+ renderFolder(folder, index) {
730
+ const timeStamp = moment(folder.Timestamp).format('DD MMM, YYYY, h:mma');
731
+ const countText = `${folder.Images ? folder.Images.length : 0} images`;
732
+
733
+ return (
734
+ <div key={index} className={'imageFolderContainer'} onClick={this.selectFolder.bind(this, folder)}>
735
+ <FontAwesome name={'folder'} className={'folderIcon'} />
736
+ <div className={'folderTitle'}>{folder.Name}</div>
737
+ {!this.isStockFolder(folder) && (
738
+ <div className={'folderAttributeGroup'}>
739
+ <div className={'folderAttribute'}>{folder.User && folder.User.displayName}</div>
740
+ <div className={'folderAttribute'} style={{ width: '130px' }}>
741
+ {timeStamp}
742
+ </div>
743
+ <div className={'folderAttribute'} style={{ textAlign: 'right', paddingRight: '5px' }}>
744
+ {countText}
745
+ </div>
746
+ {this.state.deletingFolder[folder.RowId] ? (
747
+ <FontAwesome style={styles.saveFolderSpinner} name="spinner fa-pulse fa-fw" />
748
+ ) : (
749
+ this.canManageLibrary() && (
750
+ <a onClick={(event) => this.deleteFolder(event, folder)}>
751
+ <FontAwesome style={styles.removeFolderButton} name="minus-circle" />
752
+ </a>
753
+ )
754
+ )}
755
+ </div>
756
+ )}
757
+ </div>
758
+ );
759
+ }
760
+
761
+ renderFolders() {
762
+ return (
763
+ <div className={`imageInputContainer imageInputContainer-library`}>
764
+ {this.canAddImageToLibrary() && this.renderAddFolder()}
765
+ {this.state.folders.map((folder, index) => {
766
+ return this.renderFolder(folder, index);
767
+ })}
768
+ </div>
769
+ );
770
+ }
771
+
772
+ renderBreadCrumbs() {
773
+ return (
774
+ <div className={'imageBreadCrumb'}>
775
+ <div className={'root'} onClick={this.selectRoot}>
776
+ Library
777
+ </div>
778
+ <FontAwesome name={'angle-left'} className={'separator'} />
779
+ <div className={'folder'}>{this.state.selectedFolder.Name}</div>
780
+ </div>
781
+ );
782
+ }
783
+
784
+ renderFolderImages() {
785
+ const stockFolder = this.isStockFolder();
786
+ const canDelete = !stockFolder && this.canManageLibrary();
787
+ const uploadingImages = this.state.inputs.filter((i) => i.uploadingImage);
788
+ const mediaText = this.props.allowVideo ? 'media' : `image${this.props.multiple ? 's' : ''}`;
789
+
790
+ return (
791
+ <div className={`imageInputContainer imageInputContainer-library`} style={{ ...this.props.style }}>
792
+ {this.renderBreadCrumbs()}
793
+ {stockFolder ? (
794
+ this.state.imageLibrary.map((image, index) => {
795
+ return this.renderLibraryImage(image, index);
796
+ })
797
+ ) : (
798
+ <div>
799
+ {this.canAddImageToLibrary() && (
800
+ <Dropzone
801
+ className={`imageInput_upload folder ${this.getErrorState()}`}
802
+ accept={`image/jpeg, image/png${this.props.allowVideo ? ', video/*' : ''}`}
803
+ onDrop={(files) => this.onDrop(files, true)}
804
+ style={{ padding: '15px' }}
805
+ >
806
+ {({ isDragActive, isDragReject }) => {
807
+ return (
808
+ <div className={isDragActive ? 'imageInput_dropZoneActive' : ''}>
809
+ <img
810
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
811
+ className="imageInput_icon"
812
+ alt="file"
813
+ />
814
+ <p className="imageInput_helpText">{isDragActive ? `Drop ${mediaText} here` : `Drag and drop ${mediaText} or`}</p>
815
+ {!isDragActive && (
816
+ <Button buttonType="secondary" className="imageInput_button" disabled>
817
+ {this.props.multiple ? `Upload ${mediaText}` : `Upload an ${mediaText}`}
818
+ </Button>
819
+ )}
820
+ </div>
821
+ );
822
+ }}
823
+ </Dropzone>
824
+ )}
825
+ {this.state.selectedFolder.parsedImages.map((image, index) => {
826
+ return this.renderLibraryImage(image, index, canDelete);
827
+ })}
828
+ {!_.isNil(uploadingImages) &&
829
+ uploadingImages.map((input, index) => {
830
+ return (
831
+ <div key={index} className={this.getClassNames(input)}>
832
+ <div className="imageInput_uploading">
833
+ <FontAwesome className="spinner imageInput_spinner" name="spinner fa-pulse fa-fw" />
834
+ </div>
835
+ </div>
836
+ );
837
+ })}
838
+ </div>
839
+ )}
840
+ </div>
841
+ );
842
+ }
843
+
844
+ renderUploadImages(inputs) {
845
+ return (
846
+ <div className={`imageInputContainer ${inputs.length === 1 ? 'imageInputContainer-noPadding' : ''}`} style={{ ...this.props.style }}>
847
+ {inputs.map((input, index) => {
848
+ return this.renderInput(input, index, inputs.length);
849
+ })}
850
+ </div>
851
+ );
852
+ }
853
+
854
+ renderContent() {
855
+ return this.renderUploadImages(this.state.inputs);
856
+ }
857
+
858
+ renderLibraryPopup() {
859
+ if (this.state.selectedTab === 'upload') {
860
+ return null;
861
+ }
862
+ let content = null;
863
+ if (this.state.selectedFolder) {
864
+ content = this.renderFolderImages();
865
+ } else {
866
+ content = this.renderFolders();
867
+ }
868
+ return (
869
+ <Popup
870
+ onClose={() => {
871
+ this.selectTab('upload');
872
+ }}
873
+ hasPadding
874
+ buttons={[
875
+ {
876
+ type: 'primary',
877
+ onClick: () => {
878
+ this.selectTab('upload');
879
+ },
880
+ isActive: true,
881
+ text: 'Done',
882
+ },
883
+ ]}
884
+ title="Select images"
885
+ >
886
+ {content}
887
+ </Popup>
888
+ );
889
+ }
890
+
891
+ renderButtons() {
892
+ if (this.props.noMenu || this.props.disabled) {
893
+ return null;
894
+ }
895
+ return (
896
+ <div className="imageInputRight">
897
+ <Dropzone
898
+ accept={`image/jpeg, image/png${this.props.allowVideo ? ', video/*' : ''}`}
899
+ onDrop={(files) => this.onDrop(files, true)}
900
+ className="imageInputRight_button"
901
+ >
902
+ <FontAwesome className="imageInputRight_button_icon" name="plus-circle" />
903
+ <p className="imageInputRight_button_text">Upload File</p>
904
+ </Dropzone>
905
+ <div
906
+ className="imageInputRight_button"
907
+ onClick={() => {
908
+ this.selectTab('library');
909
+ }}
910
+ >
911
+ <FontAwesome className="imageInputRight_button_icon" name="picture-o" />
912
+ <p className="imageInputRight_button_text">Image Library</p>
913
+ </div>
914
+ </div>
915
+ );
916
+ }
917
+
918
+ render() {
919
+ return (
920
+ <div style={this.props.containerStyle} className={`imageInputOuter ${this.getContainerClasses()}`}>
921
+ {this.renderButtons()}
922
+ {this.renderContent()}
923
+ {this.renderLibraryPopup()}
924
+ </div>
925
+ );
926
+ }
927
+ }
928
+
929
+ const styles = {
930
+ addFolderInputContainer: {
931
+ marginBottom: '0px',
932
+ paddingRight: '100px',
933
+ width: '70%',
934
+ },
935
+ addFolderInputText: {
936
+ paddingBottom: '0px',
937
+ },
938
+ addFolderCancel: {
939
+ paddingLeft: '8px',
940
+ paddingRight: '8px',
941
+ width: '80px',
942
+ marginRight: '16px',
943
+ },
944
+ addFolderSave: {
945
+ paddingLeft: '8px',
946
+ paddingRight: '8px',
947
+ width: '80px',
948
+ },
949
+ saveFolderSpinner: {
950
+ fontSize: '20px',
951
+ marginLeft: '16px',
952
+ },
953
+ saveImageSpinner: {
954
+ fontSize: '18px',
955
+ },
956
+ removeFolderButton: {
957
+ fontSize: '20px',
958
+ padding: '5px',
959
+ marginLeft: '16px',
960
+ cursor: 'pointer',
961
+ },
962
+ };
963
+
964
+ const mapStateToProps = (state) => {
965
+ return {
966
+ auth: state.auth,
967
+ };
968
+ };
969
+
970
+ const ImageInput = connect(mapStateToProps, {}, null, { withRef: true })(ImageInputComponent);
971
+ export { ImageInput };