@teselagen/ui 0.4.14 → 0.4.15

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 (119) hide show
  1. package/AdvancedOptions.d.ts +1 -1
  2. package/AssignDefaultsModeContext.d.ts +1 -1
  3. package/AsyncValidateFieldSpinner/index.d.ts +1 -1
  4. package/BlueprintError/index.d.ts +1 -1
  5. package/BounceLoader/index.d.ts +1 -1
  6. package/CollapsibleCard/index.d.ts +1 -1
  7. package/DNALoader/index.d.ts +1 -1
  8. package/DataTable/CellDragHandle.d.ts +1 -1
  9. package/DataTable/ColumnFilterMenu.d.ts +14 -0
  10. package/DataTable/DisabledLoadingComponent.d.ts +1 -1
  11. package/DataTable/DisplayOptions.d.ts +2 -2
  12. package/DataTable/DropdownCell.d.ts +8 -0
  13. package/DataTable/EditabelCell.d.ts +10 -0
  14. package/DataTable/FilterAndSortMenu.d.ts +2 -2
  15. package/DataTable/SearchBar.d.ts +1 -1
  16. package/DataTable/SortableColumns.d.ts +2 -2
  17. package/DataTable/TableFormTrackerContext.d.ts +1 -1
  18. package/DataTable/defaultProps.d.ts +1 -1
  19. package/DataTable/index.d.ts +0 -5
  20. package/DataTable/utils/computePresets.d.ts +1 -1
  21. package/DataTable/utils/formatPasteData.d.ts +5 -0
  22. package/DataTable/utils/getAllRows.d.ts +1 -0
  23. package/DataTable/utils/getCellCopyText.d.ts +1 -0
  24. package/DataTable/utils/getCellInfo.d.ts +17 -0
  25. package/DataTable/utils/getFieldPathToField.d.ts +1 -0
  26. package/DataTable/utils/getIdOrCodeOrIndex.d.ts +1 -2
  27. package/DataTable/utils/getLastSelectedEntity.d.ts +1 -0
  28. package/DataTable/utils/getNewEntToSelect.d.ts +6 -0
  29. package/DataTable/utils/getRowCopyText.d.ts +3 -0
  30. package/DataTable/utils/handleCopyColumn.d.ts +1 -0
  31. package/DataTable/utils/handleCopyHelper.d.ts +1 -0
  32. package/DataTable/utils/handleCopyRows.d.ts +5 -0
  33. package/DataTable/utils/index.d.ts +21 -0
  34. package/DataTable/utils/isBottomRightCornerOfRectangle.d.ts +8 -0
  35. package/DataTable/utils/isEntityClean.d.ts +1 -0
  36. package/DataTable/utils/removeCleanRows.d.ts +4 -0
  37. package/DataTable/utils/rowClick.d.ts +10 -2
  38. package/DataTable/utils/utils.d.ts +5 -0
  39. package/DataTable/viewColumn.d.ts +2 -2
  40. package/DialogFooter/index.d.ts +1 -1
  41. package/DropdownButton.d.ts +1 -1
  42. package/FillWindow.d.ts +1 -1
  43. package/FormComponents/LoadingDots.d.ts +1 -1
  44. package/FormComponents/Uploader.d.ts +29 -1
  45. package/FormComponents/index.d.ts +34 -34
  46. package/FormComponents/itemUpload.d.ts +1 -1
  47. package/HotkeysDialog/index.d.ts +1 -1
  48. package/InfoHelper/index.d.ts +1 -1
  49. package/IntentText/index.d.ts +1 -1
  50. package/MatchHeaders.d.ts +1 -1
  51. package/MenuBar/index.d.ts +4 -4
  52. package/PromptUnsavedChanges/index.d.ts +1 -1
  53. package/README.md +18 -0
  54. package/ResizableDraggableDialog/index.d.ts +2 -2
  55. package/ScrollToTop/index.d.ts +1 -1
  56. package/SimpleStepViz.d.ts +1 -1
  57. package/Tag.d.ts +1 -1
  58. package/TagSelect/index.d.ts +1 -1
  59. package/TgSelect/index.d.ts +2 -2
  60. package/TgSuggest/index.d.ts +3 -3
  61. package/Timeline/TimelineEvent.d.ts +1 -1
  62. package/Timeline/index.d.ts +2 -2
  63. package/UploadCsvWizard.d.ts +1 -1
  64. package/customIcons.d.ts +19 -19
  65. package/enhancers/withField.d.ts +1 -1
  66. package/enhancers/withFields.d.ts +1 -1
  67. package/enhancers/withLocalStorage.d.ts +1 -1
  68. package/index.cjs.js +14026 -12765
  69. package/index.d.ts +60 -60
  70. package/index.es.js +13844 -12583
  71. package/package.json +7 -4
  72. package/showConfirmationDialog/index.d.ts +2 -2
  73. package/src/DataTable/CellDragHandle.js +6 -7
  74. package/src/DataTable/ColumnFilterMenu.js +60 -0
  75. package/src/DataTable/DropdownCell.js +61 -0
  76. package/src/DataTable/EditabelCell.js +55 -0
  77. package/src/DataTable/PagingTool.js +1 -1
  78. package/src/DataTable/SortableColumns.js +53 -18
  79. package/src/DataTable/dataTableEnhancer.js +1 -1
  80. package/src/DataTable/index.js +385 -759
  81. package/src/DataTable/utils/formatPasteData.js +16 -0
  82. package/src/DataTable/utils/getAllRows.js +11 -0
  83. package/src/DataTable/utils/getCellCopyText.js +7 -0
  84. package/src/DataTable/utils/getCellInfo.js +36 -0
  85. package/src/DataTable/utils/getFieldPathToField.js +7 -0
  86. package/src/DataTable/utils/getIdOrCodeOrIndex.js +1 -1
  87. package/src/DataTable/utils/getLastSelectedEntity.js +11 -0
  88. package/src/DataTable/utils/getNewEntToSelect.js +25 -0
  89. package/src/DataTable/utils/getRowCopyText.js +28 -0
  90. package/src/DataTable/utils/handleCopyColumn.js +21 -0
  91. package/src/DataTable/utils/handleCopyHelper.js +15 -0
  92. package/src/DataTable/utils/handleCopyRows.js +23 -0
  93. package/src/DataTable/utils/index.js +51 -0
  94. package/src/DataTable/utils/isBottomRightCornerOfRectangle.js +20 -0
  95. package/src/DataTable/utils/isEntityClean.js +15 -0
  96. package/src/DataTable/utils/removeCleanRows.js +22 -0
  97. package/src/DataTable/utils/rowClick.js +7 -4
  98. package/src/DataTable/utils/selection.js +1 -1
  99. package/src/DataTable/utils/utils.js +37 -0
  100. package/src/DataTable/validateTableWideErrors.js +1 -1
  101. package/src/FillWindow.js +2 -3
  102. package/src/FormComponents/Uploader.js +400 -400
  103. package/src/FormComponents/tryToMatchSchemas.js +0 -6
  104. package/src/UploadCsvWizard.js +312 -371
  105. package/src/index.js +3 -3
  106. package/src/showDialogOnDocBody.js +5 -9
  107. package/src/useDialog.js +7 -4
  108. package/src/utils/renderOnDoc.js +8 -5
  109. package/style.css +7 -7
  110. package/useDialog.d.ts +2 -2
  111. package/utils/adHoc.d.ts +1 -1
  112. package/utils/commandControls.d.ts +5 -5
  113. package/utils/hotkeyUtils.d.ts +1 -1
  114. package/utils/menuUtils.d.ts +7 -7
  115. package/utils/renderOnDoc.d.ts +1 -1
  116. package/utils/tagUtils.d.ts +1 -1
  117. package/utils/tgFormValues.d.ts +1 -1
  118. package/utils/withStore.d.ts +1 -1
  119. package/wrapDialog.d.ts +1 -1
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useMemo, useRef, useState } from "react";
2
2
  import {
3
3
  Button,
4
4
  Callout,
@@ -16,7 +16,6 @@ import classnames from "classnames";
16
16
  import { nanoid } from "nanoid";
17
17
  import papaparse, { unparse } from "papaparse";
18
18
  import downloadjs from "downloadjs";
19
- import { configure, makeObservable, observable } from "mobx";
20
19
  import { observer } from "mobx-react";
21
20
  import UploadCsvWizardDialog, {
22
21
  SimpleInsertDataDialog
@@ -30,7 +29,7 @@ import {
30
29
  removeExt
31
30
  } from "@teselagen/file-utils";
32
31
  import tryToMatchSchemas from "./tryToMatchSchemas";
33
- import { forEach, isArray, isFunction, isPlainObject, noop } from "lodash-es";
32
+ import { isArray, isFunction, isPlainObject, noop } from "lodash-es";
34
33
  import { flatMap } from "lodash-es";
35
34
  import urljoin from "url-join";
36
35
  import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
@@ -38,14 +37,12 @@ import writeXlsxFile from "write-excel-file";
38
37
  import { startCase } from "lodash-es";
39
38
  import { getNewName } from "./getNewName";
40
39
  import { isObject } from "lodash-es";
41
- import { connect } from "react-redux";
40
+ import { useDispatch } from "react-redux";
42
41
  import { initialize } from "redux-form";
43
42
  import classNames from "classnames";
44
- import { compose } from "recompose";
45
43
  import convertSchema from "../DataTable/utils/convertSchema";
46
44
  import { LoadingDots } from "./LoadingDots";
47
45
 
48
- configure({ isolateGlobalState: true });
49
46
  const helperText = [
50
47
  `How to Use This Template to Upload New Data`,
51
48
  `1. Go to the first tab and delete the example data.`,
@@ -64,58 +61,106 @@ const helperSchema = [
64
61
  }
65
62
  ];
66
63
 
67
- class ValidateAgainstSchema {
68
- fields = [];
69
-
70
- constructor() {
71
- makeObservable(this, {
72
- fields: observable.shallow
73
- });
74
- }
75
-
76
- setValidateAgainstSchema(newValidateAgainstSchema) {
77
- if (!newValidateAgainstSchema) {
78
- this.fields = [];
79
- return;
80
- }
81
- const schema = convertSchema(newValidateAgainstSchema);
82
- if (
83
- schema.fields.some(f => {
84
- if (f.path === "id") {
85
- return true;
86
- }
87
- return false;
88
- })
89
- ) {
90
- throw new Error(
91
- `Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
92
- );
93
- }
94
- forEach(schema, (v, k) => {
95
- this[k] = v;
96
- });
64
+ const setValidateAgainstSchema = newValidateAgainstSchema => {
65
+ if (!newValidateAgainstSchema) return { fields: [] };
66
+ const schema = convertSchema(newValidateAgainstSchema);
67
+ if (
68
+ schema.fields.some(f => {
69
+ if (f.path === "id") {
70
+ return true;
71
+ }
72
+ return false;
73
+ })
74
+ ) {
75
+ throw new Error(
76
+ `Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
77
+ );
97
78
  }
98
- }
99
-
100
- // autorun(() => {
101
- // console.log(
102
- // `validateAgainstSchemaStore?.fields:`,
103
- // JSON.stringify(validateAgainstSchemaStore?.fields, null, 4)
104
- // );
105
- // });
106
- // validateAgainstSchemaStore.fields = ["hahah"];
107
- // validateAgainstSchemaStore.fields.push("yaa");
79
+ return schema;
80
+ };
108
81
 
109
- // const validateAgainstSchema = observable.shallow({
110
- // fields: []
111
- // })
112
-
113
- // validateAgainstSchema.fields = ["hahah"];
82
+ const InnerDropZone = ({
83
+ getRootProps,
84
+ getInputProps,
85
+ isDragAccept,
86
+ isDragReject,
87
+ isDragActive,
88
+ className,
89
+ minimal,
90
+ dropzoneDisabled,
91
+ contentOverride,
92
+ simpleAccept,
93
+ innerIcon,
94
+ innerText,
95
+ validateAgainstSchema,
96
+ handleManuallyEnterData,
97
+ noBuildCsvOption,
98
+ showFilesCount,
99
+ fileList
100
+ // isDragActive
101
+ // isDragReject
102
+ // isDragAccept
103
+ }) => (
104
+ <section>
105
+ <div
106
+ {...getRootProps()}
107
+ className={classnames("tg-dropzone", className, {
108
+ "tg-dropzone-minimal": minimal,
109
+ "tg-dropzone-active": isDragActive,
110
+ "tg-dropzone-reject": isDragReject, // tnr: the acceptClassName/rejectClassName doesn't work with file extensions (only mimetypes are supported when dragging). Thus we'll just always turn the drop area blue when dragging and let the filtering occur on drop. See https://github.com/react-dropzone/react-dropzone/issues/888#issuecomment-773938074
111
+ "tg-dropzone-accept": isDragAccept,
112
+ "tg-dropzone-disabled": dropzoneDisabled,
113
+ "bp3-disabled": dropzoneDisabled
114
+ })}
115
+ >
116
+ <input {...getInputProps()} />
117
+ {contentOverride || (
118
+ <div
119
+ title={
120
+ simpleAccept
121
+ ? "Accepts only the following file types: " + simpleAccept
122
+ : "Accepts any file input"
123
+ }
124
+ className="tg-upload-inner"
125
+ >
126
+ {innerIcon || <Icon icon="upload" iconSize={minimal ? 15 : 30} />}
127
+ {innerText || (minimal ? "Upload" : "Click or drag to upload")}
128
+ {validateAgainstSchema && !noBuildCsvOption && (
129
+ <div
130
+ style={{
131
+ textAlign: "center",
132
+ // fontSize: 18,
133
+ marginTop: 7,
134
+ marginBottom: 5
135
+ }}
136
+ onClick={handleManuallyEnterData}
137
+ className="link-button"
138
+ >
139
+ ...or {manualEnterMessage}
140
+ {/* <div
141
+ style={{
142
+ fontSize: 11,
143
+ color: Colors.GRAY3,
144
+ fontStyle: "italic"
145
+ }}
146
+ >
147
+ {manualEnterSubMessage}
148
+ </div> */}
149
+ </div>
150
+ )}
151
+ </div>
152
+ )}
153
+ </div>
114
154
 
115
- // wink wink
116
- const emptyPromise = Promise.resolve.bind(Promise);
155
+ {showFilesCount ? (
156
+ <div className="tg-upload-file-list-counter">
157
+ Files: {fileList ? fileList.length : 0}
158
+ </div>
159
+ ) : null}
160
+ </section>
161
+ );
117
162
 
118
- function UploaderInner({
163
+ const UploaderInner = ({
119
164
  accept: __accept,
120
165
  contentOverride: maybeContentOverride,
121
166
  innerIcon,
@@ -130,7 +175,9 @@ function UploaderInner({
130
175
  showUploadList = true,
131
176
  beforeUpload,
132
177
  fileList, //list of files with options: {name, loading, error, url, originalName, downloadName}
133
- onFileSuccess = emptyPromise, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
178
+ onFileSuccess = async () => {
179
+ return;
180
+ }, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
134
181
  onFieldSubmit = noop, //called when all files have successfully uploaded
135
182
  // fileFinished = noop,
136
183
  onRemove = noop, //called when a file has been selected to be removed
@@ -141,22 +188,27 @@ function UploaderInner({
141
188
  autoUnzip,
142
189
  disabled: _disabled,
143
190
  noBuildCsvOption,
144
- initializeForm,
145
191
  showFilesCount,
146
192
  threeDotMenuItems,
147
193
  onPreviewClick
148
- }) {
194
+ }) => {
195
+ const dispatch = useDispatch();
149
196
  let dropzoneDisabled = _disabled;
150
197
  let _accept = __accept;
151
- const validateAgainstSchemaStore = useRef(new ValidateAgainstSchema());
152
198
  const [acceptLoading, setAcceptLoading] = useState();
153
199
  const [resolvedAccept, setResolvedAccept] = useState();
200
+
154
201
  if (resolvedAccept) {
155
202
  _accept = resolvedAccept;
156
203
  }
157
- const isAcceptPromise =
158
- __accept?.then ||
159
- (Array.isArray(__accept) ? __accept.some(a => a?.then) : false);
204
+
205
+ const isAcceptPromise = useMemo(
206
+ () =>
207
+ __accept?.then ||
208
+ (Array.isArray(__accept) ? __accept.some(a => a?.then) : false),
209
+ [__accept]
210
+ );
211
+
160
212
  useEffect(() => {
161
213
  if (isAcceptPromise) {
162
214
  setAcceptLoading(true);
@@ -169,35 +221,36 @@ function UploaderInner({
169
221
  );
170
222
  }
171
223
  }, [__accept, isAcceptPromise]);
224
+
172
225
  if (isAcceptPromise && !resolvedAccept) {
173
226
  _accept = [];
174
227
  }
228
+
175
229
  if (acceptLoading) dropzoneDisabled = true;
176
- const accept = !_accept
177
- ? undefined
178
- : isAcceptPromise && !resolvedAccept
179
- ? []
180
- : isPlainObject(_accept)
181
- ? [_accept]
182
- : isArray(_accept)
183
- ? _accept
184
- : _accept.split(",").map(a => ({ type: a }));
185
- const callout = _callout || accept?.find?.(a => a?.callout)?.callout;
230
+ const accept = useMemo(
231
+ () =>
232
+ !_accept
233
+ ? undefined
234
+ : isAcceptPromise && !resolvedAccept
235
+ ? []
236
+ : isPlainObject(_accept)
237
+ ? [_accept]
238
+ : isArray(_accept)
239
+ ? _accept
240
+ : _accept.split(",").map(a => ({ type: a })),
241
+ [_accept, isAcceptPromise, resolvedAccept]
242
+ );
186
243
 
187
- const validateAgainstSchemaToUse =
188
- _validateAgainstSchema ||
189
- accept?.find?.(a => a?.validateAgainstSchema)?.validateAgainstSchema;
244
+ const callout = _callout || accept?.find?.(a => a?.callout)?.callout;
190
245
 
191
- useEffect(() => {
192
- // validateAgainstSchema
193
- validateAgainstSchemaStore.current.setValidateAgainstSchema(
194
- validateAgainstSchemaToUse
195
- );
196
- }, [validateAgainstSchemaToUse]);
197
- let validateAgainstSchema;
198
- if (validateAgainstSchemaToUse) {
199
- validateAgainstSchema = validateAgainstSchemaStore.current;
200
- }
246
+ const validateAgainstSchema = useMemo(
247
+ () =>
248
+ setValidateAgainstSchema(
249
+ _validateAgainstSchema ||
250
+ accept?.find?.(a => a?.validateAgainstSchema)?.validateAgainstSchema
251
+ ),
252
+ [_validateAgainstSchema, accept]
253
+ );
201
254
 
202
255
  if (
203
256
  (validateAgainstSchema || autoUnzip) &&
@@ -215,6 +268,7 @@ function UploaderInner({
215
268
  const { showDialogPromise: showUploadCsvWizardDialog, comp } = useDialog({
216
269
  ModalComponent: UploadCsvWizardDialog
217
270
  });
271
+
218
272
  const { showDialogPromise: showSimpleInsertDataDialog, comp: comp2 } =
219
273
  useDialog({
220
274
  ModalComponent: SimpleInsertDataDialog
@@ -553,7 +607,7 @@ function UploaderInner({
553
607
  }
554
608
  {...getFileDownloadAttr(exampleFile)}
555
609
  key={i}
556
- ></MenuItem>
610
+ />
557
611
  );
558
612
  }
559
613
  )}
@@ -619,7 +673,7 @@ function UploaderInner({
619
673
  }}
620
674
  size={10}
621
675
  icon="download"
622
- ></Icon>
676
+ />
623
677
  )}
624
678
  </CustomTag>
625
679
  </PopOrTooltip>
@@ -631,7 +685,8 @@ function UploaderInner({
631
685
  // make the dots below "load"
632
686
 
633
687
  <>
634
- Accept Loading<LoadingDots></LoadingDots>
688
+ Accept Loading
689
+ <LoadingDots />
635
690
  </>
636
691
  ) : (
637
692
  <>Accepts {simpleAccept}</>
@@ -650,135 +705,135 @@ function UploaderInner({
650
705
  .join(", ")
651
706
  : undefined
652
707
  }
653
- {...{
654
- onDrop: async (_acceptedFiles, rejectedFiles) => {
655
- let acceptedFiles = [];
656
- for (const file of _acceptedFiles) {
657
- if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
658
- const files = await filterFilesInZip(
659
- file,
660
- simpleAccept
661
- ?.split(", ")
662
- ?.map(a => (a.startsWith(".") ? a : "." + a)) || []
663
- );
664
- acceptedFiles.push(...files.map(f => f.originFileObj));
665
- } else {
666
- acceptedFiles.push(file);
667
- }
668
- }
669
- cleanupFiles();
670
- if (rejectedFiles.length) {
671
- let msg = "";
672
- rejectedFiles.forEach(file => {
673
- if (msg) msg += "\n";
674
- msg +=
675
- `${file.file.name}: ` +
676
- file.errors.map(err => err.message).join(", ");
677
- });
678
- window.toastr &&
679
- window.toastr.warning(
680
- <div className="preserve-newline">{msg}</div>
681
- );
708
+ onDrop={async (_acceptedFiles, rejectedFiles) => {
709
+ let acceptedFiles = [];
710
+ for (const file of _acceptedFiles) {
711
+ if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
712
+ const files = await filterFilesInZip(
713
+ file,
714
+ simpleAccept
715
+ ?.split(", ")
716
+ ?.map(a => (a.startsWith(".") ? a : "." + a)) || []
717
+ );
718
+ acceptedFiles.push(...files.map(f => f.originFileObj));
719
+ } else {
720
+ acceptedFiles.push(file);
682
721
  }
683
- if (!acceptedFiles.length) return;
684
- setLoading(true);
685
- acceptedFiles = trimFiles(acceptedFiles, fileLimit);
686
-
687
- acceptedFiles.forEach(file => {
688
- file.preview = URL.createObjectURL(file);
689
- file.loading = true;
690
- if (!file.id) {
691
- file.id = nanoid();
692
- }
693
- filesToClean.current.push(file);
722
+ }
723
+ cleanupFiles();
724
+ if (rejectedFiles.length) {
725
+ let msg = "";
726
+ rejectedFiles.forEach(file => {
727
+ if (msg) msg += "\n";
728
+ msg +=
729
+ `${file.file.name}: ` +
730
+ file.errors.map(err => err.message).join(", ");
694
731
  });
695
-
696
- if (readBeforeUpload) {
697
- acceptedFiles = await Promise.all(
698
- acceptedFiles.map(file => {
699
- return new Promise((resolve, reject) => {
700
- const reader = new FileReader();
701
- reader.readAsText(file, "UTF-8");
702
- reader.onload = evt => {
703
- file.parsedString = evt.target.result;
704
- resolve(file);
705
- };
706
- reader.onerror = err => {
707
- console.error("err:", err);
708
- reject(err);
709
- };
710
- });
711
- })
732
+ window.toastr &&
733
+ window.toastr.warning(
734
+ <div className="preserve-newline">{msg}</div>
712
735
  );
736
+ }
737
+ if (!acceptedFiles.length) return;
738
+ setLoading(true);
739
+ acceptedFiles = trimFiles(acceptedFiles, fileLimit);
740
+
741
+ acceptedFiles.forEach(file => {
742
+ file.preview = URL.createObjectURL(file);
743
+ file.loading = true;
744
+ if (!file.id) {
745
+ file.id = nanoid();
713
746
  }
714
- const cleanedAccepted = acceptedFiles.map(file => {
715
- return {
716
- originFileObj: file,
717
- originalFileObj: file,
718
- id: file.id,
719
- lastModified: file.lastModified,
720
- lastModifiedDate: file.lastModifiedDate,
721
- loading: file.loading,
722
- name: file.name,
723
- preview: file.preview,
724
- size: file.size,
725
- type: file.type,
726
- ...(file.parsedString
727
- ? { parsedString: file.parsedString }
728
- : {})
729
- };
730
- });
747
+ filesToClean.current.push(file);
748
+ });
731
749
 
732
- const toKeep = [];
733
- if (validateAgainstSchema) {
734
- const filesWIssues = [];
735
- const filesWOIssues = [];
736
- for (const [i, file] of cleanedAccepted.entries()) {
737
- if (isCsvOrExcelFile(file)) {
738
- let parsedF;
739
- try {
740
- parsedF = await parseCsvOrExcelFile(file, {
741
- csvParserOptions: isFunction(
742
- validateAgainstSchema.csvParserOptions
743
- )
744
- ? validateAgainstSchema.csvParserOptions({
745
- validateAgainstSchema
746
- })
747
- : validateAgainstSchema.csvParserOptions
748
- });
749
- } catch (error) {
750
- console.error("error:", error);
751
- window.toastr &&
752
- window.toastr.error(
753
- `There was an error parsing your file. Please try again. ${
754
- error.message || error
755
- }`
756
- );
757
- return;
758
- }
750
+ if (readBeforeUpload) {
751
+ acceptedFiles = await Promise.all(
752
+ acceptedFiles.map(file => {
753
+ return new Promise((resolve, reject) => {
754
+ const reader = new FileReader();
755
+ reader.readAsText(file, "UTF-8");
756
+ reader.onload = evt => {
757
+ file.parsedString = evt.target.result;
758
+ resolve(file);
759
+ };
760
+ reader.onerror = err => {
761
+ console.error("err:", err);
762
+ reject(err);
763
+ };
764
+ });
765
+ })
766
+ );
767
+ }
768
+ const cleanedAccepted = acceptedFiles.map(file => {
769
+ return {
770
+ originFileObj: file,
771
+ originalFileObj: file,
772
+ id: file.id,
773
+ lastModified: file.lastModified,
774
+ lastModifiedDate: file.lastModifiedDate,
775
+ loading: file.loading,
776
+ name: file.name,
777
+ preview: file.preview,
778
+ size: file.size,
779
+ type: file.type,
780
+ ...(file.parsedString
781
+ ? { parsedString: file.parsedString }
782
+ : {})
783
+ };
784
+ });
759
785
 
760
- const {
761
- csvValidationIssue: _csvValidationIssue,
762
- matchedHeaders,
763
- userSchema,
764
- searchResults,
765
- ignoredHeadersMsg
766
- } = await tryToMatchSchemas({
767
- incomingData: parsedF.data,
768
- validateAgainstSchema
786
+ const toKeep = [];
787
+ if (validateAgainstSchema) {
788
+ const filesWIssues = [];
789
+ const filesWOIssues = [];
790
+ for (const [i, file] of cleanedAccepted.entries()) {
791
+ if (isCsvOrExcelFile(file)) {
792
+ let parsedF;
793
+ try {
794
+ parsedF = await parseCsvOrExcelFile(file, {
795
+ csvParserOptions: isFunction(
796
+ validateAgainstSchema.csvParserOptions
797
+ )
798
+ ? validateAgainstSchema.csvParserOptions({
799
+ validateAgainstSchema
800
+ })
801
+ : validateAgainstSchema.csvParserOptions
769
802
  });
770
- if (userSchema?.userData?.length === 0) {
771
- console.error(
772
- `userSchema, parsedF.data:`,
773
- userSchema,
774
- parsedF.data
803
+ } catch (error) {
804
+ console.error("error:", error);
805
+ window.toastr &&
806
+ window.toastr.error(
807
+ `There was an error parsing your file. Please try again. ${
808
+ error.message || error
809
+ }`
775
810
  );
776
- } else {
777
- toKeep.push(file);
778
- let csvValidationIssue = _csvValidationIssue;
779
- if (csvValidationIssue) {
780
- if (isObject(csvValidationIssue)) {
781
- initializeForm(
811
+ return;
812
+ }
813
+
814
+ const {
815
+ csvValidationIssue: _csvValidationIssue,
816
+ matchedHeaders,
817
+ userSchema,
818
+ searchResults,
819
+ ignoredHeadersMsg
820
+ } = await tryToMatchSchemas({
821
+ incomingData: parsedF.data,
822
+ validateAgainstSchema
823
+ });
824
+ if (userSchema?.userData?.length === 0) {
825
+ console.error(
826
+ `userSchema, parsedF.data:`,
827
+ userSchema,
828
+ parsedF.data
829
+ );
830
+ } else {
831
+ toKeep.push(file);
832
+ let csvValidationIssue = _csvValidationIssue;
833
+ if (csvValidationIssue) {
834
+ if (isObject(csvValidationIssue)) {
835
+ dispatch(
836
+ initialize(
782
837
  `editableCellTable${
783
838
  cleanedAccepted.length > 1 ? `-${i}` : ""
784
839
  }`,
@@ -790,149 +845,142 @@ function UploaderInner({
790
845
  keepValues: true,
791
846
  updateUnregisteredFields: true
792
847
  }
848
+ )
849
+ );
850
+ const err = Object.values(csvValidationIssue)[0];
851
+ // csvValidationIssue = `It looks like there was an error with your data - \n\n${
852
+ // err && err.message ? err.message : err
853
+ // }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
854
+ const errMsg = err && err.message ? err.message : err;
855
+ if (isPlainObject(errMsg)) {
856
+ throw new Error(
857
+ `errMsg is an object ${JSON.stringify(
858
+ errMsg,
859
+ null,
860
+ 4
861
+ )}`
793
862
  );
794
- const err = Object.values(csvValidationIssue)[0];
795
- // csvValidationIssue = `It looks like there was an error with your data - \n\n${
796
- // err && err.message ? err.message : err
797
- // }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
798
- const errMsg =
799
- err && err.message ? err.message : err;
800
- if (isPlainObject(errMsg)) {
801
- throw new Error(
802
- `errMsg is an object ${JSON.stringify(
803
- errMsg,
804
- null,
805
- 4
806
- )}`
807
- );
808
- }
809
- csvValidationIssue = (
863
+ }
864
+ csvValidationIssue = (
865
+ <div>
810
866
  <div>
811
- <div>
812
- It looks like there was an error with your
813
- data (Correct on the Review Data page):
814
- </div>
815
- <div style={{ color: "red" }}>{errMsg}</div>
816
- <div>
817
- Please review your headers and then correct
818
- any errors on the next page.
819
- </div>
867
+ It looks like there was an error with your data
868
+ (Correct on the Review Data page):
820
869
  </div>
821
- );
822
- }
823
- filesWIssues.push({
824
- file,
825
- csvValidationIssue,
826
- ignoredHeadersMsg,
827
- matchedHeaders,
828
- userSchema,
829
- searchResults
830
- });
831
- } else {
832
- filesWOIssues.push({
833
- file,
834
- csvValidationIssue,
835
- ignoredHeadersMsg,
836
- matchedHeaders,
837
- userSchema,
838
- searchResults
839
- });
840
- const newFileName = removeExt(file.name) + `.csv`;
841
-
842
- const { newFile, cleanedEntities } = getNewCsvFile(
843
- userSchema.userData,
844
- newFileName
870
+ <div style={{ color: "red" }}>{errMsg}</div>
871
+ <div>
872
+ Please review your headers and then correct any
873
+ errors on the next page.
874
+ </div>
875
+ </div>
845
876
  );
846
-
847
- file.meta = parsedF.meta;
848
- file.hasEditClick = true;
849
- file.parsedData = cleanedEntities;
850
- file.name = newFileName;
851
- file.originFileObj = newFile;
852
- file.originalFileObj = newFile;
853
877
  }
854
- }
855
- } else {
856
- toKeep.push(file);
857
- }
858
- }
859
- if (filesWIssues.length) {
860
- const { file } = filesWIssues[0];
861
- const allFiles = [...filesWIssues, ...filesWOIssues];
862
- const doAllFilesHaveSameHeaders = allFiles.every(f => {
863
- if (f.userSchema.fields && f.userSchema.fields.length) {
864
- return f.userSchema.fields.every((h, i) => {
865
- return (
866
- h.path === allFiles[0].userSchema.fields[i].path
867
- );
878
+ filesWIssues.push({
879
+ file,
880
+ csvValidationIssue,
881
+ ignoredHeadersMsg,
882
+ matchedHeaders,
883
+ userSchema,
884
+ searchResults
868
885
  });
869
- }
870
- return false;
871
- });
872
- const multipleFiles = allFiles.length > 1;
873
- const { res } = await showUploadCsvWizardDialog(
874
- "onUploadWizardFinish",
875
- {
876
- dialogProps: {
877
- title: `Fix Up File${multipleFiles ? "s" : ""} ${
878
- multipleFiles
879
- ? ""
880
- : file.name
881
- ? `"${file.name}"`
882
- : ""
883
- }`
884
- },
885
- doAllFilesHaveSameHeaders,
886
- filesWIssues: allFiles,
887
- validateAgainstSchema
888
- }
889
- );
886
+ } else {
887
+ filesWOIssues.push({
888
+ file,
889
+ csvValidationIssue,
890
+ ignoredHeadersMsg,
891
+ matchedHeaders,
892
+ userSchema,
893
+ searchResults
894
+ });
895
+ const newFileName = removeExt(file.name) + `.csv`;
890
896
 
891
- if (!res) {
892
- window.toastr.warning(`File Upload Aborted`);
893
- return;
894
- } else {
895
- allFiles.forEach(({ file }, i) => {
896
- const newEntities = res[i];
897
- // const newFileName = removeExt(file.name) + `_updated.csv`;
898
- //swap out file with a new csv file
899
897
  const { newFile, cleanedEntities } = getNewCsvFile(
900
- newEntities,
901
- file.name
898
+ userSchema.userData,
899
+ newFileName
902
900
  );
903
901
 
902
+ file.meta = parsedF.meta;
904
903
  file.hasEditClick = true;
905
904
  file.parsedData = cleanedEntities;
906
- // file.name = newFileName;
905
+ file.name = newFileName;
907
906
  file.originFileObj = newFile;
908
907
  file.originalFileObj = newFile;
909
- });
910
- setTimeout(() => {
911
- //inside a timeout for cypress purposes
912
- window.toastr.success(
913
- `Added Fixed Up File${
914
- allFiles.length > 1 ? "s" : ""
915
- } ${allFiles.map(({ file }) => file.name).join(", ")}`
916
- );
917
- }, 200);
908
+ }
918
909
  }
910
+ } else {
911
+ toKeep.push(file);
919
912
  }
920
- } else {
921
- toKeep.push(...cleanedAccepted);
922
913
  }
914
+ if (filesWIssues.length) {
915
+ const { file } = filesWIssues[0];
916
+ const allFiles = [...filesWIssues, ...filesWOIssues];
917
+ const doAllFilesHaveSameHeaders = allFiles.every(f => {
918
+ if (f.userSchema.fields && f.userSchema.fields.length) {
919
+ return f.userSchema.fields.every((h, i) => {
920
+ return h.path === allFiles[0].userSchema.fields[i].path;
921
+ });
922
+ }
923
+ return false;
924
+ });
925
+ const multipleFiles = allFiles.length > 1;
926
+ const { res } = await showUploadCsvWizardDialog(
927
+ "onUploadWizardFinish",
928
+ {
929
+ dialogProps: {
930
+ title: `Fix Up File${multipleFiles ? "s" : ""} ${
931
+ multipleFiles ? "" : file.name ? `"${file.name}"` : ""
932
+ }`
933
+ },
934
+ doAllFilesHaveSameHeaders,
935
+ filesWIssues: allFiles,
936
+ validateAgainstSchema
937
+ }
938
+ );
939
+
940
+ if (!res) {
941
+ window.toastr.warning(`File Upload Aborted`);
942
+ return;
943
+ } else {
944
+ allFiles.forEach(({ file }, i) => {
945
+ const newEntities = res[i];
946
+ // const newFileName = removeExt(file.name) + `_updated.csv`;
947
+ //swap out file with a new csv file
948
+ const { newFile, cleanedEntities } = getNewCsvFile(
949
+ newEntities,
950
+ file.name
951
+ );
923
952
 
924
- if (toKeep.length === 0) {
925
- window.toastr &&
926
- window.toastr.error(
927
- `It looks like there wasn't any data in your file. Please add some data and try again`
928
- );
953
+ file.hasEditClick = true;
954
+ file.parsedData = cleanedEntities;
955
+ // file.name = newFileName;
956
+ file.originFileObj = newFile;
957
+ file.originalFileObj = newFile;
958
+ });
959
+ setTimeout(() => {
960
+ //inside a timeout for cypress purposes
961
+ window.toastr.success(
962
+ `Added Fixed Up File${
963
+ allFiles.length > 1 ? "s" : ""
964
+ } ${allFiles.map(({ file }) => file.name).join(", ")}`
965
+ );
966
+ }, 200);
967
+ }
929
968
  }
930
- const cleanedFileList = trimFiles(
931
- [...toKeep, ...fileListToUse],
932
- fileLimit
933
- );
934
- handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
969
+ } else {
970
+ toKeep.push(...cleanedAccepted);
935
971
  }
972
+
973
+ if (toKeep.length === 0) {
974
+ window.toastr &&
975
+ window.toastr.error(
976
+ `It looks like there wasn't any data in your file. Please add some data and try again`
977
+ );
978
+ }
979
+ const cleanedFileList = trimFiles(
980
+ [...toKeep, ...fileListToUse],
981
+ fileLimit
982
+ );
983
+ handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
936
984
  }}
937
985
  {...dropzoneProps}
938
986
  >
@@ -942,71 +990,26 @@ function UploaderInner({
942
990
  isDragAccept,
943
991
  isDragReject,
944
992
  isDragActive
945
- // isDragActive
946
- // isDragReject
947
- // isDragAccept
948
993
  }) => (
949
- <section>
950
- <div
951
- {...getRootProps()}
952
- className={classnames("tg-dropzone", className, {
953
- "tg-dropzone-minimal": minimal,
954
- "tg-dropzone-active": isDragActive,
955
- "tg-dropzone-reject": isDragReject, // tnr: the acceptClassName/rejectClassName doesn't work with file extensions (only mimetypes are supported when dragging). Thus we'll just always turn the drop area blue when dragging and let the filtering occur on drop. See https://github.com/react-dropzone/react-dropzone/issues/888#issuecomment-773938074
956
- "tg-dropzone-accept": isDragAccept,
957
- "tg-dropzone-disabled": dropzoneDisabled,
958
- "bp3-disabled": dropzoneDisabled
959
- })}
960
- >
961
- <input {...getInputProps()} />
962
- {contentOverride || (
963
- <div
964
- title={
965
- simpleAccept
966
- ? "Accepts only the following file types: " +
967
- simpleAccept
968
- : "Accepts any file input"
969
- }
970
- className="tg-upload-inner"
971
- >
972
- {innerIcon || (
973
- <Icon icon="upload" iconSize={minimal ? 15 : 30} />
974
- )}
975
- {innerText ||
976
- (minimal ? "Upload" : "Click or drag to upload")}
977
- {validateAgainstSchema && !noBuildCsvOption && (
978
- <div
979
- style={{
980
- textAlign: "center",
981
- // fontSize: 18,
982
- marginTop: 7,
983
- marginBottom: 5
984
- }}
985
- onClick={handleManuallyEnterData}
986
- className="link-button"
987
- >
988
- ...or {manualEnterMessage}
989
- {/* <div
990
- style={{
991
- fontSize: 11,
992
- color: Colors.GRAY3,
993
- fontStyle: "italic"
994
- }}
995
- >
996
- {manualEnterSubMessage}
997
- </div> */}
998
- </div>
999
- )}
1000
- </div>
1001
- )}
1002
- </div>
1003
-
1004
- {showFilesCount ? (
1005
- <div className="tg-upload-file-list-counter">
1006
- Files: {fileList ? fileList.length : 0}
1007
- </div>
1008
- ) : null}
1009
- </section>
994
+ <InnerDropZone
995
+ getRootProps={getRootProps}
996
+ getInputProps={getInputProps}
997
+ isDragAccept={isDragAccept}
998
+ isDragReject={isDragReject}
999
+ isDragActive={isDragActive}
1000
+ className={className}
1001
+ minimal={minimal}
1002
+ dropzoneDisabled={dropzoneDisabled}
1003
+ contentOverride={contentOverride}
1004
+ simpleAccept={simpleAccept}
1005
+ innerIcon={innerIcon}
1006
+ innerText={innerText}
1007
+ validateAgainstSchema={validateAgainstSchema}
1008
+ handleManuallyEnterData={handleManuallyEnterData}
1009
+ noBuildCsvOption={noBuildCsvOption}
1010
+ showFilesCount={showFilesCount}
1011
+ fileList={fileList}
1012
+ />
1010
1013
  )}
1011
1014
  </Dropzone>
1012
1015
  {/* {validateAgainstSchema && <CsvWizardHelper bindToggle={{}} validateAgainstSchema={validateAgainstSchema}></CsvWizardHelper>} */}
@@ -1188,12 +1191,9 @@ function UploaderInner({
1188
1191
  </div>
1189
1192
  </>
1190
1193
  );
1191
- }
1194
+ };
1192
1195
 
1193
- const Uploader = compose(
1194
- connect(undefined, { initializeForm: initialize }),
1195
- observer
1196
- )(UploaderInner);
1196
+ const Uploader = observer(UploaderInner);
1197
1197
 
1198
1198
  export default Uploader;
1199
1199