@teselagen/ui 0.7.33-beta.5 → 0.7.33

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.js +33 -0
  2. package/AssignDefaultsModeContext.js +22 -0
  3. package/CellDragHandle.js +132 -0
  4. package/ColumnFilterMenu.js +62 -0
  5. package/Columns.js +979 -0
  6. package/DataTable/utils/queryParams.d.ts +18 -9
  7. package/DisabledLoadingComponent.js +15 -0
  8. package/DisplayOptions.js +199 -0
  9. package/DropdownButton.js +36 -0
  10. package/DropdownCell.js +61 -0
  11. package/EditableCell.js +44 -0
  12. package/FillWindow.css +6 -0
  13. package/FillWindow.js +69 -0
  14. package/FilterAndSortMenu.js +391 -0
  15. package/FormSeparator.js +9 -0
  16. package/LoadingDots.js +14 -0
  17. package/MatchHeaders.js +234 -0
  18. package/PagingTool.js +225 -0
  19. package/RenderCell.js +191 -0
  20. package/SearchBar.js +69 -0
  21. package/SimpleStepViz.js +22 -0
  22. package/SortableColumns.js +100 -0
  23. package/TableFormTrackerContext.js +10 -0
  24. package/Tag.js +112 -0
  25. package/ThComponent.js +44 -0
  26. package/TimelineEvent.js +31 -0
  27. package/UploadCsvWizard.css +4 -0
  28. package/UploadCsvWizard.js +719 -0
  29. package/Uploader.js +1278 -0
  30. package/adHoc.js +10 -0
  31. package/autoTooltip.js +201 -0
  32. package/basicHandleActionsWithFullState.js +14 -0
  33. package/browserUtils.js +3 -0
  34. package/combineReducersWithFullState.js +14 -0
  35. package/commandControls.js +82 -0
  36. package/commandUtils.js +112 -0
  37. package/constants.js +1 -0
  38. package/convertSchema.js +69 -0
  39. package/customIcons.js +361 -0
  40. package/dataTableEnhancer.js +41 -0
  41. package/defaultFormatters.js +32 -0
  42. package/defaultValidators.js +40 -0
  43. package/determineBlackOrWhiteTextColor.js +4 -0
  44. package/editCellHelper.js +44 -0
  45. package/formatPasteData.js +16 -0
  46. package/getAllRows.js +11 -0
  47. package/getCellCopyText.js +7 -0
  48. package/getCellInfo.js +36 -0
  49. package/getCellVal.js +20 -0
  50. package/getDayjsFormatter.js +35 -0
  51. package/getFieldPathToField.js +7 -0
  52. package/getIdOrCodeOrIndex.js +9 -0
  53. package/getLastSelectedEntity.js +11 -0
  54. package/getNewEntToSelect.js +25 -0
  55. package/getNewName.js +31 -0
  56. package/getRowCopyText.js +28 -0
  57. package/getTableConfigFromStorage.js +5 -0
  58. package/getTextFromEl.js +28 -0
  59. package/getVals.js +8 -0
  60. package/handleCopyColumn.js +21 -0
  61. package/handleCopyHelper.js +15 -0
  62. package/handleCopyRows.js +23 -0
  63. package/handleCopyTable.js +16 -0
  64. package/handlerHelpers.js +24 -0
  65. package/hotkeyUtils.js +131 -0
  66. package/index.cjs.js +970 -826
  67. package/index.d.ts +0 -1
  68. package/index.es.js +970 -826
  69. package/index.js +196 -0
  70. package/isBeingCalledExcessively.js +31 -0
  71. package/isBottomRightCornerOfRectangle.js +20 -0
  72. package/isEntityClean.js +15 -0
  73. package/isTruthy.js +12 -0
  74. package/isValueEmpty.js +3 -0
  75. package/itemUpload.js +84 -0
  76. package/menuUtils.js +433 -0
  77. package/package.json +1 -2
  78. package/popoverOverflowModifiers.js +11 -0
  79. package/primarySelectedValue.js +1 -0
  80. package/pureNoFunc.js +31 -0
  81. package/queryParams.js +1058 -0
  82. package/removeCleanRows.js +22 -0
  83. package/renderOnDoc.js +32 -0
  84. package/rerenderOnWindowResize.js +26 -0
  85. package/rowClick.js +181 -0
  86. package/selection.js +8 -0
  87. package/showAppSpinner.js +12 -0
  88. package/showDialogOnDocBody.js +33 -0
  89. package/showProgressToast.js +22 -0
  90. package/sortify.js +73 -0
  91. package/src/DataTable/index.js +1 -1
  92. package/src/DataTable/utils/filterLocalEntitiesToHasura.js +14 -0
  93. package/src/DataTable/utils/filterLocalEntitiesToHasura.test.js +49 -0
  94. package/src/DataTable/utils/queryParams.js +12 -9
  95. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +146 -143
  96. package/style.css +29 -0
  97. package/tagUtils.js +45 -0
  98. package/tgFormValues.js +35 -0
  99. package/tg_modalState.js +47 -0
  100. package/throwFormError.js +16 -0
  101. package/toastr.js +148 -0
  102. package/tryToMatchSchemas.js +264 -0
  103. package/typeToCommonType.js +6 -0
  104. package/useDeepEqualMemo.js +15 -0
  105. package/useDialog.js +63 -0
  106. package/useStableReference.js +9 -0
  107. package/useTableEntities.js +38 -0
  108. package/useTraceUpdate.js +19 -0
  109. package/utils.js +37 -0
  110. package/validateTableWideErrors.js +160 -0
  111. package/viewColumn.js +97 -0
  112. package/withField.js +20 -0
  113. package/withFields.js +11 -0
  114. package/withLocalStorage.js +11 -0
  115. package/withSelectTableRecords.js +43 -0
  116. package/withSelectedEntities.js +65 -0
  117. package/withStore.js +10 -0
  118. package/withTableParams.js +301 -0
  119. package/wrapDialog.js +116 -0
