@teselagen/ui 0.7.32 → 0.7.33-beta.2

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 (254) hide show
  1. package/AdvancedOptions.js +33 -0
  2. package/AssignDefaultsModeContext.js +22 -0
  3. package/DataTable/utils/filterLocalEntitiesToHasura.d.ts +5 -0
  4. package/DataTable/utils/initializeHasuraWhereAndFilter.d.ts +2 -0
  5. package/DataTable/utils/queryParams.d.ts +8 -12
  6. package/DataTable/utils/simplifyHasuraWhere.d.ts +1 -0
  7. package/DataTable/utils/tableQueryParamsToHasuraClauses.d.ts +14 -0
  8. package/DropdownButton.js +36 -0
  9. package/FillWindow.css +6 -0
  10. package/FillWindow.js +69 -0
  11. package/{src/DataTable/FilterAndSortMenu.js → FilterAndSortMenu.js} +27 -30
  12. package/MatchHeaders.js +234 -0
  13. package/SimpleStepViz.js +22 -0
  14. package/Tag.js +112 -0
  15. package/UploadCsvWizard.css +4 -0
  16. package/UploadCsvWizard.js +719 -0
  17. package/autoTooltip.js +201 -0
  18. package/constants.js +1 -0
  19. package/customIcons.js +361 -0
  20. package/filterLocalEntitiesToHasura.js +216 -0
  21. package/index.cjs.js +695 -967
  22. package/index.d.ts +1 -0
  23. package/index.es.js +695 -967
  24. package/initializeHasuraWhereAndFilter.js +27 -0
  25. package/{src/utils/isBeingCalledExcessively.js → isBeingCalledExcessively.js} +0 -1
  26. package/package.json +1 -1
  27. package/queryParams.js +336 -0
  28. package/rerenderOnWindowResize.js +26 -0
  29. package/showAppSpinner.js +12 -0
  30. package/showDialogOnDocBody.js +33 -0
  31. package/simplifyHasuraWhere.js +80 -0
  32. package/src/CellDragHandle.js +132 -0
  33. package/src/ColumnFilterMenu.js +62 -0
  34. package/src/Columns.js +979 -0
  35. package/src/DisabledLoadingComponent.js +15 -0
  36. package/src/DisplayOptions.js +199 -0
  37. package/src/DropdownCell.js +61 -0
  38. package/src/EditableCell.js +44 -0
  39. package/src/FilterAndSortMenu.js +388 -0
  40. package/src/FormSeparator.js +9 -0
  41. package/src/LoadingDots.js +14 -0
  42. package/src/PagingTool.js +225 -0
  43. package/src/RenderCell.js +191 -0
  44. package/src/SearchBar.js +69 -0
  45. package/src/SortableColumns.js +100 -0
  46. package/src/TableFormTrackerContext.js +10 -0
  47. package/src/ThComponent.js +44 -0
  48. package/src/TimelineEvent.js +31 -0
  49. package/src/Uploader.js +1278 -0
  50. package/src/adHoc.js +10 -0
  51. package/src/basicHandleActionsWithFullState.js +14 -0
  52. package/src/browserUtils.js +3 -0
  53. package/src/combineReducersWithFullState.js +14 -0
  54. package/src/commandControls.js +82 -0
  55. package/src/commandUtils.js +112 -0
  56. package/src/convertSchema.js +69 -0
  57. package/src/dataTableEnhancer.js +41 -0
  58. package/src/defaultFormatters.js +32 -0
  59. package/src/defaultValidators.js +40 -0
  60. package/src/determineBlackOrWhiteTextColor.js +4 -0
  61. package/src/editCellHelper.js +44 -0
  62. package/src/filterLocalEntitiesToHasura.js +216 -0
  63. package/src/formatPasteData.js +16 -0
  64. package/src/getAllRows.js +11 -0
  65. package/src/getCellCopyText.js +7 -0
  66. package/src/getCellInfo.js +36 -0
  67. package/src/getCellVal.js +20 -0
  68. package/src/getDayjsFormatter.js +35 -0
  69. package/src/getFieldPathToField.js +7 -0
  70. package/src/getIdOrCodeOrIndex.js +9 -0
  71. package/src/getLastSelectedEntity.js +11 -0
  72. package/src/getNewEntToSelect.js +25 -0
  73. package/src/getNewName.js +31 -0
  74. package/src/getRowCopyText.js +28 -0
  75. package/src/getTableConfigFromStorage.js +5 -0
  76. package/src/getTextFromEl.js +28 -0
  77. package/src/getVals.js +8 -0
  78. package/src/handleCopyColumn.js +21 -0
  79. package/src/handleCopyHelper.js +15 -0
  80. package/src/handleCopyRows.js +23 -0
  81. package/src/handleCopyTable.js +16 -0
  82. package/src/handlerHelpers.js +24 -0
  83. package/src/hotkeyUtils.js +131 -0
  84. package/src/index.js +1 -87
  85. package/src/initializeHasuraWhereAndFilter.js +27 -0
  86. package/src/isBeingCalledExcessively.js +24 -0
  87. package/src/isBottomRightCornerOfRectangle.js +20 -0
  88. package/src/isEntityClean.js +15 -0
  89. package/src/isTruthy.js +12 -0
  90. package/src/isValueEmpty.js +3 -0
  91. package/src/itemUpload.js +84 -0
  92. package/src/menuUtils.js +433 -0
  93. package/src/popoverOverflowModifiers.js +11 -0
  94. package/src/primarySelectedValue.js +1 -0
  95. package/src/pureNoFunc.js +31 -0
  96. package/src/queryParams.js +336 -0
  97. package/src/removeCleanRows.js +22 -0
  98. package/src/renderOnDoc.js +32 -0
  99. package/src/rowClick.js +181 -0
  100. package/src/selection.js +8 -0
  101. package/src/showProgressToast.js +22 -0
  102. package/src/simplifyHasuraWhere.js +80 -0
  103. package/src/sortify.js +73 -0
  104. package/src/style.css +24 -260
  105. package/src/tableQueryParamsToHasuraClauses.js +113 -0
  106. package/src/{DataTable/utils/withTableParams.js → withTableParams.js} +1 -14
  107. package/tableQueryParamsToHasuraClauses.js +113 -0
  108. package/tagUtils.js +45 -0
  109. package/tgFormValues.js +35 -0
  110. package/tg_modalState.js +47 -0
  111. package/throwFormError.js +16 -0
  112. package/toastr.js +148 -0
  113. package/tryToMatchSchemas.js +264 -0
  114. package/typeToCommonType.js +6 -0
  115. package/useDeepEqualMemo.js +15 -0
  116. package/useDialog.js +63 -0
  117. package/useStableReference.js +9 -0
  118. package/useTableEntities.js +38 -0
  119. package/useTraceUpdate.js +19 -0
  120. package/utils.js +37 -0
  121. package/validateTableWideErrors.js +160 -0
  122. package/viewColumn.js +97 -0
  123. package/withField.js +20 -0
  124. package/withFields.js +11 -0
  125. package/withLocalStorage.js +11 -0
  126. package/withSelectTableRecords.js +43 -0
  127. package/withSelectedEntities.js +65 -0
  128. package/withStore.js +10 -0
  129. package/withTableParams.js +288 -0
  130. package/wrapDialog.js +116 -0
  131. package/src/AdvancedOptions.spec.js +0 -26
  132. package/src/AsyncValidateFieldSpinner/index.js +0 -12
  133. package/src/BlueprintError/index.js +0 -14
  134. package/src/BounceLoader/index.js +0 -16
  135. package/src/BounceLoader/style.css +0 -45
  136. package/src/CollapsibleCard/index.js +0 -68
  137. package/src/CollapsibleCard/style.css +0 -23
  138. package/src/DNALoader/index.js +0 -20
  139. package/src/DNALoader/style.css +0 -251
  140. package/src/DataTable/index.js +0 -3213
  141. package/src/DataTable/style.css +0 -608
  142. package/src/DataTable/utils/index.js +0 -55
  143. package/src/DataTable/utils/queryParams.js +0 -1058
  144. package/src/DialogFooter/index.js +0 -86
  145. package/src/DialogFooter/style.css +0 -9
  146. package/src/FormComponents/index.js +0 -1266
  147. package/src/FormComponents/style.css +0 -275
  148. package/src/FormComponents/utils.js +0 -6
  149. package/src/HotkeysDialog/index.js +0 -79
  150. package/src/HotkeysDialog/style.css +0 -54
  151. package/src/InfoHelper/index.js +0 -78
  152. package/src/InfoHelper/style.css +0 -7
  153. package/src/IntentText/index.js +0 -18
  154. package/src/Loading/index.js +0 -70
  155. package/src/Loading/style.css +0 -4
  156. package/src/MenuBar/index.js +0 -423
  157. package/src/MenuBar/style.css +0 -45
  158. package/src/PromptUnsavedChanges/index.js +0 -38
  159. package/src/ResizableDraggableDialog/index.js +0 -141
  160. package/src/ResizableDraggableDialog/style.css +0 -42
  161. package/src/ScrollToTop/index.js +0 -72
  162. package/src/TagSelect/index.js +0 -69
  163. package/src/TagSelect/style.css +0 -13
  164. package/src/TgHtmlSelect/index.js +0 -20
  165. package/src/TgSelect/index.js +0 -537
  166. package/src/TgSelect/style.css +0 -61
  167. package/src/TgSuggest/index.js +0 -124
  168. package/src/Timeline/index.js +0 -15
  169. package/src/enhancers/withDialog/index.js +0 -196
  170. package/src/showConfirmationDialog/index.js +0 -148
  171. /package/{src/DataTable/CellDragHandle.js → CellDragHandle.js} +0 -0
  172. /package/{src/DataTable/ColumnFilterMenu.js → ColumnFilterMenu.js} +0 -0
  173. /package/{src/DataTable/Columns.js → Columns.js} +0 -0
  174. /package/{src/DataTable/DisabledLoadingComponent.js → DisabledLoadingComponent.js} +0 -0
  175. /package/{src/DataTable/DisplayOptions.js → DisplayOptions.js} +0 -0
  176. /package/{src/DataTable/DropdownCell.js → DropdownCell.js} +0 -0
  177. /package/{src/DataTable/EditableCell.js → EditableCell.js} +0 -0
  178. /package/{src/FormComponents/FormSeparator.js → FormSeparator.js} +0 -0
  179. /package/{src/FormComponents/LoadingDots.js → LoadingDots.js} +0 -0
  180. /package/{src/DataTable/PagingTool.js → PagingTool.js} +0 -0
  181. /package/{src/DataTable/RenderCell.js → RenderCell.js} +0 -0
  182. /package/{src/DataTable/SearchBar.js → SearchBar.js} +0 -0
  183. /package/{src/DataTable/SortableColumns.js → SortableColumns.js} +0 -0
  184. /package/{src/DataTable/TableFormTrackerContext.js → TableFormTrackerContext.js} +0 -0
  185. /package/{src/DataTable/ThComponent.js → ThComponent.js} +0 -0
  186. /package/{src/Timeline/TimelineEvent.js → TimelineEvent.js} +0 -0
  187. /package/{src/FormComponents/Uploader.js → Uploader.js} +0 -0
  188. /package/{src/utils/adHoc.js → adHoc.js} +0 -0
  189. /package/{src/utils/basicHandleActionsWithFullState.js → basicHandleActionsWithFullState.js} +0 -0
  190. /package/{src/utils/browserUtils.js → browserUtils.js} +0 -0
  191. /package/{src/utils/combineReducersWithFullState.js → combineReducersWithFullState.js} +0 -0
  192. /package/{src/utils/commandControls.js → commandControls.js} +0 -0
  193. /package/{src/utils/commandUtils.js → commandUtils.js} +0 -0
  194. /package/{src/DataTable/utils/convertSchema.js → convertSchema.js} +0 -0
  195. /package/{src/DataTable/dataTableEnhancer.js → dataTableEnhancer.js} +0 -0
  196. /package/{src/DataTable/defaultFormatters.js → defaultFormatters.js} +0 -0
  197. /package/{src/DataTable/defaultValidators.js → defaultValidators.js} +0 -0
  198. /package/{src/utils/determineBlackOrWhiteTextColor.js → determineBlackOrWhiteTextColor.js} +0 -0
  199. /package/{src/DataTable/editCellHelper.js → editCellHelper.js} +0 -0
  200. /package/{src/DataTable/utils/formatPasteData.js → formatPasteData.js} +0 -0
  201. /package/{src/DataTable/utils/getAllRows.js → getAllRows.js} +0 -0
  202. /package/{src/DataTable/utils/getCellCopyText.js → getCellCopyText.js} +0 -0
  203. /package/{src/DataTable/utils/getCellInfo.js → getCellInfo.js} +0 -0
  204. /package/{src/DataTable/getCellVal.js → getCellVal.js} +0 -0
  205. /package/{src/utils/getDayjsFormatter.js → getDayjsFormatter.js} +0 -0
  206. /package/{src/DataTable/utils/getFieldPathToField.js → getFieldPathToField.js} +0 -0
  207. /package/{src/DataTable/utils/getIdOrCodeOrIndex.js → getIdOrCodeOrIndex.js} +0 -0
  208. /package/{src/DataTable/utils/getLastSelectedEntity.js → getLastSelectedEntity.js} +0 -0
  209. /package/{src/DataTable/utils/getNewEntToSelect.js → getNewEntToSelect.js} +0 -0
  210. /package/{src/FormComponents/getNewName.js → getNewName.js} +0 -0
  211. /package/{src/DataTable/utils/getRowCopyText.js → getRowCopyText.js} +0 -0
  212. /package/{src/DataTable/utils/getTableConfigFromStorage.js → getTableConfigFromStorage.js} +0 -0
  213. /package/{src/utils/getTextFromEl.js → getTextFromEl.js} +0 -0
  214. /package/{src/DataTable/getVals.js → getVals.js} +0 -0
  215. /package/{src/DataTable/utils/handleCopyColumn.js → handleCopyColumn.js} +0 -0
  216. /package/{src/DataTable/utils/handleCopyHelper.js → handleCopyHelper.js} +0 -0
  217. /package/{src/DataTable/utils/handleCopyRows.js → handleCopyRows.js} +0 -0
  218. /package/{src/DataTable/utils/handleCopyTable.js → handleCopyTable.js} +0 -0
  219. /package/{src/utils/handlerHelpers.js → handlerHelpers.js} +0 -0
  220. /package/{src/utils/hotkeyUtils.js → hotkeyUtils.js} +0 -0
  221. /package/{src/utils/hooks/index.js → index.js} +0 -0
  222. /package/{src/DataTable/utils/isBottomRightCornerOfRectangle.js → isBottomRightCornerOfRectangle.js} +0 -0
  223. /package/{src/DataTable/utils/isEntityClean.js → isEntityClean.js} +0 -0
  224. /package/{src/DataTable/isTruthy.js → isTruthy.js} +0 -0
  225. /package/{src/DataTable/isValueEmpty.js → isValueEmpty.js} +0 -0
  226. /package/{src/FormComponents/itemUpload.js → itemUpload.js} +0 -0
  227. /package/{src/utils/menuUtils.js → menuUtils.js} +0 -0
  228. /package/{src/utils/popoverOverflowModifiers.js → popoverOverflowModifiers.js} +0 -0
  229. /package/{src/DataTable/utils/primarySelectedValue.js → primarySelectedValue.js} +0 -0
  230. /package/{src/utils/pureNoFunc.js → pureNoFunc.js} +0 -0
  231. /package/{src/DataTable/utils/removeCleanRows.js → removeCleanRows.js} +0 -0
  232. /package/{src/utils/renderOnDoc.js → renderOnDoc.js} +0 -0
  233. /package/{src/DataTable/utils/rowClick.js → rowClick.js} +0 -0
  234. /package/{src/DataTable/utils/selection.js → selection.js} +0 -0
  235. /package/{src/utils/showProgressToast.js → showProgressToast.js} +0 -0
  236. /package/{src/FormComponents/sortify.js → sortify.js} +0 -0
  237. /package/src/{utils/tagUtils.js → tagUtils.js} +0 -0
  238. /package/src/{utils/tgFormValues.js → tgFormValues.js} +0 -0
  239. /package/src/{enhancers/withDialog/tg_modalState.js → tg_modalState.js} +0 -0
  240. /package/src/{FormComponents/tryToMatchSchemas.js → tryToMatchSchemas.js} +0 -0
  241. /package/src/{utils/hooks/useDeepEqualMemo.js → useDeepEqualMemo.js} +0 -0
  242. /package/src/{utils/hooks/useStableReference.js → useStableReference.js} +0 -0
  243. /package/src/{DataTable/utils/useTableEntities.js → useTableEntities.js} +0 -0
  244. /package/src/{utils/useTraceUpdate.js → useTraceUpdate.js} +0 -0
  245. /package/src/{DataTable/utils/utils.js → utils.js} +0 -0
  246. /package/src/{DataTable/validateTableWideErrors.js → validateTableWideErrors.js} +0 -0
  247. /package/src/{DataTable/viewColumn.js → viewColumn.js} +0 -0
  248. /package/src/{enhancers/withField.js → withField.js} +0 -0
  249. /package/src/{enhancers/withFields.js → withFields.js} +0 -0
  250. /package/src/{enhancers/withLocalStorage.js → withLocalStorage.js} +0 -0
  251. /package/src/{utils/withSelectTableRecords.js → withSelectTableRecords.js} +0 -0
  252. /package/src/{DataTable/utils/withSelectedEntities.js → withSelectedEntities.js} +0 -0
  253. /package/src/{utils/withStore.js → withStore.js} +0 -0
  254. /package/{src/Timeline/style.css → style.css} +0 -0
