@teselagen/ui 0.9.8 → 0.10.3
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/CollapsibleCard/index.d.ts +2 -1
- package/DataTable/Columns.d.ts +4 -1
- package/DataTable/DisplayOptions.d.ts +2 -1
- package/DataTable/index.d.ts +3 -3
- package/DividerWithText/index.d.ts +16 -0
- package/index.cjs.js +21122 -21084
- package/index.d.ts +1 -0
- package/index.es.js +21125 -21087
- package/package.json +2 -1
- package/src/CollapsibleCard/index.js +8 -1
- package/src/DataTable/Columns.js +69 -1
- package/src/DataTable/DisplayOptions.js +35 -6
- package/src/DataTable/PagingTool.js +2 -2
- package/src/DataTable/RenderCell.js +8 -4
- package/src/DataTable/index.js +1253 -229
- package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +2 -2
- package/src/DataTable/utils/tableQueryParamsToHasuraClauses.test.js +21 -0
- package/src/DataTable/utils/useTableEntities.js +3 -2
- package/src/DataTable/utils/withTableParams.js +34 -13
- package/src/DividerWithText/index.js +30 -0
- package/src/DividerWithText/style.css +37 -0
- package/src/InfoHelper/style.css +1 -0
- package/src/UploadCsvWizard.js +7 -5
- package/src/index.js +1 -0
- package/src/utils/hooks/useDeepEqualMemo.js +2 -2
- package/src/utils/isEqualIgnoreFunctions.js +23 -0
- package/src/utils/pureNoFunc.js +4 -20
- package/ui.css +38 -0
- package/utils/isEqualIgnoreFunctions.d.ts +1 -0
- package/DataTable/EditabelCell.d.ts +0 -7
- package/DataTable/ReactTable.d.ts +0 -78
- package/DataTable/defaultProps.d.ts +0 -43
- package/DataTable/utils/computePresets.d.ts +0 -1
- package/DataTable/utils/types/Entity.d.ts +0 -9
- package/DataTable/utils/types/Field.d.ts +0 -4
- package/DataTable/utils/types/OrderBy.d.ts +0 -11
- package/DataTable/utils/types/Schema.d.ts +0 -4
- package/DataTable/utils/useDeepEqualMemo.d.ts +0 -1
- package/DataTable/utils/useHotKeysWrapper.d.ts +0 -29
- package/DataTable/utils/useTableParams.d.ts +0 -49
- package/src/DataTable/Columns.jsx +0 -945
- package/src/DataTable/EditabelCell.js +0 -44
- package/src/DataTable/EditabelCell.jsx +0 -44
- package/src/DataTable/ReactTable.js +0 -738
- package/src/DataTable/RenderCell.jsx +0 -191
- package/src/DataTable/defaultProps.js +0 -45
- package/src/DataTable/utils/computePresets.js +0 -42
- package/src/DataTable/utils/convertSchema.ts +0 -79
- package/src/DataTable/utils/formatPasteData.ts +0 -34
- package/src/DataTable/utils/getAllRows.ts +0 -11
- package/src/DataTable/utils/getCellCopyText.ts +0 -7
- package/src/DataTable/utils/getCellInfo.ts +0 -46
- package/src/DataTable/utils/getFieldPathToField.ts +0 -10
- package/src/DataTable/utils/getIdOrCodeOrIndex.ts +0 -14
- package/src/DataTable/utils/getLastSelectedEntity.ts +0 -15
- package/src/DataTable/utils/getNewEntToSelect.ts +0 -32
- package/src/DataTable/utils/initializeHasuraWhereAndFilter.ts +0 -35
- package/src/DataTable/utils/isBottomRightCornerOfRectangle.ts +0 -27
- package/src/DataTable/utils/isEntityClean.ts +0 -15
- package/src/DataTable/utils/primarySelectedValue.ts +0 -1
- package/src/DataTable/utils/removeCleanRows.ts +0 -26
- package/src/DataTable/utils/selection.ts +0 -11
- package/src/DataTable/utils/types/Entity.ts +0 -13
- package/src/DataTable/utils/types/Field.ts +0 -4
- package/src/DataTable/utils/types/OrderBy.ts +0 -15
- package/src/DataTable/utils/types/Schema.ts +0 -5
- package/src/DataTable/utils/useDeepEqualMemo.js +0 -10
- package/src/DataTable/utils/useHotKeysWrapper.js +0 -395
- package/src/DataTable/utils/useTableEntities.ts +0 -60
- package/src/DataTable/utils/useTableParams.js +0 -361
- package/src/DataTable/utils/utils.ts +0 -39
- package/src/Timeline/TimelineEvent.tsx +0 -36
- package/src/Timeline/index.tsx +0 -21
- package/src/utils/browserUtils.ts +0 -3
- package/src/utils/determineBlackOrWhiteTextColor.ts +0 -11
- package/src/utils/getTextFromEl.ts +0 -45
- package/src/utils/handlerHelpers.ts +0 -32
- package/src/utils/hooks/index.ts +0 -1
- package/src/utils/hooks/useDeepEqualMemo.ts +0 -10
- package/src/utils/hooks/useStableReference.ts +0 -9
- package/src/utils/hotkeyUtils.tsx +0 -155
- package/src/utils/isBeingCalledExcessively.ts +0 -37
- package/style.css +0 -10537
package/src/DataTable/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
noop,
|
|
18
18
|
cloneDeep,
|
|
19
19
|
keyBy,
|
|
20
|
+
omit,
|
|
20
21
|
forEach,
|
|
21
22
|
lowerCase,
|
|
22
23
|
get,
|
|
@@ -29,33 +30,57 @@ import {
|
|
|
29
30
|
some,
|
|
30
31
|
identity
|
|
31
32
|
} from "lodash-es";
|
|
32
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
Button,
|
|
35
|
+
Menu,
|
|
36
|
+
MenuItem,
|
|
37
|
+
ContextMenu,
|
|
38
|
+
Icon,
|
|
39
|
+
Intent,
|
|
40
|
+
Callout,
|
|
41
|
+
Tooltip,
|
|
42
|
+
useHotkeys
|
|
43
|
+
} from "@blueprintjs/core";
|
|
33
44
|
import { arrayMove } from "@dnd-kit/sortable";
|
|
34
45
|
import classNames from "classnames";
|
|
35
46
|
import scrollIntoView from "dom-scroll-into-view";
|
|
36
|
-
import { VIRTUALIZE_CUTOFF_LENGTH } from "@teselagen/react-table";
|
|
37
|
-
import immer, { produceWithPatches, enablePatches } from "immer";
|
|
47
|
+
import ReactTable, { VIRTUALIZE_CUTOFF_LENGTH } from "@teselagen/react-table";
|
|
48
|
+
import immer, { produceWithPatches, enablePatches, applyPatches } from "immer";
|
|
38
49
|
import papaparse from "papaparse";
|
|
39
50
|
import { useDispatch, useSelector } from "react-redux";
|
|
51
|
+
import { ThComponent } from "./ThComponent";
|
|
40
52
|
import {
|
|
41
53
|
defaultParsePaste,
|
|
42
54
|
formatPasteData,
|
|
43
55
|
getAllRows,
|
|
56
|
+
getCellCopyText,
|
|
44
57
|
getCellInfo,
|
|
45
58
|
getEntityIdToEntity,
|
|
46
59
|
getFieldPathToIndex,
|
|
47
60
|
getIdOrCodeOrIndex,
|
|
61
|
+
getLastSelectedEntity,
|
|
62
|
+
getNewEntToSelect,
|
|
48
63
|
getRecordsFromIdMap,
|
|
64
|
+
getRowCopyText,
|
|
65
|
+
handleCopyColumn,
|
|
66
|
+
handleCopyHelper,
|
|
49
67
|
handleCopyRows,
|
|
50
68
|
handleCopyTable,
|
|
69
|
+
isEntityClean,
|
|
51
70
|
PRIMARY_SELECTED_VAL,
|
|
52
71
|
removeCleanRows
|
|
53
72
|
} from "./utils";
|
|
54
73
|
import { useDeepEqualMemo } from "../utils/hooks";
|
|
55
|
-
import
|
|
74
|
+
import rowClick, {
|
|
75
|
+
changeSelectedEntities,
|
|
76
|
+
finalizeSelection
|
|
77
|
+
} from "./utils/rowClick";
|
|
56
78
|
import PagingTool from "./PagingTool";
|
|
57
79
|
import SearchBar from "./SearchBar";
|
|
58
80
|
import DisplayOptions from "./DisplayOptions";
|
|
81
|
+
import DisabledLoadingComponent from "./DisabledLoadingComponent";
|
|
82
|
+
import SortableColumns from "./SortableColumns";
|
|
83
|
+
import dataTableEnhancer from "./dataTableEnhancer";
|
|
59
84
|
import "../toastr";
|
|
60
85
|
import "@teselagen/react-table/react-table.css";
|
|
61
86
|
import "./style.css";
|
|
@@ -73,21 +98,27 @@ import {
|
|
|
73
98
|
getCurrentParamsFromUrl,
|
|
74
99
|
setCurrentParamsOnUrl
|
|
75
100
|
} from "./utils/queryParams";
|
|
76
|
-
import {
|
|
101
|
+
import { useColumns } from "./Columns";
|
|
102
|
+
import { formValueSelector, change as _change } from "redux-form";
|
|
77
103
|
import { throwFormError } from "../throwFormError";
|
|
78
104
|
import { isObservableArray, toJS } from "mobx";
|
|
79
105
|
import { isBeingCalledExcessively } from "../utils/isBeingCalledExcessively";
|
|
80
106
|
import { getCCDisplayName } from "./utils/tableQueryParamsToHasuraClauses";
|
|
81
|
-
import { useHotKeysWrapper } from "./utils/useHotKeysWrapper";
|
|
82
|
-
import { withRouter } from "react-router-dom";
|
|
83
|
-
import { ReactTable } from "./ReactTable";
|
|
84
107
|
|
|
85
108
|
enablePatches();
|
|
109
|
+
const IS_LINUX = window.navigator.platform.toLowerCase().search("linux") > -1;
|
|
110
|
+
|
|
111
|
+
const itemSizeEstimators = {
|
|
112
|
+
compact: () => 25.34,
|
|
113
|
+
normal: () => 33.34,
|
|
114
|
+
comfortable: () => 41.34
|
|
115
|
+
};
|
|
86
116
|
|
|
87
117
|
const DataTable = ({
|
|
88
118
|
controlled_pageSize,
|
|
89
119
|
formName = "tgDataTable",
|
|
90
120
|
history,
|
|
121
|
+
doNotSearchHiddenColumns,
|
|
91
122
|
isSimple,
|
|
92
123
|
isLocalCall = true,
|
|
93
124
|
isTableParamsConnected,
|
|
@@ -143,15 +174,15 @@ const DataTable = ({
|
|
|
143
174
|
reduxFormEntities,
|
|
144
175
|
reduxFormQueryParams: _reduxFormQueryParams = {},
|
|
145
176
|
reduxFormSelectedEntityIdMap: _reduxFormSelectedEntityIdMap = {}
|
|
146
|
-
} = useSelector(state
|
|
147
|
-
formValueSelector(formName)(
|
|
177
|
+
} = useSelector(function dtFormParamsSelector(state) {
|
|
178
|
+
return formValueSelector(formName)(
|
|
148
179
|
state,
|
|
149
180
|
"reduxFormCellValidation",
|
|
150
181
|
"reduxFormEntities",
|
|
151
182
|
"reduxFormQueryParams",
|
|
152
183
|
"reduxFormSelectedEntityIdMap"
|
|
153
|
-
)
|
|
154
|
-
);
|
|
184
|
+
);
|
|
185
|
+
});
|
|
155
186
|
|
|
156
187
|
// We want to make sure we don't rerender everything unnecessary
|
|
157
188
|
// with redux-forms we tend to do unnecessary renders
|
|
@@ -376,7 +407,7 @@ const DataTable = ({
|
|
|
376
407
|
expandAllByDefault,
|
|
377
408
|
extraClasses = "",
|
|
378
409
|
extraCompact: _extraCompact,
|
|
379
|
-
filters,
|
|
410
|
+
filters = [],
|
|
380
411
|
fragment,
|
|
381
412
|
getCellHoverText,
|
|
382
413
|
getRowClassName,
|
|
@@ -451,15 +482,16 @@ const DataTable = ({
|
|
|
451
482
|
withSelectAll,
|
|
452
483
|
withSort,
|
|
453
484
|
withTitle = !isSimple,
|
|
454
|
-
noExcessiveCheck
|
|
485
|
+
noExcessiveCheck,
|
|
486
|
+
isEntityCountLoading
|
|
455
487
|
} = props;
|
|
456
488
|
|
|
457
489
|
const _entities = useMemo(
|
|
458
490
|
() => (reduxFormEntities?.length ? reduxFormEntities : _origEntities) || [],
|
|
459
491
|
[_origEntities, reduxFormEntities]
|
|
460
492
|
);
|
|
461
|
-
|
|
462
493
|
const entities = useDeepEqualMemo(_entities);
|
|
494
|
+
|
|
463
495
|
const entitiesAcrossPages = useDeepEqualMemo(_entitiesAcrossPages);
|
|
464
496
|
|
|
465
497
|
// This is because we need to maintain the reduxFormSelectedEntityIdMap and
|
|
@@ -474,16 +506,34 @@ const DataTable = ({
|
|
|
474
506
|
entities,
|
|
475
507
|
change
|
|
476
508
|
});
|
|
477
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
478
509
|
}, [
|
|
479
510
|
entitiesAcrossPages,
|
|
480
511
|
reduxFormSelectedEntityIdMap,
|
|
481
512
|
change,
|
|
482
|
-
noExcessiveCheck
|
|
513
|
+
noExcessiveCheck,
|
|
514
|
+
entities,
|
|
515
|
+
formName
|
|
483
516
|
]);
|
|
484
517
|
|
|
485
|
-
const [tableConfig,
|
|
486
|
-
|
|
518
|
+
const [tableConfig, _setTableConfig] = useState({ fieldOptions: [] });
|
|
519
|
+
const setTableConfig = useCallback(
|
|
520
|
+
newConfig => {
|
|
521
|
+
_setTableConfig(prev => {
|
|
522
|
+
let newConfigVal = newConfig;
|
|
523
|
+
if (typeof newConfig === "function") {
|
|
524
|
+
newConfigVal = newConfig(prev);
|
|
525
|
+
}
|
|
526
|
+
if (!isEqual(prev.fieldOptions, newConfigVal.fieldOptions)) {
|
|
527
|
+
change(
|
|
528
|
+
"reduxFormReadOnlyFieldOptions",
|
|
529
|
+
newConfigVal.fieldOptions || []
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
return newConfigVal;
|
|
533
|
+
});
|
|
534
|
+
},
|
|
535
|
+
[change]
|
|
536
|
+
);
|
|
487
537
|
useEffect(() => {
|
|
488
538
|
if (withDisplayOptions) {
|
|
489
539
|
let newTableConfig = {};
|
|
@@ -632,6 +682,7 @@ const DataTable = ({
|
|
|
632
682
|
convertedSchema,
|
|
633
683
|
currentParams,
|
|
634
684
|
entities,
|
|
685
|
+
setTableConfig,
|
|
635
686
|
history,
|
|
636
687
|
isInfinite,
|
|
637
688
|
isOpenable,
|
|
@@ -749,6 +800,7 @@ const DataTable = ({
|
|
|
749
800
|
currentUser?.user?.id,
|
|
750
801
|
deleteTableConfiguration,
|
|
751
802
|
formName,
|
|
803
|
+
setTableConfig,
|
|
752
804
|
schema.fields,
|
|
753
805
|
syncDisplayOptionsToDb,
|
|
754
806
|
tableConfig,
|
|
@@ -765,6 +817,11 @@ const DataTable = ({
|
|
|
765
817
|
extraCompact = tableConfig.density === "extraCompact";
|
|
766
818
|
}
|
|
767
819
|
|
|
820
|
+
const resized = useMemo(
|
|
821
|
+
() => tableConfig.resized || [],
|
|
822
|
+
[tableConfig?.resized]
|
|
823
|
+
);
|
|
824
|
+
|
|
768
825
|
const pageSize = controlled_pageSize || _pageSize;
|
|
769
826
|
|
|
770
827
|
const [expandedEntityIdMap, setExpandedEntityIdMap] = useState(() => {
|
|
@@ -849,85 +906,189 @@ const DataTable = ({
|
|
|
849
906
|
[schema.fields]
|
|
850
907
|
);
|
|
851
908
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (helperProp) {
|
|
856
|
-
helperProp.updateValidationHelper = () => {
|
|
857
|
-
updateValidation(entities, reduxFormCellValidation);
|
|
858
|
-
};
|
|
909
|
+
const updateValidationHelper = useCallback(() => {
|
|
910
|
+
updateValidation(entities, reduxFormCellValidation);
|
|
911
|
+
}, [entities, reduxFormCellValidation, updateValidation]);
|
|
859
912
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
913
|
+
const addEditableTableEntities = useCallback(
|
|
914
|
+
incomingEnts => {
|
|
915
|
+
updateEntitiesHelper(entities, entities => {
|
|
916
|
+
const newEntities = incomingEnts.map(e => ({
|
|
917
|
+
...e,
|
|
918
|
+
id: e.id || nanoid(),
|
|
919
|
+
_isClean: false
|
|
920
|
+
}));
|
|
867
921
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
);
|
|
875
|
-
if (every(entities, "_isClean")) {
|
|
876
|
-
forEach(newEnts, (e, i) => {
|
|
877
|
-
entities[i] = e;
|
|
878
|
-
});
|
|
879
|
-
} else {
|
|
880
|
-
entities.splice(entities.length, 0, ...newEnts);
|
|
922
|
+
const { newEnts, validationErrors } = formatAndValidateEntities(
|
|
923
|
+
newEntities,
|
|
924
|
+
{
|
|
925
|
+
useDefaultValues: true,
|
|
926
|
+
indexToStartAt: entities.length
|
|
881
927
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
928
|
+
);
|
|
929
|
+
if (every(entities, "_isClean")) {
|
|
930
|
+
forEach(newEnts, (e, i) => {
|
|
931
|
+
entities[i] = e;
|
|
886
932
|
});
|
|
887
|
-
}
|
|
888
|
-
|
|
933
|
+
} else {
|
|
934
|
+
entities.splice(entities.length, 0, ...newEnts);
|
|
935
|
+
}
|
|
889
936
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
reduxFormCellValidation
|
|
894
|
-
);
|
|
895
|
-
const validationWTableErrs = validateTableWideErrors({
|
|
896
|
-
entities: entsToUse,
|
|
897
|
-
schema,
|
|
898
|
-
newCellValidate: validationToUse
|
|
937
|
+
updateValidation(entities, {
|
|
938
|
+
...reduxFormCellValidation,
|
|
939
|
+
...validationErrors
|
|
899
940
|
});
|
|
941
|
+
});
|
|
942
|
+
},
|
|
943
|
+
[
|
|
944
|
+
entities,
|
|
945
|
+
formatAndValidateEntities,
|
|
946
|
+
reduxFormCellValidation,
|
|
947
|
+
updateEntitiesHelper,
|
|
948
|
+
updateValidation
|
|
949
|
+
]
|
|
950
|
+
);
|
|
900
951
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if (invalid) {
|
|
912
|
-
throwFormError(
|
|
913
|
-
"Please fix the errors in the table before submitting."
|
|
914
|
-
);
|
|
915
|
-
}
|
|
952
|
+
const getEditableTableInfoAndThrowFormError = useCallback(() => {
|
|
953
|
+
const { entsToUse, validationToUse } = removeCleanRows(
|
|
954
|
+
reduxFormEntities,
|
|
955
|
+
reduxFormCellValidation
|
|
956
|
+
);
|
|
957
|
+
const validationWTableErrs = validateTableWideErrors({
|
|
958
|
+
entities: entsToUse,
|
|
959
|
+
schema,
|
|
960
|
+
newCellValidate: validationToUse
|
|
961
|
+
});
|
|
916
962
|
|
|
917
|
-
|
|
918
|
-
|
|
963
|
+
if (!entsToUse?.length) {
|
|
964
|
+
throwFormError(
|
|
965
|
+
"Please add at least one row to the table before submitting."
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
const invalid =
|
|
969
|
+
isEmpty(validationWTableErrs) || !some(validationWTableErrs, v => v)
|
|
970
|
+
? undefined
|
|
971
|
+
: validationWTableErrs;
|
|
972
|
+
|
|
973
|
+
if (invalid) {
|
|
974
|
+
throwFormError("Please fix the errors in the table before submitting.");
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return entsToUse;
|
|
978
|
+
}, [reduxFormCellValidation, reduxFormEntities, schema]);
|
|
979
|
+
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
// This is bad practice, we shouldn't be assigning value to an
|
|
982
|
+
// external variable
|
|
983
|
+
if (helperProp) {
|
|
984
|
+
helperProp.updateValidationHelper = updateValidationHelper;
|
|
985
|
+
helperProp.addEditableTableEntities = addEditableTableEntities;
|
|
986
|
+
helperProp.getEditableTableInfoAndThrowFormError =
|
|
987
|
+
getEditableTableInfoAndThrowFormError;
|
|
919
988
|
}
|
|
920
989
|
}, [
|
|
921
|
-
|
|
922
|
-
|
|
990
|
+
addEditableTableEntities,
|
|
991
|
+
getEditableTableInfoAndThrowFormError,
|
|
923
992
|
helperProp,
|
|
924
|
-
|
|
925
|
-
reduxFormEntities,
|
|
926
|
-
schema,
|
|
927
|
-
updateEntitiesHelper,
|
|
928
|
-
updateValidation
|
|
993
|
+
updateValidationHelper
|
|
929
994
|
]);
|
|
930
995
|
|
|
996
|
+
const handleRowMove = useCallback(
|
|
997
|
+
(type, shiftHeld) => e => {
|
|
998
|
+
e.preventDefault();
|
|
999
|
+
e.stopPropagation();
|
|
1000
|
+
let newIdMap = {};
|
|
1001
|
+
const lastSelectedEnt = getLastSelectedEntity(
|
|
1002
|
+
reduxFormSelectedEntityIdMap
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
if (noSelect) return;
|
|
1006
|
+
if (lastSelectedEnt) {
|
|
1007
|
+
let lastSelectedIndex = entities.findIndex(
|
|
1008
|
+
ent => ent === lastSelectedEnt
|
|
1009
|
+
);
|
|
1010
|
+
if (lastSelectedIndex === -1) {
|
|
1011
|
+
if (lastSelectedEnt.id !== undefined) {
|
|
1012
|
+
lastSelectedIndex = entities.findIndex(
|
|
1013
|
+
ent => ent.id === lastSelectedEnt.id
|
|
1014
|
+
);
|
|
1015
|
+
} else if (lastSelectedEnt.code !== undefined) {
|
|
1016
|
+
lastSelectedIndex = entities.findIndex(
|
|
1017
|
+
ent => ent.code === lastSelectedEnt.code
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (lastSelectedIndex === -1) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const newEntToSelect = getNewEntToSelect({
|
|
1025
|
+
type,
|
|
1026
|
+
lastSelectedIndex,
|
|
1027
|
+
entities,
|
|
1028
|
+
isEntityDisabled
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
if (!newEntToSelect) return;
|
|
1032
|
+
if (shiftHeld && !isSingleSelect) {
|
|
1033
|
+
if (
|
|
1034
|
+
reduxFormSelectedEntityIdMap[
|
|
1035
|
+
newEntToSelect.id || newEntToSelect.code
|
|
1036
|
+
]
|
|
1037
|
+
) {
|
|
1038
|
+
//the entity being moved to has already been selected
|
|
1039
|
+
newIdMap = omit(reduxFormSelectedEntityIdMap, [
|
|
1040
|
+
lastSelectedEnt.id || lastSelectedEnt.code
|
|
1041
|
+
]);
|
|
1042
|
+
newIdMap[newEntToSelect.id || newEntToSelect.code].time =
|
|
1043
|
+
Date.now() + 1;
|
|
1044
|
+
} else {
|
|
1045
|
+
//the entity being moved to has NOT been selected yet
|
|
1046
|
+
newIdMap = {
|
|
1047
|
+
...reduxFormSelectedEntityIdMap,
|
|
1048
|
+
[newEntToSelect.id || newEntToSelect.code]: {
|
|
1049
|
+
entity: newEntToSelect,
|
|
1050
|
+
time: Date.now()
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
} else {
|
|
1055
|
+
//no shiftHeld
|
|
1056
|
+
newIdMap[newEntToSelect.id || newEntToSelect.code] = {
|
|
1057
|
+
entity: newEntToSelect,
|
|
1058
|
+
time: Date.now()
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
finalizeSelection({
|
|
1064
|
+
idMap: newIdMap,
|
|
1065
|
+
entities,
|
|
1066
|
+
props: {
|
|
1067
|
+
onDeselect,
|
|
1068
|
+
onSingleRowSelect,
|
|
1069
|
+
onMultiRowSelect,
|
|
1070
|
+
noDeselectAll,
|
|
1071
|
+
onRowSelect,
|
|
1072
|
+
noSelect,
|
|
1073
|
+
change
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
},
|
|
1077
|
+
[
|
|
1078
|
+
change,
|
|
1079
|
+
entities,
|
|
1080
|
+
isEntityDisabled,
|
|
1081
|
+
isSingleSelect,
|
|
1082
|
+
noDeselectAll,
|
|
1083
|
+
noSelect,
|
|
1084
|
+
onDeselect,
|
|
1085
|
+
onMultiRowSelect,
|
|
1086
|
+
onRowSelect,
|
|
1087
|
+
onSingleRowSelect,
|
|
1088
|
+
reduxFormSelectedEntityIdMap
|
|
1089
|
+
]
|
|
1090
|
+
);
|
|
1091
|
+
|
|
931
1092
|
const primarySelectedCellId = useMemo(() => {
|
|
932
1093
|
for (const k of Object.keys(selectedCells)) {
|
|
933
1094
|
if (selectedCells[k] === PRIMARY_SELECTED_VAL) {
|
|
@@ -963,6 +1124,50 @@ const DataTable = ({
|
|
|
963
1124
|
[change]
|
|
964
1125
|
);
|
|
965
1126
|
|
|
1127
|
+
const handleEnterStartCellEdit = useCallback(
|
|
1128
|
+
e => {
|
|
1129
|
+
e.stopPropagation();
|
|
1130
|
+
startCellEdit(primarySelectedCellId);
|
|
1131
|
+
},
|
|
1132
|
+
[primarySelectedCellId, startCellEdit]
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
const handleDeleteCell = useCallback(() => {
|
|
1136
|
+
const newCellValidate = {
|
|
1137
|
+
...reduxFormCellValidation
|
|
1138
|
+
};
|
|
1139
|
+
if (isEmpty(selectedCells)) return;
|
|
1140
|
+
const rowIds = [];
|
|
1141
|
+
updateEntitiesHelper(entities, entities => {
|
|
1142
|
+
const entityIdToEntity = getEntityIdToEntity(entities);
|
|
1143
|
+
Object.keys(selectedCells).forEach(cellId => {
|
|
1144
|
+
const [rowId, path] = cellId.split(":");
|
|
1145
|
+
rowIds.push(rowId);
|
|
1146
|
+
const entity = entityIdToEntity[rowId].e;
|
|
1147
|
+
delete entity._isClean;
|
|
1148
|
+
const { error } = editCellHelper({
|
|
1149
|
+
entity,
|
|
1150
|
+
path,
|
|
1151
|
+
schema,
|
|
1152
|
+
newVal: ""
|
|
1153
|
+
});
|
|
1154
|
+
if (error) {
|
|
1155
|
+
newCellValidate[cellId] = error;
|
|
1156
|
+
} else {
|
|
1157
|
+
delete newCellValidate[cellId];
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
updateValidation(entities, newCellValidate);
|
|
1161
|
+
});
|
|
1162
|
+
}, [
|
|
1163
|
+
entities,
|
|
1164
|
+
reduxFormCellValidation,
|
|
1165
|
+
schema,
|
|
1166
|
+
selectedCells,
|
|
1167
|
+
updateEntitiesHelper,
|
|
1168
|
+
updateValidation
|
|
1169
|
+
]);
|
|
1170
|
+
|
|
966
1171
|
const waitUntilAllRowsAreRendered = useCallback(() => {
|
|
967
1172
|
return new Promise(resolve => {
|
|
968
1173
|
const interval = setInterval(() => {
|
|
@@ -977,6 +1182,74 @@ const DataTable = ({
|
|
|
977
1182
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
978
1183
|
}, []);
|
|
979
1184
|
|
|
1185
|
+
const handleCopySelectedCells = useCallback(async () => {
|
|
1186
|
+
// if the current selection is consecutive cells then copy with
|
|
1187
|
+
// tabs between. if not then just select primary selected cell
|
|
1188
|
+
if (isEmpty(selectedCells)) return;
|
|
1189
|
+
|
|
1190
|
+
// Temporarily disable virtualization for large tables
|
|
1191
|
+
if (entities.length > VIRTUALIZE_CUTOFF_LENGTH) {
|
|
1192
|
+
setNoVirtual(true);
|
|
1193
|
+
await waitUntilAllRowsAreRendered();
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const pathToIndex = getFieldPathToIndex(schema);
|
|
1197
|
+
const entityIdToEntity = getEntityIdToEntity(entities);
|
|
1198
|
+
const selectionGrid = [];
|
|
1199
|
+
let firstRowIndex;
|
|
1200
|
+
let firstCellIndex;
|
|
1201
|
+
Object.keys(selectedCells).forEach(key => {
|
|
1202
|
+
const [rowId, path] = key.split(":");
|
|
1203
|
+
const eInfo = entityIdToEntity[rowId];
|
|
1204
|
+
if (eInfo) {
|
|
1205
|
+
if (firstRowIndex === undefined || eInfo.i < firstRowIndex) {
|
|
1206
|
+
firstRowIndex = eInfo.i;
|
|
1207
|
+
}
|
|
1208
|
+
if (!selectionGrid[eInfo.i]) {
|
|
1209
|
+
selectionGrid[eInfo.i] = [];
|
|
1210
|
+
}
|
|
1211
|
+
const cellIndex = pathToIndex[path];
|
|
1212
|
+
if (firstCellIndex === undefined || cellIndex < firstCellIndex) {
|
|
1213
|
+
firstCellIndex = cellIndex;
|
|
1214
|
+
}
|
|
1215
|
+
selectionGrid[eInfo.i][cellIndex] = true;
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
if (firstRowIndex === undefined) return;
|
|
1219
|
+
const allRows = getAllRows(tableRef);
|
|
1220
|
+
let fullCellText = "";
|
|
1221
|
+
const fullJson = [];
|
|
1222
|
+
times(selectionGrid.length, i => {
|
|
1223
|
+
const row = selectionGrid[i];
|
|
1224
|
+
if (fullCellText) {
|
|
1225
|
+
fullCellText += "\n";
|
|
1226
|
+
}
|
|
1227
|
+
if (!row) {
|
|
1228
|
+
return;
|
|
1229
|
+
} else {
|
|
1230
|
+
const jsonRow = [];
|
|
1231
|
+
// ignore header
|
|
1232
|
+
let [rowCopyText, json] = getRowCopyText(allRows[i + 1]);
|
|
1233
|
+
rowCopyText = rowCopyText.split("\t");
|
|
1234
|
+
times(row.length, i => {
|
|
1235
|
+
const cell = row[i];
|
|
1236
|
+
if (cell) {
|
|
1237
|
+
fullCellText += rowCopyText[i];
|
|
1238
|
+
jsonRow.push(json[i]);
|
|
1239
|
+
}
|
|
1240
|
+
if (i !== row.length - 1 && i >= firstCellIndex) fullCellText += "\t";
|
|
1241
|
+
});
|
|
1242
|
+
fullJson.push(jsonRow);
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
if (!fullCellText) return window.toastr.warning("No text to copy");
|
|
1246
|
+
|
|
1247
|
+
handleCopyHelper(fullCellText, fullJson, "Selected cells copied");
|
|
1248
|
+
|
|
1249
|
+
// Re-enable virtualization if it was disabled
|
|
1250
|
+
setNoVirtual(false);
|
|
1251
|
+
}, [entities, selectedCells, schema, waitUntilAllRowsAreRendered]);
|
|
1252
|
+
|
|
980
1253
|
const handleCopySelectedRows = useCallback(
|
|
981
1254
|
async selectedRecords => {
|
|
982
1255
|
if (entities.length > VIRTUALIZE_CUTOFF_LENGTH) {
|
|
@@ -1019,36 +1292,237 @@ const DataTable = ({
|
|
|
1019
1292
|
[entities, waitUntilAllRowsAreRendered]
|
|
1020
1293
|
);
|
|
1021
1294
|
|
|
1022
|
-
const
|
|
1295
|
+
const handleCopyHotkey = useCallback(
|
|
1296
|
+
e => {
|
|
1297
|
+
if (isCellEditable) {
|
|
1298
|
+
handleCopySelectedCells(e);
|
|
1299
|
+
} else {
|
|
1300
|
+
handleCopySelectedRows(
|
|
1301
|
+
getRecordsFromIdMap(reduxFormSelectedEntityIdMap),
|
|
1302
|
+
e
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
[
|
|
1307
|
+
handleCopySelectedCells,
|
|
1308
|
+
handleCopySelectedRows,
|
|
1309
|
+
isCellEditable,
|
|
1310
|
+
reduxFormSelectedEntityIdMap
|
|
1311
|
+
]
|
|
1312
|
+
);
|
|
1313
|
+
|
|
1314
|
+
const handleCut = useCallback(
|
|
1315
|
+
e => {
|
|
1316
|
+
handleDeleteCell();
|
|
1317
|
+
handleCopyHotkey(e);
|
|
1318
|
+
},
|
|
1319
|
+
[handleCopyHotkey, handleDeleteCell]
|
|
1320
|
+
);
|
|
1321
|
+
|
|
1322
|
+
const flashTableBorder = () => {
|
|
1323
|
+
try {
|
|
1324
|
+
const table = tableRef.current.tableRef;
|
|
1325
|
+
table.classList.add("tgBorderBlue");
|
|
1326
|
+
setTimeout(() => {
|
|
1327
|
+
table.classList.remove("tgBorderBlue");
|
|
1328
|
+
}, 300);
|
|
1329
|
+
} catch (e) {
|
|
1330
|
+
console.error(`err when flashing table border:`, e);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
const handleUndo = useCallback(() => {
|
|
1335
|
+
if (entitiesUndoRedoStack.currentVersion > 0) {
|
|
1336
|
+
flashTableBorder();
|
|
1337
|
+
const nextState = applyPatches(
|
|
1338
|
+
entities,
|
|
1339
|
+
entitiesUndoRedoStack[entitiesUndoRedoStack.currentVersion]
|
|
1340
|
+
.inversePatches
|
|
1341
|
+
);
|
|
1342
|
+
const { newEnts, validationErrors } =
|
|
1343
|
+
formatAndValidateEntities(nextState);
|
|
1344
|
+
setEntitiesUndoRedoStack(prev => ({
|
|
1345
|
+
...prev,
|
|
1346
|
+
currentVersion: prev.currentVersion - 1
|
|
1347
|
+
}));
|
|
1348
|
+
updateValidation(newEnts, validationErrors);
|
|
1349
|
+
change("reduxFormEntities", newEnts);
|
|
1350
|
+
}
|
|
1351
|
+
}, [
|
|
1023
1352
|
change,
|
|
1024
1353
|
entities,
|
|
1354
|
+
formatAndValidateEntities,
|
|
1025
1355
|
entitiesUndoRedoStack,
|
|
1356
|
+
updateValidation
|
|
1357
|
+
]);
|
|
1358
|
+
|
|
1359
|
+
const handleRedo = useCallback(() => {
|
|
1360
|
+
const nextV = entitiesUndoRedoStack.currentVersion + 1;
|
|
1361
|
+
if (entitiesUndoRedoStack[nextV]) {
|
|
1362
|
+
flashTableBorder();
|
|
1363
|
+
const nextState = applyPatches(
|
|
1364
|
+
entities,
|
|
1365
|
+
entitiesUndoRedoStack[nextV].patches
|
|
1366
|
+
);
|
|
1367
|
+
const { newEnts, validationErrors } =
|
|
1368
|
+
formatAndValidateEntities(nextState);
|
|
1369
|
+
change("reduxFormEntities", newEnts);
|
|
1370
|
+
updateValidation(newEnts, validationErrors);
|
|
1371
|
+
setEntitiesUndoRedoStack(prev => ({
|
|
1372
|
+
...prev,
|
|
1373
|
+
currentVersion: nextV
|
|
1374
|
+
}));
|
|
1375
|
+
}
|
|
1376
|
+
}, [
|
|
1377
|
+
change,
|
|
1378
|
+
entities,
|
|
1026
1379
|
formatAndValidateEntities,
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1380
|
+
entitiesUndoRedoStack,
|
|
1381
|
+
updateValidation
|
|
1382
|
+
]);
|
|
1383
|
+
|
|
1384
|
+
const handleSelectAllRows = useCallback(
|
|
1385
|
+
e => {
|
|
1386
|
+
if (isSingleSelect) return;
|
|
1387
|
+
e.preventDefault();
|
|
1388
|
+
|
|
1389
|
+
if (isCellEditable) {
|
|
1390
|
+
const schemaPaths = schema.fields.map(f => f.path);
|
|
1391
|
+
const newSelectedCells = {};
|
|
1392
|
+
entities.forEach((entity, i) => {
|
|
1393
|
+
if (isEntityDisabled(entity)) return;
|
|
1394
|
+
const entityId = getIdOrCodeOrIndex(entity, i);
|
|
1395
|
+
schemaPaths.forEach(p => {
|
|
1396
|
+
newSelectedCells[`${entityId}:${p}`] = true;
|
|
1397
|
+
});
|
|
1398
|
+
});
|
|
1399
|
+
setSelectedCells(newSelectedCells);
|
|
1400
|
+
} else {
|
|
1401
|
+
const newIdMap = {};
|
|
1402
|
+
|
|
1403
|
+
entities.forEach((entity, i) => {
|
|
1404
|
+
if (isEntityDisabled(entity)) return;
|
|
1405
|
+
const entityId = getIdOrCodeOrIndex(entity, i);
|
|
1406
|
+
newIdMap[entityId] = { entity };
|
|
1407
|
+
});
|
|
1408
|
+
finalizeSelection({
|
|
1409
|
+
idMap: newIdMap,
|
|
1410
|
+
entities,
|
|
1411
|
+
props: {
|
|
1412
|
+
onDeselect,
|
|
1413
|
+
onSingleRowSelect,
|
|
1414
|
+
onMultiRowSelect,
|
|
1415
|
+
noDeselectAll,
|
|
1416
|
+
onRowSelect,
|
|
1417
|
+
noSelect,
|
|
1418
|
+
change
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
},
|
|
1423
|
+
[
|
|
1424
|
+
change,
|
|
1425
|
+
entities,
|
|
1426
|
+
isCellEditable,
|
|
1427
|
+
isEntityDisabled,
|
|
1428
|
+
isSingleSelect,
|
|
1429
|
+
noDeselectAll,
|
|
1430
|
+
noSelect,
|
|
1431
|
+
onDeselect,
|
|
1432
|
+
onMultiRowSelect,
|
|
1433
|
+
onRowSelect,
|
|
1434
|
+
onSingleRowSelect,
|
|
1435
|
+
schema.fields
|
|
1436
|
+
]
|
|
1437
|
+
);
|
|
1051
1438
|
|
|
1439
|
+
const hotKeys = useMemo(
|
|
1440
|
+
() => [
|
|
1441
|
+
{
|
|
1442
|
+
global: false,
|
|
1443
|
+
combo: "up",
|
|
1444
|
+
label: "Move Up a Row",
|
|
1445
|
+
onKeyDown: handleRowMove("up")
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
global: false,
|
|
1449
|
+
combo: "down",
|
|
1450
|
+
label: "Move Down a Row",
|
|
1451
|
+
onKeyDown: handleRowMove("down")
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
global: false,
|
|
1455
|
+
combo: "up+shift",
|
|
1456
|
+
label: "Move Up a Row",
|
|
1457
|
+
onKeyDown: handleRowMove("up", true)
|
|
1458
|
+
},
|
|
1459
|
+
...(isCellEditable
|
|
1460
|
+
? [
|
|
1461
|
+
{
|
|
1462
|
+
global: false,
|
|
1463
|
+
combo: "enter",
|
|
1464
|
+
label: "Enter -> Start Cell Edit",
|
|
1465
|
+
onKeyDown: handleEnterStartCellEdit
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
global: false,
|
|
1469
|
+
combo: "mod+x",
|
|
1470
|
+
label: "Cut",
|
|
1471
|
+
onKeyDown: handleCut
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
global: false,
|
|
1475
|
+
combo: IS_LINUX ? "alt+z" : "mod+z",
|
|
1476
|
+
label: "Undo",
|
|
1477
|
+
onKeyDown: handleUndo
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
global: false,
|
|
1481
|
+
combo: IS_LINUX ? "alt+shift+z" : "mod+shift+z",
|
|
1482
|
+
label: "Redo",
|
|
1483
|
+
onKeyDown: handleRedo
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
global: false,
|
|
1487
|
+
combo: "backspace",
|
|
1488
|
+
label: "Delete Cell",
|
|
1489
|
+
onKeyDown: handleDeleteCell
|
|
1490
|
+
}
|
|
1491
|
+
]
|
|
1492
|
+
: []),
|
|
1493
|
+
{
|
|
1494
|
+
global: false,
|
|
1495
|
+
combo: "down+shift",
|
|
1496
|
+
label: "Move Down a Row",
|
|
1497
|
+
onKeyDown: handleRowMove("down", true)
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
global: false,
|
|
1501
|
+
combo: "mod + c",
|
|
1502
|
+
label: "Copy rows",
|
|
1503
|
+
onKeyDown: handleCopyHotkey
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
global: false,
|
|
1507
|
+
combo: "mod + a",
|
|
1508
|
+
label: "Select rows",
|
|
1509
|
+
onKeyDown: handleSelectAllRows
|
|
1510
|
+
}
|
|
1511
|
+
],
|
|
1512
|
+
[
|
|
1513
|
+
handleCopyHotkey,
|
|
1514
|
+
handleCut,
|
|
1515
|
+
handleDeleteCell,
|
|
1516
|
+
handleEnterStartCellEdit,
|
|
1517
|
+
handleRedo,
|
|
1518
|
+
handleRowMove,
|
|
1519
|
+
handleSelectAllRows,
|
|
1520
|
+
handleUndo,
|
|
1521
|
+
isCellEditable
|
|
1522
|
+
]
|
|
1523
|
+
);
|
|
1524
|
+
|
|
1525
|
+
const { handleKeyDown, handleKeyUp } = useHotkeys(hotKeys);
|
|
1052
1526
|
const [columns, setColumns] = useState([]);
|
|
1053
1527
|
const [fullscreen, setFullscreen] = useState(false);
|
|
1054
1528
|
const [selectingAll, setSelectingAll] = useState(false);
|
|
@@ -1352,6 +1826,42 @@ const DataTable = ({
|
|
|
1352
1826
|
}
|
|
1353
1827
|
}, [entities, selectedIds, selectAllByDefault, setSelectedIds]);
|
|
1354
1828
|
|
|
1829
|
+
const TheadComponent = useCallback(
|
|
1830
|
+
({ className, style, children }) => {
|
|
1831
|
+
const moveColumn = ({ oldIndex, newIndex }) => {
|
|
1832
|
+
let oldStateColumnIndex, newStateColumnIndex;
|
|
1833
|
+
columns.forEach((column, i) => {
|
|
1834
|
+
if (oldIndex === column.columnIndex) oldStateColumnIndex = i;
|
|
1835
|
+
if (newIndex === column.columnIndex) newStateColumnIndex = i;
|
|
1836
|
+
});
|
|
1837
|
+
// because it is all handled in state we need
|
|
1838
|
+
// to perform the move and update the columnIndices
|
|
1839
|
+
// because they are used for the sortable columns
|
|
1840
|
+
const newColumns = arrayMove(
|
|
1841
|
+
columns,
|
|
1842
|
+
oldStateColumnIndex,
|
|
1843
|
+
newStateColumnIndex
|
|
1844
|
+
).map((column, i) => {
|
|
1845
|
+
return {
|
|
1846
|
+
...column,
|
|
1847
|
+
columnIndex: i
|
|
1848
|
+
};
|
|
1849
|
+
});
|
|
1850
|
+
setColumns(newColumns);
|
|
1851
|
+
};
|
|
1852
|
+
return (
|
|
1853
|
+
<SortableColumns
|
|
1854
|
+
className={className}
|
|
1855
|
+
style={style}
|
|
1856
|
+
moveColumn={moveColumnPersist || moveColumn}
|
|
1857
|
+
>
|
|
1858
|
+
{children}
|
|
1859
|
+
</SortableColumns>
|
|
1860
|
+
);
|
|
1861
|
+
},
|
|
1862
|
+
[columns, moveColumnPersist]
|
|
1863
|
+
);
|
|
1864
|
+
|
|
1355
1865
|
const addEntitiesToSelection = entities => {
|
|
1356
1866
|
const idMap = reduxFormSelectedEntityIdMap || {};
|
|
1357
1867
|
const newIdMap = cloneDeep(idMap) || {};
|
|
@@ -1588,6 +2098,426 @@ const DataTable = ({
|
|
|
1588
2098
|
]
|
|
1589
2099
|
);
|
|
1590
2100
|
|
|
2101
|
+
const showContextMenu = useCallback(
|
|
2102
|
+
(e, { idMap, selectedCells } = {}) => {
|
|
2103
|
+
let selectedRecords;
|
|
2104
|
+
if (isCellEditable) {
|
|
2105
|
+
const rowIds = {};
|
|
2106
|
+
Object.keys(selectedCells).forEach(cellKey => {
|
|
2107
|
+
const [rowId] = cellKey.split(":");
|
|
2108
|
+
rowIds[rowId] = true;
|
|
2109
|
+
});
|
|
2110
|
+
selectedRecords = entities.filter(
|
|
2111
|
+
ent => rowIds[getIdOrCodeOrIndex(ent)]
|
|
2112
|
+
);
|
|
2113
|
+
} else {
|
|
2114
|
+
selectedRecords = getRecordsFromIdMap(idMap);
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
const itemsToRender = contextMenu({
|
|
2118
|
+
selectedRecords,
|
|
2119
|
+
history
|
|
2120
|
+
});
|
|
2121
|
+
if (!itemsToRender && !isCopyable) return null;
|
|
2122
|
+
const copyMenuItems = [];
|
|
2123
|
+
|
|
2124
|
+
e.persist();
|
|
2125
|
+
if (isCopyable) {
|
|
2126
|
+
//compute the cellWrapper here so we don't lose access to it
|
|
2127
|
+
const cellWrapper =
|
|
2128
|
+
e.target.querySelector(".tg-cell-wrapper") ||
|
|
2129
|
+
e.target.closest(".tg-cell-wrapper");
|
|
2130
|
+
if (cellWrapper) {
|
|
2131
|
+
copyMenuItems.push(
|
|
2132
|
+
<MenuItem
|
|
2133
|
+
key="copyCell"
|
|
2134
|
+
onClick={() => {
|
|
2135
|
+
//TODOCOPY: we need to make sure that the cell copy is being used by the row copy.. right now we have 2 different things going on
|
|
2136
|
+
//do we need to be able to copy hidden cells? It seems like it should just copy what's on the page..?
|
|
2137
|
+
const specificColumn = cellWrapper.getAttribute("data-test");
|
|
2138
|
+
handleCopyRows([cellWrapper.closest(".rt-tr")], {
|
|
2139
|
+
specificColumn,
|
|
2140
|
+
onFinishMsg: "Cell copied"
|
|
2141
|
+
});
|
|
2142
|
+
const [text, jsonText] = getCellCopyText(cellWrapper);
|
|
2143
|
+
handleCopyHelper(text, jsonText);
|
|
2144
|
+
}}
|
|
2145
|
+
text="Cell"
|
|
2146
|
+
/>
|
|
2147
|
+
);
|
|
2148
|
+
|
|
2149
|
+
copyMenuItems.push(
|
|
2150
|
+
<MenuItem
|
|
2151
|
+
key="copyColumn"
|
|
2152
|
+
onClick={() => {
|
|
2153
|
+
handleCopyColumn(tableRef, cellWrapper);
|
|
2154
|
+
}}
|
|
2155
|
+
text="Column"
|
|
2156
|
+
/>
|
|
2157
|
+
);
|
|
2158
|
+
if (selectedRecords.length > 1) {
|
|
2159
|
+
copyMenuItems.push(
|
|
2160
|
+
<MenuItem
|
|
2161
|
+
key="copyColumnSelected"
|
|
2162
|
+
onClick={() => {
|
|
2163
|
+
handleCopyColumn(tableRef, cellWrapper, selectedRecords);
|
|
2164
|
+
}}
|
|
2165
|
+
text="Column (Selected)"
|
|
2166
|
+
/>
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
if (selectedRecords.length === 0 || selectedRecords.length === 1) {
|
|
2171
|
+
//compute the row here so we don't lose access to it
|
|
2172
|
+
const cell =
|
|
2173
|
+
e.target.querySelector(".tg-cell-wrapper") ||
|
|
2174
|
+
e.target.closest(".tg-cell-wrapper") ||
|
|
2175
|
+
e.target.closest(".rt-td");
|
|
2176
|
+
const row = cell.closest(".rt-tr");
|
|
2177
|
+
copyMenuItems.push(
|
|
2178
|
+
<MenuItem
|
|
2179
|
+
key="copySelectedRows"
|
|
2180
|
+
onClick={() => {
|
|
2181
|
+
handleCopyRows([row]);
|
|
2182
|
+
// loop through each cell in the row
|
|
2183
|
+
}}
|
|
2184
|
+
text="Row"
|
|
2185
|
+
/>
|
|
2186
|
+
);
|
|
2187
|
+
} else if (selectedRecords.length > 1) {
|
|
2188
|
+
copyMenuItems.push(
|
|
2189
|
+
<MenuItem
|
|
2190
|
+
key="copySelectedRows"
|
|
2191
|
+
onClick={() => {
|
|
2192
|
+
handleCopySelectedRows(selectedRecords, e);
|
|
2193
|
+
// loop through each cell in the row
|
|
2194
|
+
}}
|
|
2195
|
+
text="Rows"
|
|
2196
|
+
/>
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
copyMenuItems.push(
|
|
2200
|
+
<MenuItem
|
|
2201
|
+
key="copyFullTableRows"
|
|
2202
|
+
onClick={() => {
|
|
2203
|
+
handleCopyTable(tableRef);
|
|
2204
|
+
// loop through each cell in the row
|
|
2205
|
+
}}
|
|
2206
|
+
text="Table"
|
|
2207
|
+
/>
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
const selectedRowIds = Object.keys(selectedCells).map(cellId => {
|
|
2211
|
+
const [rowId] = cellId.split(":");
|
|
2212
|
+
return rowId;
|
|
2213
|
+
});
|
|
2214
|
+
|
|
2215
|
+
const menu = (
|
|
2216
|
+
<Menu>
|
|
2217
|
+
{itemsToRender}
|
|
2218
|
+
{copyMenuItems.length && (
|
|
2219
|
+
<MenuItem icon="clipboard" key="copyOpts" text="Copy">
|
|
2220
|
+
{copyMenuItems}
|
|
2221
|
+
</MenuItem>
|
|
2222
|
+
)}
|
|
2223
|
+
{isCellEditable && (
|
|
2224
|
+
<>
|
|
2225
|
+
<MenuItem
|
|
2226
|
+
icon="add-row-top"
|
|
2227
|
+
text="Add Row Above"
|
|
2228
|
+
key="addRowAbove"
|
|
2229
|
+
onClick={() => {
|
|
2230
|
+
insertRows({ above: true });
|
|
2231
|
+
}}
|
|
2232
|
+
/>
|
|
2233
|
+
<MenuItem
|
|
2234
|
+
icon="add-row-top"
|
|
2235
|
+
text="Add Row Below"
|
|
2236
|
+
key="addRowBelow"
|
|
2237
|
+
onClick={() => {
|
|
2238
|
+
insertRows({});
|
|
2239
|
+
}}
|
|
2240
|
+
/>
|
|
2241
|
+
<MenuItem
|
|
2242
|
+
icon="remove"
|
|
2243
|
+
text={`Remove Row${selectedRowIds.length > 1 ? "s" : ""}`}
|
|
2244
|
+
key="removeRow"
|
|
2245
|
+
onClick={() => {
|
|
2246
|
+
const selectedRowIds = Object.keys(selectedCells).map(
|
|
2247
|
+
cellId => {
|
|
2248
|
+
const [rowId] = cellId.split(":");
|
|
2249
|
+
return rowId;
|
|
2250
|
+
}
|
|
2251
|
+
);
|
|
2252
|
+
updateEntitiesHelper(entities, entities => {
|
|
2253
|
+
const ents = entities.filter(
|
|
2254
|
+
(e, i) =>
|
|
2255
|
+
!selectedRowIds.includes(getIdOrCodeOrIndex(e, i))
|
|
2256
|
+
);
|
|
2257
|
+
updateValidation(
|
|
2258
|
+
ents,
|
|
2259
|
+
omitBy(reduxFormCellValidation, (v, cellId) =>
|
|
2260
|
+
selectedRowIds.includes(cellId.split(":")[0])
|
|
2261
|
+
)
|
|
2262
|
+
);
|
|
2263
|
+
return ents;
|
|
2264
|
+
});
|
|
2265
|
+
refocusTable();
|
|
2266
|
+
}}
|
|
2267
|
+
/>
|
|
2268
|
+
</>
|
|
2269
|
+
)}
|
|
2270
|
+
</Menu>
|
|
2271
|
+
);
|
|
2272
|
+
ContextMenu.show(menu, { left: e.clientX, top: e.clientY });
|
|
2273
|
+
},
|
|
2274
|
+
[
|
|
2275
|
+
contextMenu,
|
|
2276
|
+
entities,
|
|
2277
|
+
handleCopySelectedRows,
|
|
2278
|
+
history,
|
|
2279
|
+
insertRows,
|
|
2280
|
+
isCellEditable,
|
|
2281
|
+
isCopyable,
|
|
2282
|
+
reduxFormCellValidation,
|
|
2283
|
+
refocusTable,
|
|
2284
|
+
updateEntitiesHelper,
|
|
2285
|
+
updateValidation
|
|
2286
|
+
]
|
|
2287
|
+
);
|
|
2288
|
+
|
|
2289
|
+
const getTableRowProps = useCallback(
|
|
2290
|
+
(state, rowInfo) => {
|
|
2291
|
+
if (!rowInfo) {
|
|
2292
|
+
return {
|
|
2293
|
+
className: "no-row-data"
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
const entity = rowInfo.original;
|
|
2297
|
+
const rowId = getIdOrCodeOrIndex(entity, rowInfo.index);
|
|
2298
|
+
const rowSelected = reduxFormSelectedEntityIdMap[rowId];
|
|
2299
|
+
const isExpanded = expandedEntityIdMap[rowId];
|
|
2300
|
+
const rowDisabled = isEntityDisabled(entity);
|
|
2301
|
+
const dataId = entity.id || entity.code;
|
|
2302
|
+
return {
|
|
2303
|
+
onClick: e => {
|
|
2304
|
+
if (isCellEditable) return;
|
|
2305
|
+
// if checkboxes are activated or row expander is clicked don't select row
|
|
2306
|
+
if (e.target.matches(".tg-expander, .tg-expander *")) {
|
|
2307
|
+
setExpandedEntityIdMap(prev => ({ ...prev, [rowId]: !isExpanded }));
|
|
2308
|
+
return;
|
|
2309
|
+
} else if (
|
|
2310
|
+
e.target.closest(".tg-react-table-checkbox-cell-container")
|
|
2311
|
+
) {
|
|
2312
|
+
return;
|
|
2313
|
+
} else if (mustClickCheckboxToSelect) {
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
if (e.detail > 1) {
|
|
2317
|
+
return; //cancel multiple quick clicks
|
|
2318
|
+
}
|
|
2319
|
+
rowClick(e, rowInfo, entities, {
|
|
2320
|
+
reduxFormSelectedEntityIdMap,
|
|
2321
|
+
isSingleSelect,
|
|
2322
|
+
noSelect,
|
|
2323
|
+
onRowClick,
|
|
2324
|
+
isEntityDisabled,
|
|
2325
|
+
withCheckboxes,
|
|
2326
|
+
onDeselect,
|
|
2327
|
+
onSingleRowSelect,
|
|
2328
|
+
onMultiRowSelect,
|
|
2329
|
+
noDeselectAll,
|
|
2330
|
+
onRowSelect,
|
|
2331
|
+
change
|
|
2332
|
+
});
|
|
2333
|
+
},
|
|
2334
|
+
//row right click
|
|
2335
|
+
onContextMenu: e => {
|
|
2336
|
+
e.preventDefault();
|
|
2337
|
+
if (rowId === undefined || rowDisabled || isCellEditable) return;
|
|
2338
|
+
const oldIdMap = cloneDeep(reduxFormSelectedEntityIdMap) || {};
|
|
2339
|
+
let newIdMap;
|
|
2340
|
+
if (withCheckboxes) {
|
|
2341
|
+
newIdMap = oldIdMap;
|
|
2342
|
+
} else {
|
|
2343
|
+
// if we are not using checkboxes we need to make sure
|
|
2344
|
+
// that the id of the record gets added to the id map
|
|
2345
|
+
newIdMap = oldIdMap[rowId] ? oldIdMap : { [rowId]: { entity } };
|
|
2346
|
+
|
|
2347
|
+
// tgreen: this will refresh the selection with fresh data. The entities in redux might not be up to date
|
|
2348
|
+
const keyedEntities = keyBy(entities, getIdOrCodeOrIndex);
|
|
2349
|
+
forEach(newIdMap, (val, key) => {
|
|
2350
|
+
const freshEntity = keyedEntities[key];
|
|
2351
|
+
if (freshEntity) {
|
|
2352
|
+
newIdMap[key] = { ...newIdMap[key], entity: freshEntity };
|
|
2353
|
+
}
|
|
2354
|
+
});
|
|
2355
|
+
finalizeSelection({
|
|
2356
|
+
idMap: newIdMap,
|
|
2357
|
+
entities,
|
|
2358
|
+
props: {
|
|
2359
|
+
onDeselect,
|
|
2360
|
+
onSingleRowSelect,
|
|
2361
|
+
onMultiRowSelect,
|
|
2362
|
+
noDeselectAll,
|
|
2363
|
+
onRowSelect,
|
|
2364
|
+
noSelect,
|
|
2365
|
+
change
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
showContextMenu(e, { idMap: newIdMap, selectedCells });
|
|
2370
|
+
},
|
|
2371
|
+
className: classNames(
|
|
2372
|
+
"with-row-data",
|
|
2373
|
+
getRowClassName && getRowClassName(rowInfo, state),
|
|
2374
|
+
{
|
|
2375
|
+
disabled: rowDisabled,
|
|
2376
|
+
selected: rowSelected && !withCheckboxes,
|
|
2377
|
+
"rt-tr-last-row": rowInfo.index === entities.length - 1
|
|
2378
|
+
}
|
|
2379
|
+
),
|
|
2380
|
+
"data-test-id": dataId === undefined ? rowInfo.index : dataId,
|
|
2381
|
+
"data-index": rowInfo.index,
|
|
2382
|
+
"data-tip": typeof rowDisabled === "string" ? rowDisabled : undefined,
|
|
2383
|
+
onDoubleClick: e => {
|
|
2384
|
+
if (rowDisabled) return;
|
|
2385
|
+
onDoubleClick &&
|
|
2386
|
+
onDoubleClick(rowInfo.original, rowInfo.index, history, e);
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
},
|
|
2390
|
+
[
|
|
2391
|
+
change,
|
|
2392
|
+
entities,
|
|
2393
|
+
expandedEntityIdMap,
|
|
2394
|
+
getRowClassName,
|
|
2395
|
+
history,
|
|
2396
|
+
isCellEditable,
|
|
2397
|
+
isEntityDisabled,
|
|
2398
|
+
isSingleSelect,
|
|
2399
|
+
mustClickCheckboxToSelect,
|
|
2400
|
+
noDeselectAll,
|
|
2401
|
+
noSelect,
|
|
2402
|
+
onDeselect,
|
|
2403
|
+
onDoubleClick,
|
|
2404
|
+
onMultiRowSelect,
|
|
2405
|
+
onRowClick,
|
|
2406
|
+
onRowSelect,
|
|
2407
|
+
onSingleRowSelect,
|
|
2408
|
+
reduxFormSelectedEntityIdMap,
|
|
2409
|
+
selectedCells,
|
|
2410
|
+
showContextMenu,
|
|
2411
|
+
withCheckboxes
|
|
2412
|
+
]
|
|
2413
|
+
);
|
|
2414
|
+
|
|
2415
|
+
const getTableCellProps = useCallback(
|
|
2416
|
+
(state, rowInfo, column) => {
|
|
2417
|
+
if (!isCellEditable) return {}; //only allow cell selection to do stuff here
|
|
2418
|
+
if (!rowInfo) return {};
|
|
2419
|
+
if (!reduxFormCellValidation) return {};
|
|
2420
|
+
const entity = rowInfo.original;
|
|
2421
|
+
const rowIndex = rowInfo.index;
|
|
2422
|
+
const rowId = getIdOrCodeOrIndex(entity, rowIndex);
|
|
2423
|
+
const {
|
|
2424
|
+
cellId,
|
|
2425
|
+
cellIdAbove,
|
|
2426
|
+
cellIdToRight,
|
|
2427
|
+
cellIdBelow,
|
|
2428
|
+
cellIdToLeft,
|
|
2429
|
+
rowDisabled,
|
|
2430
|
+
columnIndex
|
|
2431
|
+
} = getCellInfo({
|
|
2432
|
+
columnIndex: column.index,
|
|
2433
|
+
columnPath: column.path,
|
|
2434
|
+
rowId,
|
|
2435
|
+
schema,
|
|
2436
|
+
entities,
|
|
2437
|
+
rowIndex,
|
|
2438
|
+
isEntityDisabled,
|
|
2439
|
+
entity
|
|
2440
|
+
});
|
|
2441
|
+
|
|
2442
|
+
const _isClean =
|
|
2443
|
+
(entity._isClean && doNotValidateUntouchedRows) ||
|
|
2444
|
+
isEntityClean(entity);
|
|
2445
|
+
|
|
2446
|
+
const err = !_isClean && reduxFormCellValidation[cellId];
|
|
2447
|
+
let selectedTopBorder,
|
|
2448
|
+
selectedRightBorder,
|
|
2449
|
+
selectedBottomBorder,
|
|
2450
|
+
selectedLeftBorder;
|
|
2451
|
+
if (selectedCells[cellId]) {
|
|
2452
|
+
selectedTopBorder = !selectedCells[cellIdAbove];
|
|
2453
|
+
selectedRightBorder = !selectedCells[cellIdToRight];
|
|
2454
|
+
selectedBottomBorder = !selectedCells[cellIdBelow];
|
|
2455
|
+
selectedLeftBorder = !selectedCells[cellIdToLeft];
|
|
2456
|
+
}
|
|
2457
|
+
const isPrimarySelected = selectedCells[cellId] === PRIMARY_SELECTED_VAL;
|
|
2458
|
+
const className = classNames({
|
|
2459
|
+
isSelectedCell: selectedCells[cellId],
|
|
2460
|
+
isPrimarySelected,
|
|
2461
|
+
isSecondarySelected: selectedCells[cellId] === true,
|
|
2462
|
+
noSelectedTopBorder: !selectedTopBorder,
|
|
2463
|
+
isCleanRow: _isClean,
|
|
2464
|
+
noSelectedRightBorder: !selectedRightBorder,
|
|
2465
|
+
noSelectedBottomBorder: !selectedBottomBorder,
|
|
2466
|
+
noSelectedLeftBorder: !selectedLeftBorder,
|
|
2467
|
+
isDropdownCell: column.type === "dropdown",
|
|
2468
|
+
isEditingCell: reduxFormEditingCell === cellId,
|
|
2469
|
+
hasCellError: !!err,
|
|
2470
|
+
"no-data-tip": selectedCells[cellId]
|
|
2471
|
+
});
|
|
2472
|
+
return {
|
|
2473
|
+
onDoubleClick: () => {
|
|
2474
|
+
// cell double click
|
|
2475
|
+
if (rowDisabled) return;
|
|
2476
|
+
startCellEdit(cellId);
|
|
2477
|
+
},
|
|
2478
|
+
...(err && {
|
|
2479
|
+
"data-tip": err?.message || err,
|
|
2480
|
+
"data-no-child-data-tip": true
|
|
2481
|
+
}),
|
|
2482
|
+
onContextMenu: e => {
|
|
2483
|
+
const newSelectedCells = { ...selectedCells };
|
|
2484
|
+
if (!isPrimarySelected) {
|
|
2485
|
+
if (primarySelectedCellId) {
|
|
2486
|
+
newSelectedCells[primarySelectedCellId] = true;
|
|
2487
|
+
}
|
|
2488
|
+
newSelectedCells[cellId] = PRIMARY_SELECTED_VAL;
|
|
2489
|
+
setSelectedCells(newSelectedCells);
|
|
2490
|
+
}
|
|
2491
|
+
showContextMenu(e, { selectedCells: newSelectedCells });
|
|
2492
|
+
},
|
|
2493
|
+
onClick: event => {
|
|
2494
|
+
handleCellClick({
|
|
2495
|
+
event,
|
|
2496
|
+
cellId,
|
|
2497
|
+
rowDisabled,
|
|
2498
|
+
rowIndex,
|
|
2499
|
+
columnIndex
|
|
2500
|
+
});
|
|
2501
|
+
},
|
|
2502
|
+
className
|
|
2503
|
+
};
|
|
2504
|
+
},
|
|
2505
|
+
[
|
|
2506
|
+
doNotValidateUntouchedRows,
|
|
2507
|
+
entities,
|
|
2508
|
+
handleCellClick,
|
|
2509
|
+
isCellEditable,
|
|
2510
|
+
isEntityDisabled,
|
|
2511
|
+
primarySelectedCellId,
|
|
2512
|
+
reduxFormCellValidation,
|
|
2513
|
+
reduxFormEditingCell,
|
|
2514
|
+
schema,
|
|
2515
|
+
selectedCells,
|
|
2516
|
+
showContextMenu,
|
|
2517
|
+
startCellEdit
|
|
2518
|
+
]
|
|
2519
|
+
);
|
|
2520
|
+
|
|
1591
2521
|
if (withSelectAll && !safeQuery) {
|
|
1592
2522
|
throw new Error("safeQuery is needed for selecting all table records");
|
|
1593
2523
|
}
|
|
@@ -1603,7 +2533,7 @@ const DataTable = ({
|
|
|
1603
2533
|
: "";
|
|
1604
2534
|
|
|
1605
2535
|
const hasFilters =
|
|
1606
|
-
filters
|
|
2536
|
+
filters.length ||
|
|
1607
2537
|
searchTerm ||
|
|
1608
2538
|
schema.fields.some(
|
|
1609
2539
|
field => field.filterIsActive && field.filterIsActive(currentParams)
|
|
@@ -1616,7 +2546,7 @@ const DataTable = ({
|
|
|
1616
2546
|
|
|
1617
2547
|
const filtersOnNonDisplayedFields = useMemo(() => {
|
|
1618
2548
|
const _filtersOnNonDisplayedFields = [];
|
|
1619
|
-
if (filters
|
|
2549
|
+
if (filters && filters.length) {
|
|
1620
2550
|
schema.fields.forEach(field => {
|
|
1621
2551
|
const ccDisplayName = getCCDisplayName(field);
|
|
1622
2552
|
if (field.isHidden) {
|
|
@@ -1641,6 +2571,18 @@ const DataTable = ({
|
|
|
1641
2571
|
);
|
|
1642
2572
|
const selectedRowCount = Object.keys(idMap).filter(key => idMap[key]).length;
|
|
1643
2573
|
|
|
2574
|
+
let rowsToShow = doNotShowEmptyRows
|
|
2575
|
+
? Math.min(numRows, entities.length)
|
|
2576
|
+
: numRows;
|
|
2577
|
+
// if there are no entities then provide enough space to show
|
|
2578
|
+
// no rows found message
|
|
2579
|
+
if (entities.length === 0 && rowsToShow < 3) rowsToShow = 3;
|
|
2580
|
+
const expandedRows = entities.reduce((acc, row, index) => {
|
|
2581
|
+
const rowId = getIdOrCodeOrIndex(row, index);
|
|
2582
|
+
acc[index] = expandedEntityIdMap[rowId];
|
|
2583
|
+
return acc;
|
|
2584
|
+
}, {});
|
|
2585
|
+
|
|
1644
2586
|
const showHeader = (withTitle || withSearch || children) && !noHeader;
|
|
1645
2587
|
const toggleFullscreenButton = (
|
|
1646
2588
|
<Button
|
|
@@ -1714,19 +2656,44 @@ const DataTable = ({
|
|
|
1714
2656
|
_selectedAndTotalMessage += `/ `;
|
|
1715
2657
|
}
|
|
1716
2658
|
if (showCount) {
|
|
1717
|
-
|
|
2659
|
+
if (isEntityCountLoading && entityCount < 1) {
|
|
2660
|
+
_selectedAndTotalMessage += `Loading...`;
|
|
2661
|
+
} else {
|
|
2662
|
+
_selectedAndTotalMessage += `${entityCount || 0} Total`;
|
|
2663
|
+
}
|
|
1718
2664
|
}
|
|
1719
2665
|
if (_selectedAndTotalMessage) {
|
|
1720
2666
|
_selectedAndTotalMessage = <div>{_selectedAndTotalMessage}</div>;
|
|
1721
2667
|
}
|
|
1722
2668
|
return _selectedAndTotalMessage;
|
|
1723
|
-
}, [
|
|
2669
|
+
}, [
|
|
2670
|
+
entityCount,
|
|
2671
|
+
selectedRowCount,
|
|
2672
|
+
showCount,
|
|
2673
|
+
showNumSelected,
|
|
2674
|
+
isEntityCountLoading
|
|
2675
|
+
]);
|
|
1724
2676
|
|
|
1725
2677
|
const shouldShowPaging =
|
|
1726
2678
|
!isInfinite &&
|
|
1727
2679
|
withPaging &&
|
|
1728
2680
|
(hidePageSizeWhenPossible ? entityCount > pageSize : true);
|
|
1729
2681
|
|
|
2682
|
+
const SubComponentToUse = useMemo(() => {
|
|
2683
|
+
if (SubComponent) {
|
|
2684
|
+
return row => {
|
|
2685
|
+
let shouldShow = true;
|
|
2686
|
+
if (shouldShowSubComponent) {
|
|
2687
|
+
shouldShow = shouldShowSubComponent(row.original);
|
|
2688
|
+
}
|
|
2689
|
+
if (shouldShow) {
|
|
2690
|
+
return SubComponent(row);
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
return;
|
|
2695
|
+
}, [SubComponent, shouldShowSubComponent]);
|
|
2696
|
+
|
|
1730
2697
|
const nonDisplayedFilterComp = useMemo(() => {
|
|
1731
2698
|
if (filtersOnNonDisplayedFields.length) {
|
|
1732
2699
|
const content = filtersOnNonDisplayedFields.map(
|
|
@@ -1771,12 +2738,179 @@ const DataTable = ({
|
|
|
1771
2738
|
return null;
|
|
1772
2739
|
}, [filtersOnNonDisplayedFields]);
|
|
1773
2740
|
|
|
2741
|
+
const filteredEnts = useMemo(() => {
|
|
2742
|
+
if (onlyShowRowsWErrors) {
|
|
2743
|
+
const rowToErrorMap = {};
|
|
2744
|
+
forEach(reduxFormCellValidation, (err, cellId) => {
|
|
2745
|
+
if (err) {
|
|
2746
|
+
const [rowId] = cellId.split(":");
|
|
2747
|
+
rowToErrorMap[rowId] = true;
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
return entities.filter(e => {
|
|
2751
|
+
return rowToErrorMap[e.id];
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
return entities;
|
|
2755
|
+
}, [entities, onlyShowRowsWErrors, reduxFormCellValidation]);
|
|
2756
|
+
|
|
2757
|
+
const renderColumns = useColumns({
|
|
2758
|
+
addFilters,
|
|
2759
|
+
cellRenderer,
|
|
2760
|
+
columns,
|
|
2761
|
+
currentParams,
|
|
2762
|
+
compact,
|
|
2763
|
+
withDisplayOptions,
|
|
2764
|
+
resetDefaultVisibility,
|
|
2765
|
+
editingCellSelectAll,
|
|
2766
|
+
entities,
|
|
2767
|
+
expandedEntityIdMap,
|
|
2768
|
+
extraCompact,
|
|
2769
|
+
filters,
|
|
2770
|
+
formName,
|
|
2771
|
+
getCellHoverText,
|
|
2772
|
+
isCellEditable,
|
|
2773
|
+
isEntityDisabled,
|
|
2774
|
+
isLocalCall,
|
|
2775
|
+
isSimple,
|
|
2776
|
+
isSingleSelect,
|
|
2777
|
+
isSelectionARectangle,
|
|
2778
|
+
noDeselectAll,
|
|
2779
|
+
noSelect,
|
|
2780
|
+
noUserSelect,
|
|
2781
|
+
onDeselect,
|
|
2782
|
+
onMultiRowSelect,
|
|
2783
|
+
onRowClick,
|
|
2784
|
+
onRowSelect,
|
|
2785
|
+
onSingleRowSelect,
|
|
2786
|
+
order,
|
|
2787
|
+
primarySelectedCellId,
|
|
2788
|
+
reduxFormCellValidation,
|
|
2789
|
+
reduxFormSelectedEntityIdMap,
|
|
2790
|
+
refocusTable,
|
|
2791
|
+
removeSingleFilter,
|
|
2792
|
+
schema,
|
|
2793
|
+
selectedCells,
|
|
2794
|
+
setExpandedEntityIdMap,
|
|
2795
|
+
setNewParams,
|
|
2796
|
+
setOrder,
|
|
2797
|
+
setSelectedCells,
|
|
2798
|
+
shouldShowSubComponent,
|
|
2799
|
+
startCellEdit,
|
|
2800
|
+
SubComponent,
|
|
2801
|
+
tableRef,
|
|
2802
|
+
updateColumnVisibility,
|
|
2803
|
+
updateEntitiesHelper,
|
|
2804
|
+
updateValidation,
|
|
2805
|
+
withCheckboxes,
|
|
2806
|
+
withExpandAndCollapseAllButton,
|
|
2807
|
+
withFilter,
|
|
2808
|
+
withSort,
|
|
2809
|
+
recordIdToIsVisibleMap,
|
|
2810
|
+
setRecordIdToIsVisibleMap
|
|
2811
|
+
});
|
|
2812
|
+
|
|
1774
2813
|
const scrollToTop = useCallback(
|
|
1775
2814
|
() =>
|
|
1776
2815
|
tableRef.current?.tableRef?.children?.[0]?.children?.[0]?.scrollIntoView(),
|
|
1777
2816
|
[]
|
|
1778
2817
|
);
|
|
1779
2818
|
|
|
2819
|
+
const reactTable = useMemo(
|
|
2820
|
+
() => (
|
|
2821
|
+
<ReactTable
|
|
2822
|
+
data={filteredEnts}
|
|
2823
|
+
ref={tableRef}
|
|
2824
|
+
noVirtual={noVirtual}
|
|
2825
|
+
className={classNames({
|
|
2826
|
+
isCellEditable,
|
|
2827
|
+
"tg-table-loading": isLoading,
|
|
2828
|
+
"tg-table-disabled": disabled
|
|
2829
|
+
})}
|
|
2830
|
+
itemSizeEstimator={
|
|
2831
|
+
extraCompact
|
|
2832
|
+
? itemSizeEstimators.compact
|
|
2833
|
+
: compact
|
|
2834
|
+
? itemSizeEstimators.normal
|
|
2835
|
+
: itemSizeEstimators.comfortable
|
|
2836
|
+
}
|
|
2837
|
+
TfootComponent={() => {
|
|
2838
|
+
return <button>hasdfasdf</button>;
|
|
2839
|
+
}}
|
|
2840
|
+
// We should try to not give all the props to the render column
|
|
2841
|
+
columns={renderColumns}
|
|
2842
|
+
pageSize={rowsToShow}
|
|
2843
|
+
expanded={expandedRows}
|
|
2844
|
+
showPagination={false}
|
|
2845
|
+
sortable={false}
|
|
2846
|
+
// loading={isLoading || disabled}
|
|
2847
|
+
loading={disabled}
|
|
2848
|
+
defaultResized={resized}
|
|
2849
|
+
onResizedChange={(newResized = []) => {
|
|
2850
|
+
const resizedToUse = newResized.map(column => {
|
|
2851
|
+
// have a min width of 50 so that columns don't disappear
|
|
2852
|
+
if (column.value < 50) {
|
|
2853
|
+
return {
|
|
2854
|
+
...column,
|
|
2855
|
+
value: 50
|
|
2856
|
+
};
|
|
2857
|
+
} else {
|
|
2858
|
+
return column;
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
resizePersist(resizedToUse);
|
|
2862
|
+
}}
|
|
2863
|
+
TheadComponent={TheadComponent}
|
|
2864
|
+
ThComponent={ThComponent}
|
|
2865
|
+
getTrGroupProps={getTableRowProps}
|
|
2866
|
+
getTdProps={getTableCellProps}
|
|
2867
|
+
NoDataComponent={({ children }) =>
|
|
2868
|
+
isLoading ? (
|
|
2869
|
+
<div className="rt-noData">Loading...</div>
|
|
2870
|
+
) : (
|
|
2871
|
+
<div className="rt-noData">{noRowsFoundMessage || children}</div>
|
|
2872
|
+
)
|
|
2873
|
+
}
|
|
2874
|
+
LoadingComponent={({ loadingText, loading }) => (
|
|
2875
|
+
<DisabledLoadingComponent
|
|
2876
|
+
loading={loading}
|
|
2877
|
+
loadingText={loadingText}
|
|
2878
|
+
disabled={disabled}
|
|
2879
|
+
/>
|
|
2880
|
+
)}
|
|
2881
|
+
style={{
|
|
2882
|
+
maxHeight,
|
|
2883
|
+
minHeight: 150,
|
|
2884
|
+
...style
|
|
2885
|
+
}}
|
|
2886
|
+
SubComponent={SubComponentToUse}
|
|
2887
|
+
{...ReactTableProps}
|
|
2888
|
+
/>
|
|
2889
|
+
),
|
|
2890
|
+
[
|
|
2891
|
+
ReactTableProps,
|
|
2892
|
+
SubComponentToUse,
|
|
2893
|
+
TheadComponent,
|
|
2894
|
+
compact,
|
|
2895
|
+
disabled,
|
|
2896
|
+
expandedRows,
|
|
2897
|
+
extraCompact,
|
|
2898
|
+
filteredEnts,
|
|
2899
|
+
getTableCellProps,
|
|
2900
|
+
getTableRowProps,
|
|
2901
|
+
isCellEditable,
|
|
2902
|
+
isLoading,
|
|
2903
|
+
maxHeight,
|
|
2904
|
+
noRowsFoundMessage,
|
|
2905
|
+
renderColumns,
|
|
2906
|
+
resizePersist,
|
|
2907
|
+
resized,
|
|
2908
|
+
rowsToShow,
|
|
2909
|
+
style,
|
|
2910
|
+
noVirtual
|
|
2911
|
+
]
|
|
2912
|
+
);
|
|
2913
|
+
|
|
1780
2914
|
return (
|
|
1781
2915
|
<div
|
|
1782
2916
|
tabIndex="1"
|
|
@@ -2072,84 +3206,7 @@ const DataTable = ({
|
|
|
2072
3206
|
/>
|
|
2073
3207
|
</div>
|
|
2074
3208
|
)}
|
|
2075
|
-
|
|
2076
|
-
addFilters={addFilters}
|
|
2077
|
-
cellRenderer={cellRenderer}
|
|
2078
|
-
change={change}
|
|
2079
|
-
columns={columns}
|
|
2080
|
-
compact={compact}
|
|
2081
|
-
contextMenu={contextMenu}
|
|
2082
|
-
currentParams={currentParams}
|
|
2083
|
-
disabled={disabled}
|
|
2084
|
-
doNotShowEmptyRows={doNotShowEmptyRows}
|
|
2085
|
-
doNotValidateUntouchedRows={doNotValidateUntouchedRows}
|
|
2086
|
-
editingCellSelectAll={editingCellSelectAll}
|
|
2087
|
-
entities={entities}
|
|
2088
|
-
expandedEntityIdMap={expandedEntityIdMap}
|
|
2089
|
-
extraCompact={extraCompact}
|
|
2090
|
-
filters={filters}
|
|
2091
|
-
formName={formName}
|
|
2092
|
-
getCellHoverText={getCellHoverText}
|
|
2093
|
-
getRowClassName={getRowClassName}
|
|
2094
|
-
handleCellClick={handleCellClick}
|
|
2095
|
-
handleCopySelectedRows={handleCopySelectedRows}
|
|
2096
|
-
history={history}
|
|
2097
|
-
insertRows={insertRows}
|
|
2098
|
-
isCellEditable={isCellEditable}
|
|
2099
|
-
isCopyable={isCopyable}
|
|
2100
|
-
isEntityDisabled={isEntityDisabled}
|
|
2101
|
-
isLoading={isLoading}
|
|
2102
|
-
isLocalCall={isLocalCall}
|
|
2103
|
-
isSelectionARectangle={isSelectionARectangle}
|
|
2104
|
-
isSimple={isSimple}
|
|
2105
|
-
isSingleSelect={isSingleSelect}
|
|
2106
|
-
maxHeight={maxHeight}
|
|
2107
|
-
moveColumnPersist={moveColumnPersist}
|
|
2108
|
-
mustClickCheckboxToSelect={mustClickCheckboxToSelect}
|
|
2109
|
-
noDeselectAll={noDeselectAll}
|
|
2110
|
-
noRowsFoundMessage={noRowsFoundMessage}
|
|
2111
|
-
noSelect={noSelect}
|
|
2112
|
-
noUserSelect={noUserSelect}
|
|
2113
|
-
noVirtual={noVirtual}
|
|
2114
|
-
numRows={numRows}
|
|
2115
|
-
onDeselect={onDeselect}
|
|
2116
|
-
onDoubleClick={onDoubleClick}
|
|
2117
|
-
onlyShowRowsWErrors={onlyShowRowsWErrors}
|
|
2118
|
-
onMultiRowSelect={onMultiRowSelect}
|
|
2119
|
-
onRowClick={onRowClick}
|
|
2120
|
-
onRowSelect={onRowSelect}
|
|
2121
|
-
onSingleRowSelect={onSingleRowSelect}
|
|
2122
|
-
order={order}
|
|
2123
|
-
primarySelectedCellId={primarySelectedCellId}
|
|
2124
|
-
ReactTableProps={ReactTableProps}
|
|
2125
|
-
recordIdToIsVisibleMap={recordIdToIsVisibleMap}
|
|
2126
|
-
reduxFormCellValidation={reduxFormCellValidation}
|
|
2127
|
-
reduxFormEditingCell={reduxFormEditingCell}
|
|
2128
|
-
reduxFormSelectedEntityIdMap={reduxFormSelectedEntityIdMap}
|
|
2129
|
-
refocusTable={refocusTable}
|
|
2130
|
-
removeSingleFilter={removeSingleFilter}
|
|
2131
|
-
resizePersist={resizePersist}
|
|
2132
|
-
schema={schema}
|
|
2133
|
-
selectedCells={selectedCells}
|
|
2134
|
-
setColumns={setColumns}
|
|
2135
|
-
setExpandedEntityIdMap={setExpandedEntityIdMap}
|
|
2136
|
-
setNewParams={setNewParams}
|
|
2137
|
-
setOrder={setOrder}
|
|
2138
|
-
setRecordIdToIsVisibleMap={setRecordIdToIsVisibleMap}
|
|
2139
|
-
setSelectedCells={setSelectedCells}
|
|
2140
|
-
shouldShowSubComponent={shouldShowSubComponent}
|
|
2141
|
-
startCellEdit={startCellEdit}
|
|
2142
|
-
style={style}
|
|
2143
|
-
SubComponent={SubComponent}
|
|
2144
|
-
tableConfig={tableConfig}
|
|
2145
|
-
tableRef={tableRef}
|
|
2146
|
-
updateEntitiesHelper={updateEntitiesHelper}
|
|
2147
|
-
updateValidation={updateValidation}
|
|
2148
|
-
withCheckboxes={withCheckboxes}
|
|
2149
|
-
withExpandAndCollapseAllButton={withExpandAndCollapseAllButton}
|
|
2150
|
-
withFilter={withFilter}
|
|
2151
|
-
withSort={withSort}
|
|
2152
|
-
/>
|
|
3209
|
+
{reactTable}
|
|
2153
3210
|
{isCellEditable && (
|
|
2154
3211
|
<div style={{ display: "flex" }}>
|
|
2155
3212
|
<div
|
|
@@ -2195,6 +3252,7 @@ const DataTable = ({
|
|
|
2195
3252
|
{!noFullscreenButton && toggleFullscreenButton}
|
|
2196
3253
|
{withDisplayOptions && (
|
|
2197
3254
|
<DisplayOptions
|
|
3255
|
+
doNotSearchHiddenColumns={doNotSearchHiddenColumns}
|
|
2198
3256
|
compact={compact}
|
|
2199
3257
|
extraCompact={extraCompact}
|
|
2200
3258
|
disabled={disabled}
|
|
@@ -2247,41 +3305,7 @@ const DataTable = ({
|
|
|
2247
3305
|
);
|
|
2248
3306
|
};
|
|
2249
3307
|
|
|
2250
|
-
const
|
|
2251
|
-
|
|
2252
|
-
const
|
|
2253
|
-
if (props.noRouter) {
|
|
2254
|
-
return <DataTable {...props} />;
|
|
2255
|
-
}
|
|
2256
|
-
return <WithRouterDatatable {...props} />;
|
|
2257
|
-
};
|
|
2258
|
-
|
|
2259
|
-
const WithReduxFormDataTable = reduxForm({})(RouterDTWrapper);
|
|
2260
|
-
|
|
2261
|
-
const ReduxFormDTWrapper = props => {
|
|
2262
|
-
if (props.noForm) {
|
|
2263
|
-
return <RouterDTWrapper {...props} form={props.formName} />;
|
|
2264
|
-
}
|
|
2265
|
-
return <WithReduxFormDataTable {...props} form={props.formName} />;
|
|
2266
|
-
};
|
|
2267
|
-
|
|
2268
|
-
const WithRouterPagingTool = withRouter(PagingTool);
|
|
2269
|
-
|
|
2270
|
-
const RouterPTWrapper = props => {
|
|
2271
|
-
if (props.noRouter) {
|
|
2272
|
-
return <DataTable {...props} />;
|
|
2273
|
-
}
|
|
2274
|
-
return <WithRouterPagingTool {...props} />;
|
|
2275
|
-
};
|
|
2276
|
-
|
|
2277
|
-
const WithReduxFormPagingTool = reduxForm({})(RouterPTWrapper);
|
|
2278
|
-
|
|
2279
|
-
const ConnectedPagingTool = props => {
|
|
2280
|
-
if (props.noForm) {
|
|
2281
|
-
return <RouterPTWrapper {...props} form={props.formName} />;
|
|
2282
|
-
}
|
|
2283
|
-
return <WithReduxFormPagingTool {...props} form={props.formName} />;
|
|
2284
|
-
};
|
|
2285
|
-
|
|
2286
|
-
export default ReduxFormDTWrapper;
|
|
3308
|
+
const WrappedDT = dataTableEnhancer(DataTable);
|
|
3309
|
+
export default WrappedDT;
|
|
3310
|
+
const ConnectedPagingTool = dataTableEnhancer(PagingTool);
|
|
2287
3311
|
export { ConnectedPagingTool };
|