@nokecy/qc-ui 1.1.87 → 1.1.89

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.
Files changed (98) hide show
  1. package/dist/cjs/ag-grid-plus/ag-gridPlus.js +151 -145
  2. package/dist/cjs/ag-grid-plus/context/useSchemeDataFlow.js +4 -4
  3. package/dist/cjs/ag-grid-plus/interface.d.ts +8 -0
  4. package/dist/cjs/ag-grid-plus/queryForm/index.d.ts +4 -0
  5. package/dist/cjs/ag-grid-plus/queryForm/index.js +63 -53
  6. package/dist/cjs/ag-grid-plus/queryForm/queryColumns.d.ts +8 -0
  7. package/dist/cjs/ag-grid-plus/queryForm/queryColumns.js +52 -0
  8. package/dist/cjs/ag-grid-plus/queryForm/schemaKey.d.ts +15 -8
  9. package/dist/cjs/ag-grid-plus/queryForm/schemaKey.js +64 -12
  10. package/dist/cjs/ag-grid-plus/setting/ColumnConfigForm.js +3 -0
  11. package/dist/cjs/ag-grid-plus/setting/ColumnSetting.js +69 -106
  12. package/dist/cjs/ag-grid-plus/setting/QuerySchemeManager.js +3 -1
  13. package/dist/cjs/ag-grid-plus/setting/columnGroupTypes.d.ts +2 -0
  14. package/dist/cjs/ag-grid-plus/setting/columnGroupUtils.d.ts +14 -0
  15. package/dist/cjs/ag-grid-plus/setting/columnGroupUtils.js +98 -110
  16. package/dist/cjs/ag-grid-plus/types.d.ts +6 -0
  17. package/dist/cjs/array-table/batchRemove.d.ts +36 -0
  18. package/dist/cjs/array-table/batchRemove.js +299 -0
  19. package/dist/cjs/array-table/cellFocusScope.js +44 -8
  20. package/dist/cjs/array-table/cellInteractionLock.d.ts +13 -0
  21. package/dist/cjs/array-table/cellInteractionLock.js +26 -0
  22. package/dist/cjs/array-table/hooks.d.ts +11 -6
  23. package/dist/cjs/array-table/hooks.js +138 -88
  24. package/dist/cjs/array-table/index.d.ts +8 -2
  25. package/dist/cjs/array-table/index.js +55 -32
  26. package/dist/cjs/array-table/indexColumn.d.ts +1 -1
  27. package/dist/cjs/array-table/indexColumn.js +3 -2
  28. package/dist/cjs/array-table/interface.d.ts +13 -1
  29. package/dist/cjs/array-table/preview.js +8 -0
  30. package/dist/cjs/array-table/readOnlyFormatters.d.ts +9 -1
  31. package/dist/cjs/array-table/readOnlyFormatters.js +41 -13
  32. package/dist/cjs/array-table/rowIdentity.d.ts +17 -0
  33. package/dist/cjs/array-table/rowIdentity.js +43 -0
  34. package/dist/cjs/array-table/sorting.d.ts +3 -0
  35. package/dist/cjs/array-table/sorting.js +46 -0
  36. package/dist/cjs/array-table/utils.js +146 -67
  37. package/dist/cjs/dialogSelect/index.d.ts +3 -0
  38. package/dist/cjs/dialogSelect/index.js +13 -3
  39. package/dist/cjs/editable/index.js +101 -139
  40. package/dist/cjs/index.d.ts +2 -2
  41. package/dist/cjs/index.js +14 -0
  42. package/dist/cjs/readPretty/arrayTable.js +29 -14
  43. package/dist/cjs/readPretty/formily.d.ts +1 -0
  44. package/dist/cjs/readPretty/formily.js +7 -0
  45. package/dist/cjs/readPretty/index.d.ts +1 -0
  46. package/dist/cjs/readPretty/index.js +7 -0
  47. package/dist/cjs/readPretty/schemaScope.d.ts +2 -0
  48. package/dist/cjs/readPretty/schemaScope.js +49 -0
  49. package/dist/esm/ag-grid-plus/ag-gridPlus.js +151 -145
  50. package/dist/esm/ag-grid-plus/context/useSchemeDataFlow.js +4 -4
  51. package/dist/esm/ag-grid-plus/interface.d.ts +8 -0
  52. package/dist/esm/ag-grid-plus/queryForm/index.d.ts +4 -0
  53. package/dist/esm/ag-grid-plus/queryForm/index.js +64 -55
  54. package/dist/esm/ag-grid-plus/queryForm/queryColumns.d.ts +8 -0
  55. package/dist/esm/ag-grid-plus/queryForm/queryColumns.js +46 -0
  56. package/dist/esm/ag-grid-plus/queryForm/schemaKey.d.ts +15 -8
  57. package/dist/esm/ag-grid-plus/queryForm/schemaKey.js +64 -12
  58. package/dist/esm/ag-grid-plus/setting/ColumnConfigForm.js +3 -0
  59. package/dist/esm/ag-grid-plus/setting/ColumnSetting.js +70 -107
  60. package/dist/esm/ag-grid-plus/setting/QuerySchemeManager.js +3 -1
  61. package/dist/esm/ag-grid-plus/setting/columnGroupTypes.d.ts +2 -0
  62. package/dist/esm/ag-grid-plus/setting/columnGroupUtils.d.ts +14 -0
  63. package/dist/esm/ag-grid-plus/setting/columnGroupUtils.js +95 -110
  64. package/dist/esm/ag-grid-plus/types.d.ts +6 -0
  65. package/dist/esm/array-table/batchRemove.d.ts +36 -0
  66. package/dist/esm/array-table/batchRemove.js +289 -0
  67. package/dist/esm/array-table/cellFocusScope.js +45 -9
  68. package/dist/esm/array-table/cellInteractionLock.d.ts +13 -0
  69. package/dist/esm/array-table/cellInteractionLock.js +21 -0
  70. package/dist/esm/array-table/hooks.d.ts +11 -6
  71. package/dist/esm/array-table/hooks.js +139 -89
  72. package/dist/esm/array-table/index.d.ts +8 -2
  73. package/dist/esm/array-table/index.js +55 -32
  74. package/dist/esm/array-table/indexColumn.d.ts +1 -1
  75. package/dist/esm/array-table/indexColumn.js +3 -2
  76. package/dist/esm/array-table/interface.d.ts +13 -1
  77. package/dist/esm/array-table/preview.js +8 -0
  78. package/dist/esm/array-table/readOnlyFormatters.d.ts +9 -1
  79. package/dist/esm/array-table/readOnlyFormatters.js +41 -12
  80. package/dist/esm/array-table/rowIdentity.d.ts +17 -0
  81. package/dist/esm/array-table/rowIdentity.js +37 -0
  82. package/dist/esm/array-table/sorting.d.ts +3 -0
  83. package/dist/esm/array-table/sorting.js +40 -0
  84. package/dist/esm/array-table/utils.js +147 -68
  85. package/dist/esm/dialogSelect/index.d.ts +3 -0
  86. package/dist/esm/dialogSelect/index.js +13 -3
  87. package/dist/esm/editable/index.js +102 -140
  88. package/dist/esm/index.d.ts +2 -2
  89. package/dist/esm/index.js +1 -1
  90. package/dist/esm/readPretty/arrayTable.js +30 -13
  91. package/dist/esm/readPretty/formily.d.ts +1 -0
  92. package/dist/esm/readPretty/formily.js +1 -0
  93. package/dist/esm/readPretty/index.d.ts +1 -0
  94. package/dist/esm/readPretty/index.js +1 -0
  95. package/dist/esm/readPretty/schemaScope.d.ts +2 -0
  96. package/dist/esm/readPretty/schemaScope.js +42 -0
  97. package/dist/umd/qc-ui.min.js +1 -1
  98. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useEffect, useRef } from 'react';
