@hzab/list-render 1.11.0 → 1.12.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.
@@ -17,7 +17,7 @@ import { getVal, getFieldList } from "../common/utils";
17
17
  import { handleReactions } from "../common/handleReactions";
18
18
 
19
19
  import { useEditTable, EditableCell } from "../components/Formily/FormilyEditTable";
20
- import CellEditTable from "../components/CellEditTable"
20
+ import CellEditTable from "@hzab/edit-table"
21
21
  import "./index.less";
22
22
 
23
23
  const scenario = "table-render";
@@ -99,12 +99,131 @@ const TableRender = forwardRef(function (props, tableRef) {
99
99
  topProps: props.topProps,
100
100
  });
101
101
 
102
+ /** 处理childrn */
103
+ const handleChildColumns = (childColumns) => {
104
+ const fieldSchemas = formilyRef.current?.fields;
105
+ const { Slots = {} } = props;
106
+ return childColumns?.map((field, colIndex) => {
107
+ if (!(field.inTable !== false)) return
108
+
109
+ const { name, title } = field;
110
+ const comName = field["x-component"];
111
+ let _colConf = {};
112
+ if (props.config?.colConf && props.config?.colConf[field?.name]) {
113
+ _colConf = props.config?.colConf[field?.name];
114
+ }
115
+
116
+ let colRender = undefined;
117
+ if (Slots && Slots[field?.name]) {
118
+ colRender = function (text, record, index) {
119
+ const Slot = Slots[field?.name];
120
+ const slotProps = {
121
+ text,
122
+ record,
123
+ index,
124
+ field: { ...field, ...fieldSchemas?.[field?.name] },
125
+ fieldSchema: fieldSchemas?.[field?.name],
126
+ };
127
+ return <Slot {...slotProps} />;
128
+ };
129
+ } else {
130
+ colRender = function (text, record, index, ...args) {
131
+ const { width, ellipsis, emptyValue, showTags, showPrefixNode, showMode, enumRenderProps } = _colConf || {};
132
+ const schemaDefaultValue = fieldSchemas[field?.name]?.componentProps?.emptyValue;
133
+ const defaultValue = emptyValue ?? schemaDefaultValue ?? tableEmptyValue ?? "";
134
+
135
+ let val = getVal({ ...field, ...fieldSchemas[field?.name] }, record, {
136
+ fieldSchema: fieldSchemas?.[field?.name],
137
+ });
138
+
139
+ if (val === "" || val === undefined || val === null) {
140
+ val = defaultValue;
141
+ }
142
+
143
+ const content = (
144
+ <span className="inline-block-max-w" style={{ width }} title={ellipsis?.showTitle ? val : ""}>
145
+ {val}
146
+ </span>
147
+ );
148
+ if (ellipsis === true || (ellipsis && ellipsis?.showTitle != true)) {
149
+ return (
150
+ <Tooltip className="table-cell-ellipsis" {...ellipsis} title={val}>
151
+ {content}
152
+ </Tooltip>
153
+ );
154
+ }
155
+
156
+ if (showMode || showTags || showPrefixNode || (showPrefixNode !== false && comName === "Switch")) {
157
+ // field 通过 fieldSchemas 获取最新的 field 数据
158
+ let _showMode = showMode;
159
+ if (showTags) {
160
+ _showMode = SHOW_MODE_TYPES.tags;
161
+ } else if (showPrefixNode) {
162
+ _showMode = SHOW_MODE_TYPES.prefixNode;
163
+ }
164
+
165
+ return (
166
+ <EnumRender
167
+ showMode={_showMode}
168
+ value={_.get(record, field?.name)}
169
+ field={{ ...field, ...fieldSchemas?.[field?.name] }}
170
+ {...enumRenderProps}
171
+ />
172
+ );
173
+ }
174
+
175
+ return content;
176
+ };
177
+ }
178
+
179
+
180
+ let _title = isFunction(title) ? title() : title;
181
+ if (_colConf?.title) {
182
+ _title = isFunction(_colConf?.title) ? _colConf?.title() : _colConf?.title;
183
+ }
184
+ const decoratorProps = field["x-decorator-props"] || {};
185
+ if (decoratorProps.tooltip) {
186
+ _title = (
187
+ <span className="col-title-tooltip-wrap inline-block-max-w">
188
+ {_title}
189
+ <Tooltip className="col-title-tooltip" title={decoratorProps.tooltip}>
190
+ <QuestionCircleOutlined className="col-title-tooltip-icon" />
191
+ </Tooltip>
192
+ </span>
193
+ );
194
+ }
195
+ const childSelectList = fieldSchemas?.[field?.name]?.dataSource?.map((el) => ({
196
+ ...el,
197
+ label: el?.[field?.["x-component-props"]?.["fieldNames"]?.["label"]] || el?.label,
198
+ value: el?.[field?.["x-component-props"]?.["fieldNames"]?.["value"]] || el?.value,
199
+ }))
200
+ return {
201
+ getField: () => field,
202
+ editable: true,
203
+ ..._colConf,
204
+ onCell: (record, rowIndex, ci) =>
205
+ _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[field?.name] || {}) } }, rowIndex, ci) || {},
206
+ // 函数式传入,解决 title ReactNode 传入报错问题(table 组件内部对 columns 进行 lodash.deepClone 导致 ReactNode 变成对象无法正常渲染) Uncaught TypeError: this.queryFeedbacks is not a function
207
+ title: () => _title,
208
+ key: field?.name,
209
+ dataIndex: field?.name,
210
+ type: field["x-validator"] == "number" ? "Number" : comName,
211
+ validatorList: field["x-validator"] || [],
212
+ nonEditable: editMode != "cell",
213
+ values: field?.enum || childSelectList || [],
214
+ cellProps: field.cellProps,
215
+ children: handleChildColumns(field?.children),
216
+ render: getColRender(colRender),
217
+ }
218
+
219
+ })?.filter((el) => (el))
220
+ }
102
221
  useEffect(() => {
103
222
  if (!(props.schema && props.schema.properties)) {
104
223
  return;
105
224
  }
106
225
  const fieldList = getFieldList(props.schema, [], { ...props.getFieldListOpt, formilyRef }, isTableSortXIdex);
107
- const columns = [];
226
+ let columns = [];
108
227
 
109
228
  // 序号列
110
229
  if (orderColType === "page") {
@@ -130,123 +249,7 @@ const TableRender = forwardRef(function (props, tableRef) {
130
249
  scope: props.schemaScope,
131
250
  formilyRef,
132
251
  });
133
-
134
- _fieldList.forEach((field, colIndex) => {
135
- const fieldSchemas = formilyRef.current?.fields;
136
- if (field.inTable !== false) {
137
- const { name, title } = field;
138
- const comName = field["x-component"];
139
-
140
- let _colConf = {};
141
-
142
- if (props.config?.colConf && props.config?.colConf[name]) {
143
- _colConf = props.config?.colConf[name];
144
- }
145
-
146
- let colRender = undefined;
147
- if (Slots && Slots[name]) {
148
- colRender = function (text, record, index) {
149
- const Slot = Slots[name];
150
- const slotProps = {
151
- text,
152
- record,
153
- index,
154
- field: { ...field, ...fieldSchemas?.[name] },
155
- fieldSchema: fieldSchemas?.[name],
156
- };
157
- return <Slot {...slotProps} />;
158
- };
159
- } else {
160
- colRender = function (text, record, index, ...args) {
161
- const { width, ellipsis, emptyValue, showTags, showPrefixNode, showMode, enumRenderProps } = _colConf || {};
162
- const schemaDefaultValue = fieldSchemas[name]?.componentProps?.emptyValue;
163
- const defaultValue = emptyValue ?? schemaDefaultValue ?? tableEmptyValue ?? "";
164
-
165
- let val = getVal({ ...field, ...fieldSchemas[name] }, record, {
166
- fieldSchema: fieldSchemas?.[name],
167
- });
168
-
169
- if (val === "" || val === undefined || val === null) {
170
- val = defaultValue;
171
- }
172
-
173
- const content = (
174
- <span className="inline-block-max-w" style={{ width }} title={ellipsis?.showTitle ? val : ""}>
175
- {val}
176
- </span>
177
- );
178
- if (ellipsis === true || (ellipsis && ellipsis?.showTitle != true)) {
179
- return (
180
- <Tooltip className="table-cell-ellipsis" {...ellipsis} title={val}>
181
- {content}
182
- </Tooltip>
183
- );
184
- }
185
-
186
- if (showMode || showTags || showPrefixNode || (showPrefixNode !== false && comName === "Switch")) {
187
- // field 通过 fieldSchemas 获取最新的 field 数据
188
- let _showMode = showMode;
189
- if (showTags) {
190
- _showMode = SHOW_MODE_TYPES.tags;
191
- } else if (showPrefixNode) {
192
- _showMode = SHOW_MODE_TYPES.prefixNode;
193
- }
194
-
195
- return (
196
- <EnumRender
197
- showMode={_showMode}
198
- value={_.get(record, name)}
199
- field={{ ...field, ...fieldSchemas?.[name] }}
200
- {...enumRenderProps}
201
- />
202
- );
203
- }
204
-
205
- return content;
206
- };
207
- }
208
-
209
- let _title = isFunction(title) ? title() : title;
210
- if (_colConf?.title) {
211
- _title = isFunction(_colConf?.title) ? _colConf?.title() : _colConf?.title;
212
- }
213
- const decoratorProps = field["x-decorator-props"] || {};
214
- if (decoratorProps.tooltip) {
215
- _title = (
216
- <span className="col-title-tooltip-wrap inline-block-max-w">
217
- {_title}
218
- <Tooltip className="col-title-tooltip" title={decoratorProps.tooltip}>
219
- <QuestionCircleOutlined className="col-title-tooltip-icon" />
220
- </Tooltip>
221
- </span>
222
- );
223
- }
224
- const selectList = fieldSchemas?.[name]?.dataSource?.map((el) => ({
225
- ...el,
226
- label: el?.[field?.["x-component-props"]?.["fieldNames"]?.["label"]] || el?.label,
227
- value: el?.[field?.["x-component-props"]?.["fieldNames"]?.["value"]] || el?.value,
228
- }))
229
-
230
- columns.push({
231
- // field, // HACK: 直接传入 field 在 title 传入 ReactNode,内部深克隆导致页面报错白屏。使用函数获取解决
232
- getField: () => field,
233
- editable: true,
234
- ..._colConf,
235
- onCell: (record, rowIndex, ci) =>
236
- _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex, ci) || {},
237
- // 函数式传入,解决 title ReactNode 传入报错问题(table 组件内部对 columns 进行 lodash.deepClone 导致 ReactNode 变成对象无法正常渲染) Uncaught TypeError: this.queryFeedbacks is not a function
238
- title: () => _title,
239
- key: name,
240
- dataIndex: name,
241
- type: field["x-validator"] == "number" ? "Number" : comName,
242
- nonEditable: editMode != "cell",
243
- values: field?.enum || selectList || [],
244
- cellProps: field.cellProps,
245
- children: field?.children,
246
- render: getColRender(colRender),
247
- });
248
- }
249
- });
252
+ columns = columns.concat(handleChildColumns(_fieldList))
250
253
 
251
254
  if (props.hasAction !== false) {
252
255
  const { hasEdit, hasDel, hasDelTips, hasDetail = false } = props.config || {};
@@ -439,6 +442,7 @@ const TableRender = forwardRef(function (props, tableRef) {
439
442
  isCellEditTable ? <CellEditTable
440
443
  {...tableProps}
441
444
  {...cellEditTableProps}
445
+ {...props}
442
446
  onEditSubmit={props?.onEditSubmit}
443
447
  Slots={props?.Slots}
444
448
  rowSelection={config?.rowSelection}
@@ -1,18 +0,0 @@
1
- .expandList-template {
2
- display: flex;
3
- align-items: center;
4
-
5
-
6
-
7
- .expandList-template-icon {
8
- cursor: pointer;
9
- }
10
-
11
- .expandList-template-name {
12
- margin-left: 8px;
13
- }
14
-
15
- .expandList-template-indent {
16
- margin-left: 32px;
17
- }
18
- }
@@ -1,92 +0,0 @@
1
- import { MinusCircleOutlined, MinusSquareOutlined, PlusCircleOutlined } from "@ant-design/icons";
2
- import { useEffect, useState, memo } from "react";
3
- import "./index.less";
4
- export const ExpandListTemplate = (props) => {
5
- const [expand, setExpand] = useState<boolean>(false);
6
- const { record, item } = props;
7
- const { Slots, setExpandKeys, expandKeys }: any = item;
8
- const onExpand = (isExpand) => {
9
- item?.setNewDataSource((pre) => {
10
- const tempPre = [...pre];
11
- const childrenIds = props?.record?.children?.map((el) => el?.groupId);
12
- if (isExpand) {
13
- const findIndex = pre?.findIndex((el) => el?.groupId == props?.record?.groupId);
14
- const filterList = pre?.filter((el) => childrenIds?.includes(el?.groupId)) || [];
15
- if (filterList?.length == 0) {
16
- tempPre.splice(findIndex + 1, 0, ...(props?.record?.children || []));
17
- }
18
- return [...tempPre];
19
- }
20
-
21
- return tempPre?.filter((el) => !childrenIds?.includes(el?.groupId));
22
- });
23
- };
24
- useEffect(() => {
25
- if (expandKeys?.includes(props?.record?.groupId)) {
26
- setExpand(true);
27
- onExpand(true);
28
- } else {
29
- setExpand(false);
30
- onExpand(false);
31
- }
32
-
33
- // setExpand(props?.record?.expand);
34
- }, [props?.record]);
35
-
36
- const colRender = function (text, record) {
37
- const Slot = Slots[item?.columnId];
38
- const slotProps = {
39
- text,
40
- record,
41
- // field: { ...field, ...fieldSchemas?.[name] },
42
- // fieldSchema: fieldSchemas?.[name],
43
- };
44
- return <Slot {...slotProps} />;
45
- };
46
- return (
47
- <div className="expandList-template">
48
- {props?.record?.children && (
49
- <>
50
- {!expand ? (
51
- <PlusCircleOutlined
52
- className="expandList-template-icon"
53
- onClick={() => {
54
- props.record["expand"] = !expand;
55
- setExpandKeys((preExpandKeys) => {
56
- if (preExpandKeys?.includes(props?.record?.groupId)) {
57
- return preExpandKeys?.filter((el) => el != props?.record?.groupId);
58
- }
59
- return [...preExpandKeys, props?.record?.groupId];
60
- });
61
- setExpand(!expand);
62
- onExpand(!expand);
63
- }}
64
- />
65
- ) : (
66
- <MinusCircleOutlined
67
- className="expandList-template-icon"
68
- onClick={() => {
69
- props.record["expand"] = !expand;
70
- setExpandKeys((preExpandKeys) => {
71
- if (preExpandKeys?.includes(props?.record?.groupId)) {
72
- return preExpandKeys?.filter((el) => el != props?.record?.groupId);
73
- }
74
- return [...preExpandKeys, props?.record?.groupId];
75
- });
76
- setExpand(!expand);
77
- onExpand(!expand);
78
- }}
79
- />
80
- )}
81
- </>
82
- )}
83
-
84
- <div
85
- className={`expandList-template-name ${record?.spiltTag == 0 ? "expandList-template-indent" : ""}`}
86
- style={record?.spiltTag == 1 && !record?.children ? { marginLeft: "22px" } : {}}
87
- >
88
- {Slots?.[item?.columnId] ? colRender("", record) : record[item?.columnId]}
89
- </div>
90
- </div>
91
- );
92
- };
@@ -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 "./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();