@teselagen/ui 0.0.11 → 0.0.13

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 (125) hide show
  1. package/README.md +7 -0
  2. package/cypress.config.ts +6 -0
  3. package/index.html +12 -0
  4. package/package.json +5 -11
  5. package/project.json +74 -0
  6. package/src/AdvancedOptions.js +33 -0
  7. package/src/AdvancedOptions.spec.js +24 -0
  8. package/src/AssignDefaultsModeContext.js +21 -0
  9. package/src/AsyncValidateFieldSpinner/index.js +12 -0
  10. package/src/BlueprintError/index.js +14 -0
  11. package/src/BounceLoader/index.js +16 -0
  12. package/src/BounceLoader/style.css +45 -0
  13. package/src/CollapsibleCard/index.js +92 -0
  14. package/src/CollapsibleCard/style.css +21 -0
  15. package/src/DNALoader/index.js +20 -0
  16. package/src/DNALoader/style.css +251 -0
  17. package/src/DataTable/CellDragHandle.js +130 -0
  18. package/src/DataTable/DisabledLoadingComponent.js +15 -0
  19. package/src/DataTable/DisplayOptions.js +218 -0
  20. package/src/DataTable/FilterAndSortMenu.js +397 -0
  21. package/src/DataTable/PagingTool.js +232 -0
  22. package/src/DataTable/SearchBar.js +57 -0
  23. package/src/DataTable/SortableColumns.js +53 -0
  24. package/src/DataTable/TableFormTrackerContext.js +10 -0
  25. package/src/DataTable/dataTableEnhancer.js +291 -0
  26. package/src/DataTable/defaultFormatters.js +32 -0
  27. package/src/DataTable/defaultProps.js +45 -0
  28. package/src/DataTable/defaultValidators.js +40 -0
  29. package/src/DataTable/editCellHelper.js +44 -0
  30. package/src/DataTable/getCellVal.js +20 -0
  31. package/src/DataTable/getVals.js +8 -0
  32. package/src/DataTable/index.js +3537 -0
  33. package/src/DataTable/isTruthy.js +12 -0
  34. package/src/DataTable/isValueEmpty.js +3 -0
  35. package/src/DataTable/style.css +600 -0
  36. package/src/DataTable/utils/computePresets.js +42 -0
  37. package/src/DataTable/utils/convertSchema.js +69 -0
  38. package/src/DataTable/utils/getIdOrCodeOrIndex.js +9 -0
  39. package/src/DataTable/utils/getTableConfigFromStorage.js +5 -0
  40. package/src/DataTable/utils/queryParams.js +1032 -0
  41. package/src/DataTable/utils/rowClick.js +156 -0
  42. package/src/DataTable/utils/selection.js +8 -0
  43. package/src/DataTable/utils/withSelectedEntities.js +65 -0
  44. package/src/DataTable/utils/withTableParams.js +328 -0
  45. package/src/DataTable/validateTableWideErrors.js +135 -0
  46. package/src/DataTable/viewColumn.js +37 -0
  47. package/src/DialogFooter/index.js +79 -0
  48. package/src/DialogFooter/style.css +9 -0
  49. package/src/DropdownButton.js +36 -0
  50. package/src/FillWindow.css +6 -0
  51. package/src/FillWindow.js +69 -0
  52. package/src/FormComponents/Uploader.js +1197 -0
  53. package/src/FormComponents/getNewName.js +31 -0
  54. package/src/FormComponents/index.js +1384 -0
  55. package/src/FormComponents/itemUpload.js +84 -0
  56. package/src/FormComponents/sortify.js +73 -0
  57. package/src/FormComponents/style.css +247 -0
  58. package/src/FormComponents/tryToMatchSchemas.js +222 -0
  59. package/src/FormComponents/utils.js +6 -0
  60. package/src/HotkeysDialog/index.js +79 -0
  61. package/src/HotkeysDialog/style.css +54 -0
  62. package/src/InfoHelper/index.js +83 -0
  63. package/src/InfoHelper/style.css +7 -0
  64. package/src/IntentText/index.js +18 -0
  65. package/src/Loading/index.js +74 -0
  66. package/src/Loading/style.css +4 -0
  67. package/src/MatchHeaders.js +223 -0
  68. package/src/MenuBar/index.js +416 -0
  69. package/src/MenuBar/style.css +45 -0
  70. package/src/PromptUnsavedChanges/index.js +40 -0
  71. package/src/ResizableDraggableDialog/index.js +138 -0
  72. package/src/ResizableDraggableDialog/style.css +42 -0
  73. package/src/ScrollToTop/index.js +72 -0
  74. package/src/SimpleStepViz.js +26 -0
  75. package/src/TgSelect/index.js +465 -0
  76. package/src/TgSelect/style.css +34 -0
  77. package/src/TgSuggest/index.js +121 -0
  78. package/src/Timeline/TimelineEvent.js +31 -0
  79. package/src/Timeline/index.js +22 -0
  80. package/src/Timeline/style.css +29 -0
  81. package/src/UploadCsvWizard.css +4 -0
  82. package/src/UploadCsvWizard.js +731 -0
  83. package/src/autoTooltip.js +89 -0
  84. package/src/constants.js +1 -0
  85. package/src/customIcons.js +361 -0
  86. package/src/enhancers/withDialog/index.js +196 -0
  87. package/src/enhancers/withDialog/tg_modalState.js +46 -0
  88. package/src/enhancers/withField.js +20 -0
  89. package/src/enhancers/withFields.js +11 -0
  90. package/src/enhancers/withLocalStorage.js +11 -0
  91. package/src/index.js +76 -0
  92. package/src/rerenderOnWindowResize.js +27 -0
  93. package/src/showAppSpinner.js +12 -0
  94. package/src/showConfirmationDialog/index.js +116 -0
  95. package/src/showDialogOnDocBody.js +37 -0
  96. package/src/style.css +214 -0
  97. package/src/toastr.js +92 -0
  98. package/src/typeToCommonType.js +6 -0
  99. package/src/useDialog.js +64 -0
  100. package/src/utils/S3Download.js +14 -0
  101. package/src/utils/adHoc.js +10 -0
  102. package/src/utils/basicHandleActionsWithFullState.js +14 -0
  103. package/src/utils/combineReducersWithFullState.js +14 -0
  104. package/src/utils/commandControls.js +83 -0
  105. package/src/utils/commandUtils.js +112 -0
  106. package/src/utils/determineBlackOrWhiteTextColor.js +4 -0
  107. package/src/utils/getDayjsFormatter.js +35 -0
  108. package/src/utils/getTextFromEl.js +28 -0
  109. package/src/utils/handlerHelpers.js +30 -0
  110. package/src/utils/hotkeyUtils.js +129 -0
  111. package/src/utils/menuUtils.js +402 -0
  112. package/src/utils/popoverOverflowModifiers.js +11 -0
  113. package/src/utils/pureNoFunc.js +31 -0
  114. package/src/utils/renderOnDoc.js +29 -0
  115. package/src/utils/showProgressToast.js +22 -0
  116. package/src/utils/tagUtils.js +45 -0
  117. package/src/utils/tgFormValues.js +32 -0
  118. package/src/utils/withSelectTableRecords.js +38 -0
  119. package/src/utils/withStore.js +10 -0
  120. package/src/wrapDialog.js +112 -0
  121. package/tsconfig.json +4 -0
  122. package/vite.config.ts +7 -0
  123. package/index.mjs +0 -109378
  124. package/index.umd.js +0 -109381
  125. package/style.css +0 -10421
