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

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.1",
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"
@@ -1,7 +1,7 @@
1
1
  import { useMemo } from "react";
2
2
  import { getVal } from "@hzab/list-render/src/common/utils";
3
3
  export const GetBaseTemplate = (props) => {
4
- const { pattern, cell, componentProps, Com } = props;
4
+ const { pattern, cell, componentProps, Com, cellRender } = props;
5
5
 
6
6
  const echoValue = useMemo(() => {
7
7
  if (!cell?.getField) {
@@ -12,6 +12,11 @@ export const GetBaseTemplate = (props) => {
12
12
  { [cell?.getField && cell?.getField().name]: cell.text },
13
13
  );
14
14
  }, [cell]);
15
+ if (cellRender && cellRender(props)) {
16
+ const Slot = cellRender;
17
+ return <Slot {...props}></Slot>;
18
+ }
19
+
15
20
  return (
16
21
  <div className="base-hoc">
17
22
  {pattern == "readPretty" ? (
@@ -0,0 +1,5 @@
1
+ .number-cell-template {
2
+ .rg-input {
3
+ border: none;
4
+ }
5
+ }
@@ -0,0 +1,16 @@
1
+ import { Input } from "antd";
2
+ import { GetBaseTemplate } from "../GetBaseTemplate";
3
+ import "./index.less";
4
+ const NumberCellTemplate = (props: any) => {
5
+ return (
6
+ <GetBaseTemplate
7
+ {...props}
8
+ componentProps={{
9
+ ...props?.componentProps,
10
+ type: "number",
11
+ }}
12
+ Com={Input}
13
+ ></GetBaseTemplate>
14
+ );
15
+ };
16
+ export default NumberCellTemplate;
@@ -0,0 +1,5 @@
1
+ .text-cell-template {
2
+ .rg-input {
3
+ border: none;
4
+ }
5
+ }
@@ -0,0 +1,8 @@
1
+ import { Input } from "antd";
2
+ import { GetBaseTemplate } from "../GetBaseTemplate";
3
+ import "./index.less";
4
+
5
+ const TextCellTemplate = (props: any) => {
6
+ return <GetBaseTemplate {...props} Com={Input}></GetBaseTemplate>;
7
+ };
8
+ 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;
@@ -34,45 +33,25 @@ interface DIProps {
34
33
  isInEditMode?: boolean;
35
34
  }
36
35
 
37
- export const WidthWrapHoc = (WrapComponent: any) => {
36
+ export const WidthWrapHoc = (WrapComponent: any, props = {}) => {
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,112 @@ 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
+ {...props}
200
+ onPointerDown={(e) => e.stopPropagation()}
154
201
  componentProps={{
155
202
  style: { width: "100%", display: "flex", alignItems: "center" },
156
203
  value: comValue,
157
204
  options: cell.values,
158
205
  onChange: (e) => {
159
- cell["commit"] = "change";
160
- const targetValue = e?.target?.value ?? e;
206
+ let targetValue = e?.target?.value ?? e;
161
207
  setComValue(targetValue);
162
208
  comValueInit.current = targetValue;
163
-
164
- onCellChanged(
165
- this.getCompatibleCell({
166
- ...cell,
167
- text: targetValue,
168
- selectedValue: targetValue,
169
- }),
170
- true,
171
- );
209
+ onValidator(targetValue);
172
210
  },
173
211
  onBlur: (e) => {
174
- cell["commit"] = "submit";
175
- if (isValidator) {
176
- cell["commit"] = "verify";
212
+ onBlurSubmit();
213
+ if (!isTriggerBlur?.includes(cell?.type)) {
214
+ setEdit(false);
177
215
  }
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
216
  },
189
217
 
190
218
  onPointerDown: (e) => {
@@ -193,9 +221,10 @@ export const WidthWrapHoc = (WrapComponent: any) => {
193
221
  onKeyDown: (e) => {
194
222
  if (isAlphaNumericKey(e.keyCode) || isNavigationKey(e.keyCode)) e.stopPropagation();
195
223
  },
224
+ ...dataPickerProps,
196
225
  ...((cell?.getField && cell?.getField()["x-component-props"]) || {}),
197
226
  }}
198
- pattern={isOpen ? "" : "readPretty"}
227
+ pattern={edit ? "" : "readPretty"}
199
228
  cell={cell}
200
229
  ></WrapComponent>
201
230
  {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
@@ -4,6 +4,14 @@
4
4
  max-height: 100vh;
5
5
  padding-bottom: 16px;
6
6
 
7
+ .ant-input-affix-wrapper-focused,
8
+ .ant-input-affix-wrapper:focus {
9
+ box-shadow: none;
10
+ }
11
+
12
+ .rg-input {
13
+ border: none;
14
+ }
7
15
 
8
16
 
9
17
 
@@ -18,6 +26,28 @@
18
26
  display: flex;
19
27
  align-items: center;
20
28
  justify-content: center;
29
+ z-index: 2;
30
+ }
31
+
32
+ // .rg-header-cell {
33
+ // z-index: 99;
34
+ // }
35
+
36
+ .rg-partial-area {
37
+ z-index: 3;
38
+
39
+ }
40
+
41
+ .rg-cell {
42
+ padding: 0px !important;
43
+ }
44
+
45
+ .rg-cell-focus {
46
+ z-index: 2;
47
+ }
48
+
49
+ .rg-fill-handle {
50
+ z-index: 2;
21
51
  }
22
52
  }
23
53
 
@@ -27,6 +57,13 @@
27
57
  display: flex;
28
58
  align-items: center;
29
59
  justify-content: center;
60
+ z-index: 1;
61
+ padding: 4px;
62
+ }
63
+
64
+ .base-hoc-edit {
65
+ background-color: #fff;
66
+
30
67
  }
31
68
 
32
69
  .validator-icon {
@@ -46,4 +83,22 @@
46
83
  display: flex;
47
84
  align-items: center;
48
85
  justify-content: center;
86
+ }
87
+
88
+ .rg-pane-left,
89
+ .rg-pane-right {
90
+ z-index: 10 !important;
91
+ }
92
+
93
+ .custom-highlights-row {
94
+ background: #E9F8FF;
95
+ }
96
+
97
+ .rg-pane-top {
98
+ z-index: 9 !important;
99
+ }
100
+
101
+ .shadow-top-left-corner,
102
+ .shadow-top-right-corner {
103
+ z-index: 11 !important;
49
104
  }
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,24 @@ 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
+ const highlightsRowIdRef = useRef();
69
+
68
70
  const gridRef = useRef<any>();
71
+ const currentCell = useRef<any>();
69
72
 
73
+ useImperativeHandle(ref, () => {
74
+ return {
75
+ rows,
76
+ };
77
+ });
70
78
  /** 递归处理子节点数据 */
71
79
  const handleDataSource = (arr, level = 0) => {
72
80
  arr?.forEach((el) => {
@@ -82,6 +90,14 @@ export default function CellEditTable(props: CellEditTableProps) {
82
90
  setNewDataSource(dataSource);
83
91
  }, [dataSource]);
84
92
 
93
+ /** 判断当前选中行是否还存在 */
94
+ useEffect(() => {
95
+ const validRowIds = rows.map((r) => r.rowId);
96
+ if (!validRowIds?.includes(highlightsRowIdRef.current)) {
97
+ setHighlights([]);
98
+ }
99
+ }, [rows]);
100
+
85
101
  /**将数组转为对象 */
86
102
  const handleArrayToObj = (item) => {
87
103
  const record = item?.cells?.[0]?.record;
@@ -92,6 +108,9 @@ export default function CellEditTable(props: CellEditTableProps) {
92
108
  item?.cells?.forEach((el) => {
93
109
  if (el?.columnId && !fieldList?.includes(el?.columnId)) {
94
110
  obj[el?.columnId] = el?.text || el?.value || el?.selectedValue || "";
111
+ if (el?.type == "Number") {
112
+ obj[el?.columnId] = el?.value;
113
+ }
95
114
  }
96
115
  });
97
116
 
@@ -108,6 +127,7 @@ export default function CellEditTable(props: CellEditTableProps) {
108
127
  type: "ExpandListTemplate",
109
128
  width: 14,
110
129
  key: "ExpandListTemplate",
130
+ nonEditable: true,
111
131
  style: rowSelection?.style || {
112
132
  background: "rgba(128, 128, 128, 0.1)",
113
133
  },
@@ -116,6 +136,7 @@ export default function CellEditTable(props: CellEditTableProps) {
116
136
  if (rowSelection) {
117
137
  columns.unshift({
118
138
  type: "rowSelection",
139
+ nonEditable: true,
119
140
  width: rowSelection?.width || 30,
120
141
  key: "rowSelection",
121
142
  style: rowSelection?.style || {
@@ -207,13 +228,16 @@ export default function CellEditTable(props: CellEditTableProps) {
207
228
  };
208
229
  });
209
230
  const mergerList = [...headerList, ...tempDataSource];
210
- if (firstRender.current && tempDataSource?.length > 0) {
211
- initRowsRef.current = mergerList;
212
- firstRender.current = false;
213
- }
214
231
  setRows(mergerList);
215
232
  }, [newDataSource, tableColumns, headerList]);
216
-
233
+ useEffect(() => {
234
+ if (loading) {
235
+ const item = rows?.find((el) => el?.rowId == currentCell.current?.rowId);
236
+ let newItem = item?.cells?.find((it) => it?.columnId == currentCell.current?.columnId);
237
+ newItem = { ...currentCell.current };
238
+ initRowsRef.current = rows;
239
+ }
240
+ }, [loading, rows]);
217
241
  // 单元格编辑回调
218
242
  const handleCellsChanged = async (changes) => {
219
243
  const batchQuery: any = [];
@@ -226,11 +250,10 @@ export default function CellEditTable(props: CellEditTableProps) {
226
250
 
227
251
  setRows((prevRows) => {
228
252
  changes.forEach(async (change) => {
229
- // const hasDiff = JSON.stringify( change?.newCell?.text) != JSON.stringify(change.previousCell?.text);
230
- // if (hasDiff) return;
231
253
  const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
232
254
  const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
233
255
  prevRows[changeRowIdx].cells[changeColumnIdx] = { ...change.newCell };
256
+ currentCell.current = { ...change.newCell };
234
257
  batchQuery.push(handleArrayToObj(prevRows[changeRowIdx]));
235
258
  if (formulaConfig) {
236
259
  for (let key in formulaConfig) {
@@ -249,18 +272,15 @@ export default function CellEditTable(props: CellEditTableProps) {
249
272
  prevRows[changeRowIdx].cells[resultColumnIdx] = newCell;
250
273
  }
251
274
  }
252
-
253
- if (change?.newCell?.commit == "submit" && onEditSubmit && !isBatchSubmit) {
275
+ if (onEditSubmit && !isBatchSubmit) {
254
276
  try {
255
277
  await onEditSubmit(batchQuery);
256
278
  } catch (err) {
257
279
  getList && getList();
258
280
  }
259
281
  }
260
-
261
282
  onChange && onChange(prevRows[changeRowIdx]);
262
283
  });
263
- initRowsRef.current = prevRows;
264
284
  return [...prevRows];
265
285
  });
266
286
 
@@ -272,13 +292,14 @@ export default function CellEditTable(props: CellEditTableProps) {
272
292
  }
273
293
  }
274
294
  };
295
+
275
296
  /** 自定义组件注册 */
276
297
  const baseTemplate = useMemo(() => {
277
298
  const mergeTemplate: any = { ...(antdComponents || {}), ...(customComponents || {}), ...childAntdComponent };
278
299
  const newMergeTemplate: any = {};
279
300
  for (let key in mergeTemplate) {
280
- const NewCom = (props) => {
281
- return <GetBaseTemplate {...props} Com={mergeTemplate[key]} />;
301
+ const NewCom = (newComProps) => {
302
+ return <GetBaseTemplate {...newComProps} {...props} Com={mergeTemplate[key]} />;
282
303
  };
283
304
  newMergeTemplate[key] = WidthWrapHoc(NewCom);
284
305
  }
@@ -286,12 +307,13 @@ export default function CellEditTable(props: CellEditTableProps) {
286
307
  }, [antdComponents, customComponents]);
287
308
 
288
309
  const ActionDom = (props) => {
310
+ const rowData = rows?.find((el) => el?.rowId == props?.cell?.rowId);
289
311
  return (
290
312
  <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} />}
313
+ {Slots?.tableActionsSlot && <Slots.tableActionsSlot {...props?.cell} rowData={rowData} />}
314
+ {Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...props?.cell} rowData={rowData} />}
315
+ {Slots?.actionCenterSlot && <Slots.actionCenterSlot {...props?.cell} rowData={rowData} />}
316
+ {Slots?.actionSuffixSlot && <Slots.actionSuffixSlot {...props?.cell} rowData={rowData} />}
295
317
  </div>
296
318
  );
297
319
  };
@@ -372,12 +394,26 @@ export default function CellEditTable(props: CellEditTableProps) {
372
394
  return templatesObj;
373
395
  }, [reactGridProps?.customCellTemplates]);
374
396
 
397
+ /** 选中高亮整行 */
398
+ const onHightsRow = (cell: any, rows: any) => {
399
+ const list = rows
400
+ ?.find((el) => el?.rowId == cell?.rowId)
401
+ ?.cells?.map((item) => ({
402
+ columnId: item?.columnId,
403
+ rowId: cell?.rowId,
404
+ className: "custom-highlights-row",
405
+ borderColor: "transparent",
406
+ }));
407
+ highlightsRowIdRef.current = cell?.rowId;
408
+ setHighlights(list);
409
+ };
410
+
375
411
  return (
376
412
  <Spin spinning={loading}>
377
413
  <div className={`grid-container ${className}`} style={reactGridStyle}>
378
414
  <ReactGrid
379
415
  ref={gridRef}
380
- rows={rows}
416
+ rows={loading ? initRowsRef.current : rows}
381
417
  key={dataSource?.length == 0 ? 0 : 1}
382
418
  columns={tableColumns}
383
419
  onCellsChanged={handleCellsChanged}
@@ -385,15 +421,23 @@ export default function CellEditTable(props: CellEditTableProps) {
385
421
  enableRangeSelection={true}
386
422
  horizontalStickyBreakpoint={120}
387
423
  {...reactGridProps}
424
+ highlights={reactGridProps?.highlights ?? highlights}
388
425
  onFocusLocationChanging={(cell: any) => {
389
426
  const datatypeId = cell?.columnId + cell?.rowId;
390
427
  (window as any).gridEditId = datatypeId;
428
+ if (typeof cell?.rowId == "string" && cell?.rowId?.indexOf("header-") > -1) {
429
+ return false;
430
+ }
431
+ if (reactGridProps?.isRowHighlights && cell?.columnId) {
432
+ onHightsRow(cell, rows);
433
+ }
391
434
  if (["_$actions", "rowSelection", "ExpandListTemplate"]?.includes(cell?.columnId)) {
392
435
  return false;
393
436
  }
394
437
  if (reactGridProps?.onFocusLocationChanging) {
395
438
  return reactGridProps?.onFocusLocationChanging(cell, rows);
396
439
  }
440
+
397
441
  return cell;
398
442
  }}
399
443
  customCellTemplates={{
@@ -403,8 +447,8 @@ export default function CellEditTable(props: CellEditTableProps) {
403
447
  ...headerCustomTemplate,
404
448
  _$actions: WidthWrapHoc(ActionDom),
405
449
  rowSelection: WidthWrapHoc(CheckBoxDom),
406
- Input: TextTemplate,
407
- Number: NumberTemplate,
450
+ // Input: WidthWrapHoc(TextTemplate,props),
451
+ Number: WidthWrapHoc(NumberTemplate, props),
408
452
  }}
409
453
  />
410
454
  {dataSource?.length <= 0 && (
@@ -416,3 +460,5 @@ export default function CellEditTable(props: CellEditTableProps) {
416
460
  </Spin>
417
461
  );
418
462
  }
463
+
464
+ 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();