@teselagen/ui 0.5.23-beta.18 → 0.5.23-beta.19
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.
- package/index.cjs.js +11 -8
- package/index.es.js +11 -8
- package/package.json +1 -1
- package/src/DataTable/Columns.jsx +945 -0
- package/src/DataTable/EditabelCell.jsx +44 -0
- package/src/DataTable/index.js +11 -9
package/index.cjs.js
CHANGED
|
@@ -48360,7 +48360,7 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48360
48360
|
const {
|
|
48361
48361
|
reduxFormCellValidation: _reduxFormCellValidation,
|
|
48362
48362
|
reduxFormEditingCell,
|
|
48363
|
-
reduxFormEntities
|
|
48363
|
+
reduxFormEntities,
|
|
48364
48364
|
reduxFormQueryParams: _reduxFormQueryParams = {},
|
|
48365
48365
|
reduxFormSearchInput: _reduxFormSearchInput = "",
|
|
48366
48366
|
reduxFormSelectedEntityIdMap: _reduxFormSelectedEntityIdMap = {}
|
|
@@ -48375,7 +48375,6 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48375
48375
|
)
|
|
48376
48376
|
);
|
|
48377
48377
|
const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
|
|
48378
|
-
const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
|
|
48379
48378
|
const reduxFormQueryParams = useDeepEqualMemo(_reduxFormQueryParams);
|
|
48380
48379
|
const reduxFormSearchInput = useDeepEqualMemo(_reduxFormSearchInput);
|
|
48381
48380
|
const reduxFormSelectedEntityIdMap = useDeepEqualMemo(
|
|
@@ -48922,8 +48921,7 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48922
48921
|
}
|
|
48923
48922
|
}));
|
|
48924
48923
|
},
|
|
48925
|
-
|
|
48926
|
-
[]
|
|
48924
|
+
[change]
|
|
48927
48925
|
);
|
|
48928
48926
|
const formatAndValidateEntities = React$1.useCallback(
|
|
48929
48927
|
(entities2, { useDefaultValues, indexToStartAt } = {}) => {
|
|
@@ -49486,22 +49484,27 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
49486
49484
|
const [selectingAll, setSelectingAll] = React$1.useState(false);
|
|
49487
49485
|
React$1.useEffect(() => {
|
|
49488
49486
|
const formatAndValidateTableInitial = /* @__PURE__ */ __name(() => {
|
|
49489
|
-
const { newEnts, validationErrors } = formatAndValidateEntities(
|
|
49487
|
+
const { newEnts, validationErrors } = formatAndValidateEntities(entities);
|
|
49490
49488
|
const toKeep = {};
|
|
49491
49489
|
forEach(reduxFormCellValidation, (v2, k2) => {
|
|
49492
49490
|
if (v2 && v2._isTableAsyncWideError) {
|
|
49493
49491
|
toKeep[k2] = v2;
|
|
49494
49492
|
}
|
|
49495
49493
|
});
|
|
49496
|
-
change("reduxFormEntities",
|
|
49494
|
+
change("reduxFormEntities", (prev) => {
|
|
49495
|
+
if (!isEqual(prev, newEnts)) {
|
|
49496
|
+
return newEnts;
|
|
49497
|
+
}
|
|
49498
|
+
return prev;
|
|
49499
|
+
});
|
|
49497
49500
|
updateValidation(newEnts, __spreadValues(__spreadValues({}, toKeep), validationErrors));
|
|
49498
49501
|
}, "formatAndValidateTableInitial");
|
|
49499
49502
|
isCellEditable && formatAndValidateTableInitial();
|
|
49500
49503
|
}, [
|
|
49501
|
-
_origEntities,
|
|
49502
|
-
isCellEditable,
|
|
49503
49504
|
change,
|
|
49505
|
+
entities,
|
|
49504
49506
|
formatAndValidateEntities,
|
|
49507
|
+
isCellEditable,
|
|
49505
49508
|
reduxFormCellValidation,
|
|
49506
49509
|
updateValidation
|
|
49507
49510
|
]);
|
package/index.es.js
CHANGED
|
@@ -48342,7 +48342,7 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48342
48342
|
const {
|
|
48343
48343
|
reduxFormCellValidation: _reduxFormCellValidation,
|
|
48344
48344
|
reduxFormEditingCell,
|
|
48345
|
-
reduxFormEntities
|
|
48345
|
+
reduxFormEntities,
|
|
48346
48346
|
reduxFormQueryParams: _reduxFormQueryParams = {},
|
|
48347
48347
|
reduxFormSearchInput: _reduxFormSearchInput = "",
|
|
48348
48348
|
reduxFormSelectedEntityIdMap: _reduxFormSelectedEntityIdMap = {}
|
|
@@ -48357,7 +48357,6 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48357
48357
|
)
|
|
48358
48358
|
);
|
|
48359
48359
|
const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
|
|
48360
|
-
const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
|
|
48361
48360
|
const reduxFormQueryParams = useDeepEqualMemo(_reduxFormQueryParams);
|
|
48362
48361
|
const reduxFormSearchInput = useDeepEqualMemo(_reduxFormSearchInput);
|
|
48363
48362
|
const reduxFormSelectedEntityIdMap = useDeepEqualMemo(
|
|
@@ -48904,8 +48903,7 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
48904
48903
|
}
|
|
48905
48904
|
}));
|
|
48906
48905
|
},
|
|
48907
|
-
|
|
48908
|
-
[]
|
|
48906
|
+
[change$1]
|
|
48909
48907
|
);
|
|
48910
48908
|
const formatAndValidateEntities = useCallback(
|
|
48911
48909
|
(entities2, { useDefaultValues, indexToStartAt } = {}) => {
|
|
@@ -49468,22 +49466,27 @@ const DataTable = /* @__PURE__ */ __name((_G) => {
|
|
|
49468
49466
|
const [selectingAll, setSelectingAll] = useState(false);
|
|
49469
49467
|
useEffect(() => {
|
|
49470
49468
|
const formatAndValidateTableInitial = /* @__PURE__ */ __name(() => {
|
|
49471
|
-
const { newEnts, validationErrors } = formatAndValidateEntities(
|
|
49469
|
+
const { newEnts, validationErrors } = formatAndValidateEntities(entities);
|
|
49472
49470
|
const toKeep = {};
|
|
49473
49471
|
forEach(reduxFormCellValidation, (v2, k2) => {
|
|
49474
49472
|
if (v2 && v2._isTableAsyncWideError) {
|
|
49475
49473
|
toKeep[k2] = v2;
|
|
49476
49474
|
}
|
|
49477
49475
|
});
|
|
49478
|
-
change$1("reduxFormEntities",
|
|
49476
|
+
change$1("reduxFormEntities", (prev) => {
|
|
49477
|
+
if (!isEqual(prev, newEnts)) {
|
|
49478
|
+
return newEnts;
|
|
49479
|
+
}
|
|
49480
|
+
return prev;
|
|
49481
|
+
});
|
|
49479
49482
|
updateValidation(newEnts, __spreadValues(__spreadValues({}, toKeep), validationErrors));
|
|
49480
49483
|
}, "formatAndValidateTableInitial");
|
|
49481
49484
|
isCellEditable && formatAndValidateTableInitial();
|
|
49482
49485
|
}, [
|
|
49483
|
-
_origEntities,
|
|
49484
|
-
isCellEditable,
|
|
49485
49486
|
change$1,
|
|
49487
|
+
entities,
|
|
49486
49488
|
formatAndValidateEntities,
|
|
49489
|
+
isCellEditable,
|
|
49487
49490
|
reduxFormCellValidation,
|
|
49488
49491
|
updateValidation
|
|
49489
49492
|
]);
|
package/package.json
CHANGED
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
import React, { isValidElement, useCallback } from "react";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import { Button, Classes, Checkbox, Icon } from "@blueprintjs/core";
|
|
4
|
+
import {
|
|
5
|
+
set,
|
|
6
|
+
toString,
|
|
7
|
+
camelCase,
|
|
8
|
+
startCase,
|
|
9
|
+
noop,
|
|
10
|
+
cloneDeep,
|
|
11
|
+
get,
|
|
12
|
+
padStart
|
|
13
|
+
} from "lodash-es";
|
|
14
|
+
import dayjs from "dayjs";
|
|
15
|
+
import localizedFormat from "dayjs/plugin/localizedFormat";
|
|
16
|
+
import ReactMarkdown from "react-markdown";
|
|
17
|
+
import remarkGfm from "remark-gfm";
|
|
18
|
+
import joinUrl from "url-join";
|
|
19
|
+
import InfoHelper from "../InfoHelper";
|
|
20
|
+
import {
|
|
21
|
+
getEntityIdToEntity,
|
|
22
|
+
getFieldPathToIndex,
|
|
23
|
+
getFieldPathToField,
|
|
24
|
+
getIdOrCodeOrIndex,
|
|
25
|
+
getNumberStrAtEnd,
|
|
26
|
+
getSelectedRowsFromEntities,
|
|
27
|
+
PRIMARY_SELECTED_VAL,
|
|
28
|
+
stripNumberAtEnd
|
|
29
|
+
} from "./utils";
|
|
30
|
+
import FilterAndSortMenu from "./FilterAndSortMenu";
|
|
31
|
+
import { ColumnFilterMenu } from "./ColumnFilterMenu";
|
|
32
|
+
import getTextFromEl from "../utils/getTextFromEl";
|
|
33
|
+
import rowClick, { finalizeSelection } from "./utils/rowClick";
|
|
34
|
+
import { editCellHelper } from "./editCellHelper";
|
|
35
|
+
import { getCellVal } from "./getCellVal";
|
|
36
|
+
import { getCCDisplayName } from "./utils/queryParams";
|
|
37
|
+
import { useDispatch } from "react-redux";
|
|
38
|
+
import { change as _change } from "redux-form";
|
|
39
|
+
import { RenderCell } from "./RenderCell";
|
|
40
|
+
|
|
41
|
+
dayjs.extend(localizedFormat);
|
|
42
|
+
|
|
43
|
+
const RenderColumnHeader = ({
|
|
44
|
+
addFilters,
|
|
45
|
+
column,
|
|
46
|
+
compact,
|
|
47
|
+
currentParams,
|
|
48
|
+
entities,
|
|
49
|
+
extraCompact,
|
|
50
|
+
filters,
|
|
51
|
+
isCellEditable,
|
|
52
|
+
isLocalCall,
|
|
53
|
+
order,
|
|
54
|
+
removeSingleFilter,
|
|
55
|
+
setNewParams,
|
|
56
|
+
setOrder,
|
|
57
|
+
updateEntitiesHelper,
|
|
58
|
+
withFilter,
|
|
59
|
+
withSort
|
|
60
|
+
}) => {
|
|
61
|
+
const {
|
|
62
|
+
displayName,
|
|
63
|
+
description,
|
|
64
|
+
isUnique,
|
|
65
|
+
sortDisabled,
|
|
66
|
+
filterDisabled,
|
|
67
|
+
columnFilterDisabled,
|
|
68
|
+
renderTitleInner,
|
|
69
|
+
filterIsActive = noop,
|
|
70
|
+
noTitle,
|
|
71
|
+
isNotEditable,
|
|
72
|
+
type,
|
|
73
|
+
path
|
|
74
|
+
} = column;
|
|
75
|
+
const columnDataType = column.type;
|
|
76
|
+
const isActionColumn = columnDataType === "action";
|
|
77
|
+
const disableSorting =
|
|
78
|
+
sortDisabled ||
|
|
79
|
+
isActionColumn ||
|
|
80
|
+
(!isLocalCall && typeof path === "string" && path.includes(".")) ||
|
|
81
|
+
columnDataType === "color";
|
|
82
|
+
const disableFiltering =
|
|
83
|
+
filterDisabled ||
|
|
84
|
+
columnDataType === "color" ||
|
|
85
|
+
isActionColumn ||
|
|
86
|
+
columnFilterDisabled;
|
|
87
|
+
const ccDisplayName = getCCDisplayName(column);
|
|
88
|
+
let columnTitle = displayName || startCase(camelCase(path));
|
|
89
|
+
if (isActionColumn) columnTitle = "";
|
|
90
|
+
|
|
91
|
+
const currentFilter =
|
|
92
|
+
filters &&
|
|
93
|
+
!!filters.length &&
|
|
94
|
+
filters.filter(({ filterOn }) => {
|
|
95
|
+
return filterOn === ccDisplayName;
|
|
96
|
+
})[0];
|
|
97
|
+
const filterActiveForColumn =
|
|
98
|
+
!!currentFilter || filterIsActive(currentParams);
|
|
99
|
+
let ordering;
|
|
100
|
+
if (order && order.length) {
|
|
101
|
+
order.forEach(order => {
|
|
102
|
+
const orderField = order.replace("-", "");
|
|
103
|
+
if (orderField === ccDisplayName) {
|
|
104
|
+
if (orderField === order) {
|
|
105
|
+
ordering = "asc";
|
|
106
|
+
} else {
|
|
107
|
+
ordering = "desc";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const sortDown = ordering && ordering === "asc";
|
|
114
|
+
const sortUp = ordering && !sortDown;
|
|
115
|
+
const FilterMenu = column.FilterMenu || FilterAndSortMenu;
|
|
116
|
+
|
|
117
|
+
let maybeCheckbox;
|
|
118
|
+
if (isCellEditable && !isNotEditable && type === "boolean") {
|
|
119
|
+
let isIndeterminate = false;
|
|
120
|
+
let isChecked = !!entities.length;
|
|
121
|
+
let hasFalse;
|
|
122
|
+
let hasTrue;
|
|
123
|
+
entities.some(e => {
|
|
124
|
+
if (!get(e, path)) {
|
|
125
|
+
isChecked = false;
|
|
126
|
+
hasFalse = true;
|
|
127
|
+
} else {
|
|
128
|
+
hasTrue = true;
|
|
129
|
+
}
|
|
130
|
+
if (hasFalse && hasTrue) {
|
|
131
|
+
isIndeterminate = true;
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
});
|
|
136
|
+
maybeCheckbox = (
|
|
137
|
+
<Checkbox
|
|
138
|
+
style={{ marginBottom: 0, marginLeft: 3 }}
|
|
139
|
+
onChange={() => {
|
|
140
|
+
updateEntitiesHelper(entities, ents => {
|
|
141
|
+
ents.forEach(e => {
|
|
142
|
+
delete e._isClean;
|
|
143
|
+
set(e, path, isIndeterminate ? true : !isChecked);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}}
|
|
147
|
+
indeterminate={isIndeterminate}
|
|
148
|
+
checked={isChecked}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const columnTitleTextified = getTextFromEl(columnTitle);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div
|
|
157
|
+
{...(description && {
|
|
158
|
+
"data-tip": `<div>
|
|
159
|
+
<strong>${columnTitle}:</strong> <br>
|
|
160
|
+
${description} ${isUnique ? "<br>Must be unique" : ""}</div>`
|
|
161
|
+
})}
|
|
162
|
+
data-test={columnTitleTextified}
|
|
163
|
+
data-path={path}
|
|
164
|
+
data-copy-text={columnTitleTextified}
|
|
165
|
+
data-copy-json={JSON.stringify({
|
|
166
|
+
__strVal: columnTitleTextified,
|
|
167
|
+
__isHeaderCell: true
|
|
168
|
+
})}
|
|
169
|
+
className={classNames("tg-react-table-column-header", {
|
|
170
|
+
"sort-active": sortUp || sortDown
|
|
171
|
+
})}
|
|
172
|
+
>
|
|
173
|
+
{columnTitleTextified && !noTitle && (
|
|
174
|
+
<>
|
|
175
|
+
{maybeCheckbox}
|
|
176
|
+
<span
|
|
177
|
+
title={columnTitleTextified}
|
|
178
|
+
className={classNames({
|
|
179
|
+
"tg-react-table-name": true,
|
|
180
|
+
"no-data-tip": !!description
|
|
181
|
+
})}
|
|
182
|
+
style={{
|
|
183
|
+
...(description && { fontStyle: "italic" }),
|
|
184
|
+
display: "inline-block"
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
{renderTitleInner ? renderTitleInner : columnTitle}{" "}
|
|
188
|
+
</span>
|
|
189
|
+
</>
|
|
190
|
+
)}
|
|
191
|
+
<div
|
|
192
|
+
style={{ display: "flex", marginLeft: "auto", alignItems: "center" }}
|
|
193
|
+
>
|
|
194
|
+
{withSort && !disableSorting && (
|
|
195
|
+
<div className="tg-sort-arrow-container">
|
|
196
|
+
<Icon
|
|
197
|
+
data-tip="Sort Z-A (Hold shift to sort multiple columns)"
|
|
198
|
+
icon="chevron-up"
|
|
199
|
+
className={classNames({
|
|
200
|
+
active: sortUp
|
|
201
|
+
})}
|
|
202
|
+
color={sortUp ? "#106ba3" : undefined}
|
|
203
|
+
iconSize={extraCompact ? 10 : 12}
|
|
204
|
+
onClick={e => {
|
|
205
|
+
setOrder("-" + ccDisplayName, sortUp, e.shiftKey);
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
<Icon
|
|
209
|
+
data-tip="Sort A-Z (Hold shift to sort multiple columns)"
|
|
210
|
+
icon="chevron-down"
|
|
211
|
+
className={classNames({
|
|
212
|
+
active: sortDown
|
|
213
|
+
})}
|
|
214
|
+
color={sortDown ? "#106ba3" : undefined}
|
|
215
|
+
iconSize={extraCompact ? 10 : 12}
|
|
216
|
+
onClick={e => {
|
|
217
|
+
setOrder(ccDisplayName, sortDown, e.shiftKey);
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
{withFilter && !disableFiltering && (
|
|
223
|
+
<ColumnFilterMenu
|
|
224
|
+
FilterMenu={FilterMenu}
|
|
225
|
+
filterActiveForColumn={filterActiveForColumn}
|
|
226
|
+
addFilters={addFilters}
|
|
227
|
+
removeSingleFilter={removeSingleFilter}
|
|
228
|
+
currentFilter={currentFilter}
|
|
229
|
+
filterOn={ccDisplayName}
|
|
230
|
+
dataType={columnDataType}
|
|
231
|
+
schemaForField={column}
|
|
232
|
+
currentParams={currentParams}
|
|
233
|
+
setNewParams={setNewParams}
|
|
234
|
+
compact={compact}
|
|
235
|
+
extraCompact={extraCompact}
|
|
236
|
+
/>
|
|
237
|
+
)}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const renderCheckboxHeader = ({
|
|
244
|
+
change,
|
|
245
|
+
entities,
|
|
246
|
+
isEntityDisabled,
|
|
247
|
+
isSingleSelect,
|
|
248
|
+
noDeselectAll,
|
|
249
|
+
noSelect,
|
|
250
|
+
noUserSelect = false,
|
|
251
|
+
onDeselect,
|
|
252
|
+
onMultiRowSelect,
|
|
253
|
+
onRowSelect,
|
|
254
|
+
onSingleRowSelect,
|
|
255
|
+
reduxFormSelectedEntityIdMap
|
|
256
|
+
}) => {
|
|
257
|
+
const checkedRows = getSelectedRowsFromEntities(
|
|
258
|
+
entities,
|
|
259
|
+
reduxFormSelectedEntityIdMap
|
|
260
|
+
);
|
|
261
|
+
const checkboxProps = {
|
|
262
|
+
checked: false,
|
|
263
|
+
indeterminate: false
|
|
264
|
+
};
|
|
265
|
+
const notDisabledEntityCount = entities.reduce((acc, e) => {
|
|
266
|
+
return isEntityDisabled(e) ? acc : acc + 1;
|
|
267
|
+
}, 0);
|
|
268
|
+
if (checkedRows.length === notDisabledEntityCount) {
|
|
269
|
+
//tnr: maybe this will need to change if we want enable select all across pages
|
|
270
|
+
checkboxProps.checked = notDisabledEntityCount !== 0;
|
|
271
|
+
} else {
|
|
272
|
+
if (checkedRows.length) {
|
|
273
|
+
checkboxProps.indeterminate = true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return !isSingleSelect ? (
|
|
278
|
+
<Checkbox
|
|
279
|
+
name="checkBoxHeader"
|
|
280
|
+
disabled={noSelect || noUserSelect}
|
|
281
|
+
onChange={() => {
|
|
282
|
+
const newIdMap = cloneDeep(reduxFormSelectedEntityIdMap) || {};
|
|
283
|
+
entities.forEach((entity, i) => {
|
|
284
|
+
if (isEntityDisabled(entity)) return;
|
|
285
|
+
const entityId = getIdOrCodeOrIndex(entity, i);
|
|
286
|
+
if (checkboxProps.checked) {
|
|
287
|
+
delete newIdMap[entityId];
|
|
288
|
+
} else {
|
|
289
|
+
newIdMap[entityId] = { entity };
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
finalizeSelection({
|
|
294
|
+
idMap: newIdMap,
|
|
295
|
+
entities,
|
|
296
|
+
props: {
|
|
297
|
+
onDeselect,
|
|
298
|
+
onSingleRowSelect,
|
|
299
|
+
onMultiRowSelect,
|
|
300
|
+
noDeselectAll,
|
|
301
|
+
onRowSelect,
|
|
302
|
+
noSelect,
|
|
303
|
+
change
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}}
|
|
307
|
+
{...checkboxProps}
|
|
308
|
+
/>
|
|
309
|
+
) : null;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export const useColumns = ({
|
|
313
|
+
addFilters,
|
|
314
|
+
cellRenderer,
|
|
315
|
+
columns,
|
|
316
|
+
currentParams,
|
|
317
|
+
compact,
|
|
318
|
+
editingCell,
|
|
319
|
+
editingCellSelectAll,
|
|
320
|
+
entities,
|
|
321
|
+
expandedEntityIdMap,
|
|
322
|
+
extraCompact,
|
|
323
|
+
filters,
|
|
324
|
+
formName,
|
|
325
|
+
getCellHoverText,
|
|
326
|
+
isCellEditable,
|
|
327
|
+
isEntityDisabled,
|
|
328
|
+
isLocalCall,
|
|
329
|
+
isSimple,
|
|
330
|
+
isSingleSelect,
|
|
331
|
+
isSelectionARectangle,
|
|
332
|
+
noDeselectAll,
|
|
333
|
+
noSelect,
|
|
334
|
+
noUserSelect,
|
|
335
|
+
onDeselect,
|
|
336
|
+
onMultiRowSelect,
|
|
337
|
+
onRowClick,
|
|
338
|
+
onRowSelect,
|
|
339
|
+
onSingleRowSelect,
|
|
340
|
+
order,
|
|
341
|
+
primarySelectedCellId,
|
|
342
|
+
reduxFormCellValidation,
|
|
343
|
+
reduxFormSelectedEntityIdMap,
|
|
344
|
+
refocusTable,
|
|
345
|
+
removeSingleFilter = noop,
|
|
346
|
+
schema,
|
|
347
|
+
selectedCells,
|
|
348
|
+
setExpandedEntityIdMap,
|
|
349
|
+
setNewParams,
|
|
350
|
+
setOrder = noop,
|
|
351
|
+
setSelectedCells,
|
|
352
|
+
shouldShowSubComponent,
|
|
353
|
+
startCellEdit,
|
|
354
|
+
SubComponent,
|
|
355
|
+
tableRef,
|
|
356
|
+
updateEntitiesHelper,
|
|
357
|
+
updateValidation,
|
|
358
|
+
withCheckboxes,
|
|
359
|
+
withExpandAndCollapseAllButton,
|
|
360
|
+
withFilter: _withFilter,
|
|
361
|
+
withSort = true
|
|
362
|
+
}) => {
|
|
363
|
+
const dispatch = useDispatch();
|
|
364
|
+
const change = useCallback(
|
|
365
|
+
(...args) => dispatch(_change(formName, ...args)),
|
|
366
|
+
[dispatch, formName]
|
|
367
|
+
);
|
|
368
|
+
const withFilter = _withFilter === undefined ? !isSimple : _withFilter;
|
|
369
|
+
|
|
370
|
+
const onDragEnd = useCallback(
|
|
371
|
+
cellsToSelect => {
|
|
372
|
+
const [primaryRowId, primaryCellPath] = primarySelectedCellId.split(":");
|
|
373
|
+
const pathToField = getFieldPathToField(schema);
|
|
374
|
+
const { selectedPaths, selectionGrid } = isSelectionARectangle();
|
|
375
|
+
let allSelectedPaths = selectedPaths;
|
|
376
|
+
if (!allSelectedPaths) {
|
|
377
|
+
allSelectedPaths = [primaryCellPath];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
updateEntitiesHelper(entities, entities => {
|
|
381
|
+
let newSelectedCells;
|
|
382
|
+
if (selectedPaths) {
|
|
383
|
+
newSelectedCells = {
|
|
384
|
+
...selectedCells
|
|
385
|
+
};
|
|
386
|
+
} else {
|
|
387
|
+
newSelectedCells = {
|
|
388
|
+
[primarySelectedCellId]: PRIMARY_SELECTED_VAL
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const newCellValidate = {
|
|
393
|
+
...reduxFormCellValidation
|
|
394
|
+
};
|
|
395
|
+
const entityMap = getEntityIdToEntity(entities);
|
|
396
|
+
const { e: selectedEnt } = entityMap[primaryRowId];
|
|
397
|
+
const firstCellToSelectRowIndex =
|
|
398
|
+
entityMap[cellsToSelect[0]?.split(":")[0]]?.i;
|
|
399
|
+
const pathToIndex = getFieldPathToIndex(schema);
|
|
400
|
+
|
|
401
|
+
allSelectedPaths.forEach(selectedPath => {
|
|
402
|
+
const column = pathToField[selectedPath];
|
|
403
|
+
|
|
404
|
+
const selectedCellVal = getCellVal(selectedEnt, selectedPath, column);
|
|
405
|
+
const cellIndexOfSelectedPath = pathToIndex[selectedPath];
|
|
406
|
+
let incrementStart;
|
|
407
|
+
let incrementPrefix;
|
|
408
|
+
let incrementPad = 0;
|
|
409
|
+
if (column.type === "string" || column.type === "number") {
|
|
410
|
+
const cellNumStr = getNumberStrAtEnd(selectedCellVal);
|
|
411
|
+
const cellNum = Number(cellNumStr);
|
|
412
|
+
const entityAbovePrimaryCell =
|
|
413
|
+
entities[entityMap[primaryRowId].i - 1];
|
|
414
|
+
if (cellNumStr !== null && !isNaN(cellNum)) {
|
|
415
|
+
if (
|
|
416
|
+
entityAbovePrimaryCell &&
|
|
417
|
+
(!selectionGrid || selectionGrid.length <= 1)
|
|
418
|
+
) {
|
|
419
|
+
const cellAboveVal = get(
|
|
420
|
+
entityAbovePrimaryCell,
|
|
421
|
+
selectedPath,
|
|
422
|
+
""
|
|
423
|
+
);
|
|
424
|
+
const cellAboveNumStr = getNumberStrAtEnd(cellAboveVal);
|
|
425
|
+
const cellAboveNum = Number(cellAboveNumStr);
|
|
426
|
+
if (!isNaN(cellAboveNum)) {
|
|
427
|
+
const isIncremental = cellNum - cellAboveNum === 1;
|
|
428
|
+
if (isIncremental) {
|
|
429
|
+
const cellTextNoNum = stripNumberAtEnd(selectedCellVal);
|
|
430
|
+
const sameText =
|
|
431
|
+
stripNumberAtEnd(cellAboveVal) === cellTextNoNum;
|
|
432
|
+
if (sameText) {
|
|
433
|
+
incrementStart = cellNum + 1;
|
|
434
|
+
incrementPrefix = cellTextNoNum || "";
|
|
435
|
+
if (cellNumStr && cellNumStr.startsWith("0")) {
|
|
436
|
+
incrementPad = cellNumStr.length;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (incrementStart === undefined) {
|
|
443
|
+
const draggingDown =
|
|
444
|
+
firstCellToSelectRowIndex > selectionGrid?.[0][0].rowIndex;
|
|
445
|
+
if (selectedPaths && draggingDown) {
|
|
446
|
+
let checkIncrement;
|
|
447
|
+
let prefix;
|
|
448
|
+
let maybePad;
|
|
449
|
+
// determine if all the cells in this column of the selectionGrid are incrementing
|
|
450
|
+
const allAreIncrementing = selectionGrid.every(row => {
|
|
451
|
+
// see if cell is selected
|
|
452
|
+
const cellInfo = row[cellIndexOfSelectedPath];
|
|
453
|
+
if (!cellInfo) return false;
|
|
454
|
+
const { cellId } = cellInfo;
|
|
455
|
+
const [rowId] = cellId.split(":");
|
|
456
|
+
const cellVal = getCellVal(
|
|
457
|
+
entityMap[rowId].e,
|
|
458
|
+
selectedPath,
|
|
459
|
+
pathToField[selectedPath]
|
|
460
|
+
);
|
|
461
|
+
const cellNumStr = getNumberStrAtEnd(cellVal);
|
|
462
|
+
const cellNum = Number(cellNumStr);
|
|
463
|
+
const cellTextNoNum = stripNumberAtEnd(cellVal);
|
|
464
|
+
if (cellNumStr?.startsWith("0")) {
|
|
465
|
+
maybePad = cellNumStr.length;
|
|
466
|
+
}
|
|
467
|
+
if (cellTextNoNum && !prefix) {
|
|
468
|
+
prefix = cellTextNoNum;
|
|
469
|
+
}
|
|
470
|
+
if (cellTextNoNum && prefix !== cellTextNoNum) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
if (!isNaN(cellNum)) {
|
|
474
|
+
if (!checkIncrement) {
|
|
475
|
+
checkIncrement = cellNum;
|
|
476
|
+
return true;
|
|
477
|
+
} else {
|
|
478
|
+
return ++checkIncrement === cellNum;
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
if (allAreIncrementing) {
|
|
486
|
+
incrementStart = checkIncrement + 1;
|
|
487
|
+
incrementPrefix = prefix || "";
|
|
488
|
+
incrementPad = maybePad;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
let firstSelectedCellRowIndex;
|
|
496
|
+
if (selectionGrid) {
|
|
497
|
+
selectionGrid[0].some(cell => {
|
|
498
|
+
if (cell) {
|
|
499
|
+
firstSelectedCellRowIndex = cell.rowIndex;
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
return false;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
cellsToSelect.forEach(cellId => {
|
|
507
|
+
const [rowId, cellPath] = cellId.split(":");
|
|
508
|
+
if (cellPath !== selectedPath) return;
|
|
509
|
+
newSelectedCells[cellId] = true;
|
|
510
|
+
const { e: entityToUpdate, i: rowIndex } = entityMap[rowId] || {};
|
|
511
|
+
if (entityToUpdate) {
|
|
512
|
+
delete entityToUpdate._isClean;
|
|
513
|
+
let newVal;
|
|
514
|
+
if (incrementStart !== undefined) {
|
|
515
|
+
const num = incrementStart++;
|
|
516
|
+
newVal = incrementPrefix + padStart(num, incrementPad, "0");
|
|
517
|
+
} else {
|
|
518
|
+
if (selectionGrid && selectionGrid.length > 1) {
|
|
519
|
+
// if there are multiple cells selected then we want to copy them repeating
|
|
520
|
+
// ex: if we have 1,2,3 selected and we drag for 5 more rows we want it to
|
|
521
|
+
// be 1,2,3,1,2 for the new row cells in this column
|
|
522
|
+
const draggingDown = rowIndex > firstSelectedCellRowIndex;
|
|
523
|
+
const cellIndex = pathToIndex[cellPath];
|
|
524
|
+
let cellIdToCopy;
|
|
525
|
+
if (draggingDown) {
|
|
526
|
+
const { cellId } = selectionGrid[
|
|
527
|
+
(rowIndex - firstSelectedCellRowIndex) %
|
|
528
|
+
selectionGrid.length
|
|
529
|
+
].find(g => g && g.cellIndex === cellIndex);
|
|
530
|
+
cellIdToCopy = cellId;
|
|
531
|
+
} else {
|
|
532
|
+
const lastIndexInGrid =
|
|
533
|
+
selectionGrid[selectionGrid.length - 1][0].rowIndex;
|
|
534
|
+
const { cellId } = selectionGrid[
|
|
535
|
+
(rowIndex + lastIndexInGrid + 1) % selectionGrid.length
|
|
536
|
+
].find(g => g.cellIndex === cellIndex);
|
|
537
|
+
cellIdToCopy = cellId;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const [rowIdToCopy, cellPathToCopy] = cellIdToCopy.split(":");
|
|
541
|
+
newVal = getCellVal(
|
|
542
|
+
entityMap[rowIdToCopy].e,
|
|
543
|
+
cellPathToCopy,
|
|
544
|
+
pathToField[cellPathToCopy]
|
|
545
|
+
);
|
|
546
|
+
} else {
|
|
547
|
+
newVal = selectedCellVal;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const { error } = editCellHelper({
|
|
551
|
+
entity: entityToUpdate,
|
|
552
|
+
path: cellPath,
|
|
553
|
+
schema,
|
|
554
|
+
newVal
|
|
555
|
+
});
|
|
556
|
+
newCellValidate[cellId] = error;
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// select the new cells
|
|
562
|
+
updateValidation(entities, newCellValidate);
|
|
563
|
+
setSelectedCells(newSelectedCells);
|
|
564
|
+
});
|
|
565
|
+
},
|
|
566
|
+
[
|
|
567
|
+
entities,
|
|
568
|
+
isSelectionARectangle,
|
|
569
|
+
primarySelectedCellId,
|
|
570
|
+
reduxFormCellValidation,
|
|
571
|
+
schema,
|
|
572
|
+
selectedCells,
|
|
573
|
+
setSelectedCells,
|
|
574
|
+
updateEntitiesHelper,
|
|
575
|
+
updateValidation
|
|
576
|
+
]
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const getCopyTextForCell = useCallback(
|
|
580
|
+
(val, row = {}, column = {}) => {
|
|
581
|
+
// TODOCOPY we need a way to potentially omit certain columns from being added as a \t element (talk to taoh about this)
|
|
582
|
+
let text = typeof val !== "string" ? row.value : val;
|
|
583
|
+
|
|
584
|
+
// We should try to take out the props from here, it produces
|
|
585
|
+
// unnecessary rerenders
|
|
586
|
+
const record = row.original;
|
|
587
|
+
if (column.getClipboardData) {
|
|
588
|
+
text = column.getClipboardData(row.value, record, row);
|
|
589
|
+
} else if (column.getValueToFilterOn) {
|
|
590
|
+
text = column.getValueToFilterOn(record);
|
|
591
|
+
} else if (column.render) {
|
|
592
|
+
text = column.render(row.value, record, row, {
|
|
593
|
+
currentParams,
|
|
594
|
+
setNewParams
|
|
595
|
+
});
|
|
596
|
+
} else if (cellRenderer && cellRenderer[column.path]) {
|
|
597
|
+
text = cellRenderer[column.path](row.value, row.original, row, {
|
|
598
|
+
currentParams,
|
|
599
|
+
setNewParams
|
|
600
|
+
});
|
|
601
|
+
} else if (text) {
|
|
602
|
+
text = isValidElement(text) ? text : String(text);
|
|
603
|
+
}
|
|
604
|
+
const getTextFromElementOrLink = text => {
|
|
605
|
+
if (isValidElement(text)) {
|
|
606
|
+
if (text.props?.to) {
|
|
607
|
+
// this will convert Link elements to url strings
|
|
608
|
+
return joinUrl(
|
|
609
|
+
window.location.origin,
|
|
610
|
+
window.frontEndConfig?.clientBasePath || "",
|
|
611
|
+
text.props.to
|
|
612
|
+
);
|
|
613
|
+
} else {
|
|
614
|
+
return getTextFromEl(text);
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
return text;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
text = getTextFromElementOrLink(text);
|
|
621
|
+
|
|
622
|
+
if (Array.isArray(text)) {
|
|
623
|
+
let arrText = text.map(getTextFromElementOrLink).join(", ");
|
|
624
|
+
// because we sometimes insert commas after links when mapping over an array of elements we will have double ,'s
|
|
625
|
+
arrText = arrText.replace(/, ,/g, ",");
|
|
626
|
+
text = arrText;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const stringText = toString(text);
|
|
630
|
+
if (stringText === "[object Object]") return "";
|
|
631
|
+
return stringText;
|
|
632
|
+
},
|
|
633
|
+
[cellRenderer, currentParams, setNewParams]
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
const renderCheckboxCell = useCallback(
|
|
637
|
+
row => {
|
|
638
|
+
const rowIndex = row.index;
|
|
639
|
+
const checkedRows = getSelectedRowsFromEntities(
|
|
640
|
+
entities,
|
|
641
|
+
reduxFormSelectedEntityIdMap
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
const isSelected = checkedRows.some(rowNum => {
|
|
645
|
+
return rowNum === rowIndex;
|
|
646
|
+
});
|
|
647
|
+
if (rowIndex >= entities.length) {
|
|
648
|
+
return <div />;
|
|
649
|
+
}
|
|
650
|
+
const entity = entities[rowIndex];
|
|
651
|
+
return (
|
|
652
|
+
<Checkbox
|
|
653
|
+
name={`${getIdOrCodeOrIndex(entity, rowIndex)}-checkbox`}
|
|
654
|
+
disabled={noSelect || noUserSelect || isEntityDisabled(entity)}
|
|
655
|
+
onClick={e => {
|
|
656
|
+
rowClick(e, row, entities, {
|
|
657
|
+
reduxFormSelectedEntityIdMap,
|
|
658
|
+
isSingleSelect,
|
|
659
|
+
noSelect,
|
|
660
|
+
onRowClick,
|
|
661
|
+
isEntityDisabled,
|
|
662
|
+
withCheckboxes,
|
|
663
|
+
onDeselect,
|
|
664
|
+
onSingleRowSelect,
|
|
665
|
+
onMultiRowSelect,
|
|
666
|
+
noDeselectAll,
|
|
667
|
+
onRowSelect,
|
|
668
|
+
change
|
|
669
|
+
});
|
|
670
|
+
}}
|
|
671
|
+
checked={isSelected}
|
|
672
|
+
/>
|
|
673
|
+
);
|
|
674
|
+
},
|
|
675
|
+
[
|
|
676
|
+
change,
|
|
677
|
+
entities,
|
|
678
|
+
isEntityDisabled,
|
|
679
|
+
isSingleSelect,
|
|
680
|
+
noDeselectAll,
|
|
681
|
+
noSelect,
|
|
682
|
+
noUserSelect,
|
|
683
|
+
onDeselect,
|
|
684
|
+
onMultiRowSelect,
|
|
685
|
+
onRowClick,
|
|
686
|
+
onRowSelect,
|
|
687
|
+
onSingleRowSelect,
|
|
688
|
+
reduxFormSelectedEntityIdMap,
|
|
689
|
+
withCheckboxes
|
|
690
|
+
]
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
const finishCellEdit = useCallback(
|
|
694
|
+
(cellId, newVal, doNotStopEditing) => {
|
|
695
|
+
const [rowId, path] = cellId.split(":");
|
|
696
|
+
!doNotStopEditing && change("reduxFormEditingCell", null);
|
|
697
|
+
updateEntitiesHelper(entities, entities => {
|
|
698
|
+
const entity = entities.find((e, i) => {
|
|
699
|
+
return getIdOrCodeOrIndex(e, i) === rowId;
|
|
700
|
+
});
|
|
701
|
+
delete entity._isClean;
|
|
702
|
+
const { error } = editCellHelper({
|
|
703
|
+
entity,
|
|
704
|
+
path,
|
|
705
|
+
schema,
|
|
706
|
+
newVal
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
updateValidation(entities, {
|
|
710
|
+
...reduxFormCellValidation,
|
|
711
|
+
[cellId]: error
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
!doNotStopEditing && refocusTable();
|
|
715
|
+
},
|
|
716
|
+
[
|
|
717
|
+
change,
|
|
718
|
+
entities,
|
|
719
|
+
reduxFormCellValidation,
|
|
720
|
+
refocusTable,
|
|
721
|
+
schema,
|
|
722
|
+
updateEntitiesHelper,
|
|
723
|
+
updateValidation
|
|
724
|
+
]
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
const cancelCellEdit = useCallback(() => {
|
|
728
|
+
change("reduxFormEditingCell", null);
|
|
729
|
+
refocusTable();
|
|
730
|
+
}, [change, refocusTable]);
|
|
731
|
+
|
|
732
|
+
if (!columns.length) {
|
|
733
|
+
return columns;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const columnsToRender = [];
|
|
737
|
+
if (SubComponent) {
|
|
738
|
+
columnsToRender.push({
|
|
739
|
+
...(withExpandAndCollapseAllButton && {
|
|
740
|
+
Header: () => {
|
|
741
|
+
const showCollapseAll =
|
|
742
|
+
Object.values(expandedEntityIdMap).filter(i => i).length ===
|
|
743
|
+
entities.length;
|
|
744
|
+
return (
|
|
745
|
+
<InfoHelper
|
|
746
|
+
content={showCollapseAll ? "Collapse All" : "Expand All"}
|
|
747
|
+
isButton
|
|
748
|
+
minimal
|
|
749
|
+
small
|
|
750
|
+
style={{ padding: 2 }}
|
|
751
|
+
popoverProps={{
|
|
752
|
+
modifiers: {
|
|
753
|
+
preventOverflow: { enabled: false },
|
|
754
|
+
hide: { enabled: false }
|
|
755
|
+
}
|
|
756
|
+
}}
|
|
757
|
+
onClick={() => {
|
|
758
|
+
showCollapseAll
|
|
759
|
+
? setExpandedEntityIdMap({})
|
|
760
|
+
: setExpandedEntityIdMap(prev => {
|
|
761
|
+
const newMap = { ...prev };
|
|
762
|
+
entities.forEach(e => {
|
|
763
|
+
newMap[getIdOrCodeOrIndex(e)] = true;
|
|
764
|
+
});
|
|
765
|
+
return newMap;
|
|
766
|
+
});
|
|
767
|
+
}}
|
|
768
|
+
className={classNames("tg-expander-all")}
|
|
769
|
+
icon={showCollapseAll ? "chevron-down" : "chevron-right"}
|
|
770
|
+
/>
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
}),
|
|
774
|
+
expander: true,
|
|
775
|
+
Expander: ({ isExpanded, original: record }) => {
|
|
776
|
+
let shouldShow = true;
|
|
777
|
+
if (shouldShowSubComponent) {
|
|
778
|
+
shouldShow = shouldShowSubComponent(record);
|
|
779
|
+
}
|
|
780
|
+
if (!shouldShow) return null;
|
|
781
|
+
return (
|
|
782
|
+
<Button
|
|
783
|
+
className={classNames(
|
|
784
|
+
"tg-expander",
|
|
785
|
+
Classes.MINIMAL,
|
|
786
|
+
Classes.SMALL
|
|
787
|
+
)}
|
|
788
|
+
icon={isExpanded ? "chevron-down" : "chevron-right"}
|
|
789
|
+
/>
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (withCheckboxes) {
|
|
796
|
+
columnsToRender.push({
|
|
797
|
+
Header: renderCheckboxHeader({
|
|
798
|
+
change,
|
|
799
|
+
entities,
|
|
800
|
+
isEntityDisabled,
|
|
801
|
+
isSingleSelect,
|
|
802
|
+
noDeselectAll,
|
|
803
|
+
noSelect,
|
|
804
|
+
noUserSelect,
|
|
805
|
+
onDeselect,
|
|
806
|
+
onMultiRowSelect,
|
|
807
|
+
onRowSelect,
|
|
808
|
+
onSingleRowSelect,
|
|
809
|
+
reduxFormSelectedEntityIdMap
|
|
810
|
+
}),
|
|
811
|
+
Cell: renderCheckboxCell,
|
|
812
|
+
width: 35,
|
|
813
|
+
resizable: false,
|
|
814
|
+
getHeaderProps: () => {
|
|
815
|
+
return {
|
|
816
|
+
className: "tg-react-table-checkbox-header-container",
|
|
817
|
+
immovable: "true"
|
|
818
|
+
};
|
|
819
|
+
},
|
|
820
|
+
getProps: () => {
|
|
821
|
+
return {
|
|
822
|
+
className: "tg-react-table-checkbox-cell-container"
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const tableColumns = columns.map(column => {
|
|
829
|
+
const tableColumn = {
|
|
830
|
+
...column,
|
|
831
|
+
Header: RenderColumnHeader({
|
|
832
|
+
column,
|
|
833
|
+
isLocalCall,
|
|
834
|
+
filters,
|
|
835
|
+
currentParams,
|
|
836
|
+
order,
|
|
837
|
+
setOrder,
|
|
838
|
+
withSort,
|
|
839
|
+
extraCompact,
|
|
840
|
+
withFilter,
|
|
841
|
+
addFilters,
|
|
842
|
+
removeSingleFilter,
|
|
843
|
+
setNewParams,
|
|
844
|
+
compact,
|
|
845
|
+
isCellEditable,
|
|
846
|
+
entities,
|
|
847
|
+
updateEntitiesHelper
|
|
848
|
+
}),
|
|
849
|
+
accessor: column.path,
|
|
850
|
+
getHeaderProps: () => ({
|
|
851
|
+
// needs to be a string because it is getting passed
|
|
852
|
+
// to the dom
|
|
853
|
+
immovable: column.immovable ? "true" : "false",
|
|
854
|
+
columnindex: column.columnIndex
|
|
855
|
+
})
|
|
856
|
+
};
|
|
857
|
+
const noEllipsis = column.noEllipsis;
|
|
858
|
+
if (column.width) {
|
|
859
|
+
tableColumn.width = column.width;
|
|
860
|
+
}
|
|
861
|
+
if (cellRenderer && cellRenderer[column.path]) {
|
|
862
|
+
tableColumn.Cell = row => {
|
|
863
|
+
const val = cellRenderer[column.path](row.value, row.original, row, {
|
|
864
|
+
currentParams,
|
|
865
|
+
setNewParams
|
|
866
|
+
});
|
|
867
|
+
return val;
|
|
868
|
+
};
|
|
869
|
+
} else if (column.render) {
|
|
870
|
+
tableColumn.Cell = row => {
|
|
871
|
+
const val = column.render(row.value, row.original, row, {
|
|
872
|
+
currentParams,
|
|
873
|
+
setNewParams
|
|
874
|
+
});
|
|
875
|
+
return val;
|
|
876
|
+
};
|
|
877
|
+
} else if (column.type === "timestamp") {
|
|
878
|
+
tableColumn.Cell = ({ value }) => {
|
|
879
|
+
return value ? dayjs(value).format("lll") : "";
|
|
880
|
+
};
|
|
881
|
+
} else if (column.type === "color") {
|
|
882
|
+
tableColumn.Cell = ({ value }) => {
|
|
883
|
+
return value ? (
|
|
884
|
+
<div
|
|
885
|
+
style={{
|
|
886
|
+
height: 20,
|
|
887
|
+
width: 40,
|
|
888
|
+
background: value,
|
|
889
|
+
border: "1px solid #182026",
|
|
890
|
+
borderRadius: 5
|
|
891
|
+
}}
|
|
892
|
+
/>
|
|
893
|
+
) : (
|
|
894
|
+
""
|
|
895
|
+
);
|
|
896
|
+
};
|
|
897
|
+
} else if (column.type === "boolean") {
|
|
898
|
+
if (isCellEditable) {
|
|
899
|
+
tableColumn.Cell = ({ value }) => (value ? "True" : "False");
|
|
900
|
+
} else {
|
|
901
|
+
tableColumn.Cell = ({ value }) => (
|
|
902
|
+
<Icon
|
|
903
|
+
className={classNames({
|
|
904
|
+
[Classes.TEXT_MUTED]: !value
|
|
905
|
+
})}
|
|
906
|
+
icon={value ? "tick" : "cross"}
|
|
907
|
+
/>
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
} else if (column.type === "markdown") {
|
|
911
|
+
tableColumn.Cell = ({ value }) => (
|
|
912
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{value}</ReactMarkdown>
|
|
913
|
+
);
|
|
914
|
+
} else {
|
|
915
|
+
tableColumn.Cell = ({ value }) => value;
|
|
916
|
+
}
|
|
917
|
+
const oldFunc = tableColumn.Cell;
|
|
918
|
+
|
|
919
|
+
tableColumn.Cell = (...args) => (
|
|
920
|
+
<RenderCell
|
|
921
|
+
oldFunc={oldFunc}
|
|
922
|
+
formName={formName}
|
|
923
|
+
getCopyTextForCell={getCopyTextForCell}
|
|
924
|
+
column={column}
|
|
925
|
+
isCellEditable={isCellEditable}
|
|
926
|
+
isEntityDisabled={isEntityDisabled}
|
|
927
|
+
finishCellEdit={finishCellEdit}
|
|
928
|
+
noEllipsis={noEllipsis}
|
|
929
|
+
editingCell={editingCell}
|
|
930
|
+
cancelCellEdit={cancelCellEdit}
|
|
931
|
+
editingCellSelectAll={editingCellSelectAll}
|
|
932
|
+
getCellHoverText={getCellHoverText}
|
|
933
|
+
selectedCells={selectedCells}
|
|
934
|
+
isSelectionARectangle={isSelectionARectangle}
|
|
935
|
+
startCellEdit={startCellEdit}
|
|
936
|
+
tableRef={tableRef}
|
|
937
|
+
onDragEnd={onDragEnd}
|
|
938
|
+
args={args}
|
|
939
|
+
/>
|
|
940
|
+
);
|
|
941
|
+
return tableColumn;
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
return columnsToRender.concat(tableColumns);
|
|
945
|
+
};
|
|
@@ -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
|
+
};
|
package/src/DataTable/index.js
CHANGED
|
@@ -166,7 +166,7 @@ const DataTable = ({
|
|
|
166
166
|
const {
|
|
167
167
|
reduxFormCellValidation: _reduxFormCellValidation,
|
|
168
168
|
reduxFormEditingCell,
|
|
169
|
-
reduxFormEntities
|
|
169
|
+
reduxFormEntities,
|
|
170
170
|
reduxFormQueryParams: _reduxFormQueryParams = {},
|
|
171
171
|
reduxFormSearchInput: _reduxFormSearchInput = "",
|
|
172
172
|
reduxFormSelectedEntityIdMap: _reduxFormSelectedEntityIdMap = {}
|
|
@@ -184,7 +184,6 @@ const DataTable = ({
|
|
|
184
184
|
// We want to make sure we don't rerender everything unnecessary
|
|
185
185
|
// with redux-forms we tend to do unnecessary renders
|
|
186
186
|
const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
|
|
187
|
-
const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
|
|
188
187
|
const reduxFormQueryParams = useDeepEqualMemo(_reduxFormQueryParams);
|
|
189
188
|
const reduxFormSearchInput = useDeepEqualMemo(_reduxFormSearchInput);
|
|
190
189
|
const reduxFormSelectedEntityIdMap = useDeepEqualMemo(
|
|
@@ -839,8 +838,7 @@ const DataTable = ({
|
|
|
839
838
|
}
|
|
840
839
|
}));
|
|
841
840
|
},
|
|
842
|
-
|
|
843
|
-
[]
|
|
841
|
+
[change]
|
|
844
842
|
);
|
|
845
843
|
|
|
846
844
|
const formatAndValidateEntities = useCallback(
|
|
@@ -1472,8 +1470,7 @@ const DataTable = ({
|
|
|
1472
1470
|
// "formats", not "changes".
|
|
1473
1471
|
useEffect(() => {
|
|
1474
1472
|
const formatAndValidateTableInitial = () => {
|
|
1475
|
-
const { newEnts, validationErrors } =
|
|
1476
|
-
formatAndValidateEntities(_origEntities);
|
|
1473
|
+
const { newEnts, validationErrors } = formatAndValidateEntities(entities);
|
|
1477
1474
|
const toKeep = {};
|
|
1478
1475
|
//on the initial load we want to keep any async table wide errors
|
|
1479
1476
|
forEach(reduxFormCellValidation, (v, k) => {
|
|
@@ -1481,7 +1478,12 @@ const DataTable = ({
|
|
|
1481
1478
|
toKeep[k] = v;
|
|
1482
1479
|
}
|
|
1483
1480
|
});
|
|
1484
|
-
change("reduxFormEntities",
|
|
1481
|
+
change("reduxFormEntities", prev => {
|
|
1482
|
+
if (!isEqual(prev, newEnts)) {
|
|
1483
|
+
return newEnts;
|
|
1484
|
+
}
|
|
1485
|
+
return prev;
|
|
1486
|
+
});
|
|
1485
1487
|
updateValidation(newEnts, {
|
|
1486
1488
|
...toKeep,
|
|
1487
1489
|
...validationErrors
|
|
@@ -1489,10 +1491,10 @@ const DataTable = ({
|
|
|
1489
1491
|
};
|
|
1490
1492
|
isCellEditable && formatAndValidateTableInitial();
|
|
1491
1493
|
}, [
|
|
1492
|
-
_origEntities,
|
|
1493
|
-
isCellEditable,
|
|
1494
1494
|
change,
|
|
1495
|
+
entities,
|
|
1495
1496
|
formatAndValidateEntities,
|
|
1497
|
+
isCellEditable,
|
|
1496
1498
|
reduxFormCellValidation,
|
|
1497
1499
|
updateValidation
|
|
1498
1500
|
]);
|