@@ -0,0 +1,84 @@
1
+ import React from "react";
2
+ import {
3
+ ProgressBar,
4
+ Intent,
5
+ Button,
6
+ Tooltip,
7
+ Position,
8
+ Icon
9
+ } from "@blueprintjs/core";
10
+
11
+ const itemUpload = props => {
12
+ const item = props.item;
13
+ const name = nameShortener(item.name);
14
+
15
+ return (
16
+ <div className="item-upload-container" key={item.id}>
17
+ <div className="item-upload">
18
+ <div>
19
+ {!props.saved ? (
20
+ <Tooltip
21
+ content={
22
+ <span>
23
+ {`Click to
24
+ ${props.active ? "abort this uploading" : "remove this file"}`}
25
+ </span>
26
+ }
27
+ position={Position.LEFT}
28
+ usePortal={true}
29
+ >
30
+ <Button
31
+ intent={Intent.DANGER}
32
+ minimal
33
+ icon="delete"
34
+ onClick={props.onCancel}
35
+ />
36
+ </Tooltip>
37
+ ) : null}
38
+ </div>
39
+ <div>
40
+ <span>{name}</span>
41
+ </div>
42
+ <div>
43
+ {props.active ? `${props.value}%` : null}
44
+ {props.error ? (
45
+ <Tooltip content={props.error} usePortal={true}>
46
+ <Icon icon="error" intent={Intent.DANGER} />
47
+ </Tooltip>
48
+ ) : null}
49
+ {props.saved ? (
50
+ <Tooltip content={"File Saved"} usePortal={true}>
51
+ <Icon icon="saved" intent={Intent.SUCCESS} />
52
+ </Tooltip>
53
+ ) : null}
54
+ </div>
55
+ </div>
56
+ {props.active ? (
57
+ <ProgressBar intent={Intent.PRIMARY} value={props.value / 100} />
58
+ ) : null}
59
+ </div>
60
+ );
61
+ };
62
+
63
+ /**
64
+ * Short file names length to display
65
+ * @param {*} itemName
66
+ */
67
+ const nameShortener = itemName => {
68
+ if (itemName.length > 40) {
69
+ let name = "";
70
+ const arr = itemName.split(".");
71
+ name += `${arr[0].substring(0, 20)}...`;
72
+ if (arr.length >= 2) {
73
+ name += `${arr[arr.length - 2].substring(
74
+ arr[arr.length - 2].length - 5,
75
+ arr[arr.length - 2].length
76
+ )}`;
77
+ name += `.${arr[arr.length - 1]}`;
78
+ }
79
+ return name;
80
+ }
81
+ return itemName;
82
+ };
83
+
84
+ export default itemUpload;
@@ -0,0 +1,73 @@
1
+ /*!
2
+ * Copyright 2015-2016 Thomas Rosenau
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * Create a “sorted” version of an object.
19
+ *
20
+ * JS engines internally keep track of an object's keys in the order of
21
+ * creation time, i.e. {a:1,b:2} is treated differently from {b:2,a:1}.
22
+ * That difference can be seen when JSON.stringify is called on that object.
23
+ * This function normalizes any kind of object by rearranging the keys in
24
+ * alphabetical order (numerical keys first, since v8 does so, and there's
25
+ * nothing we can do about it).
26
+ * @param {*} o The object to be sorted
27
+ */
28
+
29
+ /* eslint-disable */
30
+ const sortKeys = o => {
31
+ if (Array.isArray(o)) {
32
+ return o.map(sortKeys);
33
+ } else if (o instanceof Object) {
34
+ // put numeric keys first
35
+ let numeric = [];
36
+ let nonNumeric = [];
37
+ Object.keys(o).forEach(key => {
38
+ if (/^(0|[1-9][0-9]*)$/.test(key)) {
39
+ numeric.push(+key);
40
+ } else {
41
+ nonNumeric.push(key);
42
+ }
43
+ });
44
+ // do the rearrangement
45
+ return numeric
46
+ .sort(function(a, b) {
47
+ return a - b;
48
+ })
49
+ .concat(nonNumeric.sort())
50
+ .reduce((result, key) => {
51
+ result[key] = sortKeys(o[key]); // recurse!
52
+ return result;
53
+ }, {});
54
+ }
55
+ return o;
56
+ };
57
+
58
+ const jsonStringify = JSON.stringify.bind(JSON);
59
+
60
+ const sortify = (value, replacer, space) => {
61
+ // replacer, toJSON(), cyclic references and other stuff is better handled by native stringifier.
62
+ // So we do JSON.stringify(sortKeys( JSON.parse(JSON.stringify()) )).
63
+ // This approach is slightly slower but much safer than a manual stringification.
64
+ let nativeJson = jsonStringify(value, replacer, 0);
65
+ if (!nativeJson || (nativeJson[0] !== "{" && nativeJson[0] !== "[")) {
66
+ // if value is not an Object or Array
67
+ return nativeJson;
68
+ }
69
+ let cleanObj = JSON.parse(nativeJson);
70
+ return jsonStringify(sortKeys(cleanObj), null, space);
71
+ };
72
+
73
+ export default sortify;
@@ -0,0 +1,247 @@
1
+ .tg-flex-form-content .bp3-form-content {
2
+ display: flex;
3
+ }
4
+
5
+ .error-popover {
6
+ margin: 5px;
7
+ color: red;
8
+ }
9
+
10
+ .te-file-upload-input {
11
+ min-width: 140px;
12
+ }
13
+
14
+ .ant-upload.ant-upload-drag {
15
+ border-width: 2px;
16
+ }
17
+
18
+ .tg-upload-inner {
19
+ margin: 10px;
20
+ display: flex;
21
+ flex-direction: column;
22
+ align-items: center;
23
+ font-size: 16px;
24
+ }
25
+ .tg-dropzone.tg-dropzone-minimal {
26
+ height: 30px !important;
27
+ width: fit-content !important;
28
+ }
29
+ .tg-dropzone-minimal .tg-upload-inner {
30
+ flex-direction: row;
31
+ margin: 4px;
32
+ }
33
+ .tg-dropzone-minimal .bp3-icon {
34
+ flex-direction: row;
35
+ margin-right: 5px;
36
+ }
37
+
38
+ .tg-hide-drop-target .ant-upload.ant-upload-drag {
39
+ display: none;
40
+ }
41
+
42
+ .tg-dropzone {
43
+ width: 100% !important;
44
+ height: 100% !important;
45
+ border-width: 2px;
46
+ border-color: #d9d9d9;
47
+ border-style: dashed;
48
+ border-radius: 5px;
49
+ cursor: pointer;
50
+ transition: all 1s;
51
+ }
52
+ .tg-dropzone:hover {
53
+ border-color: rgb(91, 157, 211);
54
+ }
55
+ .tg-dropzone-active {
56
+ border-color: rgb(91, 157, 211);
57
+ }
58
+ .tg-dropzone-accept {
59
+ border-color: rgb(123, 214, 150);
60
+ }
61
+ .tg-dropzone-reject {
62
+ border-color: rgb(91, 157, 211);
63
+ }
64
+ .tg-dropzone-disabled {
65
+ border-color: rgb(136, 133, 132) !important;
66
+ opacity: 0.8;
67
+ }
68
+ .tg-upload-file-list-item {
69
+ display: flex;
70
+ justify-content: space-between;
71
+ margin: 2px;
72
+ border-radius: 3px;
73
+ padding: 2px;
74
+ }
75
+ .tg-upload-file-list-item:hover {
76
+ background: rgb(215, 238, 255);
77
+ }
78
+ .clickableIcon {
79
+ cursor: pointer;
80
+ }
81
+ .clickableIcon:hover {
82
+ opacity: 0.7;
83
+ }
84
+
85
+ .tg-upload-file-list-item a {
86
+ display: block;
87
+ white-space: nowrap;
88
+ overflow: hidden;
89
+ text-overflow: ellipsis;
90
+ }
91
+
92
+ .tg-upload-file-list-item-overflow {
93
+ overflow: auto;
94
+ height: 150px;
95
+ }
96
+
97
+ .tg-upload-file-list-counter {
98
+ position: absolute;
99
+ padding: 5px;
100
+ }
101
+
102
+ .tg-spin {
103
+ animation-name: spin;
104
+ animation-duration: 1500ms;
105
+ animation-iteration-count: infinite;
106
+ animation-timing-function: linear;
107
+ }
108
+ @keyframes spin {
109
+ from {
110
+ transform: rotate(0deg);
111
+ }
112
+ to {
113
+ transform: rotate(360deg);
114
+ }
115
+ }
116
+
117
+ /* .Select-multi-value-wrapper {
118
+ max-height: 100px;
119
+ overflow: scroll;
120
+ } */
121
+
122
+ /* dark styles */
123
+ .bp3-dark .Select.is-open > .Select-control,
124
+ .bp3-dark .Select-control,
125
+ .bp3-dark .Select.is-disabled > .Select-control {
126
+ background: rgba(16, 22, 26, 0.3);
127
+ box-shadow: 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0),
128
+ 0 0 0 0 rgba(19, 124, 189, 0), inset 0 0 0 1px rgba(16, 22, 26, 0.3),
129
+ inset 0 1px 1px rgba(16, 22, 26, 0.4);
130
+ background: rgba(16, 22, 26, 0.3);
131
+ color: #f5f8fa;
132
+ }
133
+
134
+ .bp3-dark .Select.is-focused:not(.is-open) > .Select-control {
135
+ background: rgba(16, 22, 26, 0.3);
136
+ color: #f5f8fa;
137
+ }
138
+
139
+ .bp3-dark .Select-placeholder,
140
+ .bp3-dark .Select--single > .Select-control .Select-value {
141
+ color: rgb(191, 204, 214, 0.5); /*#bfccd6*/
142
+ }
143
+
144
+ .bp3-dark .Select-control {
145
+ border: none !important;
146
+ }
147
+
148
+ .bp3-dark
149
+ .Select.has-value.Select--single
150
+ > .Select-control
151
+ .Select-value
152
+ .Select-value-label,
153
+ .bp3-dark
154
+ .Select.has-value.is-pseudo-focused.Select--single
155
+ > .Select-control
156
+ .Select-value
157
+ .Select-value-label,
158
+ .bp3-dark .Select-option.is-selected {
159
+ color: #f5f8fa;
160
+ }
161
+
162
+ .bp3-dark .Select-option {
163
+ background: #30404d;
164
+ color: #f5f8fa;
165
+ }
166
+
167
+ .bp3-dark .Select-menu-outer {
168
+ border: 1px solid #26738cb0;
169
+ background: #202b33;
170
+ }
171
+
172
+ .bp3-dark .Select-option.is-focused {
173
+ background: rgba(0, 126, 255, 0.08);
174
+ color: #f5f8fa;
175
+ }
176
+
177
+ .bp3-dark .tg-upload-file-list-item:hover {
178
+ background: #26738cb0;
179
+ }
180
+
181
+ .tg-tooltipError .bp3-popover-wrapper {
182
+ width: 100%;
183
+ }
184
+
185
+ .tg-no-fill-field {
186
+ display: table !important;
187
+ }
188
+
189
+ .item-upload-container {
190
+ padding: 5px;
191
+ width: 290px;
192
+ }
193
+
194
+ .item-upload {
195
+ display: flex;
196
+ justify-content: space-between;
197
+ align-items: center;
198
+ }
199
+
200
+ /* .bp3-control {
201
+ display: flex;
202
+ align-items: center;
203
+ } */
204
+
205
+ .bp3-form-group.bp3-inline .bp3-form-content {
206
+ flex: 1;
207
+ }
208
+
209
+ .bp3-dark input {
210
+ color: #f5f8fa;
211
+ }
212
+
213
+ .tg-no-fill-field .bp3-checkbox {
214
+ display: flex;
215
+ align-items: center;
216
+ }
217
+
218
+ .bp3-form-group .info-helper-wrapper:not(.info-helper-wrapper-noMarginTop) {
219
+ margin-top: -9px !important;
220
+ }
221
+
222
+ .generateDefaultDot {
223
+ cursor: pointer;
224
+ border-radius: 50%;
225
+ background: #ffca00;
226
+ box-shadow: 0px 0px 2px black;
227
+ height: 8px;
228
+ width: 8px;
229
+ }
230
+ .bp3-dark .generateDefaultDot {
231
+ background: yellow;
232
+ }
233
+
234
+ .bp3-label {
235
+ font-size: 13px;
236
+ font-weight: 600;
237
+ }
238
+
239
+ :not(.no-inline-label-margins).bp3-inline .bp3-label {
240
+ /* text-align: right; */
241
+ padding-right: 20px;
242
+ min-width: 100px;
243
+ }
244
+
245
+ .bp3-dark .tg-color-picker-selector input {
246
+ color: #222222;
247
+ }
@@ -0,0 +1,222 @@
1
+ import { forEach, isArray, map } from "lodash";
2
+ import { nanoid } from "nanoid";
3
+ import Fuse from "fuse.js";
4
+ import { editCellHelper } from "../DataTable/editCellHelper";
5
+ import { validateTableWideErrors } from "../DataTable/validateTableWideErrors";
6
+ import { isEmpty } from "lodash";
7
+ import getTextFromEl from "../utils/getTextFromEl";
8
+ import { min } from "lodash";
9
+ import { camelCase } from "lodash";
10
+ import { startCase } from "lodash";
11
+
12
+ const getSchema = data => ({
13
+ fields: map(data[0], (val, path) => {
14
+ return { path, type: "string" };
15
+ }),
16
+ userData: data
17
+ // userData: data.map((d) => {
18
+ // if (!d.id) {
19
+ // d.id = nanoid();
20
+ // }
21
+ // return d
22
+ // })
23
+ });
24
+ export default async function tryToMatchSchemas({
25
+ incomingData,
26
+ validateAgainstSchema
27
+ }) {
28
+ const userSchema = getSchema(incomingData);
29
+
30
+ const { searchResults, csvValidationIssue } = await matchSchemas({
31
+ userSchema,
32
+ officialSchema: validateAgainstSchema
33
+ });
34
+
35
+ const incomingHeadersToScores = {};
36
+
37
+ searchResults.forEach(r => {
38
+ r.matches.forEach(match => {
39
+ incomingHeadersToScores[match.item.path] =
40
+ incomingHeadersToScores[match.item.path] || [];
41
+ incomingHeadersToScores[match.item.path].push(match.score);
42
+ });
43
+ });
44
+
45
+ searchResults.forEach(r => {
46
+ for (const match of r.matches) {
47
+ if (!incomingHeadersToScores[match.item.path]) continue;
48
+ const minScore = min(incomingHeadersToScores[match.item.path]);
49
+ if (minScore === match.score) {
50
+ r.topMatch = match.item.path;
51
+ r.matches.forEach(match => {
52
+ if (!incomingHeadersToScores[match.item.path]) return;
53
+ const arr = incomingHeadersToScores[match.item.path];
54
+ arr.splice(arr.indexOf(match.score), 1);
55
+ });
56
+ delete incomingHeadersToScores[match.item.path];
57
+ break;
58
+ }
59
+ }
60
+ });
61
+ const matchedHeaders = {};
62
+ searchResults.forEach(r => {
63
+ if (r.topMatch) {
64
+ matchedHeaders[r.path] = r.topMatch;
65
+ }
66
+ });
67
+
68
+ return {
69
+ csvValidationIssue,
70
+ matchedHeaders,
71
+ userSchema,
72
+ searchResults
73
+ };
74
+ }
75
+
76
+ async function matchSchemas({ userSchema, officialSchema }) {
77
+ const options = {
78
+ includeScore: true,
79
+ keys: ["path", "displayName"]
80
+ };
81
+ let csvValidationIssue = false;
82
+ const fuse = new Fuse(userSchema.fields, options);
83
+
84
+ officialSchema.fields.forEach(h => {
85
+ let hasMatch = false;
86
+ //run fuse search for results
87
+ let result = fuse.search(h.path) || [];
88
+
89
+ //if there are any exact matches, push them onto the results array
90
+ userSchema.fields.forEach((uh, i) => {
91
+ const pathMatch =
92
+ uh.path.toLowerCase().replace(/ /g, "") ===
93
+ h.path.toLowerCase().replace(/ /g, "");
94
+ const displayNameMatch =
95
+ h.displayName &&
96
+ uh.path.toLowerCase().replace(/ /g, "") ===
97
+ getTextFromEl(h.displayName)
98
+ .toLowerCase()
99
+ .replace(/ /g, "");
100
+ const hasAlternatePathMatch =
101
+ h.alternatePathMatch &&
102
+ (isArray(h.alternatePathMatch)
103
+ ? h.alternatePathMatch.some(alternatePathMatch => {
104
+ return (
105
+ uh.path.toLowerCase().replace(/ /g, "") ===
106
+ alternatePathMatch.toLowerCase().replace(/ /g, "")
107
+ );
108
+ })
109
+ : uh.path.toLowerCase().replace(/ /g, "") ===
110
+ h.alternatePathMatch.toLowerCase().replace(/ /g, ""));
111
+
112
+ if (pathMatch || displayNameMatch || hasAlternatePathMatch) {
113
+ result = result.filter(({ path }) => path === uh.path);
114
+ //add a fake perfect match result to make sure we get the match
115
+ result.unshift({
116
+ item: {
117
+ path: uh.path,
118
+ type: h.type
119
+ },
120
+ refIndex: i,
121
+ score: 0
122
+ });
123
+ hasMatch = true;
124
+ }
125
+ });
126
+ h.hasMatch = hasMatch;
127
+ h.matches = result;
128
+
129
+ if (!hasMatch && h.isRequired) {
130
+ csvValidationIssue =
131
+ "It looks like some of the headers in your uploaded file(s) do not match the expected headers. Please look over and correct any issues with the mappings below.";
132
+ }
133
+ });
134
+ if (officialSchema.coerceUserSchema) {
135
+ officialSchema.coerceUserSchema({ userSchema, officialSchema });
136
+ }
137
+
138
+ userSchema.userData = map(userSchema.userData, e => {
139
+ if (!e.id) {
140
+ return {
141
+ ...e,
142
+ id: "__generated__" + nanoid()
143
+ };
144
+ }
145
+ return e;
146
+ });
147
+ const editableFields = officialSchema.fields.filter(f => !f.isNotEditable);
148
+ let hasErr;
149
+ if (!csvValidationIssue) {
150
+ userSchema.userData.some(e => {
151
+ return editableFields.some(columnSchema => {
152
+ //mutative
153
+ const { error } = editCellHelper({
154
+ entity: e,
155
+ columnSchema,
156
+ newVal: columnSchema.hasMatch
157
+ ? e[columnSchema.matches[0]?.item?.path]
158
+ : undefined
159
+ });
160
+ if (error) {
161
+ hasErr = `${columnSchema.displayName ||
162
+ startCase(camelCase(columnSchema.path))}: ${error}`;
163
+ return true;
164
+ }
165
+
166
+ return false;
167
+ });
168
+ });
169
+ if (!hasErr) {
170
+ hasErr = Object.values(
171
+ validateTableWideErrors({
172
+ entities: userSchema.userData,
173
+ schema: officialSchema,
174
+ optionalUserSchema: userSchema,
175
+ newCellValidate: {}
176
+ })
177
+ )[0];
178
+ }
179
+ }
180
+
181
+ if (officialSchema.tableWideAsyncValidation) {
182
+ //do the table wide validation
183
+ const res = await officialSchema.tableWideAsyncValidation({
184
+ entities: userSchema.userData
185
+ });
186
+ if (!isEmpty(res)) {
187
+ csvValidationIssue = addSpecialPropToAsyncErrs(res);
188
+ }
189
+ //return errors on the tables
190
+ }
191
+
192
+ if (hasErr && !csvValidationIssue) {
193
+ if (hasErr.message) {
194
+ csvValidationIssue = hasErr;
195
+ } else {
196
+ csvValidationIssue = {
197
+ message: hasErr
198
+ };
199
+ // csvValidationIssue = ` Some of the data doesn't look quite right. Do these header mappings look correct?`;
200
+ }
201
+ // csvValidationIssue = `Some of the data doesn't look quite right. Do these header mappings look correct?`;
202
+ }
203
+ // if (!csvValidationIssue) {
204
+ // //all the headers match up as does the actual data
205
+ // return { csvValidationIssue };
206
+ // }
207
+
208
+ return {
209
+ searchResults: officialSchema.fields,
210
+ csvValidationIssue
211
+ };
212
+ }
213
+
214
+ export const addSpecialPropToAsyncErrs = res => {
215
+ forEach(res, (v, k) => {
216
+ res[k] = {
217
+ message: v,
218
+ _isTableAsyncWideError: true
219
+ };
220
+ });
221
+ return res;
222
+ };
@@ -0,0 +1,6 @@
1
+ export const REQUIRED_ERROR = "This field is required.";
2
+
3
+ export const fieldRequired = value =>
4
+ !value || (Array.isArray(value) && !value.length)
5
+ ? REQUIRED_ERROR
6
+ : undefined;
@@ -0,0 +1,79 @@
1
+ import React from "react";
2
+ import {
3
+ Dialog,
4
+ Tab,
5
+ Tabs,
6
+ KeyCombo,
7
+ Classes /*, Tooltip*/
8
+ } from "@blueprintjs/core";
9
+ // import { startCase } from "lodash";
10
+ import classNames from "classnames";
11
+ import {
12
+ getHotkeyProps /*, hotkeysById, comboToLabel*/
13
+ } from "../utils/hotkeyUtils";
14
+
15
+ import "./style.css";
16
+
17
+ export default function HotkeysDialog(props) {
18
+ if (!props.hotkeySets) {
19
+ console.error("Missing hotkeySets in HotkeysDialog");
20
+ return null;
21
+ }
22
+ const sections = Object.keys(props.hotkeySets);
23
+ return (
24
+ <Dialog
25
+ isOpen={props.isOpen}
26
+ onClose={props.onClose}
27
+ title={props.dialogTitle || "Keyboard Shortcuts"}
28
+ >
29
+ <Tabs className="tg-hotkeys-dialog">
30
+ {sections.map(name => (
31
+ <Tab
32
+ key={name}
33
+ id={name}
34
+ title={sections.length === 1 ? undefined : name}
35
+ panel={
36
+ <div className="tg-table-wrapper">
37
+ <table
38
+ className={classNames(
39
+ Classes.HTML_TABLE,
40
+ Classes.HTML_TABLE_STRIPED,
41
+ Classes.HTML_TABLE_BORDERED
42
+ )}
43
+ >
44
+ <thead>
45
+ <tr>
46
+ <th>Action</th>
47
+ <th>Shortcut</th>
48
+ </tr>
49
+ </thead>
50
+ <tbody>
51
+ {Object.keys(props.hotkeySets[name]).map(id => {
52
+ const def = getHotkeyProps(
53
+ props.hotkeySets[name][id],
54
+ id
55
+ );
56
+ return (
57
+ <tr key={id}>
58
+ <td>{def.label}</td>
59
+ <td>
60
+ <KeyCombo combo={def.combo} />
61
+ {/* <Tooltip
62
+ content={comboToLabel(def.combo, false)}
63
+ >
64
+ {comboToLabel(def.combo)}
65
+ </Tooltip> */}
66
+ </td>
67
+ </tr>
68
+ );
69
+ })}
70
+ </tbody>
71
+ </table>
72
+ </div>
73
+ }
74
+ />
75
+ ))}
76
+ </Tabs>
77
+ </Dialog>
78
+ );
79
+ }