2
2
  // setSelectionRange 仅 textarea 与 text/search/url/tel/password 类型 input 支持;
3
3
  // checkbox/number/date 等调用会抛 InvalidStateError,故用白名单守卫。@author nokecy
4
4
  var supportsSelectionRange = function supportsSelectionRange(el) {
@@ -7,94 +7,125 @@ var supportsSelectionRange = function supportsSelectionRange(el) {
7
7
  !el.closest('.ant-picker') && (el.tagName === 'TEXTAREA' || /^(text|search|url|tel|password)$/i.test(el.type || 'text'))
8
8
  );
9
9
  };
10
+ /** 点击、键盘导航和字段挂载共用一次聚焦意图,新交互使旧任务失效。@author nokecy */
11
+ var useCellFocusRequests = function useCellFocusRequests() {
12
+ var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13
+ var generation = useRef(0);
14
+ var mounted = useRef(true);
15
+ var timers = useRef(new Set());
16
+ var pendingCell = useRef();
17
+ var cancel = useCallback(function () {
18
+ generation.current += 1;
19
+ pendingCell.current = undefined;
20
+ timers.current.forEach(clearTimeout);
21
+ timers.current.clear();
22
+ }, []);
23
+ useEffect(function () {
24
+ mounted.current = true;
25
+ if (!enabled) return;
26
+ var onFocus = function onFocus(event) {
27
+ if (!pendingCell.current) return;
28
+ var cell = pendingCell.current();
29
+ if (!(cell !== null && cell !== void 0 && cell.isConnected) || !(event.target instanceof Node) || !cell.contains(event.target)) cancel();
30
+ };
31
+ document.addEventListener('pointerdown', cancel, true);
32
+ document.addEventListener('keydown', cancel, true);
33
+ document.addEventListener('focusin', onFocus, true);
34
+ window.addEventListener('blur', cancel);
35
+ return function () {
36
+ mounted.current = false;
37
+ cancel();
38
+ document.removeEventListener('pointerdown', cancel, true);
39
+ document.removeEventListener('keydown', cancel, true);
40
+ document.removeEventListener('focusin', onFocus, true);
41
+ window.removeEventListener('blur', cancel);
42
+ };
43
+ }, [cancel, enabled]);
44
+ return useCallback(function (resolveCell) {
45
+ var isTargetCurrent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
46
+ return true;
47
+ };
48
+ cancel();
49
+ var requestGeneration = generation.current;
50
+ pendingCell.current = resolveCell;
51
+ var isCurrent = function isCurrent() {
52
+ return mounted.current && generation.current === requestGeneration && isTargetCurrent();
53
+ };
54
+ return {
55
+ isCurrent: isCurrent,
56
+ schedule: function schedule(callback, delay) {
57
+ if (!isCurrent()) return;
58
+ var timer = setTimeout(function () {
59
+ timers.current.delete(timer);
60
+ if (isCurrent()) callback();
61
+ }, delay);
62
+ timers.current.add(timer);
63
+ }
64
+ };
65
+ }, [cancel]);
66
+ };
10
67
 
