@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,15 @@
1
+ import React from "react";
2
+ import { ReactTableDefaults } from "@teselagen/react-table";
3
+ const { LoadingComponent } = ReactTableDefaults;
4
+
5
+ function DisabledLoadingComponent({ disabled, loading, loadingText }) {
6
+ return (
7
+ <LoadingComponent
8
+ className={disabled ? "disabled" : ""}
9
+ loading={loading}
10
+ loadingText={disabled ? "" : loadingText}
11
+ />
12
+ );
13
+ }
14
+
15
+ export default DisabledLoadingComponent;
@@ -0,0 +1,199 @@
1
+ import React, { useState } from "react";
2
+ import { map, isEmpty, noop, startCase } from "lodash-es";
3
+ import {
4
+ Button,
5
+ Checkbox,
6
+ Menu,
7
+ MenuItem,
8
+ Classes,
9
+ InputGroup,
10
+ Popover,
11
+ Switch
12
+ } from "@blueprintjs/core";
13
+ import { getCCDisplayName } from "./utils/queryParams";
14
+
15
+ const DisplayOptions = ({
16
+ compact,
17
+ extraCompact,
18
+ disabled,
19
+ hideDisplayOptionsIcon,
20
+ resetDefaultVisibility = noop,
21
+ updateColumnVisibility = noop,
22
+ updateTableDisplayDensity,
23
+ showForcedHiddenColumns,
24
+ setShowForcedHidden,
25
+ hasOptionForForcedHidden,
26
+ schema
27
+ }) => {
28
+ const [isOpen, setIsOpen] = useState(false);
29
+ const [searchTerms, setSearchTerms] = useState({});
30
+
31
+ const changeTableDensity = e => {
32
+ updateTableDisplayDensity(e.target.value);
33
+ setIsOpen(false);
34
+ };
35
+
36
+ const toggleForcedHidden = e => setShowForcedHidden(e.target.checked);
37
+
38
+ if (hideDisplayOptionsIcon) {
39
+ return null; //don't show antyhing!
40
+ }
41
+ const { fields } = schema;
42
+ const fieldGroups = {};
43
+ const mainFields = [];
44
+
45
+ fields.forEach(field => {
46
+ if (field.hideInMenu) return;
47
+ if (!field.fieldGroup) return mainFields.push(field);
48
+ if (!fieldGroups[field.fieldGroup]) fieldGroups[field.fieldGroup] = [];
49
+ fieldGroups[field.fieldGroup].push(field);
50
+ });
51
+
52
+ let numVisible = 0;
53
+
54
+ const getFieldCheckbox = (field, i) => {
55
+ const { displayName, isHidden, isForcedHidden, path } = field;
56
+ if (isForcedHidden) return;
57
+ if (!isHidden) numVisible++;
58
+ return (
59
+ <Checkbox
60
+ name={`${path}-${i}`}
61
+ key={path || i}
62
+ onChange={() => {
63
+ if (numVisible <= 1 && !isHidden) {
64
+ return window.toastr.warning(
65
+ "We have to display at least one column :)"
66
+ );
67
+ }
68
+ updateColumnVisibility({ shouldShow: isHidden, path });
69
+ }}
70
+ checked={!isHidden}
71
+ label={displayName}
72
+ />
73
+ );
74
+ };
75
+
76
+ let fieldGroupMenu;
77
+ if (!isEmpty(fieldGroups)) {
78
+ fieldGroupMenu = map(fieldGroups, (groupFields, groupName) => {
79
+ const searchTerm = searchTerms[groupName] || "";
80
+ const anyVisible = groupFields.some(
81
+ field => !field.isHidden && !field.isForcedHidden
82
+ );
83
+ const anyNotForcedHidden = groupFields.some(
84
+ field => !field.isForcedHidden
85
+ );
86
+ if (!anyNotForcedHidden) return;
87
+ return (
88
+ <MenuItem key={groupName} text={groupName}>
89
+ <InputGroup
90
+ leftIcon="search"
91
+ value={searchTerm}
92
+ onChange={e => {
93
+ setSearchTerms(prev => ({
94
+ ...prev,
95
+ [groupName]: e.target.value
96
+ }));
97
+ }}
98
+ />
99
+ <Button
100
+ className={Classes.MINIMAL}
101
+ text={(anyVisible ? "Hide" : "Show") + " All"}
102
+ style={{ margin: "10px 0" }}
103
+ onClick={() => {
104
+ updateColumnVisibility({
105
+ shouldShow: !anyVisible,
106
+ paths: groupFields.map(field => field.path)
107
+ });
108
+ }}
109
+ />
110
+ {groupFields
111
+ .filter(
112
+ field =>
113
+ startCase(getCCDisplayName(field)) // We have to use startCase with the camelCase here because the displayName is not always a string
114
+ .toLowerCase()
115
+ .indexOf(searchTerm.toLowerCase()) > -1
116
+ )
117
+ .map(getFieldCheckbox)}
118
+ </MenuItem>
119
+ );
120
+ });
121
+ }
122
+
123
+ return (
124
+ <Popover
125
+ isOpen={isOpen}
126
+ onClose={() => setIsOpen(false)}
127
+ content={
128
+ <Menu>
129
+ <div style={{ padding: 10, paddingLeft: 20, paddingRight: 20 }}>
130
+ <h5 style={{ marginBottom: 10 }}>Display Density:</h5>
131
+ <div className={Classes.SELECT + " tg-table-display-density"}>
132
+ <select
133
+ onChange={changeTableDensity}
134
+ value={
135
+ extraCompact ? "extraCompact" : compact ? "compact" : "normal"
136
+ }
137
+ >
138
+ <option className={Classes.POPOVER_DISMISS} value="normal">
139
+ Comfortable
140
+ </option>
141
+ {/* tnr: as you can see we're calling what was "compact" Normal now in response to https://github.com/TeselaGen/lims/issues/4713 */}
142
+ <option className={Classes.POPOVER_DISMISS} value="compact">
143
+ Normal
144
+ </option>
145
+ <option
146
+ className={Classes.POPOVER_DISMISS}
147
+ value="extraCompact"
148
+ >
149
+ Compact
150
+ </option>
151
+ </select>
152
+ </div>
153
+ <h5 style={{ marginBottom: 10, marginTop: 10 }}>
154
+ Displayed Columns:
155
+ </h5>
156
+ <div style={{ maxHeight: 260, overflowY: "auto", padding: 2 }}>
157
+ {mainFields.map(getFieldCheckbox)}
158
+ </div>
159
+ <div>{fieldGroupMenu}</div>
160
+ {hasOptionForForcedHidden && (
161
+ <div style={{ marginTop: 15 }}>
162
+ <Switch
163
+ label="Show Empty Columns"
164
+ checked={showForcedHiddenColumns}
165
+ onChange={toggleForcedHidden}
166
+ />
167
+ </div>
168
+ )}
169
+ <div
170
+ style={{
171
+ width: "100%",
172
+ display: "flex",
173
+ justifyContent: "flex-end"
174
+ }}
175
+ >
176
+ <Button
177
+ onClick={resetDefaultVisibility}
178
+ title="Display Options"
179
+ minimal
180
+ >
181
+ Reset
182
+ </Button>
183
+ </div>
184
+ </div>
185
+ </Menu>
186
+ }
187
+ >
188
+ <Button
189
+ className="tg-table-display-options"
190
+ onClick={() => setIsOpen(true)}
191
+ disabled={disabled}
192
+ minimal
193
+ icon="cog"
194
+ />
195
+ </Popover>
196
+ );
197
+ };
198
+
199
+ export default DisplayOptions;
@@ -0,0 +1,61 @@
1
+ import React, { useState } from "react";
2
+ import classNames from "classnames";
3
+ import TgSelect from "../TgSelect";
4
+
5
+ export const DropdownCell = ({
6
+ options,
7
+ isMulti,
8
+ initialValue,
9
+ finishEdit,
10
+ cancelEdit,
11
+ dataTest
12
+ }) => {
13
+ const [v, setV] = useState(
14
+ isMulti
15
+ ? initialValue.split(",").map(v => ({ value: v, label: v }))
16
+ : initialValue
17
+ );
18
+ return (
19
+ <div
20
+ className={classNames("tg-dropdown-cell-edit-container", {
21
+ "tg-dropdown-cell-edit-container-multi": isMulti
22
+ })}
23
+ >
24
+ <TgSelect
25
+ small
26
+ multi={isMulti}
27
+ autoOpen
28
+ value={v}
29
+ onChange={val => {
30
+ if (isMulti) {
31
+ setV(val);
32
+ return;
33
+ }
34
+ finishEdit(val ? val.value : null);
35
+ }}
36
+ {...dataTest}
37
+ popoverProps={{
38
+ onClose: e => {
39
+ if (isMulti) {
40
+ if (e && e.key === "Escape") {
41
+ cancelEdit();
42
+ } else {
43
+ finishEdit(
44
+ v && v.map
45
+ ? v
46
+ .map(v => v.value)
47
+ .filter(v => v)
48
+ .join(",")
49
+ : v
50
+ );
51
+ }
52
+ } else {
53
+ cancelEdit();
54
+ }
55
+ }
56
+ }}
57
+ options={options.map(value => ({ label: value, value }))}
58
+ />
59
+ </div>
60
+ );
61
+ };
@@ -0,0 +1,44 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+
3
+ export const EditableCell = ({
4
+ cancelEdit,
5
+ dataTest,
6
+ finishEdit,
7
+ isNumeric,
8
+ initialValue
9
+ }) => {
10
+ const [value, setValue] = useState(initialValue);
11
+ const inputRef = useRef(null);
12
+
13
+ useEffect(() => {
14
+ if (inputRef.current) {
15
+ inputRef.current.focus();
16
+ }
17
+ }, [isNumeric]);
18
+
19
+ return (
20
+ <input
21
+ style={{
22
+ border: 0,
23
+ width: "95%",
24
+ fontSize: 12,
25
+ background: "none"
26
+ }}
27
+ ref={inputRef}
28
+ {...dataTest}
29
+ autoFocus
30
+ onKeyDown={e => {
31
+ e.stopPropagation();
32
+ if (e.key === "Enter") {
33
+ e.target.blur();
34
+ } else if (e.key === "Escape") {
35
+ cancelEdit();
36
+ }
37
+ }}
38
+ onBlur={() => finishEdit(value)}
39
+ onChange={e => setValue(e.target.value)}
40
+ type={isNumeric ? "number" : undefined}
41
+ value={value}
42
+ />
43
+ );
44
+ };
@@ -0,0 +1,388 @@
1
+ import React, { useState } from "react";
2
+ import { DateInput, DateRangeInput } from "@blueprintjs/datetime";
3
+ import { camelCase, startCase } from "lodash-es";
4
+ import classNames from "classnames";
5
+ import {
6
+ Menu,
7
+ Intent,
8
+ MenuDivider,
9
+ InputGroup,
10
+ Classes,
11
+ NumericInput,
12
+ MenuItem
13
+ } from "@blueprintjs/core";
14
+ import dayjs from "dayjs";
15
+
16
+ import getDayjsFormatter from "../utils/getDayjsFormatter";
17
+ import { onEnterHelper } from "../utils/handlerHelpers";
18
+ import DialogFooter from "../DialogFooter";
19
+ import TgSelect from "../TgSelect";
20
+ import "@teselagen/react-table/react-table.css";
21
+ import "./style.css";
22
+ import "../toastr";
23
+
24
+ const filterTypesDictionary = {
25
+ none: "",
26
+ startsWith: "text",
27
+ endsWith: "text",
28
+ contains: "text",
29
+ notContains: "text",
30
+ isExactly: "text",
31
+ isEmpty: "text",
32
+ notEmpty: "text",
33
+ inList: "list",
34
+ notInList: "list",
35
+ true: "boolean",
36
+ false: "boolean",
37
+ dateIs: "date",
38
+ notBetween: "dateRange",
39
+ isBetween: "dateRange",
40
+ isBefore: "date",
41
+ isAfter: "date",
42
+ greaterThan: "number",
43
+ lessThan: "number",
44
+ inRange: "numberRange",
45
+ outsideRange: "numberRange",
46
+ equalTo: "number",
47
+ regex: "text"
48
+ };
49
+
50
+ const isInvalidFilterValue = value => {
51
+ if (Array.isArray(value) && value.length) {
52
+ return value.some(item => isInvalidFilterValue(item));
53
+ }
54
+ return value === "" || value === undefined || value.length === 0;
55
+ };
56
+
57
+ const FilterAndSortMenu = ({
58
+ dataType,
59
+ togglePopover,
60
+ filterOn,
61
+ addFilters,
62
+ removeSingleFilter,
63
+ currentFilter
64
+ }) => {
65
+ const [selectedFilter, setSelectedFilter] = useState(
66
+ currentFilter?.selectedFilter ?? camelCase(getFilterMenuItems(dataType)[0])
67
+ );
68
+ const [filterValue, setFilterValue] = useState(
69
+ currentFilter?.filterValue ?? ""
70
+ );
71
+
72
+ const handleFilterChange = selectedFilter => {
73
+ if (
74
+ filterValue &&
75
+ !Array.isArray(filterValue) &&
76
+ filterTypesDictionary[selectedFilter] === "list"
77
+ ) {
78
+ setFilterValue(filterValue?.split(" ") || []);
79
+ } else if (
80
+ filterTypesDictionary[selectedFilter] === "text" &&
81
+ Array.isArray(filterValue)
82
+ ) {
83
+ setFilterValue(filterValue.join(" "));
84
+ }
85
+ setSelectedFilter(camelCase(selectedFilter));
86
+ };
87
+
88
+ const handleFilterValueChange = filterValue => setFilterValue(filterValue);
89
+
90
+ const handleFilterSubmit = () => {
91
+ const ccSelectedFilter = camelCase(selectedFilter);
92
+ let filterValToUse = filterValue;
93
+ if (ccSelectedFilter === "true" || ccSelectedFilter === "false") {
94
+ //manually set the filterValue because none is set when type=boolean
95
+ filterValToUse = ccSelectedFilter;
96
+ } else if (ccSelectedFilter === "notEmpty") {
97
+ // manually set filter value (nothing is selected by user)
98
+ filterValToUse = true;
99
+ } else if (ccSelectedFilter === "isEmpty") {
100
+ // manually set filter value (nothing is selected by user)
101
+ filterValToUse = false;
102
+ } else if (
103
+ ccSelectedFilter === "inList" ||
104
+ ccSelectedFilter === "notInList"
105
+ ) {
106
+ if (dataType === "number") {
107
+ filterValToUse =
108
+ filterValue &&
109
+ filterValue.map(val => parseFloat(val.replaceAll(",", "")));
110
+ }
111
+ }
112
+
113
+ if (isInvalidFilterValue(filterValToUse)) {
114
+ togglePopover();
115
+ return removeSingleFilter(filterOn);
116
+ }
117
+ addFilters([
118
+ {
119
+ filterOn,
120
+ selectedFilter: ccSelectedFilter,
121
+ filterValue: filterValToUse
122
+ }
123
+ ]);
124
+ togglePopover();
125
+ };
126
+
127
+ const filterMenuItems = getFilterMenuItems(dataType);
128
+ const ccSelectedFilter = camelCase(selectedFilter);
129
+ const requiresValue = ccSelectedFilter && ccSelectedFilter !== "none";
130
+
131
+ return (
132
+ <Menu className="data-table-header-menu">
133
+ <div className="custom-menu-item">
134
+ <div className={classNames(Classes.SELECT, Classes.FILL)}>
135
+ <select
136
+ onChange={function (e) {
137
+ const ccSelectedFilter = camelCase(e.target.value);
138
+ handleFilterChange(ccSelectedFilter);
139
+ }}
140
+ value={ccSelectedFilter}
141
+ >
142
+ {filterMenuItems.map(function (menuItem, index) {
143
+ return (
144
+ <option key={index} value={camelCase(menuItem)}>
145
+ {menuItem}
146
+ </option>
147
+ );
148
+ })}
149
+ </select>
150
+ </div>
151
+ </div>
152
+ <div className="custom-menu-item">
153
+ <FilterInput
154
+ dataType={dataType}
155
+ requiresValue={requiresValue}
156
+ handleFilterSubmit={handleFilterSubmit}
157
+ filterValue={filterValue}
158
+ handleFilterValueChange={handleFilterValueChange}
159
+ filterSubType={camelCase(selectedFilter)}
160
+ filterType={filterTypesDictionary[camelCase(selectedFilter)]}
161
+ />
162
+ </div>
163
+ <MenuDivider />
164
+ <DialogFooter
165
+ secondaryClassName={Classes.POPOVER_DISMISS}
166
+ onClick={() => {
167
+ handleFilterSubmit();
168
+ }}
169
+ intent={Intent.SUCCESS}
170
+ text="Filter"
171
+ secondaryText="Clear"
172
+ secondaryIntent={Intent.DANGER}
173
+ secondaryAction={() => {
174
+ if (currentFilter) removeSingleFilter(currentFilter.filterOn);
175
+ }}
176
+ />
177
+ </Menu>
178
+ );
179
+ };
180
+
181
+ export default FilterAndSortMenu;
182
+
183
+ const dateMinMaxHelpers = {
184
+ minDate: dayjs().subtract(25, "years").toDate(),
185
+ maxDate: dayjs().add(25, "years").toDate()
186
+ };
187
+
188
+ const renderCreateNewOption = (query, active, handleClick) => (
189
+ <MenuItem
190
+ icon="add"
191
+ text={query}
192
+ active={active}
193
+ onClick={handleClick}
194
+ shouldDismissPopover={false}
195
+ />
196
+ );
197
+
198
+ const FilterInput = ({
199
+ handleFilterValueChange,
200
+ handleFilterSubmit,
201
+ filterValue,
202
+ filterSubType,
203
+ filterType
204
+ }) => {
205
+ //Options: Text, Single number (before, after, equals), 2 numbers (range),
206
+ //Single Date (before, after, on), 2 dates (range)
207
+ let inputGroup = <div />;
208
+ switch (filterType) {
209
+ case "text":
210
+ inputGroup =
211
+ filterSubType === "notEmpty" || filterSubType === "isEmpty" ? (
212
+ <div />
213
+ ) : (
214
+ <div className="custom-menu-item">
215
+ <InputGroup
216
+ placeholder="Value"
217
+ onChange={function (e) {
218
+ handleFilterValueChange(e.target.value);
219
+ }}
220
+ autoFocus
221
+ {...onEnterHelper(handleFilterSubmit)}
222
+ value={filterValue}
223
+ />
224
+ </div>
225
+ );
226
+ break;
227
+ case "list":
228
+ inputGroup = (
229
+ <div className="custom-menu-item">
230
+ <TgSelect
231
+ placeholder="Add item"
232
+ renderCreateNewOption={renderCreateNewOption}
233
+ noResults={null}
234
+ multi={true}
235
+ creatable={true}
236
+ value={(filterValue || []).map(val => ({
237
+ label: val,
238
+ value: val
239
+ }))}
240
+ onChange={selectedOptions => {
241
+ selectedOptions.some(opt => opt.value === "")
242
+ ? handleFilterSubmit()
243
+ : handleFilterValueChange(
244
+ selectedOptions.map(opt => opt.value)
245
+ );
246
+ }}
247
+ options={[]}
248
+ />
249
+ </div>
250
+ );
251
+ break;
252
+ case "number":
253
+ inputGroup = (
254
+ <div className="custom-menu-item">
255
+ <NumericInput
256
+ placeholder="Value"
257
+ onValueChange={function (numVal) {
258
+ handleFilterValueChange(isNaN(numVal) ? 0 : numVal);
259
+ }}
260
+ autoFocus
261
+ {...onEnterHelper(handleFilterSubmit)}
262
+ value={filterValue}
263
+ />
264
+ </div>
265
+ );
266
+ break;
267
+ case "numberRange":
268
+ inputGroup = (
269
+ <div className="custom-menu-item">
270
+ <NumericInput
271
+ placeholder="Low"
272
+ onValueChange={function (numVal) {
273
+ handleFilterValueChange([
274
+ isNaN(numVal) ? 0 : numVal,
275
+ filterValue[1]
276
+ ]);
277
+ }}
278
+ {...onEnterHelper(handleFilterSubmit)}
279
+ value={filterValue && filterValue[0]}
280
+ />
281
+ <NumericInput
282
+ placeholder="High"
283
+ onValueChange={function (numVal) {
284
+ handleFilterValueChange([
285
+ filterValue[0],
286
+ isNaN(numVal) ? 0 : numVal
287
+ ]);
288
+ }}
289
+ {...onEnterHelper(handleFilterSubmit)}
290
+ value={filterValue && filterValue[1]}
291
+ />
292
+ </div>
293
+ );
294
+ break;
295
+ case "date":
296
+ inputGroup = (
297
+ <div className="custom-menu-item">
298
+ <DateInput
299
+ value={filterValue ? dayjs(filterValue).toDate() : undefined}
300
+ {...getDayjsFormatter("L")}
301
+ {...dateMinMaxHelpers}
302
+ onChange={selectedDates => {
303
+ handleFilterValueChange(selectedDates);
304
+ }}
305
+ />
306
+ </div>
307
+ );
308
+ break;
309
+ case "dateRange":
310
+ // eslint-disable-next-line no-case-declarations
311
+ let filterValueToUse;
312
+ if (Array.isArray(filterValue)) {
313
+ filterValueToUse = filterValue;
314
+ } else {
315
+ filterValueToUse =
316
+ filterValue && filterValue.split && filterValue.split(";");
317
+ }
318
+ inputGroup = (
319
+ <div className="custom-menu-item">
320
+ <DateRangeInput
321
+ value={
322
+ filterValueToUse && filterValueToUse[0] && filterValueToUse[1]
323
+ ? [new Date(filterValueToUse[0]), new Date(filterValueToUse[1])]
324
+ : undefined
325
+ }
326
+ popoverProps={{
327
+ captureDismiss: true
328
+ }}
329
+ formatDate={date => (date == null ? "" : date.toLocaleDateString())}
330
+ parseDate={str => new Date(Date.parse(str))}
331
+ placeholder="JS Date"
332
+ {...dateMinMaxHelpers}
333
+ onChange={selectedDates => {
334
+ if (selectedDates[0] && selectedDates[1]) {
335
+ handleFilterValueChange(selectedDates);
336
+ }
337
+ }}
338
+ />
339
+ </div>
340
+ );
341
+ break;
342
+ default:
343
+ // to do
344
+ }
345
+ return inputGroup;
346
+ };
347
+
348
+ function getFilterMenuItems(dataType) {
349
+ let filterMenuItems = [];
350
+ if (dataType === "string") {
351
+ filterMenuItems = [
352
+ "contains",
353
+ "notContains",
354
+ "startsWith",
355
+ "endsWith",
356
+ "isExactly",
357
+ "regex",
358
+ "inList",
359
+ "notInList",
360
+ "isEmpty",
361
+ "notEmpty"
362
+ ];
363
+ } else if (dataType === "lookup") {
364
+ filterMenuItems = [
365
+ "contains",
366
+ "notContains",
367
+ "startsWith",
368
+ "endsWith",
369
+ "isExactly",
370
+ "regex"
371
+ ];
372
+ } else if (dataType === "boolean") {
373
+ filterMenuItems = ["true", "false"];
374
+ } else if (dataType === "number" || dataType === "integer") {
375
+ filterMenuItems = [
376
+ "greaterThan",
377
+ "lessThan",
378
+ "inRange",
379
+ "outsideRange",
380
+ "equalTo",
381
+ "inList",
382
+ "notInList"
383
+ ];
384
+ } else if (dataType === "timestamp") {
385
+ filterMenuItems = ["isBetween", "notBetween", "isBefore", "isAfter"];
386
+ }
387
+ return filterMenuItems.map(item => startCase(item));
388
+ }