package/viewColumn.js ADDED
@@ -0,0 +1,97 @@
1
+ import React from "react";
2
+ import { Icon, Button, Tooltip } from "@blueprintjs/core";
3
+ import { reduce } from "lodash-es";
4
+
5
+ export const viewColumn = {
6
+ width: 35,
7
+ noEllipsis: true,
8
+ hideInMenu: true,
9
+ immovable: true,
10
+ type: "action",
11
+ render: () => {
12
+ return <Icon className="dt-eyeIcon" icon="eye-open" />;
13
+ }
14
+ };
15
+
16
+ export const openColumn = ({ onDoubleClick, history }) => ({
17
+ ...viewColumn,
18
+ render: (val, record, rowInfo) => {
19
+ return (
20
+ <Tooltip content="Open">
21
+ <Button
22
+ onClick={e => {
23
+ e.stopPropagation();
24
+ onDoubleClick &&
25
+ onDoubleClick(rowInfo.original, rowInfo.index, history);
26
+ }}
27
+ minimal
28
+ small
29
+ className="dt-eyeIcon"
30
+ icon="document-open"
31
+ />
32
+ </Tooltip>
33
+ );
34
+ }
35
+ });
36
+
37
+ export const multiViewColumn = {
38
+ ...viewColumn,
39
+ columnHeader: ({ recordIdToIsVisibleMap, setRecordIdToIsVisibleMap }) => {
40
+ const allVisible = reduce(
41
+ recordIdToIsVisibleMap,
42
+ (acc, val) => acc && val,
43
+ true
44
+ );
45
+ return (
46
+ <Tooltip content={allVisible ? "Hide All" : "Show All"}>
47
+ <Button
48
+ className={`showHideAllButton-${allVisible ? "visible" : "hidden"}`}
49
+ minimal
50
+ onClick={() => {
51
+ setRecordIdToIsVisibleMap(
52
+ reduce(
53
+ recordIdToIsVisibleMap,
54
+ (acc, val, key) => {
55
+ acc[key] = !allVisible;
56
+ return acc;
57
+ },
58
+ {}
59
+ )
60
+ );
61
+ }}
62
+ icon={allVisible ? "eye-open" : "eye-off"}
63
+ />
64
+ </Tooltip>
65
+ );
66
+ },
67
+ render: (
68
+ val,
69
+ record,
70
+ row,
71
+ { recordIdToIsVisibleMap, setRecordIdToIsVisibleMap }
72
+ ) => {
73
+ if (!recordIdToIsVisibleMap) {
74
+ return null;
75
+ }
76
+ const isVisible = recordIdToIsVisibleMap[record.id];
77
+ return (
78
+ <Tooltip content={isVisible ? "Hide" : "Show"}>
79
+ <Button
80
+ onClick={e => {
81
+ e.stopPropagation();
82
+ e.preventDefault();
83
+ setRecordIdToIsVisibleMap(
84
+ Object.assign({}, recordIdToIsVisibleMap, {
85
+ [record.id]: !isVisible
86
+ })
87
+ );
88
+ }}
89
+ minimal
90
+ small
91
+ className={`showHideButton-${isVisible ? "visible" : "hidden"}-${record.id}`}
92
+ icon={isVisible ? "eye-open" : "eye-off"}
93
+ />
94
+ </Tooltip>
95
+ );
96
+ }
97
+ };
package/withField.js ADDED
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { Field } from "redux-form";
3
+ import { fieldRequired } from "../FormComponents/utils";
4
+
5
+ //simple enhancer that wraps a component in a redux <Field/> component
6
+ //all options are passed as props to <Field/>
7
+ export default function WithField(fieldProps) {
8
+ return function AddFieldHOC(Component) {
9
+ return function AddField({ isRequired, ...rest }) {
10
+ return (
11
+ <Field
12
+ {...(isRequired && { validate: fieldRequired })}
13
+ {...fieldProps}
14
+ {...rest}
15
+ component={Component}
16
+ />
17
+ );
18
+ };
19
+ };
20
+ }
package/withFields.js ADDED
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { Fields } from "redux-form";
3
+ //simple enhancer that wraps a component in a redux <Fields/> component
4
+ //all options are passed as props to <Fields/>
5
+ export default function WithFields(fieldsProps) {
6
+ return function AddFieldsHOC(Component) {
7
+ return function AddFields(props) {
8
+ return <Fields {...fieldsProps} {...props} component={Component} />;
9
+ };
10
+ };
11
+ }
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { Field } from "redux-form";
3
+ //simple enhancer that wraps a component in a redux <Field/> component
4
+ //all options are passed as props to <Field/>
5
+ export default function WithField(fieldProps) {
6
+ return function AddFieldHOC(Component) {
7
+ return function AddField(props) {
8
+ return <Field {...fieldProps} {...props} component={Component} />;
9
+ };
10
+ };
11
+ }
@@ -0,0 +1,43 @@
1
+ /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
2
+ import { compose, withHandlers } from "recompose";
3
+ import { connect } from "react-redux";
4
+ import { change, initialize } from "redux-form";
5
+
6
+ export default function (_tableFormName, propName = "selectTableRecords") {
7
+ return compose(
8
+ connect(null, {
9
+ changeFormValue: change,
10
+ initializeForm: initialize
11
+ }),
12
+ withHandlers({
13
+ [propName]:
14
+ props =>
15
+ (_records = []) => {
16
+ let tableFormName = _tableFormName;
17
+ if (typeof _tableFormName === "function") {
18
+ tableFormName = _tableFormName(props);
19
+ }
20
+ // initialize if needed so that the values will stay
21
+ props.initializeForm(tableFormName, {}, true, {
22
+ keepDirty: true,
23
+ updateUnregisteredFields: true,
24
+ keepValues: true
25
+ });
26
+ const selectedEntityIdMap = {};
27
+ let records = _records;
28
+ if (_records && !Array.isArray(_records)) records = [records];
29
+ records.forEach(record => {
30
+ selectedEntityIdMap[record.id] = {
31
+ entity: record,
32
+ time: Date.now()
33
+ };
34
+ });
35
+ props.changeFormValue(
36
+ tableFormName,
37
+ "reduxFormSelectedEntityIdMap",
38
+ selectedEntityIdMap
39
+ );
40
+ }
41
+ })
42
+ );
43
+ }
@@ -0,0 +1,65 @@
1
+ import { formValueSelector } from "redux-form";
2
+ import { reduce } from "lodash-es";
3
+ import { connect } from "react-redux";
4
+ /**
5
+ * @param {*string} formName
6
+ * @param {*string} formName
7
+ * @param {*string} formName
8
+ * @param {*string} ...etc
9
+ * adds a new prop `${formName}SelectedEntities` eg sequenceTableSelectedEntities
10
+ */
11
+ export default function withSelectedEntities(...formNames) {
12
+ if (!formNames.length) {
13
+ throw new Error(
14
+ "You need to pass at least one arg to withSelectedEntities"
15
+ );
16
+ }
17
+ if (typeof formNames[0] === "string") {
18
+ //NEW WAY
19
+ return connect(state => {
20
+ return formNames.reduce((acc, formName) => {
21
+ acc[formName + "SelectedEntities"] = getRecordsFromReduxForm(
22
+ state,
23
+ formName
24
+ );
25
+ return acc;
26
+ }, {});
27
+ });
28
+ } else {
29
+ //OLD WAY:
30
+ const { formName, name } = formNames[0];
31
+ if (!formName) {
32
+ throw new Error(
33
+ "Please pass a {formName} option when using withSelectedEntities"
34
+ );
35
+ }
36
+ return connect(state => {
37
+ return {
38
+ [name || "selectedEntities"]: getRecordsFromReduxForm(state, formName)
39
+ };
40
+ });
41
+ }
42
+ }
43
+
44
+ export function getRecordsFromReduxForm(state, formName) {
45
+ const selector = formValueSelector(formName);
46
+ return getRecordsFromIdMap(selector(state, "reduxFormSelectedEntityIdMap"));
47
+ }
48
+
49
+ export function getRecordsFromIdMap(idMap = {}) {
50
+ return reduce(
51
+ idMap,
52
+ (acc, item) => {
53
+ if (item && item.entity) acc.push(item);
54
+ return acc;
55
+ },
56
+ []
57
+ )
58
+ .sort((a, b) => a.rowIndex - b.rowIndex)
59
+ .map(item => item.entity);
60
+ }
61
+
62
+ export function getSelectedEntities(storeOrState, formName) {
63
+ const state = storeOrState.getState ? storeOrState.getState() : storeOrState;
64
+ return getRecordsFromReduxForm(state, formName);
65
+ }
package/withStore.js ADDED
@@ -0,0 +1,10 @@
1
+ import { useStore } from "react-redux";
2
+ import React from "react";
3
+
4
+ const withStore = Component => {
5
+ return props => {
6
+ const store = useStore();
7
+ return <Component {...props} store={store} />;
8
+ };
9
+ };
10
+ export default withStore;
@@ -0,0 +1,301 @@
1
+ import React, { useCallback, useMemo } from "react";
2
+ import { change as _change, formValueSelector } from "redux-form";
3
+ import { useDispatch, useSelector } from "react-redux";
4
+ import convertSchema from "./convertSchema";
5
+ import {
6
+ makeDataTableHandlers,
7
+ getQueryParams,
8
+ setCurrentParamsOnUrl,
9
+ getCurrentParamsFromUrl,
10
+ getCCDisplayName
11
+ } from "./queryParams";
12
+ import { withRouter } from "react-router-dom";
13
+ import getTableConfigFromStorage from "./getTableConfigFromStorage";
14
+ import { useDeepEqualMemo } from "../../utils/hooks/useDeepEqualMemo";
15
+ import { branch, compose } from "recompose";
16
+
17
+ /**
18
+ * Note all these options can be passed at Design Time or at Runtime (like reduxForm())
19
+ *
20
+ * @export
21
+ *
22
+ * @param {compOrOpts} compOrOpts
23
+ * @typedef {object} compOrOpts
24
+ * @property {*string} formName - required unique identifier for the table
25
+ * @property {Object | Function} schema - The data table schema or a function returning it. The function wll be called with props as the argument.
26
+ * @property {boolean} urlConnected - whether the table should connect to/update the URL
27
+ * @property {boolean} withSelectedEntities - whether or not to pass the selected entities
28
+ * @property {boolean} isCodeModel - whether the model is keyed by code instead of id in the db
29
+ * @property {object} defaults - tableParam defaults such as pageSize, filter, etc
30
+ * @property {boolean} noOrderError - won't console an error if an order is not found on schema
31
+ */
32
+ export const useTableParams = props => {
33
+ const {
34
+ additionalFilter,
35
+ additionalOrFilter,
36
+ controlled_pageSize,
37
+ defaults: _defaults,
38
+ doNotCoercePageSize,
39
+ entities,
40
+ formName = "tgDataTable",
41
+ history,
42
+ initialValues,
43
+ isCodeModel,
44
+ isInfinite,
45
+ isLocalCall = false,
46
+ isSimple,
47
+ noForm,
48
+ noOrderError,
49
+ onlyOneFilter,
50
+ orderByFirstColumn,
51
+ pageSize,
52
+ schema,
53
+ syncDisplayOptionsToDb,
54
+ tableParams: _tableParams,
55
+ urlConnected,
56
+ withDisplayOptions,
57
+ withPaging,
58
+ withSelectedEntities
59
+ } = props;
60
+
61
+ const defaults = useMemo(
62
+ () => ({
63
+ pageSize: controlled_pageSize || 25,
64
+ order: [], //[-name, statusCode] //an array of camelCase display names with - sign to denote reverse
65
+ searchTerm: "",
66
+ page: 1,
67
+ filters: [
68
+ //filters look like this:
69
+ // {
70
+ // selectedFilter: 'textContains', //camel case
71
+ // filterOn: ccDisplayName, //camel case display name if available and string, otherwise path
72
+ // filterValue: 'thomas',
73
+ // }
74
+ ],
75
+ ..._defaults
76
+ }),
77
+ [_defaults, controlled_pageSize]
78
+ );
79
+
80
+ const convertedSchema = useMemo(() => convertSchema(schema), [schema]);
81
+
82
+ if (isLocalCall) {
83
+ if (!noForm && (!formName || formName === "tgDataTable")) {
84
+ console.error(
85
+ "Please pass a unique 'formName' prop to the locally connected <DataTable/> component with schema: ",
86
+ schema
87
+ );
88
+ }
89
+ if (orderByFirstColumn && !defaults?.order?.length) {
90
+ defaults.order = [getCCDisplayName(convertedSchema.fields[0])];
91
+ }
92
+ } else {
93
+ //in user instantiated withTableParams() call
94
+ if (!formName || formName === "tgDataTable") {
95
+ console.error(
96
+ "Please pass a unique 'formName' prop to the withTableParams() with schema: ",
97
+ schema
98
+ );
99
+ }
100
+ }
101
+
102
+ const {
103
+ reduxFormQueryParams: _reduxFormQueryParams = {},
104
+ reduxFormSelectedEntityIdMap: _reduxFormSelectedEntityIdMap = {}
105
+ } = useSelector(state =>
106
+ formValueSelector(formName)(
107
+ state,
108
+ "reduxFormQueryParams",
109
+ "reduxFormSelectedEntityIdMap"
110
+ )
111
+ );
112
+
113
+ // We want to make sure we don't rerender everything unnecessary
114
+ // with redux-forms we tend to do unnecessary renders
115
+ const reduxFormQueryParams = useDeepEqualMemo(_reduxFormQueryParams);
116
+ const reduxFormSelectedEntityIdMap = useDeepEqualMemo(
117
+ _reduxFormSelectedEntityIdMap
118
+ );
119
+
120
+ const _currentParams = useMemo(() => {
121
+ const tmp =
122
+ (urlConnected
123
+ ? getCurrentParamsFromUrl(history?.location) //important to use history location and not ownProps.location because for some reason the location path lags one render behind!!
124
+ : reduxFormQueryParams) || {};
125
+
126
+ return tmp;
127
+ }, [history?.location, reduxFormQueryParams, urlConnected]);
128
+
129
+ const selectedEntities = useMemo(
130
+ () =>
131
+ withSelectedEntities
132
+ ? Object.values(reduxFormSelectedEntityIdMap)
133
+ .sort((a, b) => a.rowIndex - b.rowIndex)
134
+ .map(item => item.entity)
135
+ : undefined,
136
+ [reduxFormSelectedEntityIdMap, withSelectedEntities]
137
+ );
138
+
139
+ const currentParams = useDeepEqualMemo(_currentParams);
140
+
141
+ const defaultsToUse = useMemo(() => {
142
+ const _tableConfig = getTableConfigFromStorage(formName);
143
+ const userSetPageSize =
144
+ _tableConfig?.userSetPageSize &&
145
+ parseInt(_tableConfig.userSetPageSize, 10);
146
+ let _defaultsToUse = defaults;
147
+ if (!syncDisplayOptionsToDb && userSetPageSize) {
148
+ _defaultsToUse = _defaultsToUse || {};
149
+ _defaultsToUse.pageSize = userSetPageSize;
150
+ }
151
+
152
+ return _defaultsToUse;
153
+ }, [defaults, formName, syncDisplayOptionsToDb]);
154
+
155
+ const passingProps = useMemo(
156
+ () => ({
157
+ formName: "tgDataTable",
158
+ ...props,
159
+ pageSize: controlled_pageSize || pageSize,
160
+ defaults: defaultsToUse,
161
+ location: history?.location
162
+ }),
163
+ // We don't want to rerender this every time a prop changes
164
+ // eslint-disable-next-line react-hooks/exhaustive-deps
165
+ [controlled_pageSize, defaultsToUse, pageSize, history?.location]
166
+ );
167
+
168
+ const queryParams = useMemo(() => {
169
+ const additionalFilterToUse =
170
+ typeof additionalFilter === "function"
171
+ ? additionalFilter
172
+ : () => additionalFilter;
173
+
174
+ const additionalOrFilterToUse =
175
+ typeof additionalOrFilter === "function"
176
+ ? additionalOrFilter
177
+ : () => additionalOrFilter;
178
+
179
+ return getQueryParams({
180
+ doNotCoercePageSize,
181
+ currentParams,
182
+ entities, // for local table
183
+ urlConnected,
184
+ defaults: defaultsToUse,
185
+ schema: convertedSchema,
186
+ isInfinite: isInfinite || (isSimple && !withPaging),
187
+ isLocalCall,
188
+ additionalFilter: additionalFilterToUse,
189
+ additionalOrFilter: additionalOrFilterToUse,
190
+ noOrderError,
191
+ isCodeModel,
192
+ ownProps: passingProps
193
+ });
194
+ }, [
195
+ additionalFilter,
196
+ passingProps,
197
+ additionalOrFilter,
198
+ doNotCoercePageSize,
199
+ currentParams,
200
+ entities,
201
+ urlConnected,
202
+ defaultsToUse,
203
+ convertedSchema,
204
+ isInfinite,
205
+ isSimple,
206
+ withPaging,
207
+ isLocalCall,
208
+ noOrderError,
209
+ isCodeModel
210
+ ]);
211
+
212
+ const dispatch = useDispatch();
213
+ const change = useCallback(
214
+ (...args) => dispatch(_change(formName, ...args)),
215
+ [dispatch, formName]
216
+ );
217
+
218
+ const setNewParams = useCallback(
219
+ newParams => {
220
+ // we always will update the redux params as a workaround for withRouter not always working
221
+ // if inside a redux-connected container https://github.com/ReactTraining/react-router/issues/5037
222
+ change("reduxFormQueryParams", prev => {
223
+ let tmp = newParams;
224
+ if (typeof tmp === "function") tmp = newParams(prev);
225
+ urlConnected && setCurrentParamsOnUrl(tmp, history?.replace);
226
+ return tmp;
227
+ });
228
+ },
229
+ [change, history?.replace, urlConnected]
230
+ );
231
+
232
+ const dispatchProps = useMemo(
233
+ () =>
234
+ makeDataTableHandlers({
235
+ setNewParams,
236
+ defaults,
237
+ onlyOneFilter
238
+ }),
239
+ [defaults, onlyOneFilter, setNewParams]
240
+ );
241
+
242
+ const tableParams = useMemo(
243
+ () => ({
244
+ changeFormValue: (...args) => change(...args),
245
+ selectedEntities,
246
+ ..._tableParams,
247
+ formName,
248
+ initialValues,
249
+ isLocalCall,
250
+ schema,
251
+ currentParams,
252
+ withDisplayOptions,
253
+ ...queryParams,
254
+ ...dispatchProps,
255
+ form: formName, //this will override the default redux form name
256
+ isTableParamsConnected: true //let the table know not to do local sorting/filtering etc.
257
+ }),
258
+ [
259
+ _tableParams,
260
+ change,
261
+ currentParams,
262
+ dispatchProps,
263
+ formName,
264
+ initialValues,
265
+ isLocalCall,
266
+ queryParams,
267
+ schema,
268
+ selectedEntities,
269
+ withDisplayOptions
270
+ ]
271
+ );
272
+
273
+ return {
274
+ isLocalCall,
275
+ schema,
276
+ ...queryParams,
277
+ ...(withSelectedEntities &&
278
+ typeof withSelectedEntities === "string" && {
279
+ [withSelectedEntities]: selectedEntities
280
+ }),
281
+ currentParams,
282
+ selectedEntities,
283
+ tableParams,
284
+ urlConnected
285
+ };
286
+ };
287
+
288
+ const withTableParams = topLevelOptions =>
289
+ compose(
290
+ //don't use withRouter if noRouter is passed!
291
+ branch(({ noRouter }) => !noRouter, withRouter),
292
+ Comp => props => {
293
+ const tableParams = useTableParams({
294
+ ...topLevelOptions,
295
+ ...props
296
+ });
297
+ return <Comp {...props} {...tableParams} />;
298
+ }
299
+ );
300
+
301
+ export default withTableParams;
package/wrapDialog.js ADDED
@@ -0,0 +1,116 @@
1
+ /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
2
+ import React, { useMemo, useRef } from "react";
3
+ import { Dialog, useHotkeys } from "@blueprintjs/core";
4
+ import { noop, isFunction } from "lodash-es";
5
+ import { ResizableDraggableDialog } from ".";
6
+
7
+ export default (topLevelDialogProps = {}) =>
8
+ Component =>
9
+ props => {
10
+ const r = useRef();
11
+ const memoedHotkeys = useMemo(
12
+ () => [
13
+ {
14
+ combo: topLevelDialogProps.useCmdEnter ? "cmd+enter" : "enter",
15
+ global: true,
16
+ allowInInput: true,
17
+ onKeyDown: () => {
18
+ function doNotTriggerClick() {
19
+ //leave this here for debugging purposes
20
+ // console.log(`Not triggering dialog submit`);
21
+ }
22
+
23
+ try {
24
+ if (!document.activeElement) return doNotTriggerClick();
25
+ if (
26
+ !document.activeElement.closest(".tg-allow-dialog-form-enter")
27
+ ) {
28
+ //don't do this if you're in any type of bp multi select by default
29
+ if (document.activeElement.closest(".bp3-multi-select"))
30
+ return doNotTriggerClick();
31
+ //don't do this if there is an explicit class saying not to
32
+ if (
33
+ document.activeElement.closest(".tg-stop-dialog-form-enter")
34
+ )
35
+ return doNotTriggerClick();
36
+ //don't do this in text areas
37
+ if (document.activeElement.type === "textarea")
38
+ return doNotTriggerClick();
39
+ }
40
+ const parentEl = r.current?.closest(".bp3-dialog-container");
41
+ // eslint-disable-next-line no-inner-declarations
42
+ function triggerClick() {
43
+ parentEl?.querySelector(`button[type='submit']`).click();
44
+ }
45
+
46
+ const dialogs = document.querySelectorAll(
47
+ ".bp3-dialog-container"
48
+ );
49
+ const numDialogs = dialogs?.length;
50
+
51
+ if (numDialogs > 1) {
52
+ const topMostDialog = dialogs[numDialogs - 1];
53
+ if (topMostDialog === parentEl) {
54
+ triggerClick();
55
+ }
56
+ } else {
57
+ //just 1 dialog
58
+ triggerClick();
59
+ }
60
+ } catch (error) {
61
+ console.error(`error:`, error);
62
+ }
63
+ }
64
+ }
65
+ ],
66
+ []
67
+ );
68
+
69
+ useHotkeys(memoedHotkeys);
70
+
71
+ let otherTopLevelProps,
72
+ getDialogProps = noop;
73
+ if (isFunction(topLevelDialogProps)) {
74
+ getDialogProps = topLevelDialogProps;
75
+ } else {
76
+ const {
77
+ footerProps,
78
+ getDialogProps: _pullOff,
79
+ ...additionalProps
80
+ } = topLevelDialogProps;
81
+ otherTopLevelProps = additionalProps;
82
+ getDialogProps = topLevelDialogProps.getDialogProps || noop;
83
+ }
84
+ const { dialogProps, hideModal, ...otherProps } = props;
85
+
86
+ const extraDialogProps = {
87
+ ...otherTopLevelProps,
88
+ ...dialogProps,
89
+ ...getDialogProps(props)
90
+ };
91
+ const DialogToUse = extraDialogProps.isDraggable
92
+ ? ResizableDraggableDialog
93
+ : Dialog;
94
+ return (
95
+ <DialogToUse
96
+ canOutsideClickClose={false}
97
+ isOpen
98
+ onClose={e => {
99
+ e.stopPropagation();
100
+ if (
101
+ e.key === "Escape" &&
102
+ extraDialogProps.canEscapeKeyClose === false
103
+ ) {
104
+ return;
105
+ }
106
+ hideModal(e);
107
+ }}
108
+ {...extraDialogProps}
109
+ canEscapeKeyClose={true}
110
+ style={{ ...extraDialogProps.style }}
111
+ >
112
+ <div ref={r}></div>
113
+ <Component hideModal={hideModal} {...otherProps} />
114
+ </DialogToUse>
115
+ );
116
+ };