@nokecy/qc-ui 1.1.51 → 1.1.53
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/dist/cjs/array-table/cellFocusScope.d.ts +21 -0
- package/dist/cjs/array-table/cellFocusScope.js +62 -0
- package/dist/cjs/array-table/hooks.d.ts +17 -0
- package/dist/cjs/array-table/hooks.js +62 -1
- package/dist/cjs/array-table/index.js +12 -20
- package/dist/cjs/array-table/preview.js +3 -1
- package/dist/cjs/array-table/readOnlyFormatters.d.ts +0 -15
- package/dist/cjs/array-table/readOnlyFormatters.js +8 -6
- package/dist/cjs/array-table/theme.d.ts +16 -0
- package/dist/cjs/array-table/theme.js +50 -0
- package/dist/cjs/array-table/utils.d.ts +13 -6
- package/dist/cjs/array-table/utils.js +181 -33
- package/dist/esm/array-table/cellFocusScope.d.ts +21 -0
- package/dist/esm/array-table/cellFocusScope.js +54 -0
- package/dist/esm/array-table/hooks.d.ts +17 -0
- package/dist/esm/array-table/hooks.js +61 -0
- package/dist/esm/array-table/index.js +12 -20
- package/dist/esm/array-table/preview.js +3 -1
- package/dist/esm/array-table/readOnlyFormatters.d.ts +0 -15
- package/dist/esm/array-table/readOnlyFormatters.js +6 -4
- package/dist/esm/array-table/theme.d.ts +16 -0
- package/dist/esm/array-table/theme.js +44 -0
- package/dist/esm/array-table/utils.d.ts +13 -6
- package/dist/esm/array-table/utils.js +179 -33
- package/dist/umd/qc-ui.min.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
|
|
4
|
+
*
|
|
5
|
+
* 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
|
|
6
|
+
* 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
|
|
7
|
+
* 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
|
|
8
|
+
*
|
|
9
|
+
* 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
|
|
10
|
+
* blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
|
|
11
|
+
* (窗口切走时输入框依旧是 activeElement),同样不算离开。
|
|
12
|
+
*
|
|
13
|
+
* 用 display: contents,不产生盒子,不改变单元格里原有的布局。
|
|
14
|
+
*
|
|
15
|
+
* @author nokecy
|
|
16
|
+
*/
|
|
17
|
+
export type CellLeaveHandler = (isStale: () => boolean) => void;
|
|
18
|
+
export declare const CellFocusScope: ({ onLeave, children }: {
|
|
19
|
+
onLeave: CellLeaveHandler;
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.CellFocusScope = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
10
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
11
|
+
/**
|
|
12
|
+
* 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
|
|
13
|
+
*
|
|
14
|
+
* 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
|
|
15
|
+
* 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
|
|
16
|
+
* 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
|
|
17
|
+
*
|
|
18
|
+
* 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
|
|
19
|
+
* blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
|
|
20
|
+
* (窗口切走时输入框依旧是 activeElement),同样不算离开。
|
|
21
|
+
*
|
|
22
|
+
* 用 display: contents,不产生盒子,不改变单元格里原有的布局。
|
|
23
|
+
*
|
|
24
|
+
* @author nokecy
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
var CellFocusScope = exports.CellFocusScope = function CellFocusScope(_ref) {
|
|
28
|
+
var onLeave = _ref.onLeave,
|
|
29
|
+
children = _ref.children;
|
|
30
|
+
var rootRef = (0, _react.useRef)(null);
|
|
31
|
+
var timerRef = (0, _react.useRef)();
|
|
32
|
+
// 每次焦点回到格内、或卸载时递增;进行中的异步判定拿它判断自己是否已过期
|
|
33
|
+
var generationRef = (0, _react.useRef)(0);
|
|
34
|
+
var onLeaveRef = (0, _react.useRef)(onLeave);
|
|
35
|
+
onLeaveRef.current = onLeave;
|
|
36
|
+
var cancel = function cancel() {
|
|
37
|
+
generationRef.current += 1;
|
|
38
|
+
window.clearTimeout(timerRef.current);
|
|
39
|
+
};
|
|
40
|
+
(0, _react.useEffect)(function () {
|
|
41
|
+
return cancel;
|
|
42
|
+
}, []);
|
|
43
|
+
var handleBlur = function handleBlur() {
|
|
44
|
+
cancel();
|
|
45
|
+
var generation = generationRef.current;
|
|
46
|
+
timerRef.current = window.setTimeout(function () {
|
|
47
|
+
var _rootRef$current;
|
|
48
|
+
if ((_rootRef$current = rootRef.current) !== null && _rootRef$current !== void 0 && _rootRef$current.contains(document.activeElement)) return;
|
|
49
|
+
onLeaveRef.current(function () {
|
|
50
|
+
return generation !== generationRef.current;
|
|
51
|
+
});
|
|
52
|
+
}, 0);
|
|
53
|
+
};
|
|
54
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
55
|
+
ref: rootRef,
|
|
56
|
+
style: {
|
|
57
|
+
display: 'contents'
|
|
58
|
+
},
|
|
59
|
+
onFocus: cancel,
|
|
60
|
+
onBlur: handleBlur
|
|
61
|
+
}, children);
|
|
62
|
+
};
|
|
@@ -22,3 +22,20 @@ export declare const useKeyboardNavigation: (ref: RefObject<any>, dataSource: an
|
|
|
22
22
|
export declare const useRowDrag: (field: ArrayField) => {
|
|
23
23
|
handleRowDragMove: (event: any) => void;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* 这个按键该不该交给单元格里的输入框自己处理(浏览器原生行为),而不是当成表格的快捷键/单元格导航。
|
|
27
|
+
*
|
|
28
|
+
* AG-Grid 把 Ctrl+A 当成「选中所有单元格」、Home/End 当成「跳到行首/行尾单元格」并 preventDefault;
|
|
29
|
+
* 我们的 handleCellKeyDown 又把方向键一律当成「跳到相邻单元格」——输入框里全选、用方向键移动光标、
|
|
30
|
+
* Shift+方向键扩选文字就全都做不了。规则:
|
|
31
|
+
* - Ctrl/Cmd+A、Home/End、带 Shift/Ctrl/Alt/Cmd 的方向键、输入法组字期间的按键 -> 输入框自己处理;
|
|
32
|
+
* - 左右方向键:光标还能动就移动光标(有选区时先收起选区),已经在最左/最右再按才跳到相邻单元格;
|
|
33
|
+
* - 上下方向键:单行输入框没有纵向光标,照旧在行间导航;多行文本域在首行/末行才导航;
|
|
34
|
+
* - 焦点不在输入框里(在单元格本身)、只读输入框(如下拉选择)、不支持光标的输入框:照旧导航。
|
|
35
|
+
* 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
|
|
36
|
+
*/
|
|
37
|
+
export declare const isNativeInputKey: (event: KeyboardEvent) => boolean;
|
|
38
|
+
/** AG-Grid 的 suppressKeyboardEvent:让 AG-Grid 不再拦截输入框自己处理的按键,并记下这次按键的判定 */
|
|
39
|
+
export declare const suppressNativeInputKeys: ({ event }: {
|
|
40
|
+
event: KeyboardEvent;
|
|
41
|
+
}) => boolean;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.useRowDrag = exports.useKeyboardNavigation = exports.useCellFocus = void 0;
|
|
6
|
+
exports.useRowDrag = exports.useKeyboardNavigation = exports.useCellFocus = exports.suppressNativeInputKeys = exports.isNativeInputKey = void 0;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
// setSelectionRange 仅 textarea 与 text/search/url/tel/password 类型 input 支持;
|
|
9
9
|
// checkbox/number/date 等调用会抛 InvalidStateError,故用白名单守卫。@author nokecy
|
|
@@ -17,6 +17,12 @@ var supportsSelectionRange = function supportsSelectionRange(el) {
|
|
|
17
17
|
*/
|
|
18
18
|
var useCellFocus = exports.useCellFocus = function useCellFocus() {
|
|
19
19
|
var focusInputInCell = (0, _react.useCallback)(function (cell) {
|
|
20
|
+
// 焦点已经在本格的输入框里:用户直接点进去定位光标、双击选词、按住拖选,这些留下的光标/选区都是他的。
|
|
21
|
+
// 不能再 focus() 并把光标挪到末尾——那会把这些操作在 10ms 后全部抹掉。
|
|
22
|
+
// 首次点开、键盘导航进来时焦点还不在本格输入框里,不受影响。@author nokecy
|
|
23
|
+
var active = document.activeElement;
|
|
24
|
+
if (active && cell.contains(active) && active.matches('input, textarea')) return;
|
|
25
|
+
|
|
20
26
|
// 首先查找Editable包装的元素
|
|
21
27
|
var editableElement = cell.querySelector('.ant-formily-editable');
|
|
22
28
|
if (editableElement) {
|
|
@@ -101,11 +107,15 @@ var useCellFocus = exports.useCellFocus = function useCellFocus() {
|
|
|
101
107
|
*/
|
|
102
108
|
var useKeyboardNavigation = exports.useKeyboardNavigation = function useKeyboardNavigation(ref, dataSource, focusInputInCell, activateCell) {
|
|
103
109
|
var handleCellKeyDown = (0, _react.useCallback)(function (event) {
|
|
110
|
+
var _nativeKeyDecisions$g;
|
|
104
111
|
var keyEvent = event.event,
|
|
105
112
|
node = event.node,
|
|
106
113
|
colDef = event.colDef,
|
|
107
114
|
api = event.api;
|
|
108
115
|
if (!keyEvent) return;
|
|
116
|
+
// 输入框里移动光标/扩选/全选的按键归输入框,表格不导航(见 isNativeInputKey)。
|
|
117
|
+
// 判定用 keydown 当时记下的(见 suppressNativeInputKeys);没记过(不是 AG-Grid 触发的)才现算。
|
|
118
|
+
if ((_nativeKeyDecisions$g = nativeKeyDecisions.get(keyEvent)) !== null && _nativeKeyDecisions$g !== void 0 ? _nativeKeyDecisions$g : isNativeInputKey(keyEvent)) return;
|
|
109
119
|
|
|
110
120
|
// 处理上下左右键导航
|
|
111
121
|
if (keyEvent.key === 'ArrowDown' || keyEvent.key === 'ArrowUp' || keyEvent.key === 'ArrowLeft' || keyEvent.key === 'ArrowRight') {
|
|
@@ -209,4 +219,55 @@ var useRowDrag = exports.useRowDrag = function useRowDrag(field) {
|
|
|
209
219
|
return {
|
|
210
220
|
handleRowDragMove: handleRowDragMove
|
|
211
221
|
};
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 这个按键该不该交给单元格里的输入框自己处理(浏览器原生行为),而不是当成表格的快捷键/单元格导航。
|
|
226
|
+
*
|
|
227
|
+
* AG-Grid 把 Ctrl+A 当成「选中所有单元格」、Home/End 当成「跳到行首/行尾单元格」并 preventDefault;
|
|
228
|
+
* 我们的 handleCellKeyDown 又把方向键一律当成「跳到相邻单元格」——输入框里全选、用方向键移动光标、
|
|
229
|
+
* Shift+方向键扩选文字就全都做不了。规则:
|
|
230
|
+
* - Ctrl/Cmd+A、Home/End、带 Shift/Ctrl/Alt/Cmd 的方向键、输入法组字期间的按键 -> 输入框自己处理;
|
|
231
|
+
* - 左右方向键:光标还能动就移动光标(有选区时先收起选区),已经在最左/最右再按才跳到相邻单元格;
|
|
232
|
+
* - 上下方向键:单行输入框没有纵向光标,照旧在行间导航;多行文本域在首行/末行才导航;
|
|
233
|
+
* - 焦点不在输入框里(在单元格本身)、只读输入框(如下拉选择)、不支持光标的输入框:照旧导航。
|
|
234
|
+
* 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
|
|
235
|
+
*/
|
|
236
|
+
var isNativeInputKey = exports.isNativeInputKey = function isNativeInputKey(event) {
|
|
237
|
+
var _target$matches;
|
|
238
|
+
var target = event.target;
|
|
239
|
+
if (!(target !== null && target !== void 0 && (_target$matches = target.matches) !== null && _target$matches !== void 0 && _target$matches.call(target, 'input, textarea, [contenteditable="true"]'))) return false;
|
|
240
|
+
var key = event.key;
|
|
241
|
+
if (event.isComposing) return true;
|
|
242
|
+
if ((event.ctrlKey || event.metaKey) && key.toLowerCase() === 'a') return true;
|
|
243
|
+
if (key === 'Home' || key === 'End') return true;
|
|
244
|
+
if (!key.startsWith('Arrow')) return false;
|
|
245
|
+
if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) return true;
|
|
246
|
+
var el = target;
|
|
247
|
+
var start = el.selectionStart,
|
|
248
|
+
end = el.selectionEnd,
|
|
249
|
+
value = el.value;
|
|
250
|
+
// 不支持光标的输入框(如 checkbox)、只读输入框:没有光标可动,方向键用来导航
|
|
251
|
+
if (start == null || end == null || el.readOnly || el.disabled) return false;
|
|
252
|
+
var collapsed = start === end;
|
|
253
|
+
if (key === 'ArrowLeft') return !(collapsed && start === 0);
|
|
254
|
+
if (key === 'ArrowRight') return !(collapsed && end === value.length);
|
|
255
|
+
if (el.tagName !== 'TEXTAREA') return false;
|
|
256
|
+
// 多行文本域:光标不在首行才能往上移,不在末行才能往下移
|
|
257
|
+
return key === 'ArrowUp' ? value.slice(0, start).includes('\n') : value.slice(end).includes('\n');
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* keydown 当时的判定。AG-Grid 的 onCellKeyDown 回调是在 setTimeout(0) 里才触发的,那时浏览器已经执行完默认动作、
|
|
262
|
+
* 光标被挪过了:光标从 1 挪到最左、选区收起到末尾之后再算,会误当成「本来就在边缘」而跳格。
|
|
263
|
+
* suppressKeyboardEvent 是 keydown 时同步调用的,状态还没变,所以在那里记下,回调里直接用。
|
|
264
|
+
*/
|
|
265
|
+
var nativeKeyDecisions = new WeakMap();
|
|
266
|
+
|
|
267
|
+
/** AG-Grid 的 suppressKeyboardEvent:让 AG-Grid 不再拦截输入框自己处理的按键,并记下这次按键的判定 */
|
|
268
|
+
var suppressNativeInputKeys = exports.suppressNativeInputKeys = function suppressNativeInputKeys(_ref) {
|
|
269
|
+
var event = _ref.event;
|
|
270
|
+
var native = isNativeInputKey(event);
|
|
271
|
+
nativeKeyDecisions.set(event, native);
|
|
272
|
+
return native;
|
|
212
273
|
};
|
|
@@ -18,7 +18,7 @@ var _preview = _interopRequireDefault(require("./preview"));
|
|
|
18
18
|
var _antd = require("antd");
|
|
19
19
|
var _setting = _interopRequireDefault(require("../ag-grid-plus/setting"));
|
|
20
20
|
var _columnGroupUtils = require("../ag-grid-plus/setting/columnGroupUtils");
|
|
21
|
-
var _theme = require("
|
|
21
|
+
var _theme = require("./theme");
|
|
22
22
|
var _agGridPlus = require("../ag-grid-plus");
|
|
23
23
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
24
24
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
@@ -34,18 +34,7 @@ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructur
|
|
|
34
34
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
35
35
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
36
36
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
37
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } //
|
|
38
|
-
// array-table 派生主题:去掉单元格聚焦/选区视觉(行内编辑更干净)。
|
|
39
|
-
// focusShadow -> 聚焦光环
|
|
40
|
-
// rangeSelectionBorder -> 单格点击形成的 1×1 选区蓝边
|
|
41
|
-
// rangeSelectionBackground -> 选区淡蓝填充
|
|
42
|
-
// 仅去视觉,不动焦点功能——键盘导航/点击进编辑仍依赖 cell focus。共用主题不受影响。@author nokecy
|
|
43
|
-
var arrayTableTheme = _theme.agGridTheme.withParams({
|
|
44
|
-
focusShadow: 'none',
|
|
45
|
-
rangeSelectionBorderColor: 'transparent',
|
|
46
|
-
rangeSelectionBackgroundColor: 'transparent'
|
|
47
|
-
});
|
|
48
|
-
|
|
37
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } // AG-Grid v33 主题:行高、单元格居中、去聚焦/选区视觉(编辑态与阅读态共用一份,见 theme.ts)
|
|
49
38
|
//@ts-ignore
|
|
50
39
|
var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
51
40
|
var _addition$props;
|
|
@@ -62,7 +51,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
|
62
51
|
isAdd = _useState2[0],
|
|
63
52
|
setIsAdd = _useState2[1];
|
|
64
53
|
var apiRef = (0, _react.useRef)();
|
|
65
|
-
//
|
|
54
|
+
// 懒挂载激活态:存放当前挂着真控件的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读写:
|
|
55
|
+
// 点击/导航进来时这里写入,必填且为空的格自己写入,焦点离开且校验通过后自己删除。@author nokecy
|
|
66
56
|
var activeCellsRef = (0, _react.useRef)(new Set());
|
|
67
57
|
// 无 _id/id 的外部数据按对象引用分配稳定 rowId(避免 getRowId 返回 undefined)。@author nokecy
|
|
68
58
|
var rowIdMapRef = (0, _react.useRef)(new WeakMap());
|
|
@@ -178,15 +168,15 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
|
178
168
|
|
|
179
169
|
/**
|
|
180
170
|
* 激活某个懒挂载单元格:加入激活集合并强制重渲该格 -> LazyAgField 改挂 AgField 全树。
|
|
181
|
-
*
|
|
171
|
+
* 焦点离开后 LazyAgField 自己把它从集合里移除并卸回文本(见 utils.tsx)。
|
|
172
|
+
* 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/已挂载着,
|
|
173
|
+
* 如必填且为空的格本就挂着)。新增行的格与存量行一视同仁:非必填的也是先文本、点了才挂。@author nokecy
|
|
182
174
|
*/
|
|
183
175
|
var activateCell = (0, _react.useCallback)(function (api, node, colId) {
|
|
184
|
-
var _node$data;
|
|
185
176
|
if (!api || !node || !colId) return false;
|
|
186
177
|
if (!lazyColIds.has(colId)) return false; // 非懒列(已是 AgField/纯文本)
|
|
187
|
-
if ((_node$data = node.data) !== null && _node$data !== void 0 && _node$data._isNew) return false; // 新增行本就 eager 挂着
|
|
188
178
|
var key = "".concat(node.id, ":").concat(colId);
|
|
189
|
-
if (activeCellsRef.current.has(key)) return false; //
|
|
179
|
+
if (activeCellsRef.current.has(key)) return false; // 已挂载
|
|
190
180
|
activeCellsRef.current.add(key);
|
|
191
181
|
api.refreshCells({
|
|
192
182
|
rowNodes: [node],
|
|
@@ -340,7 +330,7 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
|
340
330
|
minHeight: 0
|
|
341
331
|
}
|
|
342
332
|
}, /*#__PURE__*/_react.default.createElement(_agGridReact.AgGridReact, {
|
|
343
|
-
theme: arrayTableTheme,
|
|
333
|
+
theme: _theme.arrayTableTheme,
|
|
344
334
|
context: gridContext,
|
|
345
335
|
onGridReady: function onGridReady(params) {
|
|
346
336
|
params.api.hidePopupMenu();
|
|
@@ -391,6 +381,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
|
391
381
|
enableCellTextSelection: true,
|
|
392
382
|
defaultColDef: {
|
|
393
383
|
resizable: true,
|
|
384
|
+
// 输入框里全选、移动光标、扩选等文字编辑按键交给输入框,不让 AG-Grid 当成表格快捷键拦走
|
|
385
|
+
suppressKeyboardEvent: _hooks.suppressNativeInputKeys,
|
|
394
386
|
headerComponentParams: {
|
|
395
387
|
template: '<div class="ag-cell-label-container" role="presentation">' +
|
|
396
388
|
// ' <span data-ref="eMenu" class="ag-header-icon ag-header-cell-menu-button" aria-hidden="true"></span>' +
|
|
@@ -427,7 +419,7 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
|
|
|
427
419
|
// }
|
|
428
420
|
// }}
|
|
429
421
|
,
|
|
430
|
-
rowHeight:
|
|
422
|
+
rowHeight: _theme.ARRAY_TABLE_ROW_HEIGHT,
|
|
431
423
|
pagination: false,
|
|
432
424
|
columnDefs: gridColumnDefs,
|
|
433
425
|
rowData: dataSource
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireDefault(require("react"));
|
|
|
8
8
|
var _react2 = require("@formily/react");
|
|
9
9
|
var _utils = require("./utils");
|
|
10
10
|
var _agGridPlus = require("../ag-grid-plus");
|
|
11
|
+
var _theme = require("./theme");
|
|
11
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
13
|
//@ts-ignore
|
|
13
14
|
var ArrayTable = function ArrayTable(props) {
|
|
@@ -31,7 +32,8 @@ var ArrayTable = function ArrayTable(props) {
|
|
|
31
32
|
},
|
|
32
33
|
hideTool: true,
|
|
33
34
|
search: false,
|
|
34
|
-
|
|
35
|
+
theme: _theme.arrayTablePreviewTheme,
|
|
36
|
+
rowHeight: _theme.ARRAY_TABLE_ROW_HEIGHT,
|
|
35
37
|
pagination: false,
|
|
36
38
|
columnDefs: columns,
|
|
37
39
|
rowData: dataSource
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 只读列「纯文本快路径」格式化器注册表
|
|
3
|
-
*
|
|
4
|
-
* 背景:ArrayTable 每个可见单元格默认挂一棵完整 Formily 字段树(AgField),
|
|
5
|
-
* 行虚拟化滚动时整行销毁重建,列多必然掉帧。只读列没有编辑/校验/写回,
|
|
6
|
-
* 完全可以不挂 Formily 树、改用 AG-Grid 的 valueFormatter 出纯文本。
|
|
7
|
-
*
|
|
8
|
-
* 本注册表负责「内层组件类型 -> 显示文本」的映射。命中返回格式化器,
|
|
9
|
-
* 未命中返回 null —— 调用方据此安全回退 AgField(正确优先于速度)。
|
|
10
|
-
*
|
|
11
|
-
* 取舍:这是 Formily readPretty 之外的一份精简显示逻辑,可能与定制 readPretty
|
|
12
|
-
* 发散。靠「未命中即回退 + 消费方可 registerReadOnlyFormatter 扩展」控制风险。
|
|
13
|
-
*
|
|
14
|
-
* @author nokecy
|
|
15
|
-
*/
|
|
16
1
|
export type ReadOnlyFormatterCtx = {
|
|
17
2
|
/** 列的 x-component-props(headerName/width/options/format 等) */
|
|
18
3
|
columnProps: any;
|
|
@@ -4,8 +4,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.registerReadOnlyFormatter = exports.getReadOnlyFormatter = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
8
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } /**
|
|
9
10
|
* 只读列「纯文本快路径」格式化器注册表
|
|
10
11
|
*
|
|
11
12
|
* 背景:ArrayTable 每个可见单元格默认挂一棵完整 Formily 字段树(AgField),
|
|
@@ -20,7 +21,6 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
|
|
|
20
21
|
*
|
|
21
22
|
* @author nokecy
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
24
|
/** 从字段 schema / 列 props 里尽量取到 Select 的可选项(value->label 映射用) */
|
|
25
25
|
var resolveOptions = function resolveOptions(ctx) {
|
|
26
26
|
var _ref, _ref2, _ref3, _fieldSchema$enum, _fieldSchema$xCompon, _fieldSchema$xCompon2;
|
|
@@ -62,9 +62,11 @@ var dateFormatter = function dateFormatter(value, ctx) {
|
|
|
62
62
|
var _ref6, _ctx$columnProps$form, _ctx$columnProps, _ctx$fieldSchema;
|
|
63
63
|
if (value == null || value === '') return '';
|
|
64
64
|
var fmt = (_ref6 = (_ctx$columnProps$form = (_ctx$columnProps = ctx.columnProps) === null || _ctx$columnProps === void 0 ? void 0 : _ctx$columnProps.format) !== null && _ctx$columnProps$form !== void 0 ? _ctx$columnProps$form : (_ctx$fieldSchema = ctx.fieldSchema) === null || _ctx$fieldSchema === void 0 || (_ctx$fieldSchema = _ctx$fieldSchema['x-component-props']) === null || _ctx$fieldSchema === void 0 ? void 0 : _ctx$fieldSchema.format) !== null && _ref6 !== void 0 ? _ref6 : 'YYYY-MM-DD';
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
if (typeof value.format === 'function') return value.format(fmt);
|
|
66
|
+
// 后端回来的多是 ISO 串(2024-10-19T00:00:00)或时间戳:与 Formily readPretty 一致,按 dayjs(value).format(fmt) 显示;
|
|
67
|
+
// 解析不了的文本(如「待定」)原样显示,不吞掉。dayjs 随 antd 传递依赖引入,本库其他模块已直接使用。
|
|
68
|
+
var parsed = (0, _dayjs.default)(value);
|
|
69
|
+
return parsed.isValid() ? parsed.format(fmt) : String(value);
|
|
68
70
|
};
|
|
69
71
|
var booleanFormatter = function booleanFormatter(value, ctx) {
|
|
70
72
|
var _ctx$fieldSchema$xCo, _ctx$fieldSchema2, _props$checkedChildre, _props$unCheckedChild;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 行高 38:放得下 32px 高的输入框。网格选项 rowHeight 与主题参数 rowHeight 必须同值——
|
|
3
|
+
* AG-Grid 单元格文字的行高(--ag-internal-content-line-height)是从主题的 --ag-row-height 算的,
|
|
4
|
+
* 只设网格选项会让文字的行盒仍是主题默认行高(29.6-3=26.6px),顶在 37px 的格子上方(偏上约 4.5px)。
|
|
5
|
+
*/
|
|
6
|
+
export declare const ARRAY_TABLE_ROW_HEIGHT = 38;
|
|
7
|
+
/** 阅读态主题:共用主题 + 行高 + 单元格居中 */
|
|
8
|
+
export declare const arrayTablePreviewTheme: import("ag-grid-community").Theme<import("ag-grid-community").CoreParams & import("ag-grid-community").ButtonStyleParams & import("ag-grid-community").CheckboxStyleParams & import("ag-grid-community").TabStyleParams & import("ag-grid-community").InputStyleParams & {}>;
|
|
9
|
+
/**
|
|
10
|
+
* 编辑态主题:在阅读态主题上去掉单元格聚焦/选区视觉(行内编辑更干净)。
|
|
11
|
+
* focusShadow -> 聚焦光环
|
|
12
|
+
* rangeSelectionBorder -> 单格点击形成的 1×1 选区蓝边
|
|
13
|
+
* rangeSelectionBackground -> 选区淡蓝填充
|
|
14
|
+
* 仅去视觉,不动焦点功能——键盘导航/点击进编辑仍依赖 cell focus。
|
|
15
|
+
*/
|
|
16
|
+
export declare const arrayTableTheme: import("ag-grid-community").Theme<import("ag-grid-community").CoreParams & import("ag-grid-community").ButtonStyleParams & import("ag-grid-community").CheckboxStyleParams & import("ag-grid-community").TabStyleParams & import("ag-grid-community").InputStyleParams & {}>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.arrayTableTheme = exports.arrayTablePreviewTheme = exports.ARRAY_TABLE_ROW_HEIGHT = void 0;
|
|
7
|
+
var _agGridCommunity = require("ag-grid-community");
|
|
8
|
+
var _theme = require("../ag-grid-plus/theme");
|
|
9
|
+
/**
|
|
10
|
+
* ArrayTable(明细表格)的 AG-Grid 主题:编辑态(index.tsx)与阅读态(preview.tsx)共用。
|
|
11
|
+
*
|
|
12
|
+
* @author nokecy
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 行高 38:放得下 32px 高的输入框。网格选项 rowHeight 与主题参数 rowHeight 必须同值——
|
|
17
|
+
* AG-Grid 单元格文字的行高(--ag-internal-content-line-height)是从主题的 --ag-row-height 算的,
|
|
18
|
+
* 只设网格选项会让文字的行盒仍是主题默认行高(29.6-3=26.6px),顶在 37px 的格子上方(偏上约 4.5px)。
|
|
19
|
+
*/
|
|
20
|
+
var ARRAY_TABLE_ROW_HEIGHT = exports.ARRAY_TABLE_ROW_HEIGHT = 38;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 单元格里的表单项与内容区垂直居中(只对使用本主题的网格生效,共用主题不受影响):
|
|
24
|
+
* - .ag-cell-wrapper 撑满格子,align-items:center 才起作用(默认只有内容高,贴在格子顶部);
|
|
25
|
+
* - FormItem 默认 margin-bottom:24px(loose 反馈布局给错误文字预留的位置),会把 Editable 外壳撑到 56px、
|
|
26
|
+
* 输入框偏上、还超出 37px 的格子,归零;
|
|
27
|
+
* - 错误提示文字移出文档流(位置仍在输入框下方),出错时输入框不会被挤得上下跳。
|
|
28
|
+
*/
|
|
29
|
+
var arrayTableCellFit = (0, _agGridCommunity.createPart)({
|
|
30
|
+
feature: 'arrayTableCellFit',
|
|
31
|
+
css: "\n .ag-cell .ag-cell-wrapper { height: 100%; }\n .ag-cell .ant-formily-item { margin-bottom: 0; }\n .ag-cell .ant-formily-item-help { position: absolute; }\n "
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** 阅读态主题:共用主题 + 行高 + 单元格居中 */
|
|
35
|
+
var arrayTablePreviewTheme = exports.arrayTablePreviewTheme = _theme.agGridTheme.withParams({
|
|
36
|
+
rowHeight: ARRAY_TABLE_ROW_HEIGHT
|
|
37
|
+
}).withPart(arrayTableCellFit);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 编辑态主题:在阅读态主题上去掉单元格聚焦/选区视觉(行内编辑更干净)。
|
|
41
|
+
* focusShadow -> 聚焦光环
|
|
42
|
+
* rangeSelectionBorder -> 单格点击形成的 1×1 选区蓝边
|
|
43
|
+
* rangeSelectionBackground -> 选区淡蓝填充
|
|
44
|
+
* 仅去视觉,不动焦点功能——键盘导航/点击进编辑仍依赖 cell focus。
|
|
45
|
+
*/
|
|
46
|
+
var arrayTableTheme = exports.arrayTableTheme = arrayTablePreviewTheme.withParams({
|
|
47
|
+
focusShadow: 'none',
|
|
48
|
+
rangeSelectionBorderColor: 'transparent',
|
|
49
|
+
rangeSelectionBackgroundColor: 'transparent'
|
|
50
|
+
});
|
|
@@ -10,14 +10,21 @@ declare const useArrayTableActions: () => ObservableColumnSource[];
|
|
|
10
10
|
* 懒挂载单元格渲染器(可编辑列)。
|
|
11
11
|
*
|
|
12
12
|
* 已有行的可编辑格被 Editable 装饰器默认转成 readPretty 只读文本态(点击才进编辑),
|
|
13
|
-
* 但仍挂着完整 Formily
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
13
|
+
* 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器让每个格在两种形态间切换:
|
|
14
|
+
* - 懒挂载文本:只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。默认形态。
|
|
15
|
+
* - 挂载态:挂 AgField 全树,可编辑、可校验。
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* 何时挂载:
|
|
18
|
+
* 1. 被激活(点击/键盘导航进来):激活态存放在 AG-Grid context.activeCells(Set),
|
|
19
|
+
* 由 index.tsx 写入并 refreshCells;
|
|
20
|
+
* 2. 必填且为空:空的必填格必须是真控件,否则没有字段节点,提交时没人校验它。
|
|
21
|
+
* 不区分新增行、存量行、initialValues 带进来的行——只看「必填且空」。
|
|
22
|
+
* 例外:Editable 装饰器的非新增行,Editable 自己会把它转成只读态,挂了也不校验,留在文本态。
|
|
23
|
+
* 何时卸回文本:焦点离开本格,且该字段校验通过(必填的已填上、没有格式错误)。
|
|
24
|
+
* 必填仍为空或校验有错则留着控件,让用户看见错误——与 Editable 失焦时的判断一致。
|
|
25
|
+
* 卸掉的只是渲染树,Formily 字段节点与值都还在,再次点击会接着用。
|
|
19
26
|
*
|
|
20
|
-
*
|
|
27
|
+
* 校验安全:空的必填格不会卸成文本(见上);其余格卸载前已校验通过。@author nokecy
|
|
21
28
|
*/
|
|
22
29
|
declare const LazyAgField: (params: any) => React.JSX.Element;
|
|
23
30
|
/**
|