@hzab/edit-table 0.1.1 → 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,11 @@
1
+ # @hzab/edit-table@0.1.3
2
+
3
+ fix: 编辑数据优化
4
+
5
+ # @hzab/edit-table@0.1.2
6
+
7
+ fix: checkbox 选择子项时拿到对应行的数据
8
+
1
9
  # @hzab/edit-table@0.1.1
2
10
 
3
11
  fix: 无数据时取消聚焦框
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/edit-table",
3
- "version": "0.1.1",
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 || {}),
@@ -206,13 +220,17 @@ export default function CellEditTable(props: CellEditTableProps) {
206
220
  }),
207
221
  };
208
222
  });
209
- if (firstRender.current) {
210
- initRowsRef.current = [...headerList, ...tempDataSource];
211
- firstRender.current = false;
212
- }
213
- setRows([...headerList, ...tempDataSource]);
223
+ const mergerList = [...headerList, ...tempDataSource];
224
+ setRows(mergerList);
214
225
  }, [newDataSource, tableColumns, headerList]);
215
-
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]);
216
234
  // 单元格编辑回调
217
235
  const handleCellsChanged = async (changes) => {
218
236
  const batchQuery: any = [];
@@ -225,11 +243,10 @@ export default function CellEditTable(props: CellEditTableProps) {
225
243
 
226
244
  setRows((prevRows) => {
227
245
  changes.forEach(async (change) => {
228
- // const hasDiff = JSON.stringify( change?.newCell?.text) != JSON.stringify(change.previousCell?.text);
229
- // if (hasDiff) return;
230
246
  const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
231
247
  const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
232
248
  prevRows[changeRowIdx].cells[changeColumnIdx] = { ...change.newCell };
249
+ currentCell.current = { ...change.newCell };
233
250
  batchQuery.push(handleArrayToObj(prevRows[changeRowIdx]));
234
251
  if (formulaConfig) {
235
252
  for (let key in formulaConfig) {
@@ -248,18 +265,15 @@ export default function CellEditTable(props: CellEditTableProps) {
248
265
  prevRows[changeRowIdx].cells[resultColumnIdx] = newCell;
249
266
  }
250
267
  }
251
-
252
- if (change?.newCell?.commit == "submit" && onEditSubmit && !isBatchSubmit) {
268
+ if (onEditSubmit && !isBatchSubmit) {
253
269
  try {
254
270
  await onEditSubmit(batchQuery);
255
271
  } catch (err) {
256
272
  getList && getList();
257
273
  }
258
274
  }
259
-
260
275
  onChange && onChange(prevRows[changeRowIdx]);
261
276
  });
262
- initRowsRef.current = prevRows;
263
277
  return [...prevRows];
264
278
  });
265
279
 
@@ -271,6 +285,7 @@ export default function CellEditTable(props: CellEditTableProps) {
271
285
  }
272
286
  }
273
287
  };
288
+
274
289
  /** 自定义组件注册 */
275
290
  const baseTemplate = useMemo(() => {
276
291
  const mergeTemplate: any = { ...(antdComponents || {}), ...(customComponents || {}), ...childAntdComponent };
@@ -285,50 +300,64 @@ export default function CellEditTable(props: CellEditTableProps) {
285
300
  }, [antdComponents, customComponents]);
286
301
 
287
302
  const ActionDom = (props) => {
303
+ const rowData = rows?.find((el) => el?.rowId == props?.cell?.rowId);
288
304
  return (
289
305
  <div onPointerDown={(e) => e.stopPropagation()}>
290
- {Slots?.tableActionsSlot && <Slots.tableActionsSlot {...props?.cell} />}
291
- {Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...props?.cell} />}
292
- {Slots?.actionCenterSlot && <Slots.actionCenterSlot {...props?.cell} />}
293
- {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} />}
294
310
  </div>
295
311
  );
296
312
  };
297
-
313
+ function flattenDataSource(arr: any) {
314
+ const result: any = [];
315
+ function loop(list: any) {
316
+ for (const item of list) {
317
+ result.push(item);
318
+ if (item.children && item.children.length) {
319
+ loop(item.children);
320
+ }
321
+ }
322
+ }
323
+ loop(arr);
324
+ return result;
325
+ }
298
326
  const CheckBoxDom = (props) => {
299
327
  const { record, rowId } = props?.cell;
328
+ const newDataSource = flattenDataSource(dataSource);
300
329
  return (
301
330
  <Checkbox
302
331
  {...props?.componentProps}
303
332
  checked={
304
333
  rowSelection?.selectedRowKeys?.includes((record && record[rowKey]) || "") ||
305
- (rowSelection?.selectedRowKeys?.length == dataSource?.length && dataSource?.length > 0)
334
+ (rowSelection?.selectedRowKeys?.length == newDataSource?.length && newDataSource?.length > 0)
306
335
  }
307
336
  indeterminate={
308
337
  !rowId &&
309
338
  rowSelection?.selectedRowKeys?.length > 0 &&
310
- rowSelection?.selectedRowKeys?.length < dataSource?.length
339
+ rowSelection?.selectedRowKeys?.length < newDataSource?.length
311
340
  }
312
341
  onChange={(e) => {
313
342
  if (e.target.checked && rowId) {
314
343
  const newSelectedRowKeys = [...rowSelection?.selectedRowKeys, record[rowKey]];
315
- const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
344
+ const rows = newDataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
316
345
 
317
346
  rowSelection?.onChange(newSelectedRowKeys, rows);
318
347
  } else if (!e.target.checked && rowId) {
319
348
  const newSelectedRowKeys = rowSelection?.selectedRowKeys?.filter((el) => el != record[rowKey]);
320
- const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
349
+ const rows = newDataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
321
350
 
322
351
  rowSelection?.onChange(
323
352
  rowSelection?.selectedRowKeys?.filter((el) => el != record[rowKey]),
324
353
  rows,
325
354
  );
326
355
  } else if (e.target.checked && !rowId) {
327
- const newSelectedRowKeys = dataSource?.map((el) => el[rowKey]);
328
- const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
356
+ const newSelectedRowKeys = newDataSource?.map((el) => el[rowKey]);
357
+ const rows = newDataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
329
358
 
330
359
  rowSelection?.onChange(
331
- dataSource?.map((el) => el[rowKey]),
360
+ newDataSource?.map((el) => el[rowKey]),
332
361
  rows,
333
362
  );
334
363
  } else if (!e.target.checked && !rowId) {
@@ -340,7 +369,8 @@ export default function CellEditTable(props: CellEditTableProps) {
340
369
  };
341
370
  useEffect(() => {
342
371
  if (rowSelection) {
343
- const rows = dataSource?.filter((el) => selectedRowKeys?.includes(el[rowKey]));
372
+ const newDataSource = flattenDataSource(dataSource);
373
+ const rows = newDataSource?.filter((el) => selectedRowKeys?.includes(el[rowKey]));
344
374
  rowSelection?.onChange(selectedRowKeys, rows);
345
375
  }
346
376
  }, [selectedRowKeys, dataSource]);
@@ -357,6 +387,19 @@ export default function CellEditTable(props: CellEditTableProps) {
357
387
  return templatesObj;
358
388
  }, [reactGridProps?.customCellTemplates]);
359
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
+
360
403
  return (
361
404
  <Spin spinning={loading}>
362
405
  <div className={`grid-container ${className}`} style={reactGridStyle}>
@@ -370,15 +413,23 @@ export default function CellEditTable(props: CellEditTableProps) {
370
413
  enableRangeSelection={true}
371
414
  horizontalStickyBreakpoint={120}
372
415
  {...reactGridProps}
416
+ highlights={reactGridProps?.highlights ?? highlights}
373
417
  onFocusLocationChanging={(cell: any) => {
374
418
  const datatypeId = cell?.columnId + cell?.rowId;
375
419
  (window as any).gridEditId = datatypeId;
376
420
  if (["_$actions", "rowSelection", "ExpandListTemplate"]?.includes(cell?.columnId)) {
377
421
  return false;
378
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
+ }
379
429
  if (reactGridProps?.onFocusLocationChanging) {
380
430
  return reactGridProps?.onFocusLocationChanging(cell, rows);
381
431
  }
432
+
382
433
  return cell;
383
434
  }}
384
435
  customCellTemplates={{
@@ -388,8 +439,8 @@ export default function CellEditTable(props: CellEditTableProps) {
388
439
  ...headerCustomTemplate,
389
440
  _$actions: WidthWrapHoc(ActionDom),
390
441
  rowSelection: WidthWrapHoc(CheckBoxDom),
391
- Input: TextTemplate,
392
- Number: NumberTemplate,
442
+ Input: WidthWrapHoc(TextTemplate),
443
+ Number: WidthWrapHoc(NumberTemplate),
393
444
  }}
394
445
  />
395
446
  {dataSource?.length <= 0 && (
@@ -401,3 +452,5 @@ export default function CellEditTable(props: CellEditTableProps) {
401
452
  </Spin>
402
453
  );
403
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();