@nokecy/qc-ui 1.1.51 → 1.1.52

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.
@@ -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,13 @@ 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
+ * AG-Grid 的 suppressKeyboardEvent:单元格里的输入框按 Ctrl/Cmd+A 时,让浏览器自己全选文字。
27
+ *
28
+ * AG-Grid 把 Ctrl+A 当成「选中所有单元格」并 preventDefault,输入框(文本框、数字框都一样)
29
+ * 就选不中里面的文字。这里只放行这一个组合;方向键等其余按键仍归 handleCellKeyDown 做单元格导航。
30
+ * 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
31
+ */
32
+ export declare const suppressSelectAllInInput: ({ event }: {
33
+ event: KeyboardEvent;
34
+ }) => 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.suppressSelectAllInInput = 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
@@ -209,4 +209,19 @@ var useRowDrag = exports.useRowDrag = function useRowDrag(field) {
209
209
  return {
210
210
  handleRowDragMove: handleRowDragMove
211
211
  };
212
+ };
213
+
214
+ /**
215
+ * AG-Grid 的 suppressKeyboardEvent:单元格里的输入框按 Ctrl/Cmd+A 时,让浏览器自己全选文字。
216
+ *
217
+ * AG-Grid 把 Ctrl+A 当成「选中所有单元格」并 preventDefault,输入框(文本框、数字框都一样)
218
+ * 就选不中里面的文字。这里只放行这一个组合;方向键等其余按键仍归 handleCellKeyDown 做单元格导航。
219
+ * 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
220
+ */
221
+ var suppressSelectAllInInput = exports.suppressSelectAllInInput = function suppressSelectAllInInput(_ref) {
222
+ var _target$matches;
223
+ var event = _ref.event;
224
+ if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'a') return false;
225
+ var target = event.target;
226
+ return !!(target !== null && target !== void 0 && (_target$matches = target.matches) !== null && _target$matches !== void 0 && _target$matches.call(target, 'input, textarea, [contenteditable="true"]'));
212
227
  };
@@ -62,7 +62,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
62
62
  isAdd = _useState2[0],
63
63
  setIsAdd = _useState2[1];
64
64
  var apiRef = (0, _react.useRef)();
65
- // 懒挂载激活态:存放已激活(点击/导航进过)的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读取。@author nokecy
65
+ // 懒挂载激活态:存放当前挂着真控件的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读写:
66
+ // 点击/导航进来时这里写入,必填且为空的格自己写入,焦点离开且校验通过后自己删除。@author nokecy
66
67
  var activeCellsRef = (0, _react.useRef)(new Set());
67
68
  // 无 _id/id 的外部数据按对象引用分配稳定 rowId(避免 getRowId 返回 undefined)。@author nokecy
68
69
  var rowIdMapRef = (0, _react.useRef)(new WeakMap());
@@ -178,15 +179,15 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
178
179
 
179
180
  /**
180
181
  * 激活某个懒挂载单元格:加入激活集合并强制重渲该格 -> LazyAgField 改挂 AgField 全树。
181
- * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/新增行/已激活)。@author nokecy
182
+ * 焦点离开后 LazyAgField 自己把它从集合里移除并卸回文本(见 utils.tsx)。
183
+ * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/已挂载着,
184
+ * 如必填且为空的格本就挂着)。新增行的格与存量行一视同仁:非必填的也是先文本、点了才挂。@author nokecy
182
185
  */
