@teselagen/ui 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DataTable/EditabelCell.d.ts +7 -0
- package/DataTable/defaultProps.d.ts +43 -0
- package/DataTable/utils/computePresets.d.ts +1 -0
- package/DataTable/utils/useDeepEqualMemo.d.ts +1 -0
- package/DataTable/utils/useTableParams.d.ts +49 -0
- package/index.cjs.js +29 -7
- package/index.es.js +29 -7
- package/package.json +1 -1
- package/src/DataTable/Columns.jsx +945 -0
- package/src/DataTable/EditabelCell.js +44 -0
- package/src/DataTable/EditabelCell.jsx +44 -0
- package/src/DataTable/RenderCell.js +3 -3
- package/src/DataTable/RenderCell.jsx +191 -0
- package/src/DataTable/defaultProps.js +45 -0
- package/src/DataTable/index.js +28 -4
- package/src/DataTable/utils/computePresets.js +42 -0
- package/src/DataTable/utils/queryParams.js +1 -0
- package/src/DataTable/utils/useDeepEqualMemo.js +10 -0
- package/src/DataTable/utils/useTableParams.js +361 -0
- package/style.css +10537 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export const EditableCell = ({
|
|
4
|
+
cancelEdit,
|
|
5
|
+
dataTest,
|
|
6
|
+
finishEdit,
|
|
7
|
+
isNumeric,
|
|
8
|
+
initialValue
|
|
9
|
+
}) => {
|
|
10
|
+
const [value, setValue] = useState(initialValue);
|
|
11
|
+
const inputRef = useRef(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (inputRef.current) {
|
|
15
|
+
inputRef.current.focus();
|
|
16
|
+
}
|
|
17
|
+
}, [isNumeric]);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<input
|
|
21
|
+
style={{
|
|
22
|
+
border: 0,
|
|
23
|
+
width: "95%",
|
|
24
|
+
fontSize: 12,
|
|
25
|
+
background: "none"
|
|
26
|
+
}}
|
|
27
|
+
ref={inputRef}
|
|
28
|
+
{...dataTest}
|
|
29
|
+
autoFocus
|
|
30
|
+
onKeyDown={e => {
|
|
31
|
+
e.stopPropagation();
|
|
32
|
+
if (e.key === "Enter") {
|
|
33
|
+
e.target.blur();
|
|
34
|
+
} else if (e.key === "Escape") {
|
|
35
|
+
cancelEdit();
|
|
36
|
+
}
|
|
37
|
+
}}
|
|
38
|
+
onBlur={() => finishEdit(value)}
|
|
39
|
+
onChange={e => setValue(e.target.value)}
|
|
40
|
+
type={isNumeric ? "number" : undefined}
|
|
41
|
+
value={value}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -0,0 +1,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
|
+
};
|
|
@@ -32,8 +32,8 @@ export const RenderCell = ({
|
|
|
32
32
|
const editingCell = useSelector(
|
|
33
33
|
state => state.form?.[formName]?.values?.reduxFormEditingCell
|
|
34
34
|
);
|
|
35
|
-
const
|
|
36
|
-
state => state.form?.[formName]?.values?.
|
|
35
|
+
const shouldEditableCellInputBeCleared = useSelector(
|
|
36
|
+
state => state.form?.[formName]?.values?.shouldEditableCellInputBeCleared
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
const [row] = args;
|
|
@@ -85,7 +85,7 @@ export const RenderCell = ({
|
|
|
85
85
|
dataTest={dataTest}
|
|
86
86
|
cancelEdit={cancelCellEdit}
|
|
87
87
|
isNumeric={column.type === "number"}
|
|
88
|
-
initialValue={
|
|
88
|
+
initialValue={shouldEditableCellInputBeCleared ? undefined : text}
|
|
89
89
|
finishEdit={newVal => {
|
|
90
90
|
finishCellEdit(cellId, newVal);
|
|
91
91
|
}}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { Checkbox, Icon } from "@blueprintjs/core";
|
|
4
|
+
import {
|
|
5
|
+
getIdOrCodeOrIndex,
|
|
6
|
+
isBottomRightCornerOfRectangle,
|
|
7
|
+
PRIMARY_SELECTED_VAL
|
|
8
|
+
} from "./utils";
|
|
9
|
+
import { DropdownCell } from "./DropdownCell";
|
|
10
|
+
import { EditableCell } from "./EditabelCell";
|
|
11
|
+
import { getVals } from "./getVals";
|
|
12
|
+
import { CellDragHandle } from "./CellDragHandle";
|
|
13
|
+
|
|
14
|
+
export const RenderCell = ({
|
|
15
|
+
oldFunc,
|
|
16
|
+
getCopyTextForCell,
|
|
17
|
+
column,
|
|
18
|
+
isCellEditable,
|
|
19
|
+
isEntityDisabled,
|
|
20
|
+
finishCellEdit,
|
|
21
|
+
formName,
|
|
22
|
+
noEllipsis,
|
|
23
|
+
cancelCellEdit,
|
|
24
|
+
getCellHoverText,
|
|
25
|
+
selectedCells,
|
|
26
|
+
isSelectionARectangle,
|
|
27
|
+
startCellEdit,
|
|
28
|
+
tableRef,
|
|
29
|
+
onDragEnd,
|
|
30
|
+
args
|
|
31
|
+
}) => {
|
|
32
|
+
const editingCell = useSelector(
|
|
33
|
+
state => state.form?.[formName]?.values?.reduxFormEditingCell
|
|
34
|
+
);
|
|
35
|
+
const initialValue = useSelector(
|
|
36
|
+
state => state.form?.[formName]?.values?.reduxFormInitialValue
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const [row] = args;
|
|
40
|
+
const rowId = getIdOrCodeOrIndex(row.original, row.index);
|
|
41
|
+
const cellId = `${rowId}:${row.column.path}`;
|
|
42
|
+
const isEditingCell = editingCell === cellId;
|
|
43
|
+
let val = oldFunc(...args);
|
|
44
|
+
const oldVal = val;
|
|
45
|
+
const text = getCopyTextForCell(val, row, column);
|
|
46
|
+
const dataTest = {
|
|
47
|
+
"data-test": "tgCell_" + column.path
|
|
48
|
+
};
|
|
49
|
+
const fullValue = row.original?.[row.column.path];
|
|
50
|
+
|
|
51
|
+
if (isEditingCell) {
|
|
52
|
+
if (column.type === "genericSelect") {
|
|
53
|
+
const GenericSelectComp = column.GenericSelectComp;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<GenericSelectComp
|
|
57
|
+
rowId={rowId}
|
|
58
|
+
fullValue={fullValue}
|
|
59
|
+
initialValue={text}
|
|
60
|
+
{...dataTest}
|
|
61
|
+
finishEdit={(newVal, doNotStopEditing) => {
|
|
62
|
+
finishCellEdit(cellId, newVal, doNotStopEditing);
|
|
63
|
+
}}
|
|
64
|
+
dataTest={dataTest}
|
|
65
|
+
cancelEdit={cancelCellEdit}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (column.type === "dropdown" || column.type === "dropdownMulti") {
|
|
70
|
+
return (
|
|
71
|
+
<DropdownCell
|
|
72
|
+
isMulti={dataTest.isMulti || column.type === "dropdownMulti"}
|
|
73
|
+
initialValue={dataTest.initialValue || text}
|
|
74
|
+
options={getVals(column.values)}
|
|
75
|
+
finishEdit={(newVal, doNotStopEditing) => {
|
|
76
|
+
finishCellEdit(cellId, newVal, doNotStopEditing);
|
|
77
|
+
}}
|
|
78
|
+
dataTest={dataTest}
|
|
79
|
+
cancelEdit={cancelCellEdit}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
return (
|
|
84
|
+
<EditableCell
|
|
85
|
+
dataTest={dataTest}
|
|
86
|
+
cancelEdit={cancelCellEdit}
|
|
87
|
+
isNumeric={column.type === "number"}
|
|
88
|
+
initialValue={initialValue || text}
|
|
89
|
+
finishEdit={newVal => {
|
|
90
|
+
finishCellEdit(cellId, newVal);
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const isBool = column.type === "boolean";
|
|
98
|
+
if (isCellEditable && isBool) {
|
|
99
|
+
val = (
|
|
100
|
+
<Checkbox
|
|
101
|
+
disabled={isEntityDisabled(row.original)}
|
|
102
|
+
className="tg-cell-edit-boolean-checkbox"
|
|
103
|
+
checked={oldVal === "True"}
|
|
104
|
+
onChange={e => {
|
|
105
|
+
const checked = e.target.checked;
|
|
106
|
+
finishCellEdit(cellId, checked);
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
noEllipsis = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//wrap the original tableColumn.Cell function in another div in order to add a title attribute
|
|
114
|
+
let title = text;
|
|
115
|
+
if (getCellHoverText) title = getCellHoverText(...args);
|
|
116
|
+
else if (column.getTitleAttr) title = column.getTitleAttr(...args);
|
|
117
|
+
const isSelectedCell = selectedCells?.[cellId];
|
|
118
|
+
const {
|
|
119
|
+
isRect,
|
|
120
|
+
selectionGrid,
|
|
121
|
+
lastRowIndex,
|
|
122
|
+
lastCellIndex,
|
|
123
|
+
entityMap,
|
|
124
|
+
pathToIndex
|
|
125
|
+
} = isSelectionARectangle();
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
<div
|
|
130
|
+
style={{
|
|
131
|
+
...(!noEllipsis && {
|
|
132
|
+
textOverflow: "ellipsis",
|
|
133
|
+
overflow: "hidden"
|
|
134
|
+
})
|
|
135
|
+
}}
|
|
136
|
+
{...dataTest}
|
|
137
|
+
className="tg-cell-wrapper"
|
|
138
|
+
data-copy-text={text}
|
|
139
|
+
data-copy-json={JSON.stringify(
|
|
140
|
+
//tnw: eventually we'll parse these back out and use either the fullValue (for the generic selects) or the regular text vals for everything else
|
|
141
|
+
column.type === "genericSelect"
|
|
142
|
+
? {
|
|
143
|
+
__strVal: fullValue,
|
|
144
|
+
__genSelCol: column.path
|
|
145
|
+
}
|
|
146
|
+
: { __strVal: text }
|
|
147
|
+
)}
|
|
148
|
+
title={title || undefined}
|
|
149
|
+
>
|
|
150
|
+
{val}
|
|
151
|
+
</div>
|
|
152
|
+
{isCellEditable &&
|
|
153
|
+
(column.type === "dropdown" ||
|
|
154
|
+
column.type === "dropdownMulti" ||
|
|
155
|
+
column.type === "genericSelect") && (
|
|
156
|
+
<Icon
|
|
157
|
+
icon="caret-down"
|
|
158
|
+
style={{
|
|
159
|
+
position: "absolute",
|
|
160
|
+
right: 5,
|
|
161
|
+
opacity: 0.3
|
|
162
|
+
}}
|
|
163
|
+
className="cell-edit-dropdown"
|
|
164
|
+
onClick={() => {
|
|
165
|
+
startCellEdit(cellId);
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
{isSelectedCell &&
|
|
171
|
+
(isRect
|
|
172
|
+
? isBottomRightCornerOfRectangle({
|
|
173
|
+
cellId,
|
|
174
|
+
selectionGrid,
|
|
175
|
+
lastRowIndex,
|
|
176
|
+
lastCellIndex,
|
|
177
|
+
entityMap,
|
|
178
|
+
pathToIndex
|
|
179
|
+
})
|
|
180
|
+
: isSelectedCell === PRIMARY_SELECTED_VAL) && (
|
|
181
|
+
<CellDragHandle
|
|
182
|
+
key={cellId}
|
|
183
|
+
thisTable={tableRef.current.tableRef}
|
|
184
|
+
cellId={cellId}
|
|
185
|
+
isSelectionARectangle={isSelectionARectangle}
|
|
186
|
+
onDragEnd={onDragEnd}
|
|
187
|
+
/>
|
|
188
|
+
)}
|
|
189
|
+
</>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { noop } from "lodash-es";
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line import/no-anonymous-default-export
|
|
4
|
+
export default {
|
|
5
|
+
//NOTE: DO NOT SET DEFAULTS HERE FOR PROPS THAT GET COMPUTED AS PART OF PRESET GROUPS IN computePresets
|
|
6
|
+
addFilters: noop,
|
|
7
|
+
className: "",
|
|
8
|
+
clearFilters: noop,
|
|
9
|
+
contextMenu: noop,
|
|
10
|
+
disabled: false,
|
|
11
|
+
entities: [],
|
|
12
|
+
extraClasses: "",
|
|
13
|
+
filters: [],
|
|
14
|
+
isCopyable: true,
|
|
15
|
+
isEntityDisabled: noop,
|
|
16
|
+
isLoading: false,
|
|
17
|
+
isSimple: false,
|
|
18
|
+
isSingleSelect: false,
|
|
19
|
+
maxHeight: 600,
|
|
20
|
+
noHeader: false,
|
|
21
|
+
noSelect: false,
|
|
22
|
+
noUserSelect: false,
|
|
23
|
+
onDeselect: noop,
|
|
24
|
+
onMultiRowSelect: noop,
|
|
25
|
+
onRowClick: noop,
|
|
26
|
+
onRowSelect: noop,
|
|
27
|
+
onSingleRowSelect: noop,
|
|
28
|
+
page: 1,
|
|
29
|
+
pageSize: 10,
|
|
30
|
+
reduxFormExpandedEntityIdMap: {},
|
|
31
|
+
reduxFormSearchInput: "",
|
|
32
|
+
reduxFormSelectedEntityIdMap: {},
|
|
33
|
+
removeSingleFilter: noop,
|
|
34
|
+
resized: [],
|
|
35
|
+
resizePersist: noop,
|
|
36
|
+
setFilter: noop,
|
|
37
|
+
setOrder: noop,
|
|
38
|
+
setPage: noop,
|
|
39
|
+
setPageSize: noop,
|
|
40
|
+
setSearchTerm: noop,
|
|
41
|
+
showCount: false,
|
|
42
|
+
style: {},
|
|
43
|
+
withCheckboxes: false,
|
|
44
|
+
withSort: true
|
|
45
|
+
};
|
package/src/DataTable/index.js
CHANGED
|
@@ -1086,11 +1086,17 @@ const DataTable = ({
|
|
|
1086
1086
|
}, [selectedCells]);
|
|
1087
1087
|
|
|
1088
1088
|
const startCellEdit = useCallback(
|
|
1089
|
-
(cellId,
|
|
1089
|
+
(cellId, shouldClear) => {
|
|
1090
|
+
// console.log(`startCellEdit initialValue:`, initialValue)
|
|
1090
1091
|
// This initial value is not needed if the event is propagated accordingly.
|
|
1091
1092
|
// This is directly connected to the RenderCell component, which does set
|
|
1092
1093
|
// the initial value.
|
|
1093
|
-
change("
|
|
1094
|
+
// change("shouldEditableCellInputBeCleared", undefined);
|
|
1095
|
+
if (shouldClear) {
|
|
1096
|
+
change("shouldEditableCellInputBeCleared", true);
|
|
1097
|
+
} else {
|
|
1098
|
+
change("shouldEditableCellInputBeCleared", false);
|
|
1099
|
+
}
|
|
1094
1100
|
change("reduxFormEditingCell", prev => {
|
|
1095
1101
|
//check if the cell is already selected and editing and if so, don't change it
|
|
1096
1102
|
if (prev === cellId) return cellId;
|
|
@@ -2966,12 +2972,30 @@ const DataTable = ({
|
|
|
2966
2972
|
const rowDisabled = isEntityDisabled(entity);
|
|
2967
2973
|
const isNum = e.code?.startsWith("Digit");
|
|
2968
2974
|
const isLetter = e.code?.startsWith("Key");
|
|
2969
|
-
|
|
2975
|
+
const allowedSpecialChars = [
|
|
2976
|
+
"Minus",
|
|
2977
|
+
"Equal",
|
|
2978
|
+
"Backquote",
|
|
2979
|
+
"BracketLeft",
|
|
2980
|
+
"BracketRight",
|
|
2981
|
+
"Backslash",
|
|
2982
|
+
"IntlBackslash",
|
|
2983
|
+
"Semicolon",
|
|
2984
|
+
"Quote",
|
|
2985
|
+
"Comma",
|
|
2986
|
+
"Period",
|
|
2987
|
+
"Slash",
|
|
2988
|
+
"IntlRo",
|
|
2989
|
+
"IntlYen",
|
|
2990
|
+
"Space"
|
|
2991
|
+
];
|
|
2992
|
+
const isSpecialChar = allowedSpecialChars.includes(e.code);
|
|
2993
|
+
if (!isNum && !isLetter && !isSpecialChar) {
|
|
2970
2994
|
return;
|
|
2971
2995
|
}
|
|
2972
2996
|
if (rowDisabled) return;
|
|
2973
2997
|
e.stopPropagation();
|
|
2974
|
-
startCellEdit(primarySelectedCellId,
|
|
2998
|
+
startCellEdit(primarySelectedCellId, true);
|
|
2975
2999
|
}
|
|
2976
3000
|
})}
|
|
2977
3001
|
>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { omitBy, isNil } from "lodash-es";
|
|
2
|
+
//we use this to make adding preset prop groups simpler
|
|
3
|
+
export default function computePresets(props = {}) {
|
|
4
|
+
const { isSimple } = props;
|
|
5
|
+
let toReturn = omitBy(props, isNil);
|
|
6
|
+
toReturn.pageSize = toReturn.controlled_pageSize || toReturn.pageSize;
|
|
7
|
+
if (isSimple) {
|
|
8
|
+
//isSimplePreset
|
|
9
|
+
toReturn = {
|
|
10
|
+
noHeader: true,
|
|
11
|
+
noFooter: !props.withPaging,
|
|
12
|
+
noPadding: true,
|
|
13
|
+
noFullscreenButton: true,
|
|
14
|
+
hidePageSizeWhenPossible: !props.withPaging,
|
|
15
|
+
isInfinite: !props.withPaging,
|
|
16
|
+
hideSelectedCount: true,
|
|
17
|
+
withTitle: false,
|
|
18
|
+
withSearch: false,
|
|
19
|
+
compact: true,
|
|
20
|
+
withPaging: false,
|
|
21
|
+
withFilter: false,
|
|
22
|
+
...toReturn
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
toReturn = {
|
|
26
|
+
// the usual defaults:
|
|
27
|
+
noFooter: false,
|
|
28
|
+
noPadding: false,
|
|
29
|
+
compact: true,
|
|
30
|
+
noFullscreenButton: false,
|
|
31
|
+
hidePageSizeWhenPossible: false,
|
|
32
|
+
isInfinite: false,
|
|
33
|
+
hideSelectedCount: false,
|
|
34
|
+
withTitle: true,
|
|
35
|
+
withSearch: true,
|
|
36
|
+
withPaging: true,
|
|
37
|
+
withFilter: true,
|
|
38
|
+
...toReturn
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return toReturn || {};
|
|
42
|
+
}
|