11
- /**
12
- * 单元格聚焦 Hook
13
- * 处理 Editable 包装和普通输入框的聚焦逻辑
14
- */
68
+ /** 单元格聚焦;已获得焦点的控件保留用户光标,等待挂载的请求可被新操作取消。@author nokecy */
15
69
  export var useCellFocus = function useCellFocus() {
70
+ var beginFocusRequest = useCellFocusRequests();
16
71
  var focusInputInCell = useCallback(function (cell) {
17
- // 焦点已经在本格的输入框里:用户直接点进去定位光标、双击选词、按住拖选,这些留下的光标/选区都是他的。
18
- // 不能再 focus() 并把光标挪到末尾——那会把这些操作在 10ms 后全部抹掉。
19
- // 首次点开、键盘导航进来时焦点还不在本格输入框里,不受影响。@author nokecy
72
+ var _ref, _cell$querySelector;
73
+ var isTargetCurrent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
74
+ return true;
75
+ };
76
+ var row = cell.closest('.ag-row');
77
+ var rowId = row === null || row === void 0 ? void 0 : row.getAttribute('row-id');
78
+ var rowIndex = row === null || row === void 0 ? void 0 : row.getAttribute('row-index');
79
+ var colId = cell.getAttribute('col-id');
80
+ var request = beginFocusRequest(function () {
81
+ return cell;
82
+ }, function () {
83
+ return cell.isConnected && isTargetCurrent() && (row === null || row === void 0 ? void 0 : row.getAttribute('row-id')) === rowId && (row === null || row === void 0 ? void 0 : row.getAttribute('row-index')) === rowIndex && cell.getAttribute('col-id') === colId;
84
+ });
85
+ if (!request.isCurrent()) return;
86
+ var focusElement = function focusElement(element) {
87
+ if (!(element !== null && element !== void 0 && element.isConnected) || !cell.contains(element) || !request.isCurrent()) return;
88
+ if (document.activeElement === element) return;
89
+ element.focus();
90
+ // 同步设置首次进入的光标,避免延迟任务覆盖用户随后选择的文字。@author nokecy
91
+ if (document.activeElement === element && element.matches('input, textarea') && supportsSelectionRange(element)) {
92
+ var input = element;
93
+ input.setSelectionRange(input.value.length, input.value.length);
94
+ }
95
+ };
20
96
  var active = document.activeElement;
21
- if (active && cell.contains(active) && active.matches('input, textarea')) return;
22
-
23
- // 首先查找Editable包装的元素
97
+ if (active && cell.contains(active) && active.matches('input, textarea, [contenteditable="true"]')) return;
98
+ var selector = 'input:not([readonly]):not([disabled]), textarea:not([readonly]):not([disabled])';
24
99
  var editableElement = cell.querySelector('.ant-formily-editable');
25
100
  if (editableElement) {
26
- // 检查Editable是否已经处于编辑状态
27
- var isEditing = editableElement.getAttribute('data-editable-state') === 'editing';
28
- if (isEditing) {
29
- // 如果已经处于编辑状态,查找输入框并直接聚焦
30
- var existingInput = editableElement.querySelector('input:not([readonly]):not([disabled]), textarea:not([readonly]):not([disabled])');
31
- if (existingInput) {
32
- existingInput.focus();
33
- if (supportsSelectionRange(existingInput)) {
34
- var _existingInput$value;
35
- var length = ((_existingInput$value = existingInput.value) === null || _existingInput$value === void 0 ? void 0 : _existingInput$value.length) || 0;
36
- setTimeout(function () {
37
- existingInput.setSelectionRange(length, length);
38
- }, 10);
39
- }
40
- }
101
+ var focusEditor = function focusEditor() {
102
+ return focusElement(editableElement.querySelector(selector));
103
+ };
104
+ if (editableElement.dataset.editableState === 'editing') {
105
+ focusEditor();
41
106
  } else {
42
- // 如果还没有进入编辑状态,触发编辑状态
43
107
  editableElement.click();
44
- var focusEvent = new Event('focus', {
108
+ if (!request.isCurrent()) return;
109
+ editableElement.dispatchEvent(new Event('focus', {
45
110
  bubbles: true
46
- });
47
- editableElement.dispatchEvent(focusEvent);
48
-
49
- // 延迟查找输入框并设置光标
50
- setTimeout(function () {
51
- var newInput = editableElement.querySelector('input:not([readonly]):not([disabled]), textarea:not([readonly]):not([disabled])');
52
- if (newInput) {
53
- newInput.focus();
54
- if (supportsSelectionRange(newInput)) {
55
- var _newInput$value;
56
- var _length = ((_newInput$value = newInput.value) === null || _newInput$value === void 0 ? void 0 : _newInput$value.length) || 0;
57
- setTimeout(function () {
58
- newInput.setSelectionRange(_length, _length);
59
- }, 10);
60
- }
61
- }
62
- }, 150);
63
- }
64
- } else {
65
- // 如果没有Editable包装,尝试多种选择器查找输入框
66
- var inputElement = cell.querySelector('input:not([readonly]):not([disabled]), textarea:not([readonly]):not([disabled])');
67
- if (!inputElement) {
68
- // 尝试更宽泛的选择器
69
- inputElement = cell.querySelector('input, textarea');
70
- }
71
- if (!inputElement) {
72
- // 尝试查找Ant Design的输入框
73
- inputElement = cell.querySelector('.ant-input');
74
- }
75
- if (!inputElement) {
76
- // 查找任何可聚焦的元素
77
- inputElement = cell.querySelector('[contenteditable="true"], [tabindex]');
78
- }
79
- if (inputElement) {
80
- inputElement.focus();
81
-
82
- // 将光标定位到末尾
83
- if (supportsSelectionRange(inputElement)) {
84
- var _inputElement$value;
85
- var _length2 = ((_inputElement$value = inputElement.value) === null || _inputElement$value === void 0 ? void 0 : _inputElement$value.length) || 0;
86
- setTimeout(function () {
87
- inputElement.setSelectionRange(_length2, _length2);
88
- }, 10);
89
- }
90
- } else {
91
- // 尝试直接聚焦到单元格本身
92
- cell.focus();
111
+ }));
112
+ request.schedule(focusEditor, 150);
93
113
  }
114
+ return;
94
115
  }
95
- }, []);
116
+ focusElement((_ref = (_cell$querySelector = cell.querySelector(selector)) !== null && _cell$querySelector !== void 0 ? _cell$querySelector : cell.querySelector('input:not([disabled]), textarea:not([disabled]), [contenteditable="true"], [tabindex]:not([disabled])')) !== null && _ref !== void 0 ? _ref : cell);
117
+ }, [beginFocusRequest]);
118
+ var scheduleCellFocus = useCallback(function (resolveCell, delay, isTargetCurrent) {
119
+ var request = beginFocusRequest(resolveCell, isTargetCurrent);
120
+ request.schedule(function () {
121
+ var cell = resolveCell();
122
+ if (cell !== null && cell !== void 0 && cell.isConnected) focusInputInCell(cell, isTargetCurrent);
123
+ }, delay);
124
+ }, [beginFocusRequest, focusInputInCell]);
96
125
  return {
97
- focusInputInCell: focusInputInCell
126
+ focusInputInCell: focusInputInCell,
127
+ scheduleCellFocus: scheduleCellFocus,
128
+ beginFocusRequest: beginFocusRequest
98
129
  };