@@ -0,0 +1,47 @@
1
+ import { omit } from "lodash-es";
2
+
3
+ export default function tg_modalState(
4
+ state = {},
5
+ { type, name, uniqueName, props = {} }
6
+ ) {
7
+ const existingModalState = state[name] || {};
8
+ const { __registeredAs = {} } = existingModalState;
9
+ if (type === "TG_REGISTER_MODAL") {
10
+ return {
11
+ ...state,
12
+ [name]: {
13
+ ...existingModalState,
14
+ __registeredAs: { ...__registeredAs, [uniqueName]: true }
15
+ }
16
+ };
17
+ }
18
+ if (type === "TG_UNREGISTER_MODAL") {
19
+ return {
20
+ ...state,
21
+ [name]: {
22
+ ...existingModalState,
23
+ __registeredAs: omit(__registeredAs, uniqueName)
24
+ }
25
+ };
26
+ }
27
+ if (type === "TG_SHOW_MODAL") {
28
+ return {
29
+ ...state,
30
+ [name]: {
31
+ ...existingModalState,
32
+ ...props,
33
+ open: true
34
+ }
35
+ };
36
+ }
37
+ if (type === "TG_HIDE_MODAL") {
38
+ return {
39
+ ...state,
40
+ [name]: {
41
+ __registeredAs: existingModalState.__registeredAs,
42
+ open: false
43
+ }
44
+ };
45
+ }
46
+ return state;
47
+ }
@@ -0,0 +1,16 @@
1
+ import { SubmissionError } from "redux-form";
2
+
3
+ export const throwFormError = error => {
4
+ if (error.message) {
5
+ console.error("error:", error);
6
+ }
7
+ const errorToUse = error.message
8
+ ? { _error: error.message }
9
+ : typeof error === "string"
10
+ ? { _error: error }
11
+ : error;
12
+ if (!errorToUse._error) {
13
+ errorToUse._error = "Error Submitting Form";
14
+ }
15
+ throw new SubmissionError(errorToUse);
16
+ };
package/toastr.js ADDED
@@ -0,0 +1,148 @@
1
+ import { Position, Toaster, Intent, Classes } from "@blueprintjs/core";
2
+ import classNames from "classnames";
3
+
4
+ const TopToaster = Toaster.create({
5
+ className: "top-toaster",
6
+ position: Position.TOP
7
+ });
8
+
9
+ const BottomToaster = Toaster.create({
10
+ className: "bottom-toaster",
11
+ position: Position.BOTTOM
12
+ });
13
+
14
+ window.__tgClearAllToasts = () => {
15
+ TopToaster.clear();
16
+ BottomToaster.clear();
17
+ };
18
+
19
+ let counter = 5000;
20
+ const generateToast = intent => (message, options) => {
21
+ options = options || {};
22
+ const toastToUse = options.bottom ? BottomToaster : TopToaster;
23
+ let updatedTimeout;
24
+ if (options.updateTimeout) {
25
+ //generate a slightly different than default timeout to make the update stay on the page for a full 5 seconds
26
+ if (counter > 5500) {
27
+ updatedTimeout = --counter;
28
+ } else {
29
+ updatedTimeout = ++counter;
30
+ }
31
+ }
32
+ if (intent === Intent.DANGER) {
33
+ console.error("Toastr error message: ", message);
34
+ }
35
+ if (intent === Intent.WARNING) {
36
+ console.error("Toastr warning message: ", message);
37
+ }
38
+
39
+ const maybeAddClearAll = () => {
40
+ // wipe any existing clear all buttons
41
+ const existingClearAllButtons =
42
+ document.querySelectorAll(`.tg-clear-all-toasts`);
43
+ existingClearAllButtons.forEach(button => {
44
+ button.remove();
45
+ });
46
+ const activeToasts = document.querySelectorAll(
47
+ `.bp3-toast:not(.bp3-toast-exit)`
48
+ );
49
+ if (activeToasts.length > 1) {
50
+ // add custom clear all button
51
+
52
+ const topToaster = document.querySelector(`.bp3-toast`);
53
+ if (!topToaster) return;
54
+ const closeButton = document.createElement("div");
55
+ closeButton.classList.add(
56
+ Classes.BUTTON,
57
+ Classes.LARGE,
58
+ Classes.INTENT_PRIMARY,
59
+ "tg-clear-all-toasts"
60
+ );
61
+ closeButton.innerText = "Clear all";
62
+ closeButton.onclick = window.__tgClearAllToasts;
63
+ // position the button to the right of the message
64
+ closeButton.style.position = "absolute";
65
+ closeButton.style.right = "-100px";
66
+
67
+ topToaster.appendChild(closeButton);
68
+ }
69
+ };
70
+ const uniqKey = toastToUse.show(
71
+ {
72
+ intent,
73
+ message,
74
+ onDismiss: () => {
75
+ if (options.onDismiss) {
76
+ options.onDismiss();
77
+ }
78
+ setTimeout(() => {
79
+ maybeAddClearAll();
80
+ }, 0);
81
+ },
82
+ timeout:
83
+ options.timeout ||
84
+ updatedTimeout ||
85
+ (!window.Cypress &&
86
+ (intent === Intent.DANGER || intent === Intent.WARNING)
87
+ ? 60000
88
+ : undefined),
89
+ action: options.action,
90
+ icon: options.icon,
91
+ className: classNames("preserve-newline", options.className)
92
+ },
93
+ options.key
94
+ );
95
+ setTimeout(() => {
96
+ maybeAddClearAll();
97
+ }, 0);
98
+ function clear() {
99
+ toastToUse.dismiss(uniqKey);
100
+ setTimeout(() => {
101
+ maybeAddClearAll();
102
+ }, 0);
103
+ }
104
+ clear.key = uniqKey;
105
+ return clear;
106
+ };
107
+
108
+ function preventDuplicates(func) {
109
+ const previousToasts = {};
110
+ return (message, options = {}) => {
111
+ const clearToast = func(message, options);
112
+ // no duplicate check for toasts with updates
113
+
114
+ if (!options.key) {
115
+ if (!options.key && previousToasts[message]) {
116
+ previousToasts[message](); //clear it!
117
+ }
118
+
119
+ setTimeout(() => {
120
+ delete previousToasts[message];
121
+ }, options.timeout || 5000);
122
+
123
+ previousToasts[message] = clearToast;
124
+ }
125
+ return clearToast;
126
+ };
127
+ }
128
+
129
+ if (!window.toastr) window.toastr = {};
130
+ if (!window.toastr.success) {
131
+ window.toastr.success = preventDuplicates(generateToast(Intent.SUCCESS));
132
+ }
133
+
134
+ if (!window.toastr.error) {
135
+ window.toastr.error = preventDuplicates(generateToast(Intent.DANGER));
136
+ }
137
+
138
+ if (!window.toastr.warning) {
139
+ window.toastr.warning = preventDuplicates(generateToast(Intent.WARNING));
140
+ }
141
+
142
+ if (!window.toastr.info) {
143
+ window.toastr.info = preventDuplicates(generateToast(Intent.PRIMARY));
144
+ }
145
+
146
+ if (!window.toastr.default) {
147
+ window.toastr.default = preventDuplicates(generateToast(Intent.NONE));
148
+ }
@@ -0,0 +1,264 @@
1
+ import { forEach, isArray, isPlainObject, map, snakeCase } from "lodash-es";
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-es";
7
+ import getTextFromEl from "../utils/getTextFromEl";
8
+ import { min } from "lodash-es";
9
+ import { camelCase } from "lodash-es";
10
+ import { startCase } from "lodash-es";
11
+
12
+ const getSchema = data => ({
13
+ fields: map(data[0], (val, path) => {
14
+ return { path, type: "string" };
15
+ }),
16
+ userData: data
17
+ });
18
+
19
+ export default async function tryToMatchSchemas({
20
+ incomingData,
21
+ validateAgainstSchema
22
+ }) {
23
+ await resolveValidateAgainstSchema(validateAgainstSchema);
24
+ const userSchema = getSchema(incomingData);
25
+
26
+ const { searchResults, csvValidationIssue, ignoredHeadersMsg } =
27
+ await matchSchemas({
28
+ userSchema,
29
+ officialSchema: validateAgainstSchema
30
+ });
31
+
32
+ const incomingHeadersToScores = {};
33
+
34
+ searchResults.forEach(r => {
35
+ r.matches.forEach(match => {
36
+ incomingHeadersToScores[match.item.path] =
37
+ incomingHeadersToScores[match.item.path] || [];
38
+ incomingHeadersToScores[match.item.path].push(match.score);
39
+ });
40
+ });
41
+
42
+ searchResults.forEach(r => {
43
+ for (const match of r.matches) {
44
+ if (!incomingHeadersToScores[match.item.path]) continue;
45
+ const minScore = min(incomingHeadersToScores[match.item.path]);
46
+ if (minScore === match.score) {
47
+ r.topMatch = match.item.path;
48
+ r.matches.forEach(match => {
49
+ if (!incomingHeadersToScores[match.item.path]) return;
50
+ const arr = incomingHeadersToScores[match.item.path];
51
+ arr.splice(arr.indexOf(match.score), 1);
52
+ });
53
+ delete incomingHeadersToScores[match.item.path];
54
+ break;
55
+ }
56
+ }
57
+ });
58
+ const matchedHeaders = {};
59
+ searchResults.forEach(r => {
60
+ if (r.topMatch) {
61
+ matchedHeaders[r.path] = r.topMatch;
62
+ }
63
+ });
64
+
65
+ return {
66
+ ignoredHeadersMsg,
67
+ csvValidationIssue,
68
+ matchedHeaders,
69
+ userSchema,
70
+ searchResults
71
+ };
72
+ }
73
+
74
+ async function matchSchemas({ userSchema, officialSchema }) {
75
+ const options = {
76
+ includeScore: true,
77
+ keys: ["path", "displayName"]
78
+ };
79
+ let csvValidationIssue = false;
80
+ const fuse = new Fuse(userSchema.fields, options);
81
+
82
+ const matchedAltPaths = [];
83
+ officialSchema.fields.forEach(h => {
84
+ let hasMatch = false;
85
+ //run fuse search for results
86
+ let result = fuse.search(h.path) || [];
87
+
88
+ const hadNormalPathMatch = userSchema.fields.some(
89
+ uh => norm(uh.path) === norm(h.path)
90
+ );
91
+ //if there are any exact matches, push them onto the results array
92
+ userSchema.fields.forEach((uh, i) => {
93
+ const pathMatch = norm(uh.path) === norm(h.path);
94
+ const displayNameMatch =
95
+ h.displayName && norm(uh.path) === norm(getTextFromEl(h.displayName));
96
+ const hasAlternatePathMatch =
97
+ !hadNormalPathMatch &&
98
+ h.alternatePathMatch &&
99
+ (isArray(h.alternatePathMatch)
100
+ ? h.alternatePathMatch
101
+ : [h.alternatePathMatch]
102
+ ).find(alternatePathMatch => {
103
+ let altPath = alternatePathMatch;
104
+ if (isPlainObject(alternatePathMatch)) {
105
+ altPath = alternatePathMatch.path;
106
+ }
107
+ return norm(uh.path) === norm(altPath);
108
+ });
109
+
110
+ if (hasAlternatePathMatch) {
111
+ matchedAltPaths.push(
112
+ hasAlternatePathMatch.path || hasAlternatePathMatch
113
+ );
114
+ if (hasAlternatePathMatch.format) {
115
+ h.format = hasAlternatePathMatch.format;
116
+ }
117
+ }
118
+ if (pathMatch || displayNameMatch || hasAlternatePathMatch) {
119
+ result = result.filter(({ path }) => path === uh.path);
120
+ //add a fake perfect match result to make sure we get the match
121
+ result.unshift({
122
+ item: {
123
+ path: uh.path,
124
+ type: h.type
125
+ },
126
+ refIndex: i,
127
+ score: 0
128
+ });
129
+ hasMatch = true;
130
+ }
131
+ });
132
+ h.hasMatch = hasMatch;
133
+ h.matches = result;
134
+
135
+ if (!hasMatch && h.isRequired) {
136
+ csvValidationIssue =
137
+ "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.";
138
+ }
139
+ });
140
+ const ignoredUserSchemaFields = [];
141
+ userSchema.fields.forEach(uh => {
142
+ if (
143
+ !officialSchema.fields.find(
144
+ h =>
145
+ norm(h.path) === norm(uh.path) ||
146
+ norm(h.displayName) === norm(uh.path) ||
147
+ matchedAltPaths.includes(uh.path)
148
+ )
149
+ ) {
150
+ // check that the column does contain data (if it doesn't, it's probably a blank column)
151
+ if (userSchema.userData.some(e => e[uh.path])) {
152
+ ignoredUserSchemaFields.push(uh);
153
+ }
154
+ }
155
+ });
156
+
157
+ if (officialSchema.coerceUserSchema) {
158
+ officialSchema.coerceUserSchema({ userSchema, officialSchema });
159
+ }
160
+
161
+ userSchema.userData = map(userSchema.userData, e => {
162
+ if (!e.id) {
163
+ return {
164
+ ...e,
165
+ id: "__generated__" + nanoid()
166
+ };
167
+ }
168
+ return e;
169
+ });
170
+ const editableFields = officialSchema.fields.filter(f => !f.isNotEditable);
171
+ let hasErr;
172
+ if (!csvValidationIssue) {
173
+ userSchema.userData.some(e => {
174
+ return editableFields.some(columnSchema => {
175
+ //mutative
176
+ const { error } = editCellHelper({
177
+ entity: e,
178
+ columnSchema,
179
+ newVal: columnSchema.hasMatch
180
+ ? e[columnSchema.matches[0]?.item?.path]
181
+ : undefined
182
+ });
183
+ if (error) {
184
+ hasErr = `${
185
+ columnSchema.displayName || startCase(camelCase(columnSchema.path))
186
+ }: ${error}`;
187
+ return true;
188
+ }
189
+
190
+ return false;
191
+ });
192
+ });
193
+ if (!hasErr) {
194
+ hasErr = Object.values(
195
+ validateTableWideErrors({
196
+ entities: userSchema.userData,
197
+ schema: officialSchema,
198
+ optionalUserSchema: userSchema,
199
+ newCellValidate: {}
200
+ })
201
+ )[0];
202
+ }
203
+ }
204
+
205
+ if (officialSchema.tableWideAsyncValidation) {
206
+ //do the table wide validation
207
+ const res = await officialSchema.tableWideAsyncValidation({
208
+ entities: userSchema.userData
209
+ });
210
+ if (!isEmpty(res)) {
211
+ csvValidationIssue = addSpecialPropToAsyncErrs(res);
212
+ }
213
+ //return errors on the tables
214
+ }
215
+
216
+ if (hasErr && !csvValidationIssue) {
217
+ if (hasErr.message) {
218
+ csvValidationIssue = hasErr;
219
+ } else {
220
+ csvValidationIssue = {
221
+ message: hasErr
222
+ };
223
+ // csvValidationIssue = ` Some of the data doesn't look quite right. Do these header mappings look correct?`;
224
+ }
225
+ // csvValidationIssue = `Some of the data doesn't look quite right. Do these header mappings look correct?`;
226
+ }
227
+ let ignoredHeadersMsg;
228
+ if (ignoredUserSchemaFields.length) {
229
+ ignoredHeadersMsg = `It looks like the following headers in your file didn't map to any of the accepted headers: ${ignoredUserSchemaFields
230
+ .map(f => f.displayName || f.path)
231
+ .join(", ")}`;
232
+ }
233
+ return {
234
+ searchResults: officialSchema.fields,
235
+ csvValidationIssue,
236
+ ignoredHeadersMsg
237
+ };
238
+ }
239
+
240
+ export const addSpecialPropToAsyncErrs = res => {
241
+ forEach(res, (v, k) => {
242
+ res[k] = {
243
+ message: v,
244
+ _isTableAsyncWideError: true
245
+ };
246
+ });
247
+ return res;
248
+ };
249
+
250
+ async function resolveValidateAgainstSchema() {
251
+ //tnw: wip!
252
+ // mapSeries(validateAgainstSchema.fields, async f => {
253
+ // if (f.type === "dropdown") {
254
+ // console.log(`type:`, f.type)
255
+ // if (f.getValues) {
256
+ // f.values = await f.getValues(props);
257
+ // }
258
+ // }
259
+ // })
260
+ }
261
+
262
+ function norm(h) {
263
+ return snakeCase(h).toLowerCase().replace(/ /g, "");
264
+ }
@@ -0,0 +1,6 @@
1
+ export const typeToCommonType = {
2
+ string: "Text",
3
+ number: "Number",
4
+ boolean: "True/False",
5
+ dropdown: "Select One"
6
+ };
@@ -0,0 +1,15 @@
1
+ import { isEqual } from "lodash-es";
2
+ import { useEffect, useRef } from "react";
3
+
4
+ export const useDeepEqualMemo = value => {
5
+ const ref = useRef();
6
+ if (!isEqual(value, ref.current)) {
7
+ ref.current = value;
8
+ }
9
+ return ref.current;
10
+ };
11
+
12
+ export const useDeepEqualEffect = (effect, deps) => {
13
+ // eslint-disable-next-line react-hooks/exhaustive-deps
14
+ return useEffect(effect, useDeepEqualMemo(deps));
15
+ };
package/useDialog.js ADDED
@@ -0,0 +1,63 @@
1
+ import React, { useCallback, useState } from "react";
2
+
3
+ /*
4
+
5
+ const {toggleDialog, comp} = useDialog({
6
+ ModalComponent: SimpleInsertData,
7
+ });
8
+
9
+ return <div>
10
+ {comp} //stick the returned dialog comp somewhere in the dom! (it should not effect layout)
11
+ {...your code here}
12
+ </div>
13
+
14
+ */
15
+ export const useDialog = ({ ModalComponent }) => {
16
+ const [isOpen, setOpen] = useState(false);
17
+ const [additionalProps, setAdditionalProps] = useState(false);
18
+ const Comp = useCallback(
19
+ () => (
20
+ <ModalComponent
21
+ hideModal={() => {
22
+ setOpen(false);
23
+ }}
24
+ hideDialog={() => {
25
+ setOpen(false);
26
+ }}
27
+ {...additionalProps}
28
+ dialogProps={{
29
+ isOpen,
30
+ ...additionalProps?.dialogProps
31
+ }}
32
+ />
33
+ ),
34
+ [ModalComponent, additionalProps, isOpen]
35
+ );
36
+
37
+ const showDialogPromise = useCallback(async (handlerName, moreProps = {}) => {
38
+ return new Promise(resolve => {
39
+ //return a promise that can be awaited
40
+ setAdditionalProps({
41
+ hideModal: () => {
42
+ //override hideModal to resolve also
43
+ setOpen(false);
44
+ resolve({});
45
+ },
46
+ hideDialog: () => {
47
+ //override hideModal to resolve also
48
+ setOpen(false);
49
+ resolve({});
50
+ },
51
+ [handlerName]: r => {
52
+ setOpen(false);
53
+ resolve(r || {});
54
+ },
55
+ //pass any additional props to the dialog
56
+ ...moreProps
57
+ });
58
+ setOpen(true); //open the dialog
59
+ });
60
+ }, []);
61
+
62
+ return { Comp, showDialogPromise };
63
+ };
@@ -0,0 +1,9 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ export const useStableReference = value => {
4
+ const ref = useRef();
5
+ useEffect(() => {
6
+ ref.current = value;
7
+ }, [value]);
8
+ return ref;
9
+ };
@@ -0,0 +1,38 @@
1
+ import { useCallback } from "react";
2
+ import { useDispatch, useSelector } from "react-redux";
3
+ import { change, initialize } from "redux-form";
4
+
5
+ export const useTableEntities = tableFormName => {
6
+ const dispatch = useDispatch();
7
+ const selectTableEntities = useCallback(
8
+ (entities = []) => {
9
+ initialize(tableFormName, {}, true, {
10
+ keepDirty: true,
11
+ updateUnregisteredFields: true,
12
+ keepValues: true
13
+ });
14
+ const selectedEntityIdMap = {};
15
+ entities.forEach(entity => {
16
+ selectedEntityIdMap[entity.id] = {
17
+ entity,
18
+ time: Date.now()
19
+ };
20
+ });
21
+ dispatch(
22
+ change(
23
+ tableFormName,
24
+ "reduxFormSelectedEntityIdMap",
25
+ selectedEntityIdMap
26
+ )
27
+ );
28
+ },
29
+ [dispatch, tableFormName]
30
+ );
31
+
32
+ const { allOrderedEntities, selectedEntities } = useSelector(state => ({
33
+ allOrderedEntities: state.form?.[tableFormName]?.values?.allOrderedEntities,
34
+ selectedEntities:
35
+ state.form?.[tableFormName]?.values?.reduxFormSelectedEntityIdMap
36
+ }));
37
+ return { selectTableEntities, allOrderedEntities, selectedEntities };
38
+ };
@@ -0,0 +1,19 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ // useful for debugging rerender errors
4
+ export const useTraceUpdate = props => {
5
+ const prev = useRef(props);
6
+ useEffect(() => {
7
+ const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
8
+ if (prev.current[k] !== v) {
9
+ ps[k] = [prev.current[k], v];
10
+ }
11
+ return ps;
12
+ }, {});
13
+ if (Object.keys(changedProps).length > 0) {
14
+ // eslint-disable-next-line no-console
15
+ console.log("Changed props:", changedProps);
16
+ }
17
+ prev.current = props;
18
+ });
19
+ };
package/utils.js ADDED
@@ -0,0 +1,37 @@
1
+ import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
2
+
3
+ export const getFieldPathToIndex = schema => {
4
+ const fieldToIndex = {};
5
+ schema.fields.forEach((f, i) => {
6
+ fieldToIndex[f.path] = i;
7
+ });
8
+ return fieldToIndex;
9
+ };
10
+
11
+ export const defaultParsePaste = str => {
12
+ return str.split(/\r\n|\n|\r/).map(row => row.split("\t"));
13
+ };
14
+
15
+ export const getEntityIdToEntity = entities => {
16
+ const entityIdToEntity = {};
17
+ entities.forEach((e, i) => {
18
+ entityIdToEntity[getIdOrCodeOrIndex(e, i)] = { e, i };
19
+ });
20
+ return entityIdToEntity;
21
+ };
22
+
23
+ const endsWithNumber = str => {
24
+ return /[0-9]+$/.test(str);
25
+ };
26
+
27
+ export const getNumberStrAtEnd = str => {
28
+ if (endsWithNumber(str)) {
29
+ return str.match(/[0-9]+$/)[0];
30
+ }
31
+
32
+ return null;
33
+ };
34
+
35
+ export const stripNumberAtEnd = str => {
36
+ return str?.replace?.(getNumberStrAtEnd(str), "");
37
+ };