183
186
  var activateCell = (0, _react.useCallback)(function (api, node, colId) {
184
- var _node$data;
185
187
  if (!api || !node || !colId) return false;
186
188
  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
189
  var key = "".concat(node.id, ":").concat(colId);
189
- if (activeCellsRef.current.has(key)) return false; // 已激活
190
+ if (activeCellsRef.current.has(key)) return false; // 已挂载
190
191
  activeCellsRef.current.add(key);
191
192
  api.refreshCells({
192
193
  rowNodes: [node],
@@ -391,6 +392,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
391
392
  enableCellTextSelection: true,
392
393
  defaultColDef: {
393
394
  resizable: true,
395
+ // 输入框里的 Ctrl/Cmd+A 交给浏览器全选文字,不让 AG-Grid 当成「选中所有单元格」拦走
396
+ suppressKeyboardEvent: _hooks.suppressSelectAllInInput,
394
397
  headerComponentParams: {
395
398
  template: '<div class="ag-cell-label-container" role="presentation">' +
396
399
  // ' <span data-ref="eMenu" class="ag-header-icon ag-header-cell-menu-button" aria-hidden="true"></span>' +
@@ -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
- 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); }
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
- // 值多为 dayjs 对象(有 .format)或已格式化的字符串;不强引 dayjs,保持轻量
66
- if (value && typeof value.format === 'function') return value.format(fmt);
67
- return String(value);
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;
@@ -10,14 +10,21 @@ declare const useArrayTableActions: () => ObservableColumnSource[];
10
10
  * 懒挂载单元格渲染器(可编辑列)。
11
11
  *
12
12
  * 已有行的可编辑格被 Editable 装饰器默认转成 readPretty 只读文本态(点击才进编辑),
13
- * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器:
14
- * - 新增行(_isNew,保持编辑态需即时校验) 或 已被激活(点击/导航进过)的格 -> 挂 AgField 全树;
15
- * - 其余(存量行、没碰过) -> 只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。
13
+ * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器让每个格在两种形态间切换:
14
+ * - 懒挂载文本:只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。默认形态。
15
+ * - 挂载态:挂 AgField 全树,可编辑、可校验。
16
16
  *
17
- * 激活态存放在 AG-Grid context.activeCells(Set),由 index.tsx 在点击/键盘导航时写入并 refreshCells。
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
- * 校验安全:未碰过的存量格本就是 readPretty、本就不参与校验,不挂树校验语义零变化。@author nokecy
27
+ * 校验安全:空的必填格不会卸成文本(见上);其余格卸载前已校验通过。@author nokecy
21
28
  */
22
29
  declare const LazyAgField: (params: any) => React.JSX.Element;
23
30
  /**
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
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
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
7
6
  exports.useArrayTableSources = exports.useArrayTableActions = exports.useAddition = exports.isColumnComponent = exports.buildArrayTableColumns = exports.LazyReadOnlyText = exports.LazyAgField = void 0;
8
- var _react = _interopRequireDefault(require("react"));
7
+ var _react = _interopRequireWildcard(require("react"));
9
8
  var _antd = require("antd");
10
9
  var _react2 = require("@formily/react");
11
10
  var _shared = require("@formily/shared");
@@ -13,15 +12,28 @@ var _jsonSchema = require("@formily/json-schema");
13
12
  var _arrayBase = _interopRequireDefault(require("./arrayBase"));
14
13
  var _readOnlyFormatters = require("./readOnlyFormatters");
15
14
  var _columnGroup = require("./columnGroup");
15
+ var _cellFocusScope = require("./cellFocusScope");
16
16
  var _excluded = ["hide", "sort", "sortIndex", "forceAgField", "headerGroup", "defaultHide"];
17
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+ 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); }
19
+ 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; }
20
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
21
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
18
22
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
19
23
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
20
24
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
21
25
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
22
26
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
23
- function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
24
- function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
27
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
28
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
29
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
30
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
31
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
32
+ 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); }
33
+ 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; }
34
+ 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; } }
35
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
36
+ 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); }
25
37
  var isColumnComponent = exports.isColumnComponent = function isColumnComponent(schema) {
26
38
  var _schema$xComponent;
27
39
  return ((_schema$xComponent = schema['x-component']) === null || _schema$xComponent === void 0 ? void 0 : _schema$xComponent.indexOf('Column')) > -1;
@@ -242,21 +254,60 @@ var resolveLazyCellPattern = function resolveLazyCellPattern(arrayField, record,
242
254
  }
243
255
  };
244
256
 
257
+ /**
258
+ * 值算不算「没填」:null/undefined/空串,或成员全是这类值的数组/对象(如 labelInValue 的
259
+ * {value:undefined,label:undefined})。0 和 false 是有效值,不算空。@author nokecy
260
+ */
261
+ var isBlank = function isBlank(value) {
262
+ if (value == null || value === '') return true;
263
+ if (Array.isArray(value)) return value.every(isBlank);
264
+ if (Object.prototype.toString.call(value) === '[object Object]') return Object.values(value).every(isBlank);
265
+ return false;
266
+ };
267
+ var hasRequiredRule = function hasRequiredRule(rule) {
268
+ if (rule == null) return false;
269
+ if (rule === 'required') return true;
270
+ if (Array.isArray(rule)) return rule.some(hasRequiredRule);
271
+ if (_typeof(rule) === 'object') return rule.required != null && rule.required !== false;
272
+ return false;
273
+ };
274
+
275
+ /**
276
+ * 数据字段是否必填(静态判定):字段上写了 required,或 x-validator 里带 required 规则。
277
+ *
278
+ * 判不准(required 写成表达式串)时按必填算:误判必填只是多挂一个控件,
279
+ * 误判非必填会让空的必填格没人校验——与只读判定同样的安全不对称。
280
+ * 靠 x-reactions 动态改必填的列本就整列 eager,不走这里。@author nokecy
281
+ */
282
+ var isRequiredSchema = function isRequiredSchema(fieldSchema) {
283
+ if (!fieldSchema) return false;
284
+ var required = fieldSchema.required;
285
+ if (required === true || typeof required === 'string') return true;
286
+ return hasRequiredRule(fieldSchema['x-validator']);
287
+ };
288
+
245
289
  /**
246
290
  * 懒挂载单元格渲染器(可编辑列)。
247
291
  *
248
292
  * 已有行的可编辑格被 Editable 装饰器默认转成 readPretty 只读文本态(点击才进编辑),
249
- * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器:
250
- * - 新增行(_isNew,保持编辑态需即时校验) 或 已被激活(点击/导航进过)的格 -> 挂 AgField 全树;
251
- * - 其余(存量行、没碰过) -> 只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。
293
+ * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器让每个格在两种形态间切换:
294
+ * - 懒挂载文本:只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。默认形态。
295
+ * - 挂载态:挂 AgField 全树,可编辑、可校验。
252
296
  *
253
- * 激活态存放在 AG-Grid context.activeCells(Set),由 index.tsx 在点击/键盘导航时写入并 refreshCells。
254
- * 「挂了不卸」:一旦激活保持挂载(直到行被虚拟化销毁),不做失焦退回,避开一堆边界。
297
+ * 何时挂载:
298
+ * 1. 被激活(点击/键盘导航进来):激活态存放在 AG-Grid context.activeCells(Set),
299
+ * 由 index.tsx 写入并 refreshCells;
300
+ * 2. 必填且为空:空的必填格必须是真控件,否则没有字段节点,提交时没人校验它。
301
+ * 不区分新增行、存量行、initialValues 带进来的行——只看「必填且空」。
302
+ * 例外:Editable 装饰器的非新增行,Editable 自己会把它转成只读态,挂了也不校验,留在文本态。
303
+ * 何时卸回文本:焦点离开本格,且该字段校验通过(必填的已填上、没有格式错误)。
304
+ * 必填仍为空或校验有错则留着控件,让用户看见错误——与 Editable 失焦时的判断一致。
305
+ * 卸掉的只是渲染树,Formily 字段节点与值都还在,再次点击会接着用。
255
306
  *
256
- * 校验安全:未碰过的存量格本就是 readPretty、本就不参与校验,不挂树校验语义零变化。@author nokecy
307
+ * 校验安全:空的必填格不会卸成文本(见上);其余格卸载前已校验通过。@author nokecy
257
308
  */
258
309
  var LazyAgField = exports.LazyAgField = function LazyAgField(params) {
259
- var _context$activeCells, _context$activeCells$, _fieldSchema$name;
310
+ var _context$activeCells, _fieldSchema$name;
260
311
  var value = params.value,
261
312
  record = params.data,
262
313
  dataSource = params.dataSource,
@@ -267,32 +318,105 @@ var LazyAgField = exports.LazyAgField = function LazyAgField(params) {
267
318
  context = params.context,
268
319
  formatter = params.formatter,
269
320
  columnProps = params.columnProps,
270
- fieldSchema = params.fieldSchema;
321
+ fieldSchema = params.fieldSchema,
322
+ required = params.required;
271
323
  // hook 必须在下面的条件 return 之前调用
272
324
  var _theme$useToken = _antd.theme.useToken(),
273
325
  token = _theme$useToken.token;
274
326
  var scope = (0, _react2.useExpressionScope)();
327
+ var _useReducer = (0, _react.useReducer)(function (n) {
328
+ return n + 1;
329
+ }, 0),
330
+ _useReducer2 = _slicedToArray(_useReducer, 2),
331
+ rerender = _useReducer2[1];
332
+ var localCells = (0, _react.useRef)(new Set());
333
+ var activeCells = (_context$activeCells = context === null || context === void 0 ? void 0 : context.activeCells) !== null && _context$activeCells !== void 0 ? _context$activeCells : localCells.current;
275
334
  var key = "".concat(node === null || node === void 0 ? void 0 : node.id, ":").concat(colDef === null || colDef === void 0 ? void 0 : colDef.field);
276
- var activated = (record === null || record === void 0 ? void 0 : record._isNew) === true || (context === null || context === void 0 || (_context$activeCells = context.activeCells) === null || _context$activeCells === void 0 || (_context$activeCells$ = _context$activeCells.has) === null || _context$activeCells$ === void 0 ? void 0 : _context$activeCells$.call(_context$activeCells, key)) === true;
277
- if (activated) {
278
- return /*#__PURE__*/_react.default.createElement(AgField, {
279
- value: value,
280
- data: record,
281
- dataSource: dataSource,
282
- schema: schema,
283
- field: field,
284
- node: node
285
- });
286
- }
335
+ // 内层字段名:Column key 与它可能不一致(设计器形态),取值/找字段节点都按它
336
+ var dataKey = (_fieldSchema$name = fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema.name) !== null && _fieldSchema$name !== void 0 ? _fieldSchema$name : colDef === null || colDef === void 0 ? void 0 : colDef.field;
337
+
338
+ /** 焦点离开本格:校验通过才卸回懒挂载文本。isStale 为真说明校验期间焦点又回来了或格子已销毁 */
339
+ var handleLeave = /*#__PURE__*/function () {
340
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(isStale) {
341
+ var _field$value$indexOf3, _field$value3, _field$value3$indexOf, _field$form;
342
+ var index, cellField;
343
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
344
+ while (1) switch (_context.prev = _context.next) {
345
+ case 0:
346
+ index = (_field$value$indexOf3 = field === null || field === void 0 || (_field$value3 = field.value) === null || _field$value3 === void 0 || (_field$value3$indexOf = _field$value3.indexOf) === null || _field$value3$indexOf === void 0 ? void 0 : _field$value3$indexOf.call(_field$value3, record)) !== null && _field$value$indexOf3 !== void 0 ? _field$value$indexOf3 : -1; // 字段节点按数据路径找(不含 void 容器);从没挂载过就没有节点,无需校验
347
+ cellField = index >= 0 && dataKey ? (_field$form = field.form) === null || _field$form === void 0 ? void 0 : _field$form.query(field.path.concat(index).concat(dataKey)).take() : undefined;
348
+ if (!cellField) {
349
+ _context.next = 11;
350
+ break;
351
+ }
352
+ _context.prev = 3;
353
+ _context.next = 6;
354
+ return cellField.validate();
355
+ case 6:
356
+ _context.next = 11;
357
+ break;
358
+ case 8:
359
+ _context.prev = 8;
360
+ _context.t0 = _context["catch"](3);
361
+ return _context.abrupt("return");
362
+ case 11:
363
+ if (!isStale()) {
364
+ _context.next = 13;
365
+ break;
366
+ }
367
+ return _context.abrupt("return");
368
+ case 13:
369
+ if (!(required && isBlank(dataKey != null ? readByFieldPath(record, dataKey) : undefined))) {
370
+ _context.next = 15;
371
+ break;
372
+ }
373
+ return _context.abrupt("return");
374
+ case 15:
375
+ activeCells.delete(key);
376
+ rerender();
377
+ case 17:
378
+ case "end":
379
+ return _context.stop();
380
+ }
381
+ }, _callee, null, [[3, 8]]);
382
+ }));
383
+ return function handleLeave(_x) {
384
+ return _ref3.apply(this, arguments);
385
+ };
386
+ }();
287
387
 
288
- // Observer 包裹纯文本:让未激活的廉价文本格反应式跟随内层 Formily 字段值。
388
+ // Observer 让本格反应式跟随内层 Formily 字段值。
289
389
  // record 即 field.value[index] 的可观察对象;在 Observer 里按内层字段名直接读 record[dataKey]
290
390
  // (而非 AG-Grid 渲染时快照的 params.value)建立依赖后,onFieldValueChange/setValuesIn 程序化原地
291
391
  // 改值(行引用不变 → AG-Grid 不刷新该格、外层 observer 也不重渲)时,本格能自行重渲刷新——
292
392
  // 免去调用方加 forceAgField 逃生阀。字段名缺失才回退 params.value。@author nokecy
293
- var dataKey = (_fieldSchema$name = fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema.name) !== null && _fieldSchema$name !== void 0 ? _fieldSchema$name : colDef === null || colDef === void 0 ? void 0 : colDef.field;
294
393
  return /*#__PURE__*/_react.default.createElement(_react2.Observer, null, function () {
295
- var liveValue = dataKey != null && record ? readByFieldPath(record, dataKey) : value;
394
+ var activated = activeCells.has(key);
395
+ // 已挂载时不读值:挂载后每敲一个字都会变,追踪它会让整棵字段树跟着重渲
396
+ var liveValue = activated ? undefined : dataKey != null && record ? readByFieldPath(record, dataKey) : value;
397
+ // Editable 装饰器会把非新增行的格转成只读态、不参与校验(见 editable/index.tsx),
398
+ // 这时挂了没用,还会把灰色「请输入」占位换成只读态的「N/A」,所以留在文本态
399
+ var editableWillReadPretty = (fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema['x-decorator']) === 'Editable' && (record === null || record === void 0 ? void 0 : record._isNew) !== true;
400
+ if (!activated && required && !editableWillReadPretty && isBlank(liveValue)) {
401
+ var _pattern = resolveLazyCellPattern(field, record, dataKey, fieldSchema, scope);
402
+ if (_pattern == null || _pattern === 'editable') {
403
+ // 记进激活集合:用户填了第一个字后值不再为空,但格子仍在编辑,要挂到失焦为止
404
+ activeCells.add(key);
405
+ activated = true;
406
+ }
407
+ }
408
+ if (activated) {
409
+ return /*#__PURE__*/_react.default.createElement(_cellFocusScope.CellFocusScope, {
410
+ onLeave: handleLeave
411
+ }, /*#__PURE__*/_react.default.createElement(AgField, {
412
+ value: value,
413
+ data: record,
414
+ dataSource: dataSource,
415
+ schema: schema,
416
+ field: field,
417
+ node: node
418
+ }));
419
+ }
296
420
  var text = formatter ? formatter(liveValue, {
297
421
  columnProps: columnProps,
298
422
  fieldSchema: fieldSchema
@@ -403,14 +527,35 @@ var isColumnReadOnlyEligible = function isColumnReadOnlyEligible(arrayPattern, d
403
527
  return false;
404
528
  };
405
529
 
530
+ /** 明细单元格里的数字输入组件(antd InputNumber 及其 Formily 封装) */
531
+ var NUMBER_INPUT_COMPONENTS = new Set(['NumberPicker', 'InputNumber']);
532
+
533
+ /**
534
+ * 明细单元格里的数字输入默认去掉上下步进:不显示上下按钮、不响应方向键步进。
535
+ *
536
+ * 上下按钮会压住右对齐数字的末尾;方向键步进则与单元格间的方向键导航冲突——
537
+ * 在数字格里按 ↑/↓,导航到别的格的同时,被离开的那格数值也被悄悄 +1/-1。
538
+ * 字段的 x-component-props 里显式写了 controls/keyboard 的以使用方为准。
539
+ * 只对会挂真控件的列调用(只读快路径没有输入框)。幂等。@author nokecy
540
+ */
541
+ var applyNumberInputDefaults = function applyNumberInputDefaults(dataField) {
542
+ if (!dataField || !NUMBER_INPUT_COMPONENTS.has(dataField['x-component'])) return;
543
+ var props = dataField['x-component-props'];
544
+ if (props != null && _typeof(props) !== 'object') return; // 整个 props 写成了表达式串,不动
545
+ dataField['x-component-props'] = _objectSpread({
546
+ controls: false,
547
+ keyboard: false
548
+ }, props);
549
+ };
550
+
406
551
  // 重命名自 useArrayTableColumns:内部无任何 Hook 调用,use 前缀违反 Hook 命名约定且曾在 useMemo 内被调用 @author nokecy
407
552
  var buildArrayTableColumns = exports.buildArrayTableColumns = function buildArrayTableColumns(dataSource, field, sources) {
408
553
  var arrayPattern = field === null || field === void 0 ? void 0 : field.pattern;
409
- return sources.reduce(function (buf, _ref3, key) {
410
- var name = _ref3.name,
411
- columnProps = _ref3.columnProps,
412
- schema = _ref3.schema,
413
- display = _ref3.display;
554
+ return sources.reduce(function (buf, _ref4, key) {
555
+ var name = _ref4.name,
556
+ columnProps = _ref4.columnProps,
557
+ schema = _ref4.schema,
558
+ display = _ref4.display;
414
559
  if (display && display !== 'visible') return buf;
415
560
  if (!isColumnComponent(schema)) return buf;
416
561
 
@@ -445,13 +590,14 @@ var buildArrayTableColumns = exports.buildArrayTableColumns = function buildArra
445
590
  // 三分流渲染策略:@author nokecy
446
591
  // ① 只读纯文本(永不可编辑):只读列 + 有格式化器 -> valueFormatter,不挂 Formily 树。
447
592
  // ② 可编辑懒挂载:可编辑列 + 有格式化器 + 无 reactions + 未强制 eager -> LazyAgField,
448
- // 存量格先出廉价文本、点击/导航才挂全树(挂了不卸)。
593
+ // 格子先出廉价文本、点击/导航(或必填且为空)才挂全树,焦点离开且校验通过再卸回文本。
449
594
  // ③ 兜底全树:其余(无格式化器/带 reactions/forceAgField/复杂字段名/只读但无格式化器) -> AgField。
450
595
  var dataField = findDataFieldSchema(schema, name);
451
596
  var component = dataField === null || dataField === void 0 ? void 0 : dataField['x-component'];
452
597
  var formatter = (0, _readOnlyFormatters.getReadOnlyFormatter)(component);
453
598
  var readOnly = isColumnReadOnlyEligible(arrayPattern, dataField, columnProps);
454
599
  var hasReactions = !!(dataField && dataField['x-reactions']);
600
+ if (!readOnly) applyNumberInputDefaults(dataField);
455
601
  var forceEager = (columnProps === null || columnProps === void 0 ? void 0 : columnProps.forceAgField) === true;
456
602
  // Switch/Checkbox 是「单击直控」组件:可编辑态本就该常驻渲染真控件(优化前即如此)。
457
603
  // 懒挂载会把它降成「是/否」纯文本(点击才挂出开关),用户会以为开关消失。故可编辑态强制 eager。
@@ -514,7 +660,9 @@ var buildArrayTableColumns = exports.buildArrayTableColumns = function buildArra
514
660
  formatter: formatter,
515
661
  columnProps: columnProps,
516
662
  fieldSchema: dataField,
517
- lazyEditable: true // 供 index.tsx 识别懒列(激活/导航用)
663
+ lazyEditable: true,
664
+ // 供 index.tsx 识别懒列(激活/导航用)
665
+ required: isRequiredSchema(dataField) // 必填且为空的格必须挂真控件,见 LazyAgField
518
666
  }
519
667
  }));
520
668
  }
@@ -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;