99
130
  };
100
131
 
@@ -102,7 +133,9 @@ export var useCellFocus = function useCellFocus() {
102
133
  * 键盘导航 Hook
103
134
  * 处理方向键在表格单元格间的导航
104
135
  */
105
- export var useKeyboardNavigation = function useKeyboardNavigation(ref, dataSource, focusInputInCell, activateCell) {
136
+ export var useKeyboardNavigation = function useKeyboardNavigation(ref, dataSource, focusInputInCell, activateCell, beginSharedFocusRequest) {
137
+ var beginLocalFocusRequest = useCellFocusRequests(!beginSharedFocusRequest);
138
+ var beginFocusRequest = beginSharedFocusRequest !== null && beginSharedFocusRequest !== void 0 ? beginSharedFocusRequest : beginLocalFocusRequest;
106
139
  var handleCellKeyDown = useCallback(function (event) {
107
140
  var _nativeKeyDecisions$g;
108
141
  var keyEvent = event.event,
@@ -155,13 +188,31 @@ export var useKeyboardNavigation = function useKeyboardNavigation(ref, dataSourc
155
188
  // 检查目标位置是否有效
156
189
  var isValidTarget = keyEvent.key === 'ArrowLeft' || keyEvent.key === 'ArrowRight' || targetRowIndex >= 0 && targetRowIndex < dataSource.length;
157
190
  if (isValidTarget) {
191
+ var _api$getDisplayedRowA;
192
+ var targetNode = (_api$getDisplayedRowA = api.getDisplayedRowAtIndex) === null || _api$getDisplayedRowA === void 0 ? void 0 : _api$getDisplayedRowA.call(api, targetRowIndex);
193
+ var navigated = false;
194
+ var resolveTarget = function resolveTarget() {
195
+ var _ref$current;
196
+ return (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector(".ag-row[row-index=\"".concat(targetRowIndex, "\"] .ag-cell[col-id=\"").concat(targetColId, "\"]"));
197
+ };
198
+ var _isTargetCurrent = function _isTargetCurrent() {
199
+ var _ref$current2, _api$isDestroyed, _api$getDisplayedRowA2;
200
+ if (!((_ref$current2 = ref.current) !== null && _ref$current2 !== void 0 && _ref$current2.isConnected) || (_api$isDestroyed = api.isDestroyed) !== null && _api$isDestroyed !== void 0 && _api$isDestroyed.call(api)) return false;
201
+ if (targetNode && ((_api$getDisplayedRowA2 = api.getDisplayedRowAtIndex) === null || _api$getDisplayedRowA2 === void 0 ? void 0 : _api$getDisplayedRowA2.call(api, targetRowIndex)) !== targetNode) return false;
202
+ if (navigated && api.getFocusedCell) {
203
+ var _focused$column;
204
+ var focused = api.getFocusedCell();
205
+ return (focused === null || focused === void 0 ? void 0 : focused.rowIndex) === targetRowIndex && ((_focused$column = focused.column) === null || _focused$column === void 0 ? void 0 : _focused$column.getColId()) === targetColId;
206
+ }
207
+ return true;
208
+ };
209
+ var request = beginFocusRequest(resolveTarget, _isTargetCurrent);
158
210
  // 延迟执行以确保当前编辑完成
159
- setTimeout(function () {
160
- var _api$getDisplayedRowA;
211
+ request.schedule(function () {
161
212
  api.setFocusedCell(targetRowIndex, targetColId);
213
+ navigated = true;
162
214
 
163
215
  // 懒挂载:目标格若是懒列存量格,先激活挂树(refreshCells),下方 150ms 内完成挂载再聚焦 @author nokecy
164
- var targetNode = (_api$getDisplayedRowA = api.getDisplayedRowAtIndex) === null || _api$getDisplayedRowA === void 0 ? void 0 : _api$getDisplayedRowA.call(api, targetRowIndex);
165
216
  if (activateCell && targetNode) {
166
217
  activateCell(api, targetNode, targetColId);
167
218
  }
@@ -169,11 +220,10 @@ export var useKeyboardNavigation = function useKeyboardNavigation(ref, dataSourc
169
220
  // 不使用原生startEditingCell,而是手动触发编辑状态
170
221
  // v33 themeQuartz 容器不再有 .ag-theme-alpine 类;且行虚拟化下 DOM 顺序与 rowIndex 不一致,
171
222
  // 改为直接在 ref 容器内按 row-index + col-id 属性定位目标单元格 @author nokecy
172
- setTimeout(function () {
173
- var _ref$current;
174
- var targetCell = (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector(".ag-row[row-index=\"".concat(targetRowIndex, "\"] .ag-cell[col-id=\"").concat(targetColId, "\"]"));
175
- if (targetCell) {
176
- focusInputInCell(targetCell);
223
+ request.schedule(function () {
224
+ var targetCell = resolveTarget();
225
+ if (targetCell !== null && targetCell !== void 0 && targetCell.isConnected) {
226
+ focusInputInCell(targetCell, _isTargetCurrent);
177
227
  }
178
228
  }, 150);
179
229
  }, 10);
@@ -181,7 +231,7 @@ export var useKeyboardNavigation = function useKeyboardNavigation(ref, dataSourc
181
231
  keyEvent.preventDefault();
182
232
  keyEvent.stopPropagation();
183
233
  }
184
- }, [dataSource, focusInputInCell, ref, activateCell]);
234
+ }, [dataSource, focusInputInCell, ref, activateCell, beginFocusRequest]);
185
235
  return {
186
236
  handleCellKeyDown: handleCellKeyDown
187
237
  };
@@ -262,8 +312,8 @@ export var isNativeInputKey = function isNativeInputKey(event) {
262
312
  var nativeKeyDecisions = new WeakMap();
263
313
 
264
314
  /** AG-Grid 的 suppressKeyboardEvent:让 AG-Grid 不再拦截输入框自己处理的按键,并记下这次按键的判定 */
265
- export var suppressNativeInputKeys = function suppressNativeInputKeys(_ref) {
266
- var event = _ref.event;
315
+ export var suppressNativeInputKeys = function suppressNativeInputKeys(_ref2) {
316
+ var event = _ref2.event;
267
317
  var native = isNativeInputKey(event);
268
318
  nativeKeyDecisions.set(event, native);
269
319
  return native;
@@ -1,6 +1,12 @@
1
1
  import React from "react";
2
- import { AgGridReactProps } from "ag-grid-react";
3
- declare const _default: React.ForwardRefExoticComponent<Partial<AgGridReactProps<any> & {
2
+ declare const _default: React.ForwardRefExoticComponent<Partial<import("ag-grid-react").AgGridReactProps<any> & {
3
+ height?: string | number | undefined;
4
+ gridKey: string;
5
+ onRowDrag?: boolean | undefined;
6
+ disabled?: boolean | undefined;
7
+ batchRemove?: boolean | undefined;
8
+ onRemove?: ((index: number) => void) | undefined;
9
+ } & {
4
10
  children?: React.ReactNode;
5
11
  }> & React.RefAttributes<unknown>>;
6
12
  export default _default;
@@ -18,9 +18,11 @@ import { useFieldSchema } from "@formily/react";
18
18
  import { connect, mapProps, mapReadPretty } from "@formily/react";
19
19
  import uniqueId from "lodash/uniqueId";
20
20
  import { localeText } from "../ag-grid-plus/locale";
21
+ import { useArrayTableBatchRemove } from "./batchRemove";
21
22
  import { useAddition, buildArrayTableColumns, useArrayTableSources, useArrayTableActions, isColumnComponent, collectLazyDefaults, applyLazyDefaults } from "./utils";
22
23
  import { groupColumnDefs } from "./columnGroup";
23
24
  import { ARRAY_TABLE_INDEX_FIELD, hasOwnIndexColumn, makeIndexColumn } from "./indexColumn";
25
+ import { ensureArrayTableRowIds, getArrayTableRowId } from "./rowIdentity";
24
26
  import { useCellFocus, useKeyboardNavigation, useRowDrag, suppressNativeInputKeys } from "./hooks";
25
27
  import ArrayBase from "./arrayBase";
26
28
  import ArrayAddBtn from "./arrayAddBtn";
@@ -52,7 +54,7 @@ var ArrayTableInner = observer(function (props) {
52
54
  // 懒挂载激活态:存放当前挂着真控件的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读写:
53
55
  // 点击/导航进来时这里写入,必填且为空的格自己写入,焦点离开且校验通过后自己删除。@author nokecy
54
56
  var activeCellsRef = useRef(new Set());
55
- // 无 _id/id 的外部数据按对象引用分配稳定 rowId(避免 getRowId 返回 undefined)。@author nokecy
57
+ // 首次渲染先缓存无标识行的 rowId,effect 再补入行数据,供业务克隆时保留。@author nokecy
56
58
  var rowIdMapRef = useRef(new WeakMap());
57
59
  var ref = useRef();
58
60
  var field = useField();
@@ -94,10 +96,15 @@ var ArrayTableInner = observer(function (props) {
94
96
  onMoveDown = props.onMoveDown,
95
97
  onMoveUp = props.onMoveUp;
96
98
  var addition = useAddition();
99
+ var batchRemove = useArrayTableBatchRemove(field, sources, props, apiRef, function (row) {
100
+ return getArrayTableRowId(row, rowIdMapRef.current);
101
+ });
97
102
 
98
103
  // 使用自定义 hooks
99
104
  var _useCellFocus = useCellFocus(),
100
- focusInputInCell = _useCellFocus.focusInputInCell;
105
+ focusInputInCell = _useCellFocus.focusInputInCell,
106
+ scheduleCellFocus = _useCellFocus.scheduleCellFocus,
107
+ beginFocusRequest = _useCellFocus.beginFocusRequest;
101
108
  var _useRowDrag = useRowDrag(field),
102
109
  handleRowDragMove = _useRowDrag.handleRowDragMove;
103
110
 
@@ -134,7 +141,7 @@ var ArrayTableInner = observer(function (props) {
134
141
  var _buildArrayTableColum;
135
142
  // 只依赖 sources,不依赖 dataSource 和 field.value
136
143
  var cols = (_buildArrayTableColum = buildArrayTableColumns([], field, sources)) !== null && _buildArrayTableColum !== void 0 ? _buildArrayTableColum : [];
137
- // 默认在最左加序号列;schema 里已经放了 ArrayTable.Index 的不再加。@author nokecy
144
+ // 默认在业务列之前加序号列,勾选列由网格放在其左侧;schema 自带序号时不重复加。@author nokecy
138
145
  if (cols.length > 0 && !hasOwnIndexColumn(sources)) {
139
146
  cols.unshift(makeIndexColumn());
140
147
  }
@@ -155,6 +162,11 @@ var ArrayTableInner = observer(function (props) {
155
162
  if (lazyDefaults.length > 0) applyLazyDefaults(field, lazyDefaults, defaultedRowsRef.current);
156
163
  });
157
164
 
165
+ // 默认值先补齐,再持久化行身份;覆盖初始行及程序追加行,不在渲染/getRowId 中写表单。@author nokecy
166
+ useEffect(function () {
167
+ ensureArrayTableRowIds(field, rowIdMapRef.current);
168
+ });
169
+
158
170
  // 交给 AgGridReact 的列定义:按 x-component-props.headerGroup 把相邻同组的列合并成列组(多级表头)。
159
171
  // stableColumnDefs 保持扁平,懒列识别、列方案还原等内部逻辑仍用它;只有喂给 AG-Grid 的这一份带分组。@author nokecy
160
172
  var gridColumnDefs = useMemo(function () {
@@ -199,7 +211,7 @@ var ArrayTableInner = observer(function (props) {
199
211
  }, [lazyColIds]);
200
212
 
201
213
  // 键盘导航 hook 需要 activateCell(导航进懒列时先挂载再聚焦),故置于其后。@author nokecy
202
- var _useKeyboardNavigatio = useKeyboardNavigation(ref, dataSource, focusInputInCell, activateCell),
214
+ var _useKeyboardNavigatio = useKeyboardNavigation(ref, dataSource, focusInputInCell, activateCell, beginFocusRequest),
203
215
  handleCellKeyDown = _useKeyboardNavigatio.handleCellKeyDown;
204
216
 
205
217
  /**
@@ -348,13 +360,17 @@ var ArrayTableInner = observer(function (props) {
348
360
  name: key,
349
361
  onlyRenderProperties: true
350
362
  });
351
- })), /*#__PURE__*/React.createElement(Setting, {
363
+ })), /*#__PURE__*/React.createElement(Space, {
364
+ style: {
365
+ marginBottom: 5
366
+ }
367
+ }, batchRemove.toolbar, /*#__PURE__*/React.createElement(Setting, {
352
368
  gridKey: props.gridKey,
353
369
  gridApi: apiRef.current,
354
370
  gridApiState: gridApiState,
355
371
  allColDefs: stableColumnDefs,
356
372
  showQueryScheme: false
357
- })), /*#__PURE__*/React.createElement("div", {
373
+ }))), /*#__PURE__*/React.createElement("div", {
358
374
  style: {
359
375
  flex: 1,
360
376
  minHeight: 0
@@ -364,9 +380,11 @@ var ArrayTableInner = observer(function (props) {
364
380
  localeText: localeText,
365
381
  context: gridContext,
366
382
  onGridReady: function onGridReady(params) {
383
+ var _props$onGridReady;
367
384
  params.api.hidePopupMenu();
368
385
  apiRef.current = params.api;
369
386
  setGridApiState(params.api);
387
+ (_props$onGridReady = props.onGridReady) === null || _props$onGridReady === void 0 || _props$onGridReady.call(props, params);
370
388
  },
371
389
  suppressColumnVirtualisation: true,
372
390
  gridOptions: {
@@ -382,23 +400,30 @@ var ArrayTableInner = observer(function (props) {
382
400
  },
383
401
  onCellKeyDown: handleCellKeyDown,
384
402
  onCellClicked: function onCellClicked(event) {
385
- var _event$colDef;
403
+ var _event$column$getColI, _event$column, _event$colDef, _event$event;
386
404
  // 懒挂载:点击懒列存量格时先激活挂树,等挂载完成再聚焦进编辑;否则沿用原即时聚焦。@author nokecy
387
405
  var node = event.node;
388
- var colId = (_event$colDef = event.colDef) === null || _event$colDef === void 0 ? void 0 : _event$colDef.field;
406
+ var colId = (_event$column$getColI = (_event$column = event.column) === null || _event$column === void 0 ? void 0 : _event$column.getColId()) !== null && _event$column$getColI !== void 0 ? _event$column$getColI : (_event$colDef = event.colDef) === null || _event$colDef === void 0 ? void 0 : _event$colDef.field;
389
407
  var justActivated = activateCell(event.api, node, colId);
390
408
  var rowIndex = node === null || node === void 0 ? void 0 : node.rowIndex;
391
- var focus = function focus() {
392
- var _ref$current, _event$event;
409
+ var clickedCell = (_event$event = event.event) === null || _event$event === void 0 || (_event$event = _event$event.target) === null || _event$event === void 0 ? void 0 : _event$event.closest('.ag-cell');
410
+ var resolveCell = function resolveCell() {
411
+ var _ref$current;
393
412
  // 行虚拟化下 DOM 顺序与 rowIndex 不一致,按 row-index + col-id 属性精确定位刚渲染的格 @author nokecy
394
- var gridCell = justActivated && rowIndex != null ? (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector(".ag-row[row-index=\"".concat(rowIndex, "\"] .ag-cell[col-id=\"").concat(colId, "\"]")) : (_event$event = event.event) === null || _event$event === void 0 || (_event$event = _event$event.target) === null || _event$event === void 0 ? void 0 : _event$event.closest('.ag-cell');
395
- if (gridCell) focusInputInCell(gridCell);
413
+ return justActivated && rowIndex != null ? (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector(".ag-row[row-index=\"".concat(rowIndex, "\"] .ag-cell[col-id=\"").concat(colId, "\"]")) : clickedCell;
396
414
  };
397
- // 新激活需等 AgField(RecursionField/Editable) 挂载完成,给足时间;否则原 10ms。@author nokecy
398
- setTimeout(focus, justActivated ? 80 : 10);
415
+ // 点击、导航共用聚焦请求;旧格等待挂载期间用户已转移焦点或行已更新时,取消旧任务。@author nokecy
416
+ scheduleCellFocus(resolveCell, justActivated ? 80 : 10, function () {
417
+ var _ref$current2;
418
+ if (!((_ref$current2 = ref.current) !== null && _ref$current2 !== void 0 && _ref$current2.isConnected) || event.api.isDestroyed() || rowIndex == null) return false;
419
+ if (event.api.getDisplayedRowAtIndex(rowIndex) !== node) return false;
420
+ var focused = event.api.getFocusedCell();
421
+ return (focused === null || focused === void 0 ? void 0 : focused.rowIndex) === rowIndex && (focused === null || focused === void 0 ? void 0 : focused.column.getColId()) === colId;
422
+ });
399
423
  },
400
424
  onRowDragMove: handleRowDragMove,
401
425
  onRowDataUpdated: function onRowDataUpdated(e) {
426
+ var _props$onRowDataUpdat;
402
427
  // 增删行、拖动换行后行位置变了,未改动的行不会自己重算序号,统一刷新序号列 @author nokecy
403
428
  if (stableColumnDefs.some(function (c) {
404
429
  return (c === null || c === void 0 ? void 0 : c.field) === ARRAY_TABLE_INDEX_FIELD;
@@ -412,8 +437,12 @@ var ArrayTableInner = observer(function (props) {
412
437
  (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 || _apiRef$current2.ensureIndexVisible(dataSource.length - 1, "bottom");
413
438
  setIsAdd(false);
414
439
  }
440
+ (_props$onRowDataUpdat = props.onRowDataUpdated) === null || _props$onRowDataUpdat === void 0 || _props$onRowDataUpdat.call(props, e);
415
441
  },
416
442
  rowDragManaged: true,
443
+ rowSelection: batchRemove.rowSelection,
444
+ selectionColumnDef: batchRemove.selectionColumnDef,
445
+ onSelectionChanged: batchRemove.onSelectionChanged,
417
446
  suppressClipboardApi: true,
418
447
  suppressClipboardPaste: true,
419
448
  preventDefaultOnContextMenu: true,
@@ -432,32 +461,26 @@ var ArrayTableInner = observer(function (props) {
432
461
  }
433
462
  },
434
463
  getRowId: function getRowId(_ref2) {
435
- var _data$_id;
436
464
  var data = _ref2.data;
437
- var id = (_data$_id = data === null || data === void 0 ? void 0 : data._id) !== null && _data$_id !== void 0 ? _data$_id : data === null || data === void 0 ? void 0 : data.id;
438
- if (id != null) return String(id);
439
- // 既无 _id 也无 id 的外部数据:按对象引用从 WeakMap 取/分配稳定 id。
440
- // 不能用 uniqueId 直返(每次调用都变会让 AG-Grid 行身份错乱),也不能改写 data(触发 Formily 反应)。@author nokecy
441
- var gen = rowIdMapRef.current.get(data);
442
- if (!gen) {
443
- gen = uniqueId('row_');
444
- rowIdMapRef.current.set(data, gen);
445
- }
446
- return gen;
465
+ return getArrayTableRowId(data, rowIdMapRef.current);
447
466
  }
448
467
  // rowDragEntireRow={true}
449
468
  // onRowDragEnd={(event) => {
450
469
  // const { overIndex, node } = event;
451
470
  // field.move(node.rowIndex!, overIndex);
452
471
  // }}
453
- // onSortChanged={(event) => {
454
- // const columnStates = event.columnApi.getColumnState();
455
- // if (columnStates.filter((x) => x.sort).length > 0) {
456
- // const columnState = columnStates.filter((x) => x.sort)[0];
457
- // field.value = sortBy(field.value, columnState.colId);
458
- // }
459
- // }}
460
472
  ,
473
+ onSortChanged: function onSortChanged(event) {
474
+ var _props$onSortChanged;
475
+ // 排序只调整显示顺序;表单数组及行身份保持不变,序号按新的显示位置刷新。@author nokecy
476
+ if (stableColumnDefs.some(function (c) {
477
+ return (c === null || c === void 0 ? void 0 : c.field) === ARRAY_TABLE_INDEX_FIELD;
478
+ })) event.api.refreshCells({
479
+ columns: [ARRAY_TABLE_INDEX_FIELD],
480
+ force: true
481
+ });
482
+ (_props$onSortChanged = props.onSortChanged) === null || _props$onSortChanged === void 0 || _props$onSortChanged.call(props, event);
483
+ },
461
484
  rowHeight: ARRAY_TABLE_ROW_HEIGHT,
462
485
  pagination: false,
463
486
  columnDefs: gridColumnDefs,
@@ -11,7 +11,7 @@ export declare const hasOwnIndexColumn: (sources: Array<{
11
11
  }>) => boolean;
12
12
  /**
13
13
  * 默认序号列:按行在表格里的位置编号(1 起),不依赖业务行号或主键,与列表页的序号一致。
14
- * 固定在最左、不可隐藏/拖动/排序;合计等固定行不编号。
14
+ * 固定在左侧、位于勾选列之后,不可隐藏/拖动/排序;合计等固定行不编号。
15
15
  * 增删行、拖动换行后由 ArrayTable 在 onRowDataUpdated 里刷新本列,见 index.tsx。@author nokecy
16
16
  */
17
17
  export declare const makeIndexColumn: () => ColDef;
@@ -25,7 +25,7 @@ export var hasOwnIndexColumn = function hasOwnIndexColumn(sources) {
25
25
 
26
26
  /**
27
27
  * 默认序号列:按行在表格里的位置编号(1 起),不依赖业务行号或主键,与列表页的序号一致。
28
- * 固定在最左、不可隐藏/拖动/排序;合计等固定行不编号。
28
+ * 固定在左侧、位于勾选列之后,不可隐藏/拖动/排序;合计等固定行不编号。
29
29
  * 增删行、拖动换行后由 ArrayTable 在 onRowDataUpdated 里刷新本列,见 index.tsx。@author nokecy
30
30
  */
31
31
  export var makeIndexColumn = function makeIndexColumn() {
@@ -34,7 +34,8 @@ export var makeIndexColumn = function makeIndexColumn() {
34
34
  headerName: '序号',
35
35
  width: 60,
36
36
  pinned: 'left',
37
- lockPosition: 'left',
37
+ // 勾选列拥有最左位置;序号保留固定和禁止拖动,避免恢复旧方案时抢到勾选列之前。@author nokecy
38
+ lockPosition: false,
38
39
  lockPinned: true,
39
40
  lockVisible: true,
40
41
  suppressMovable: true,
@@ -23,13 +23,25 @@ interface ArrayTableColumnProps {
23
23
  defaultHide?: boolean;
24
24
  }
25
25
  interface ObservableColumnSource {
26
+ /** 当前 SchemaField 的组件表,用于读取业务组件的展示规则。@author nokecy */
27
+ components?: Record<string, any>;
26
28
  field: GeneralField;
27
29
  columnProps: ArrayTableColumnProps & Record<string, any>;
28
30
  schema: Schema;
29
31
  display: FieldDisplayTypes;
30
32
  name: string;
31
33
  }
32
- type ComposedArrayTable = React.FC<React.PropsWithChildren<AgGridReactProps>> & ArrayBaseMixins & {
34
+ /** 公共明细表格的业务属性。@author nokecy */
35
+ export type ArrayTableProps = AgGridReactProps & {
36
+ height?: number | string;
37
+ gridKey: string;
38
+ onRowDrag?: boolean;
39
+ disabled?: boolean;
40
+ /** 默认对配置标准删除入口的可编辑明细开放批量删除;false 可显式关闭。 */
41
+ batchRemove?: boolean;
42
+ onRemove?: (index: number) => void;
43
+ };
44
+ type ComposedArrayTable = React.FC<React.PropsWithChildren<ArrayTableProps>> & ArrayBaseMixins & {
33
45
  Column?: any;
34
46
  };
35
47
  export { ComposedArrayTable, ObservableColumnSource, ArrayTableColumnProps };
@@ -36,6 +36,14 @@ var ArrayTable = function ArrayTable(props) {
36
36
  theme: arrayTablePreviewTheme,
37
37
  rowHeight: ARRAY_TABLE_ROW_HEIGHT,
38
38
  pagination: false,
39
+ onSortChanged: function onSortChanged(event) {
40
+ var _props$onSortChanged;
41
+ if (showDefaultIndex) event.api.refreshCells({
42
+ columns: [ARRAY_TABLE_INDEX_FIELD],
43
+ force: true
44
+ });
45
+ (_props$onSortChanged = props.onSortChanged) === null || _props$onSortChanged === void 0 || _props$onSortChanged.call(props, event);
46
+ },
39
47
  onRowDataUpdated: function onRowDataUpdated(event) {
40
48
  // 阅读态明细替换或重排后刷新序号,不向表单写入编号。@author nokecy
41
49
  if (showDefaultIndex) {
@@ -1,4 +1,12 @@
1
1
  import type { ReactNode } from 'react';
2
+ /** 选择器的展示字段;只影响回显,不改变 labelInValue 的提交值。@author nokecy */
3
+ export type SelectDisplayField = 'value' | 'label';
4
+ /** 从选择值提取展示文字;编码展示不能因描述为空或过期而改变。@author nokecy */
5
+ export declare const formatSelectDisplay: (value: any, displayField?: SelectDisplayField) => string;
6
+ /** 给选择组件声明与其回显一致的表格格式化器,组件别名和不同页面各自生效。@author nokecy */
7
+ export declare const withReadOnlyFormatter: <T>(component: T, formatter: ReadOnlyFormatter) => T & {
8
+ readOnlyFormatter: ReadOnlyFormatter;
9
+ };
2
10
  export type ReadOnlyFormatterCtx = {
3
11
  /** 列的 x-component-props(headerName/width/options/format 等) */
4
12
  columnProps: any;
@@ -49,4 +57,4 @@ export declare const getReadOnlyRenderer: (component: string | undefined) => Rea
49
57
  /**
50
58
  * 取某组件类型的只读格式化器。命中返回函数,未命中返回 null(调用方回退 AgField)。
51
59
  */
52
- export declare const getReadOnlyFormatter: (component: string | undefined) => ReadOnlyFormatter | null;
60
+ export declare const getReadOnlyFormatter: (component: string | undefined, components?: Record<string, any>) => ReadOnlyFormatter | null;