@hzab/edit-table 0.1.2 → 0.1.3-alpha.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # @hzab/edit-table@0.1.3
2
+
3
+ fix: 编辑数据优化
4
+
1
5
  # @hzab/edit-table@0.1.2
2
6
 
3
7
  fix: checkbox 选择子项时拿到对应行的数据
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/edit-table",
3
- "version": "0.1.2",
3
+ "version": "0.1.3-alpha.0",
4
4
  "description": "",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -41,10 +41,12 @@
41
41
  "react-router-dom": "6.14.1",
42
42
  "typedoc": "0.24.8",
43
43
  "typescript": "4.9.4",
44
- "webpack": "5.75.0",
44
+ "webpack": "5.75.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@silevis/reactgrid": "^4.1.17",
45
48
  "@hzab/form-render": "^1.7.17",
46
- "@hzab/list-render": "^1.11.0",
47
- "@silevis/reactgrid": "^4.1.17"
49
+ "@hzab/list-render": "^1.11.0"
48
50
  },
49
51
  "directories": {
50
52
  "src": "src"
@@ -0,0 +1,5 @@
1
+ .number-cell-template {
2
+ .rg-input {
3
+ border: none;
4
+ }
5
+ }
@@ -0,0 +1,24 @@
1
+ import { Input } from "antd";
2
+ import "./index.less"
3
+ const NumberCellTemplate = (props: any) => {
4
+ const { pattern, cell, componentProps } = props;
5
+ return (
6
+ <div className="number-cell-template">
7
+ {pattern == "readPretty" ? (
8
+ <div>{cell?.text}</div>
9
+ ) : (
10
+ <Input
11
+ className="rg-input"
12
+ type="number"
13
+ ref={(input) => {
14
+ if (input) {
15
+ input.focus();
16
+ }
17
+ }}
18
+ {...componentProps}
19
+ />
20
+ )}
21
+ </div>
22
+ );
23
+ };
24
+ export default NumberCellTemplate
@@ -0,0 +1,5 @@
1
+ .text-cell-template {
2
+ .rg-input {
3
+ border: none;
4
+ }
5
+ }
@@ -0,0 +1,22 @@
1
+ import "./index.less"
2
+ const TextCellTemplate = (props: any) => {
3
+ const { pattern, cell, componentProps } = props;
4
+ return (
5
+ <div className="text-cell-template">
6
+ {pattern == "readPretty" ? (
7
+ <div>{cell?.text}</div>
8
+ ) : (
9
+ <input
10
+ className="rg-input"
11
+ ref={(input) => {
12
+ if (input) {
13
+ input.focus();
14
+ }
15
+ }}
16
+ {...componentProps}
17
+ />
18
+ )}
19
+ </div>
20
+ );
21
+ };
22
+ export default TextCellTemplate
@@ -14,7 +14,7 @@ import {
14
14
  } from "@silevis/reactgrid";
15
15
  import { InfoCircleOutlined } from "@ant-design/icons";
16
16
  import { Popover } from "antd";
17
-
17
+ import { isTriggerBlur } from "./childAntdComponent";
18
18
  export type OptionType = {
19
19
  label: string;
20
20
  value: string | number;
@@ -26,7 +26,6 @@ export interface BaseCell extends Cell {
26
26
  value: string | number;
27
27
  values: OptionType[];
28
28
  isDisabled?: boolean;
29
- commit?: string;
30
29
  }
31
30
  interface DIProps {
32
31
  onCellChanged: (...args: any[]) => void;
@@ -37,42 +36,22 @@ interface DIProps {
37
36
  export const WidthWrapHoc = (WrapComponent: any) => {
38
37
  class createBaseCellTemplate implements CellTemplate<BaseCell> {
39
38
  getCompatibleCell(uncertainCell: Uncertain<BaseCell>): Compatible<BaseCell> {
40
- let selectedValue: string | undefined;
41
- try {
42
- selectedValue = getCellProperty(uncertainCell, "selectedValue", "number");
43
- } catch {
44
- selectedValue = undefined;
45
- }
46
- if (!selectedValue) {
47
- try {
48
- selectedValue = getCellProperty(uncertainCell, "selectedValue", "string");
49
- } catch {
50
- selectedValue = undefined;
51
- }
52
- }
53
- let commit: string;
54
-
55
- try {
56
- commit = getCellProperty(uncertainCell, "commit", "string");
57
- } catch {
58
- commit = "submit";
59
- }
60
-
61
39
  let values: any;
62
40
  try {
63
41
  values = getCellProperty(uncertainCell, "values", "object");
64
42
  } catch {
65
43
  values = [];
66
44
  }
67
- const value = selectedValue ? parseFloat(selectedValue) : NaN;
45
+ const value = uncertainCell.selectedValue ? parseFloat(uncertainCell.selectedValue) : NaN;
68
46
  let isDisabled = true;
69
47
  try {
70
48
  isDisabled = getCellProperty(uncertainCell, "isDisabled", "boolean");
71
49
  } catch {
72
50
  isDisabled = false;
73
51
  }
52
+ const selectedValue = uncertainCell.selectedValue;
74
53
  const text = selectedValue || "";
75
- return { ...uncertainCell, selectedValue, text, value, values, isDisabled, commit };
54
+ return { ...uncertainCell, selectedValue, text, value, values, isDisabled };
76
55
  }
77
56
 
78
57
  update(cell: Compatible<BaseCell>, cellToMerge: UncertainCompatible<BaseCell>): Compatible<BaseCell> {
@@ -83,7 +62,6 @@ export const WidthWrapHoc = (WrapComponent: any) => {
83
62
  return this.getCompatibleCell({
84
63
  ...cell,
85
64
  selectedValue: selectedValueFromText,
86
- commit: cellToMerge?.commit,
87
65
  });
88
66
  }
89
67
 
@@ -104,10 +82,13 @@ export const WidthWrapHoc = (WrapComponent: any) => {
104
82
  }
105
83
  /** render 不能抽出来组件,否则点击会失效 */
106
84
  render(cell: any, isInEditMode: boolean, onCellChanged: (cell: any, commit: boolean) => void): React.ReactNode {
107
- const [isOpen, setIsOpen] = useState<boolean>(false);
85
+ const [edit, setEdit] = useState<boolean>(false);
86
+ const [open, setOpen] = useState<boolean>(false);
108
87
  const datatypeId = cell?.columnId + cell?.rowId;
109
88
  const [comValue, setComValue] = useState(cell?.text);
89
+ const [isValidator, setIsValidator] = useState(false);
110
90
  const comValueInit = useRef<any>(cell?.text);
91
+ const openTimerRef = useRef<any>();
111
92
  useEffect(() => {
112
93
  setComValue(cell?.text);
113
94
  comValueInit.current = cell?.text;
@@ -115,9 +96,9 @@ export const WidthWrapHoc = (WrapComponent: any) => {
115
96
 
116
97
  useEffect(() => {
117
98
  // 处理下拉选项弹层失焦逻辑
118
- function onClick() {
99
+ function onClick(e) {
119
100
  if ((window as any).gridEditId != datatypeId) {
120
- setIsOpen(false);
101
+ setEdit(false);
121
102
  }
122
103
  }
123
104
  document.addEventListener("click", onClick);
@@ -126,65 +107,111 @@ export const WidthWrapHoc = (WrapComponent: any) => {
126
107
  };
127
108
  }, []);
128
109
 
129
- const isValidator = useMemo(() => {
110
+ /** 校验触发器 */
111
+ const onValidator = (val) => {
130
112
  let flag = false;
131
113
  let isVerify = false;
132
114
  for (let it of cell?.validatorList || []) {
133
- if (it?.pattern?.test(comValue)) {
115
+ if (it?.pattern?.test(val)) {
134
116
  flag = true;
135
117
  }
136
118
  break;
137
119
  }
138
- isVerify = !flag && cell?.validatorList?.length > 0;
120
+ isVerify = !flag && Array.isArray(cell?.validatorList) && cell?.validatorList?.length > 0;
121
+ setIsValidator(isVerify);
122
+ };
123
+
124
+ useEffect(() => {
125
+ if (!open) return;
126
+ // 等下一帧,弹窗DOM渲染完成
127
+ const timer = setTimeout(() => {
128
+ const popup: any = document.querySelector(".ant-picker-dropdown");
129
+ if (!popup) return;
130
+
131
+ const stopPointer = (e: PointerEvent) => {
132
+ // 阻断事件往上冒泡到document
133
+ e.stopPropagation();
134
+ };
135
+ // 捕获阶段监听 pointerdown!!必须是pointerdown不是mousedown
136
+ popup.addEventListener("pointerdown", stopPointer, true);
137
+
138
+ // 清理函数
139
+ return () => {
140
+ popup.removeEventListener("pointerdown", stopPointer, true);
141
+ };
142
+ }, 0);
143
+
144
+ return () => clearTimeout(timer);
145
+ }, [open]);
146
+
147
+ /** 失焦提交 */
148
+ const onBlurSubmit = () => {
149
+ let targetValue = comValueInit.current;
150
+ onCellChanged(
151
+ this.getCompatibleCell({
152
+ ...cell,
153
+ text: targetValue,
154
+ selectedValue: targetValue,
155
+ }),
156
+ true,
157
+ );
158
+ };
139
159
 
140
- return isVerify;
141
- }, [cell?.validatorList, comValue]);
160
+ const dataPickerProps = useMemo(() => {
161
+ if (isTriggerBlur?.includes(cell?.type)) {
162
+ return {
163
+ open: open,
164
+ /** 兼容datapicker 闪烁问题 */
165
+ onOpenChange: (isOpen) => {
166
+ if (openTimerRef.current) {
167
+ clearTimeout(openTimerRef.current);
168
+ }
169
+ openTimerRef.current = setTimeout(() => {
170
+ setOpen(isOpen);
171
+ }, 80);
172
+ },
173
+ };
174
+ }
175
+ return {};
176
+ }, [cell]);
177
+ /** 监听上传组件失焦回调 */
178
+ useEffect(() => {
179
+ if (!edit && cell.type == "Upload" && (window as any).uploadEdit && (window as any).uploadEdit == datatypeId) {
180
+ (window as any).uploadEdit = null;
181
+ onBlurSubmit();
182
+ }
183
+ }, [edit, comValue]);
142
184
 
143
185
  return (
144
186
  <div
145
- className="base-hoc"
187
+ className={`base-hoc ${edit ? "base-hoc-edit" : ""}`}
146
188
  onDoubleClick={(e) => {
147
189
  if (cell?.nonEditable) {
148
190
  return;
149
191
  }
150
- setIsOpen(true);
192
+ if (cell.type === "Upload") {
193
+ (window as any).uploadEdit = datatypeId;
194
+ }
195
+ setEdit(true);
151
196
  }}
152
197
  >
153
198
  <WrapComponent
199
+ onPointerDown={(e) => e.stopPropagation()}
154
200
  componentProps={{
155
201
  style: { width: "100%", display: "flex", alignItems: "center" },
156
202
  value: comValue,
157
203
  options: cell.values,
158
204
  onChange: (e) => {
159
- cell["commit"] = "change";
160
- const targetValue = e?.target?.value ?? e;
205
+ let targetValue = e?.target?.value ?? e;
161
206
  setComValue(targetValue);
162
207
  comValueInit.current = targetValue;
163
-
164
- onCellChanged(
165
- this.getCompatibleCell({
166
- ...cell,
167
- text: targetValue,
168
- selectedValue: targetValue,
169
- }),
170
- true,
171
- );
208
+ onValidator(targetValue);
172
209
  },
173
210
  onBlur: (e) => {
174
- cell["commit"] = "submit";
175
- if (isValidator) {
176
- cell["commit"] = "verify";
211
+ onBlurSubmit();
212
+ if (!isTriggerBlur?.includes(cell?.type)) {
213
+ setEdit(false);
177
214
  }
178
- const targetValue = comValueInit.current;
179
- onCellChanged(
180
- this.getCompatibleCell({
181
- ...cell,
182
- text: targetValue,
183
- selectedValue: targetValue,
184
- }),
185
- true,
186
- );
187
- setIsOpen(false);
188
215
  },
189
216
 
190
217
  onPointerDown: (e) => {
@@ -193,9 +220,10 @@ export const WidthWrapHoc = (WrapComponent: any) => {
193
220
  onKeyDown: (e) => {
194
221
  if (isAlphaNumericKey(e.keyCode) || isNavigationKey(e.keyCode)) e.stopPropagation();
195
222
  },
223
+ ...dataPickerProps,
196
224
  ...((cell?.getField && cell?.getField()["x-component-props"]) || {}),
197
225
  }}
198
- pattern={isOpen ? "" : "readPretty"}
226
+ pattern={edit ? "" : "readPretty"}
199
227
  cell={cell}
200
228
  ></WrapComponent>
201
229
  {isValidator && (
@@ -12,3 +12,5 @@ export const childAntdComponent = {
12
12
  "Input.TextArea": Input.TextArea,
13
13
  "TimePicker.RangePicker": TimePicker.RangePicker,
14
14
  }
15
+ export const isTriggerBlur = ["DatePicker", "DatePicker.MonthPicker", "DatePicker.QuarterPicker", "DatePicker.RangePicker", "DatePicker.TimePicker", "DatePicker.WeekPicker", "DatePicker.YearPicker", "TimePicker.RangePicker"];
16
+
package/src/index.less CHANGED
@@ -18,6 +18,24 @@
18
18
  display: flex;
19
19
  align-items: center;
20
20
  justify-content: center;
21
+ z-index: 2;
22
+ }
23
+
24
+ .rg-partial-area {
25
+ z-index: 3;
26
+
27
+ }
28
+
29
+ .rg-cell {
30
+ padding: 0px !important;
31
+ }
32
+
33
+ .rg-cell-focus {
34
+ z-index: 2;
35
+ }
36
+
37
+ .rg-fill-handle {
38
+ z-index: 2;
21
39
  }
22
40
  }
23
41
 
@@ -27,6 +45,13 @@
27
45
  display: flex;
28
46
  align-items: center;
29
47
  justify-content: center;
48
+ z-index: 1;
49
+ padding: 4px;
50
+ }
51
+
52
+ .base-hoc-edit {
53
+ background-color: #fff;
54
+
30
55
  }
31
56
 
32
57
  .validator-icon {
@@ -46,4 +71,9 @@
46
71
  display: flex;
47
72
  align-items: center;
48
73
  justify-content: center;
74
+ }
75
+
76
+ .rg-pane-left,
77
+ .rg-pane-right {
78
+ z-index: 10 !important;
49
79
  }
package/src/index.tsx CHANGED
@@ -1,10 +1,10 @@
1
- import { useEffect, useMemo, useRef, useState } from "react";
1
+ import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
2
2
  import { Checkbox, Empty, Spin } from "antd";
3
3
  import { ReactGrid } from "@silevis/reactgrid";
4
- import { TextTemplate } from "./Template/TextCellTemplate";
4
+ import TextTemplate from "./Template/TextCellTemplate";
5
5
  import { antdComponents, customComponents } from "@hzab/form-render/src/index";
6
6
  import { WidthWrapHoc } from "./common/WidthWrapHoc";
7
- import { NumberTemplate } from "./Template/NumberCellTemplate";
7
+ import NumberTemplate from "./Template/NumberCellTemplate";
8
8
  import { ExpandListTemplate } from "./Template/ExpandListTemplate";
9
9
  import { convertColumnsToRows, flattenColumns, isReactElement } from "./common/utils";
10
10
  import { childAntdComponent } from "./common/childAntdComponent";
@@ -33,7 +33,7 @@ interface CellEditTableProps {
33
33
  onRow?: (v: any, ri: number, ci: number) => void;
34
34
  }
35
35
 
36
- export default function CellEditTable(props: CellEditTableProps) {
36
+ function CellEditTable(props: CellEditTableProps, ref: any) {
37
37
  const {
38
38
  columns,
39
39
  dataSource,
@@ -57,16 +57,22 @@ export default function CellEditTable(props: CellEditTableProps) {
57
57
 
58
58
  const [headerList, setHeaderList] = useState<any>([]);
59
59
  const [rows, setRows] = useState<any>([]);
60
- /** 初始行数据 */
61
60
  const initRowsRef = useRef<any>([]);
62
- const firstRender = useRef(true);
61
+ /** 初始行数据 */
63
62
  const [tableColumns, setTableColumns] = useState<any>([]);
64
63
  const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
65
64
  const [newDataSource, setNewDataSource] = useState<any>([]);
66
65
  const [expandKeys, setExpandKeys] = useState<any>([]);
67
66
  const [headerCustomTemplate, setHeaderCustomTemplate] = useState<any>([]);
67
+ const [highlights, setHighlights] = useState([]);
68
68
  const gridRef = useRef<any>();
69
+ const currentCell = useRef<any>();
69
70
 
71
+ useImperativeHandle(ref, () => {
72
+ return {
73
+ rows,
74
+ };
75
+ });
70
76
  /** 递归处理子节点数据 */
71
77
  const handleDataSource = (arr, level = 0) => {
72
78
  arr?.forEach((el) => {
@@ -92,6 +98,9 @@ export default function CellEditTable(props: CellEditTableProps) {
92
98
  item?.cells?.forEach((el) => {
93
99
  if (el?.columnId && !fieldList?.includes(el?.columnId)) {
94
100
  obj[el?.columnId] = el?.text || el?.value || el?.selectedValue || "";
101
+ if (el?.type == "Number") {
102
+ obj[el?.columnId] = el?.value;
103
+ }
95
104
  }
96
105
  });
97
106
 
@@ -108,6 +117,7 @@ export default function CellEditTable(props: CellEditTableProps) {
108
117
  type: "ExpandListTemplate",
109
118
  width: 14,
110
119
  key: "ExpandListTemplate",
120
+ nonEditable: true,
111
121
  style: rowSelection?.style || {
112
122
  background: "rgba(128, 128, 128, 0.1)",
113
123
  },
@@ -116,6 +126,7 @@ export default function CellEditTable(props: CellEditTableProps) {
116
126
  if (rowSelection) {
117
127
  columns.unshift({
118
128
  type: "rowSelection",
129
+ nonEditable: true,
119
130
  width: rowSelection?.width || 30,
120
131
  key: "rowSelection",
121
132
  style: rowSelection?.style || {
@@ -192,7 +203,10 @@ export default function CellEditTable(props: CellEditTableProps) {
192
203
  text: String(cellItem || ""),
193
204
  value: cellItem,
194
205
  selectedValue: cellItem || "",
195
- style: columnsItem?.type == "rowSelection" ? { display: "flex", justifyContent: "center" } : {},
206
+ style:
207
+ columnsItem?.type == "rowSelection"
208
+ ? { display: "flex", justifyContent: "center" }
209
+ : { color: cellItem == 0 ? "#CACDD7" : "" },
196
210
  ...((onRow && onRow(item, index, ind)) || {}),
197
211
  ...((columnsItem?.onCell && columnsItem?.onCell(item, index, ind)) || {}),
198
212
  ...(columnsItem?.cellProps || {}),
@@ -207,13 +221,16 @@ export default function CellEditTable(props: CellEditTableProps) {
207
221
  };
208
222
  });
209
223
  const mergerList = [...headerList, ...tempDataSource];
210
- if (firstRender.current && tempDataSource?.length > 0) {
211
- initRowsRef.current = mergerList;
212
- firstRender.current = false;
213
- }
214
224
  setRows(mergerList);
215
225
  }, [newDataSource, tableColumns, headerList]);
216
-
226
+ useEffect(() => {
227
+ if (loading) {
228
+ const item = rows?.find((el) => el?.rowId == currentCell.current?.rowId);
229
+ let newItem = item?.cells?.find((it) => it?.columnId == currentCell.current?.columnId);
230
+ newItem = { ...currentCell.current };
231
+ initRowsRef.current = rows;
232
+ }
233
+ }, [loading, rows]);
217
234
  // 单元格编辑回调
218
235
  const handleCellsChanged = async (changes) => {
219
236
  const batchQuery: any = [];
@@ -226,11 +243,10 @@ export default function CellEditTable(props: CellEditTableProps) {
226
243
 
227
244
  setRows((prevRows) => {
228
245
  changes.forEach(async (change) => {
229
- // const hasDiff = JSON.stringify( change?.newCell?.text) != JSON.stringify(change.previousCell?.text);
230
- // if (hasDiff) return;
231
246
  const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
232
247
  const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
233
248
  prevRows[changeRowIdx].cells[changeColumnIdx] = { ...change.newCell };
249
+ currentCell.current = { ...change.newCell };
234
250
  batchQuery.push(handleArrayToObj(prevRows[changeRowIdx]));
235
251
  if (formulaConfig) {
236
252
  for (let key in formulaConfig) {
@@ -249,18 +265,15 @@ export default function CellEditTable(props: CellEditTableProps) {
249
265
  prevRows[changeRowIdx].cells[resultColumnIdx] = newCell;
250
266
  }
251
267
  }
252
-
253
- if (change?.newCell?.commit == "submit" && onEditSubmit && !isBatchSubmit) {
268
+ if (onEditSubmit && !isBatchSubmit) {
254
269
  try {
255
270
  await onEditSubmit(batchQuery);
256
271
  } catch (err) {
257
272
  getList && getList();
258
273
  }
259
274
  }
260
-
261
275
  onChange && onChange(prevRows[changeRowIdx]);
262
276
  });
263
- initRowsRef.current = prevRows;
264
277
  return [...prevRows];
265
278
  });
266
279
 
@@ -272,6 +285,7 @@ export default function CellEditTable(props: CellEditTableProps) {
272
285
  }
273
286
  }
274
287
  };
288
+
275
289
  /** 自定义组件注册 */
276
290
  const baseTemplate = useMemo(() => {
277
291
  const mergeTemplate: any = { ...(antdComponents || {}), ...(customComponents || {}), ...childAntdComponent };
@@ -286,12 +300,13 @@ export default function CellEditTable(props: CellEditTableProps) {
286
300
  }, [antdComponents, customComponents]);
287
301
 
288
302
  const ActionDom = (props) => {
303
+ const rowData = rows?.find((el) => el?.rowId == props?.cell?.rowId);
289
304
  return (
290
305
  <div onPointerDown={(e) => e.stopPropagation()}>
291
- {Slots?.tableActionsSlot && <Slots.tableActionsSlot {...props?.cell} />}
292
- {Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...props?.cell} />}
293
- {Slots?.actionCenterSlot && <Slots.actionCenterSlot {...props?.cell} />}
294
- {Slots?.actionSuffixSlot && <Slots.actionSuffixSlot {...props?.cell} />}
306
+ {Slots?.tableActionsSlot && <Slots.tableActionsSlot {...props?.cell} rowData={rowData} />}
307
+ {Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...props?.cell} rowData={rowData} />}
308
+ {Slots?.actionCenterSlot && <Slots.actionCenterSlot {...props?.cell} rowData={rowData} />}
309
+ {Slots?.actionSuffixSlot && <Slots.actionSuffixSlot {...props?.cell} rowData={rowData} />}
295
310
  </div>
296
311
  );
297
312
  };
@@ -372,12 +387,25 @@ export default function CellEditTable(props: CellEditTableProps) {
372
387
  return templatesObj;
373
388
  }, [reactGridProps?.customCellTemplates]);
374
389
 
390
+ /** 选中高亮整行 */
391
+ const onHightsRow = (cell: any, rows: any) => {
392
+ const list = rows
393
+ ?.find((el) => el?.rowId == cell?.rowId)
394
+ ?.cells?.map((item) => ({
395
+ columnId: item?.columnId,
396
+ rowId: cell?.rowId,
397
+ className: "custom-highlights-row",
398
+ borderColor: "transparent",
399
+ }));
400
+ setHighlights(list);
401
+ };
402
+
375
403
  return (
376
404
  <Spin spinning={loading}>
377
405
  <div className={`grid-container ${className}`} style={reactGridStyle}>
378
406
  <ReactGrid
379
407
  ref={gridRef}
380
- rows={rows}
408
+ rows={loading ? initRowsRef.current : rows}
381
409
  key={dataSource?.length == 0 ? 0 : 1}
382
410
  columns={tableColumns}
383
411
  onCellsChanged={handleCellsChanged}
@@ -385,15 +413,23 @@ export default function CellEditTable(props: CellEditTableProps) {
385
413
  enableRangeSelection={true}
386
414
  horizontalStickyBreakpoint={120}
387
415
  {...reactGridProps}
416
+ highlights={reactGridProps?.highlights ?? highlights}
388
417
  onFocusLocationChanging={(cell: any) => {
389
418
  const datatypeId = cell?.columnId + cell?.rowId;
390
419
  (window as any).gridEditId = datatypeId;
391
420
  if (["_$actions", "rowSelection", "ExpandListTemplate"]?.includes(cell?.columnId)) {
392
421
  return false;
393
422
  }
423
+ if (typeof cell?.rowId == "string" && cell?.rowId?.indexOf("header-") > -1) {
424
+ return false;
425
+ }
426
+ if (reactGridProps?.isRowHighlights && cell?.columnId) {
427
+ onHightsRow(cell, rows);
428
+ }
394
429
  if (reactGridProps?.onFocusLocationChanging) {
395
430
  return reactGridProps?.onFocusLocationChanging(cell, rows);
396
431
  }
432
+
397
433
  return cell;
398
434
  }}
399
435
  customCellTemplates={{
@@ -403,8 +439,8 @@ export default function CellEditTable(props: CellEditTableProps) {
403
439
  ...headerCustomTemplate,
404
440
  _$actions: WidthWrapHoc(ActionDom),
405
441
  rowSelection: WidthWrapHoc(CheckBoxDom),
406
- Input: TextTemplate,
407
- Number: NumberTemplate,
442
+ Input: WidthWrapHoc(TextTemplate),
443
+ Number: WidthWrapHoc(NumberTemplate),
408
444
  }}
409
445
  />
410
446
  {dataSource?.length <= 0 && (
@@ -416,3 +452,5 @@ export default function CellEditTable(props: CellEditTableProps) {
416
452
  </Spin>
417
453
  );
418
454
  }
455
+
456
+ export default forwardRef(CellEditTable);
@@ -1,168 +0,0 @@
1
- import {
2
- Cell,
3
- CellTemplate,
4
- Compatible,
5
- getCellProperty,
6
- getCharFromKey,
7
- inNumericKey,
8
- isAllowedOnNumberTypingKey,
9
- isCharAllowedOnNumberInput,
10
- isFunctionKey,
11
- isNavigationKey,
12
- isNumpadNumericKey,
13
- keyCodes,
14
- Uncertain,
15
- UncertainCompatible,
16
- } from "@silevis/reactgrid";
17
- import * as React from "react";
18
- import { parseLocaleNumber } from "../common/utils";
19
-
20
- // NOTE: all modules imported below may be imported from '@silevis/reactgrid'
21
-
22
- export interface NumberCell extends Cell {
23
- type: "number";
24
- value: number;
25
- format?: Intl.NumberFormat;
26
- validator?: (value: number) => boolean;
27
- nanToZero?: boolean;
28
- hideZero?: boolean;
29
- errorMessage?: string;
30
- }
31
-
32
- export class NumberCellTemplate implements CellTemplate<NumberCell> {
33
- private wasEscKeyPressed = false;
34
-
35
- getCompatibleCell(uncertainCell: Uncertain<NumberCell>): Compatible<NumberCell> {
36
- let value: number;
37
- try {
38
- value = getCellProperty(uncertainCell, "value", "number");
39
- } catch (error) {
40
- value = NaN;
41
- }
42
- const numberFormat = uncertainCell.format || new Intl.NumberFormat(window.navigator.language);
43
- const displayValue = uncertainCell.nanToZero && Number.isNaN(value) ? 0 : value;
44
- const text =
45
- Number.isNaN(displayValue) || (uncertainCell.hideZero && displayValue === 0)
46
- ? ""
47
- : numberFormat.format(displayValue);
48
- return { ...uncertainCell, value: displayValue, text };
49
- }
50
-
51
- handleKeyDown(
52
- cell: Compatible<NumberCell>,
53
- keyCode: number,
54
- ctrl: boolean,
55
- shift: boolean,
56
- alt: boolean,
57
- key: string,
58
- capsLock: boolean,
59
- ): { cell: Compatible<NumberCell>; enableEditMode: boolean } {
60
- if (isNumpadNumericKey(keyCode)) keyCode -= 48;
61
- if (isFunctionKey(keyCode)) {
62
- if (keyCode === keyCodes.F2) return { cell, enableEditMode: true };
63
- return { cell, enableEditMode: false };
64
- }
65
-
66
- const char = getCharFromKey(key);
67
-
68
- if (!ctrl && isCharAllowedOnNumberInput(char)) {
69
- const value = Number(char);
70
-
71
- if (Number.isNaN(value) && isCharAllowedOnNumberInput(char))
72
- return { cell: { ...this.getCompatibleCell({ ...cell, value }), text: char }, enableEditMode: true };
73
- return { cell: this.getCompatibleCell({ ...cell, value }), enableEditMode: true };
74
- }
75
-
76
- return {
77
- cell,
78
- enableEditMode: keyCode === keyCodes.POINTER || keyCode === keyCodes.ENTER,
79
- };
80
- }
81
-
82
- update(cell: Compatible<NumberCell>, cellToMerge: UncertainCompatible<NumberCell>): Compatible<NumberCell> {
83
- return this.getCompatibleCell({ ...cell, value: cellToMerge.value });
84
- }
85
-
86
- private getTextFromCharCode = (cellText: string): string => {
87
- switch (cellText.charCodeAt(0)) {
88
- case keyCodes.DASH:
89
- case keyCodes.FIREFOX_DASH:
90
- case keyCodes.SUBTRACT:
91
- return "-";
92
- case keyCodes.COMMA:
93
- return ",";
94
- case keyCodes.PERIOD:
95
- case keyCodes.DECIMAL:
96
- return ".";
97
- default:
98
- return cellText;
99
- }
100
- };
101
-
102
- getClassName(cell: Compatible<NumberCell>, isInEditMode: boolean): string {
103
- const isValid = cell.validator?.(cell.value) ?? true;
104
- const className = cell.className || "";
105
- return `${!isValid ? "rg-invalid" : ""} ${className}`;
106
- }
107
-
108
- render(
109
- cell: Compatible<NumberCell>,
110
- isInEditMode: boolean,
111
- onCellChanged: (cell: Compatible<NumberCell>, commit: boolean) => void,
112
- ): React.ReactNode {
113
- if (!isInEditMode) {
114
- const isValid = cell.validator?.(cell.value) ?? true;
115
- const textToDisplay = !isValid && cell.errorMessage ? cell.errorMessage : cell.text;
116
- return textToDisplay;
117
- }
118
-
119
- const locale = cell.format ? cell.format.resolvedOptions().locale : window.navigator.languages[0];
120
- const format = new Intl.NumberFormat(locale, { useGrouping: false, maximumFractionDigits: 20 });
121
-
122
- return (
123
- <input
124
- className="rg-input"
125
- inputMode="decimal"
126
- ref={(input) => {
127
- if (input) {
128
- input.focus();
129
- input.setSelectionRange(input.value.length, input.value.length);
130
- }
131
- }}
132
- defaultValue={Number.isNaN(cell.value) ? this.getTextFromCharCode(cell.text) : format.format(cell.value)}
133
- onChange={(e) =>
134
- onCellChanged(this.getCompatibleCell({ ...cell, value: parseLocaleNumber(e.currentTarget.value) }), false)
135
- }
136
- onBlur={(e) => {
137
- onCellChanged(
138
- this.getCompatibleCell({ ...cell, value: parseLocaleNumber(e.currentTarget.value) }),
139
- !this.wasEscKeyPressed,
140
- );
141
- this.wasEscKeyPressed = false;
142
- }}
143
- onKeyDown={(e) => {
144
- if (
145
- inNumericKey(e.keyCode) ||
146
- isNavigationKey(e.keyCode) ||
147
- isAllowedOnNumberTypingKey(e.keyCode) ||
148
- ((e.ctrlKey || e.metaKey) && e.keyCode === keyCodes.KEY_A)
149
- )
150
- e.stopPropagation();
151
- if (
152
- !inNumericKey(e.keyCode) &&
153
- !isNavigationKey(e.keyCode) &&
154
- !isCharAllowedOnNumberInput(getCharFromKey(e.key))
155
- )
156
- e.preventDefault();
157
- if (e.keyCode === keyCodes.ESCAPE) this.wasEscKeyPressed = true;
158
- }}
159
- onCopy={(e) => e.stopPropagation()}
160
- onCut={(e) => e.stopPropagation()}
161
- onPaste={(e) => e.stopPropagation()}
162
- onPointerDown={(e) => e.stopPropagation()}
163
- />
164
- );
165
- }
166
- }
167
-
168
- export const NumberTemplate = new NumberCellTemplate();
@@ -1,123 +0,0 @@
1
- import {
2
- Cell,
3
- CellTemplate,
4
- Compatible,
5
- getCellProperty,
6
- getCharFromKey,
7
- isAlphaNumericKey,
8
- isFunctionKey,
9
- keyCodes,
10
- Uncertain,
11
- UncertainCompatible,
12
- } from "@silevis/reactgrid";
13
- import * as React from "react";
14
-
15
- export interface TextCell extends Cell {
16
- type: "Input";
17
- text: string;
18
- placeholder?: string;
19
- validator?: (text: string) => boolean;
20
- renderer?: (text: string) => React.ReactNode;
21
- errorMessage?: string;
22
- }
23
-
24
- export class TextCellTemplate implements CellTemplate<TextCell> {
25
- private wasEscKeyPressed = false;
26
-
27
- getCompatibleCell(uncertainCell: Uncertain<TextCell>): Compatible<TextCell> {
28
- const text = getCellProperty(uncertainCell, "text", "string");
29
- let placeholder: string | undefined;
30
- try {
31
- placeholder = getCellProperty(uncertainCell, "placeholder", "string");
32
- } catch {
33
- placeholder = "";
34
- }
35
- const value = parseFloat(text); // TODO more advanced parsing for all text based cells
36
- return { ...uncertainCell, text, value, placeholder };
37
- }
38
-
39
- update(cell: Compatible<TextCell>, cellToMerge: UncertainCompatible<TextCell>): Compatible<TextCell> {
40
- return this.getCompatibleCell({ ...cell, text: cellToMerge.text, placeholder: cellToMerge.placeholder });
41
- }
42
-
43
- handleKeyDown(
44
- cell: Compatible<TextCell>,
45
- keyCode: number,
46
- ctrl: boolean,
47
- shift: boolean,
48
- alt: boolean,
49
- key: string,
50
- capsLock: boolean,
51
- ): { cell: Compatible<TextCell>; enableEditMode: boolean } {
52
- if (isFunctionKey(keyCode)) {
53
- if (keyCode === keyCodes.F2) return { cell, enableEditMode: true };
54
- return { cell, enableEditMode: false };
55
- }
56
-
57
- const char = getCharFromKey(key, shift, capsLock);
58
-
59
- if (!ctrl && !alt && isAlphaNumericKey(keyCode) && !(shift && keyCode === keyCodes.SPACE))
60
- return { cell: this.getCompatibleCell({ ...cell, text: char }), enableEditMode: true };
61
- return {
62
- cell,
63
- enableEditMode: keyCode === keyCodes.POINTER || keyCode === keyCodes.ENTER,
64
- };
65
- }
66
-
67
- handleCompositionEnd(
68
- cell: Compatible<TextCell>,
69
- eventData: any,
70
- ): { cell: Compatible<TextCell>; enableEditMode: boolean } {
71
- return { cell: { ...cell, text: eventData }, enableEditMode: true };
72
- }
73
-
74
- getClassName(cell: Compatible<TextCell>, isInEditMode: boolean): string {
75
- const isValid = cell.validator ? cell.validator(cell.text) : true;
76
- const className = cell.className ? cell.className : "";
77
- return `${isValid ? "valid" : "rg-invalid"} ${
78
- cell.placeholder && cell.text === "" ? "placeholder" : ""
79
- } ${className}`;
80
- }
81
-
82
- render(
83
- cell: Compatible<TextCell>,
84
- isInEditMode: boolean,
85
- onCellChanged: (cell: Compatible<TextCell>, commit: boolean) => void,
86
- ): React.ReactNode {
87
- if (!isInEditMode) {
88
- const isValid = cell.validator ? cell.validator(cell.text) : true;
89
- const cellText = cell.text || cell.placeholder || "";
90
- const textToDisplay = !isValid && cell.errorMessage ? cell.errorMessage : cellText;
91
- return cell.renderer ? cell.renderer(textToDisplay) : textToDisplay;
92
- }
93
-
94
- return (
95
- <input
96
- className="rg-input"
97
- ref={(input) => {
98
- if (input) {
99
- input.focus();
100
- // input.setSelectionRange(input.value.length, input.value.length);
101
- }
102
- }}
103
- defaultValue={cell.text}
104
- onChange={(e) => onCellChanged(this.getCompatibleCell({ ...cell, text: e.currentTarget.value }), false)}
105
- onBlur={(e) => {
106
- onCellChanged(this.getCompatibleCell({ ...cell, text: e.currentTarget.value }), !this.wasEscKeyPressed);
107
- this.wasEscKeyPressed = false;
108
- }}
109
- onCopy={(e) => e.stopPropagation()}
110
- onCut={(e) => e.stopPropagation()}
111
- onPaste={(e) => e.stopPropagation()}
112
- onPointerDown={(e) => e.stopPropagation()}
113
- placeholder={cell.placeholder}
114
- onKeyDown={(e) => {
115
- // if (isAlphaNumericKey(e.keyCode) || isNavigationKey(e.keyCode)) e.stopPropagation();
116
- // if (e.keyCode === keyCodes.ESCAPE) this.wasEscKeyPressed = true;
117
- }}
118
- />
119
- );
120
- }
121
- }
122
-
123
- export const TextTemplate = new TextCellTemplate();