@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.
@@ -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 initialValue = useSelector(
36
- state => state.form?.[formName]?.values?.reduxFormInitialValue
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={initialValue || text}
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
+ };
@@ -1086,11 +1086,17 @@ const DataTable = ({
1086
1086
  }, [selectedCells]);
1087
1087
 
1088
1088
  const startCellEdit = useCallback(
1089
- (cellId, initialValue) => {
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("reduxFormInitialValue", initialValue);
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
- if (!isNum && !isLetter) {
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, e.key);
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
+ }
@@ -1023,6 +1023,7 @@ function getQueries(filters, qb, ccFields) {
1023
1023
  }, {});
1024
1024
  return subQueries;
1025
1025
  }
1026
+
1026
1027
  function getColumnCustomFilters(filters, qb, ccFields) {
1027
1028
  const subQueries = filters.reduce((acc, filter) => {
1028
1029
  if (!filter) {
@@ -0,0 +1,10 @@
1
+ import { isEqual } from "lodash-es";
2
+ import { useRef } from "react";
3
+
4
+ export const useDeepEqualMemo = value => {
5
+ const ref = useRef();
6
+ if (!isEqual(value, ref.current)) {
7
+ ref.current = value;
8
+ }
9
+ return ref.current;
10
+ };