@nokecy/qc-ui 1.1.83 → 1.1.85

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.
@@ -46,7 +46,8 @@ var ArrayAddBtn = function ArrayAddBtn(props) {
46
46
  rest = _objectWithoutProperties(props, _excluded);
47
47
  var prefixCls = (0, _builtins__.usePrefixCls)("formily-array-base");
48
48
  if (!arrayField) return null;
49
- if (arrayField.pattern !== "editable" && arrayField.pattern !== "disabled") return null;
49
+ // 明细处于只读或禁用状态时不显示新增入口,避免出现灰色的无效操作。
50
+ if (arrayField.pattern !== "editable") return null;
50
51
  return /*#__PURE__*/_react.default.createElement(_antd.Button, _extends({
51
52
  type: "primary"
52
53
  }, rest, {
@@ -2,19 +2,20 @@ import React from 'react';
2
2
  /**
3
3
  * 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
4
4
  *
5
- * 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
6
- * 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
7
- * 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
5
+ * 用 React 合成的 onFocus/onBlur 判断焦点变化:合成事件会沿 React 树冒泡,穿过 portal。
6
+ * 下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时对本格仍是「格内」。
7
+ * 同时监听指针离开:点击到不可聚焦的文本格或页面空白不会触发 blur,需要按点击位置恢复展示态;
8
+ * portal 内的指针事件仍沿 React 树到达本作用域,不会被当作离开。
8
9
  *
9
- * 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
10
- * blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
11
- * (窗口切走时输入框依旧是 activeElement),同样不算离开。
10
+ * 离开不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
11
+ * blur 之后紧跟着 focus,后者会作废这次判定。单纯 blur 时若 activeElement 仍在格内
12
+ * (如窗口切走),不算离开;指针明确点击到作用域外则按离开处理。
12
13
  *
13
14
  * 用 display: contents,不产生盒子,不改变单元格里原有的布局。
14
15
  *
15
16
  * @author nokecy
16
17
  */
17
- export type CellLeaveHandler = (isStale: () => boolean) => void;
18
+ export type CellLeaveHandler = (isStale: () => boolean, hadFocus: boolean) => void;
18
19
  export declare const CellFocusScope: ({ onLeave, children }: {
19
20
  onLeave: CellLeaveHandler;
20
21
  children: React.ReactNode;
@@ -11,13 +11,14 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
11
11
  /**
12
12
  * 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
13
13
  *
14
- * 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
15
- * 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
16
- * 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
14
+ * 用 React 合成的 onFocus/onBlur 判断焦点变化:合成事件会沿 React 树冒泡,穿过 portal。
15
+ * 下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时对本格仍是「格内」。
16
+ * 同时监听指针离开:点击到不可聚焦的文本格或页面空白不会触发 blur,需要按点击位置恢复展示态;
17
+ * portal 内的指针事件仍沿 React 树到达本作用域,不会被当作离开。
17
18
  *
18
- * 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
19
- * blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
20
- * (窗口切走时输入框依旧是 activeElement),同样不算离开。
19
+ * 离开不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
20
+ * blur 之后紧跟着 focus,后者会作废这次判定。单纯 blur 时若 activeElement 仍在格内
21
+ * (如窗口切走),不算离开;指针明确点击到作用域外则按离开处理。
21
22
  *
22
23
  * 用 display: contents,不产生盒子,不改变单元格里原有的布局。
23
24
  *
@@ -29,34 +30,69 @@ var CellFocusScope = exports.CellFocusScope = function CellFocusScope(_ref) {
29
30
  children = _ref.children;
30
31
  var rootRef = (0, _react.useRef)(null);
31
32
  var timerRef = (0, _react.useRef)();
33
+ // 通过 React portal 渲染到 body 的弹层仍属于当前格;记录经过本作用域的指针事件,
34
+ // 避免日期/下拉弹层中的点击被 document 级离开检测误判。@author nokecy
35
+ var pointerEventsInScopeRef = (0, _react.useRef)(new WeakSet());
36
+ var focusWithinRef = (0, _react.useRef)(false);
32
37
  // 每次焦点回到格内、或卸载时递增;进行中的异步判定拿它判断自己是否已过期
33
38
  var generationRef = (0, _react.useRef)(0);
34
39
  var onLeaveRef = (0, _react.useRef)(onLeave);
35
40
  onLeaveRef.current = onLeave;
36
- var cancel = function cancel() {
41
+ var cancel = (0, _react.useCallback)(function () {
37
42
  generationRef.current += 1;
38
43
  window.clearTimeout(timerRef.current);
39
- };
40
- (0, _react.useEffect)(function () {
41
- return cancel;
42
44
  }, []);
43
- var handleBlur = function handleBlur() {
45
+ var scheduleLeave = (0, _react.useCallback)(function () {
46
+ var pointerLeftScope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
47
+ var hadFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : focusWithinRef.current;
44
48
  cancel();
45
49
  var generation = generationRef.current;
46
50
  timerRef.current = window.setTimeout(function () {
47
51
  var _rootRef$current;
48
- if ((_rootRef$current = rootRef.current) !== null && _rootRef$current !== void 0 && _rootRef$current.contains(document.activeElement)) return;
52
+ // blur 后 activeElement 仍在本格通常是窗口切走或格内弹层切焦点,不算离开。
53
+ // 指针明确点到本格/其弹层之外时,即使目标不可聚焦导致 activeElement 没变,也应离开。
54
+ if (!pointerLeftScope && (_rootRef$current = rootRef.current) !== null && _rootRef$current !== void 0 && _rootRef$current.contains(document.activeElement)) return;
49
55
  onLeaveRef.current(function () {
50
56
  return generation !== generationRef.current;
51
- });
57
+ }, hadFocus);
52
58
  }, 0);
59
+ }, [cancel]);
60
+ var handleFocus = function handleFocus() {
61
+ focusWithinRef.current = true;
62
+ cancel();
53
63
  };
64
+ var handleBlur = function handleBlur() {
65
+ var hadFocus = focusWithinRef.current;
66
+ focusWithinRef.current = false;
67
+ scheduleLeave(false, hadFocus);
68
+ };
69
+ var handlePointerDownCapture = function handlePointerDownCapture(event) {
70
+ pointerEventsInScopeRef.current.add(event.nativeEvent);
71
+ };
72
+ (0, _react.useEffect)(function () {
73
+ var handleDocumentPointerDown = function handleDocumentPointerDown(event) {
74
+ var root = rootRef.current;
75
+ if (!root) return;
76
+ var target = event.target;
77
+ // DOM 内点击以及 React portal 子树内点击都属于当前格的交互。
78
+ if (target instanceof Node && root.contains(target) || pointerEventsInScopeRef.current.has(event)) return;
79
+ // 新增行的必填空格也会预先挂载控件,但可能从未获得焦点;同样需要响应离开,
80
+ // 由 onLeave 根据 hadFocus 决定是否校验未触碰的空必填格。@author nokecy
81
+ scheduleLeave(true, focusWithinRef.current);
82
+ };
83
+ document.addEventListener('pointerdown', handleDocumentPointerDown);
84
+ return function () {
85
+ document.removeEventListener('pointerdown', handleDocumentPointerDown);
86
+ cancel();
87
+ };
88
+ }, [cancel, scheduleLeave]);
54
89
  return /*#__PURE__*/_react.default.createElement("div", {
55
90
  ref: rootRef,
56
91
  style: {
57
92
  display: 'contents'
58
93
  },
59
- onFocus: cancel,
60
- onBlur: handleBlur
94
+ onFocus: handleFocus,
95
+ onBlur: handleBlur,
96
+ onPointerDownCapture: handlePointerDownCapture
61
97
  }, children);
62
98
  };
@@ -1,6 +1,5 @@
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
  });
@@ -30,6 +29,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
30
29
  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; }
31
30
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
32
31
  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); }
32
+ 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); }
33
33
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
34
34
  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."); }
35
35
  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); }
@@ -38,7 +38,6 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
38
38
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } // AG-Grid v33 主题:行高、单元格居中、去聚焦/选区视觉(编辑态与阅读态共用一份,见 theme.ts)
39
39
  //@ts-ignore
40
40
  var ArrayTableInner = (0, _react2.observer)(function (props) {
41
- var _addition$props;
42
41
  var height = props.height,
43
42
  gridKey = props.gridKey;
44
43
  var arrayTableTheme = (0, _theme.useArrayTableTheme)(true);
@@ -314,21 +313,39 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
314
313
  style: {
315
314
  marginBottom: 5
316
315
  }
317
- }, addition ? /*#__PURE__*/_react.default.createElement(_arrayAddBtn.default, _extends({
318
- title: "新增",
319
- onAdd: function onAdd() {
320
- setIsAdd(true);
321
- }
322
- }, addition.props, {
323
- // 框架关键属性钉在 spread 之后:不可被 addition.props 覆盖;
324
- // defaultValue 合并自定义字段但始终携带 _id/_isNew 标记。@author nokecy
325
- arrayField: field,
326
- schema: schema,
327
- defaultValue: _objectSpread({
328
- _id: (0, _uniqueId.default)(),
329
- _isNew: true
330
- }, ((_addition$props = addition.props) === null || _addition$props === void 0 ? void 0 : _addition$props.defaultValue) || {})
331
- })) : null, actions.map(function (column, key) {
316
+ }, addition ? function (_addition$props, _additionSchema$xCom, _ref, _additionProps$title, _additionProps$method) {
317
+ // addition 是 RecursionField 元素;业务配置在其 schema.x-component-props 中,
318
+ // 不能从 React 元素的 props 直接读取,否则默认值和标题会丢失。@author nokecy
319
+ var additionSchema = (_addition$props = addition.props) === null || _addition$props === void 0 ? void 0 : _addition$props.schema;
320
+ var additionProps = (_additionSchema$xCom = additionSchema === null || additionSchema === void 0 ? void 0 : additionSchema['x-component-props']) !== null && _additionSchema$xCom !== void 0 ? _additionSchema$xCom : {};
321
+ var configuredDefaults = additionProps.defaultValue;
322
+ var rowDefaults = configuredDefaults && _typeof(configuredDefaults) === 'object' && !Array.isArray(configuredDefaults) ? configuredDefaults : {};
323
+ return /*#__PURE__*/_react.default.createElement(_arrayAddBtn.default, _extends({}, additionProps, {
324
+ title: (_ref = (_additionProps$title = additionProps.title) !== null && _additionProps$title !== void 0 ? _additionProps$title : additionSchema === null || additionSchema === void 0 ? void 0 : additionSchema.title) !== null && _ref !== void 0 ? _ref : '新增'
325
+ // 新增行总是回到数组顺序,避免当前列排序把空白新行插到旧明细前面。
326
+ ,
327
+ onAdd: function onAdd(index) {
328
+ var _apiRef$current;
329
+ setIsAdd(true);
330
+ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 || _apiRef$current.applyColumnState({
331
+ defaultState: {
332
+ sort: null,
333
+ sortIndex: null
334
+ }
335
+ });
336
+ if (typeof additionProps.onAdd === 'function') additionProps.onAdd(index);
337
+ }
338
+ // 框架属性在业务配置之后设置;新行默认追加到明细末尾。@author nokecy
339
+ ,
340
+ method: (_additionProps$method = additionProps.method) !== null && _additionProps$method !== void 0 ? _additionProps$method : 'push',
341
+ arrayField: field,
342
+ schema: schema,
343
+ defaultValue: _objectSpread({
344
+ _id: (0, _uniqueId.default)(),
345
+ _isNew: true
346
+ }, rowDefaults)
347
+ }));
348
+ }() : null, actions.map(function (column, key) {
332
349
  return /*#__PURE__*/_react.default.createElement(_react2.RecursionField, {
333
350
  schema: column.schema,
334
351
  name: key,
@@ -393,8 +410,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
393
410
  });
394
411
  }
395
412
  if (isAdd) {
396
- var _apiRef$current;
397
- (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 || _apiRef$current.ensureIndexVisible(dataSource.length - 1, "bottom");
413
+ var _apiRef$current2;
414
+ (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 || _apiRef$current2.ensureIndexVisible(dataSource.length - 1, "bottom");
398
415
  setIsAdd(false);
399
416
  }
400
417
  },
@@ -416,9 +433,9 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
416
433
  ' <span data-ref="eSortAsc" class="ag-header-icon ag-sort-ascending-icon"></span>' + ' <span data-ref="eSortDesc" class="ag-header-icon ag-sort-descending-icon"></span>' + ' <span data-ref="eSortNone" class="ag-header-icon ag-sort-none-icon"></span>' + " </div>" + "</div>"
417
434
  }
418
435
  },
419
- getRowId: function getRowId(_ref) {
436
+ getRowId: function getRowId(_ref2) {
420
437
  var _data$_id;
421
- var data = _ref.data;
438
+ var data = _ref2.data;
422
439
  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;
423
440
  if (id != null) return String(id);
424
441
  // 既无 _id 也无 id 的外部数据:按对象引用从 WeakMap 取/分配稳定 id。
@@ -12,7 +12,8 @@ var containsIndexComponent = function containsIndexComponent(schema) {
12
12
  var _schema$properties;
13
13
  if (!schema) return false;
14
14
  var component = schema['x-component'];
15
- if (typeof component === 'string' && /(?:^|\.)Index$/.test(component)) return true;
15
+ // ArrayTable.Index / ArrayBase.Index,以及业务常用的 ArrayTableIndex(还会把行号写进数据字段)
16
+ if (typeof component === 'string' && /(?:^|\.)(?:Array(?:Table)?)?Index$/.test(component)) return true;
16
17
  return Object.values((_schema$properties = schema.properties) !== null && _schema$properties !== void 0 ? _schema$properties : {}).some(containsIndexComponent);
17
18
  };
18
19
 
@@ -385,52 +385,60 @@ var LazyAgField = exports.LazyAgField = function LazyAgField(params) {
385
385
  // 内层字段名:Column key 与它可能不一致(设计器形态),取值/找字段节点都按它
386
386
  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;
387
387
 
388
- /** 焦点离开本格:校验通过才卸回懒挂载文本。isStale 为真说明校验期间焦点又回来了或格子已销毁 */
388
+ /** 离开本格:校验通过才卸回懒挂载文本。未触碰的空必填格保留控件等待填写。@author nokecy */
389
389
  var handleLeave = /*#__PURE__*/function () {
390
- var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(isStale) {
390
+ var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(isStale, hadFocus) {
391
391
  var _field$value$indexOf3, _field$value3, _field$value3$indexOf, _field$form;
392
- var index, cellField;
392
+ var currentValue, index, cellField;
393
393
  return _regeneratorRuntime().wrap(function _callee$(_context) {
394
394
  while (1) switch (_context.prev = _context.next) {
395
395
  case 0:
396
+ currentValue = dataKey != null ? readByFieldPath(record, dataKey) : value; // 新增行的必填空格会预先挂载以参与提交校验;若用户从未进入该格,不在点击页面其他位置时
397
+ // 主动触发校验,但已带值的格子仍可恢复静默文本态。@author nokecy
398
+ if (!(required && isBlank(currentValue) && !hadFocus)) {
399
+ _context.next = 3;
400
+ break;
401
+ }
402
+ return _context.abrupt("return");
403
+ case 3:
396
404
  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 容器);从没挂载过就没有节点,无需校验
397
405
  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;
398
406
  if (!cellField) {
399
- _context.next = 11;
407
+ _context.next = 14;
400
408
  break;
401
409
  }
402
- _context.prev = 3;
403
- _context.next = 6;
410
+ _context.prev = 6;
411
+ _context.next = 9;
404
412
  return cellField.validate();
405
- case 6:
406
- _context.next = 11;
413
+ case 9:
414
+ _context.next = 14;
407
415
  break;
408
- case 8:
409
- _context.prev = 8;
410
- _context.t0 = _context["catch"](3);
411
- return _context.abrupt("return");
412
416
  case 11:
417
+ _context.prev = 11;
418
+ _context.t0 = _context["catch"](6);
419
+ return _context.abrupt("return");
420
+ case 14:
413
421
  if (!isStale()) {
414
- _context.next = 13;
422
+ _context.next = 16;
415
423
  break;
416
424
  }
417
425
  return _context.abrupt("return");
418
- case 13:
426
+ case 16:
419
427
  if (!(required && isBlank(dataKey != null ? readByFieldPath(record, dataKey) : undefined))) {
420
- _context.next = 15;
428
+ _context.next = 18;
421
429
  break;
422
430
  }
423
431
  return _context.abrupt("return");
424
- case 15:
432
+ case 18:
425
433
  activeCells.delete(key);
426
434
  rerender();
427
- case 17:
435
+ case 20:
428
436
  case "end":
429
437
  return _context.stop();
430
438
  }
431
- }, _callee, null, [[3, 8]]);
439
+ }, _callee, null, [[6, 11]]);
432
440
  }));
433
- return function handleLeave(_x) {
441
+ return function handleLeave(_x, _x2) {
434
442
  return _ref5.apply(this, arguments);
435
443
  };
436
444
  }();
@@ -7,8 +7,9 @@
7
7
  display: inline-flex;
8
8
  align-items: center;
9
9
  justify-content: center;
10
- width: 100%;
11
- height: 100%;
10
+ width: 24px;
11
+ height: 24px;
12
+ font-size: 16px;
12
13
  cursor: pointer;
13
14
  pointer-events: auto;
14
15
  color: var(--qc-dialog-select-trigger-color, rgba(0, 0, 0, 0.45));
@@ -19,18 +20,24 @@
19
20
  }
20
21
  }
21
22
 
23
+ // 为放大后的入口预留文本空间,避免选中值与图标重叠。
24
+ &.ant-select-single:not(.ant-select-allow-clear) .ant-select-selection-item,
25
+ &.ant-select-single:not(.ant-select-allow-clear) .ant-select-selection-placeholder {
26
+ padding-inline-end: 24px;
27
+ }
28
+
22
29
  // 悬停时 antd 会把后缀淡出、换成清除图标;这里让入口始终可见,清除图标左移一格并排。
23
30
  &.ant-select.ant-select:hover .ant-select-arrow:not(:last-child) {
24
31
  opacity: 1;
25
32
  }
26
33
 
27
34
  &.ant-select-allow-clear .ant-select-clear {
28
- inset-inline-end: 31px;
35
+ inset-inline-end: 37px;
29
36
  }
30
37
 
31
38
  &.ant-select-allow-clear .ant-select-selection-item,
32
39
  &.ant-select-allow-clear .ant-select-selection-placeholder {
33
- padding-inline-end: 36px;
40
+ padding-inline-end: 42px;
34
41
  }
35
42
 
36
43
  &.ant-select-multiple .ant-select-selector {
@@ -48,24 +48,31 @@ var DraggableModal = function DraggableModal(props) {
48
48
  _useState6 = _slicedToArray(_useState5, 2),
49
49
  isResizing = _useState6[0],
50
50
  setIsResizing = _useState6[1];
51
- var _useState7 = (0, _react.useState)(''),
52
- _useState8 = _slicedToArray(_useState7, 2),
53
- resizeDirection = _useState8[0],
54
- setResizeDirection = _useState8[1];
55
- var _useState9 = (0, _react.useState)({
51
+ var _useState7 = (0, _react.useState)({
56
52
  width: 0,
57
53
  height: 0
58
54
  }),
59
- _useState10 = _slicedToArray(_useState9, 2),
60
- modalSize = _useState10[0],
61
- setModalSize = _useState10[1];
62
- var _useState11 = (0, _react.useState)({
55
+ _useState8 = _slicedToArray(_useState7, 2),
56
+ modalSize = _useState8[0],
57
+ setModalSize = _useState8[1];
58
+ var _useState9 = (0, _react.useState)({
63
59
  x: 0,
64
60
  y: 0
65
61
  }),
62
+ _useState10 = _slicedToArray(_useState9, 2),
63
+ modalPosition = _useState10[0],
64
+ setModalPosition = _useState10[1];
65
+ var _useState11 = (0, _react.useState)(false),
66
66
  _useState12 = _slicedToArray(_useState11, 2),
67
- modalPosition = _useState12[0],
68
- setModalPosition = _useState12[1];
67
+ hasResized = _useState12[0],
68
+ setHasResized = _useState12[1];
69
+ var _useState13 = (0, _react.useState)({
70
+ x: 0,
71
+ y: 0
72
+ }),
73
+ _useState14 = _slicedToArray(_useState13, 2),
74
+ dragPosition = _useState14[0],
75
+ setDragPosition = _useState14[1];
69
76
 
70
77
  // 解构 props,分离出 wrapClassName 和 open
71
78
  var propsWrapClassName = props.wrapClassName,
@@ -89,6 +96,25 @@ var DraggableModal = function DraggableModal(props) {
89
96
  if (timer !== undefined) clearTimeout(timer);
90
97
  };
91
98
  }, [open]);
99
+
100
+ // 每次关闭后还原弹窗定位;下次打开仍按调用方传入的默认尺寸居中显示。@author nokecy
101
+ (0, _react.useEffect)(function () {
102
+ if (open) return;
103
+ setIsResizing(false);
104
+ setHasResized(false);
105
+ setModalSize({
106
+ width: 0,
107
+ height: 0
108
+ });
109
+ setModalPosition({
110
+ x: 0,
111
+ y: 0
112
+ });
113
+ setDragPosition({
114
+ x: 0,
115
+ y: 0
116
+ });
117
+ }, [open]);
92
118
  var _onStart = function onStart(_event, uiData) {
93
119
  var _draggleRef$current;
94
120
  var _window$document$docu = window.document.documentElement,
@@ -126,7 +152,12 @@ var DraggableModal = function DraggableModal(props) {
126
152
  x: rect.left,
127
153
  y: rect.top
128
154
  });
129
- setResizeDirection(direction);
155
+ setHasResized(true);
156
+ // 将当前视觉位置作为新定位基点,避免弹窗自身和 react-draggable 位移重复叠加。@author nokecy
157
+ setDragPosition({
158
+ x: 0,
159
+ y: 0
160
+ });
130
161
  setIsResizing(true);
131
162
  var startX = e.clientX;
132
163
  var startY = e.clientY;
@@ -139,30 +170,28 @@ var DraggableModal = function DraggableModal(props) {
139
170
  var deltaY = moveEvent.clientY - startY;
140
171
  var newWidth = startWidth;
141
172
  var newHeight = startHeight;
142
- var newLeft = startLeft;
143
- var newTop = startTop;
144
173
 
145
174
  // 根据方向计算新尺寸
146
175
  if (direction.includes('e')) {
147
- newWidth = Math.max(400, startWidth + deltaX);
176
+ newWidth = startWidth + deltaX;
148
177
  }
149
178
  if (direction.includes('w')) {
150
- newWidth = Math.max(400, startWidth - deltaX);
151
- newLeft = startLeft + (startWidth - newWidth);
179
+ newWidth = startWidth - deltaX;
152
180
  }
153
181
  if (direction.includes('s')) {
154
- newHeight = Math.max(300, startHeight + deltaY);
182
+ newHeight = startHeight + deltaY;
155
183
  }
156
184
  if (direction.includes('n')) {
157
- newHeight = Math.max(300, startHeight - deltaY);
158
- newTop = startTop + (startHeight - newHeight);
185
+ newHeight = startHeight - deltaY;
159
186
  }
160
187
 
161
- // 限制最大尺寸
188
+ // 限制最小与最大尺寸,再按固定边计算另一侧位置,确保拖哪条边就移动哪条边。@author nokecy
162
189
  var maxWidth = window.innerWidth * 0.95;
163
190
  var maxHeight = window.innerHeight * 0.95;
164
- newWidth = Math.min(newWidth, maxWidth);
165
- newHeight = Math.min(newHeight, maxHeight);
191
+ newWidth = Math.min(Math.max(400, newWidth), maxWidth);
192
+ newHeight = Math.min(Math.max(300, newHeight), maxHeight);
193
+ var newLeft = direction.includes('w') ? startLeft + startWidth - newWidth : startLeft;
194
+ var newTop = direction.includes('n') ? startTop + startHeight - newHeight : startTop;
166
195
 
167
196
  // 直接更新DOM以实现实时效果
168
197
  modal.style.width = "".concat(newWidth, "px");
@@ -191,7 +220,6 @@ var DraggableModal = function DraggableModal(props) {
191
220
  };
192
221
  var handleMouseUp = function handleMouseUp() {
193
222
  setIsResizing(false);
194
- setResizeDirection('');
195
223
  document.removeEventListener('mousemove', handleMouseMove);
196
224
  document.removeEventListener('mouseup', handleMouseUp);
197
225
 
@@ -206,7 +234,8 @@ var DraggableModal = function DraggableModal(props) {
206
234
 
207
235
  // 合并类名
208
236
  var resizingClass = isResizing ? 'modal-wrap-resizing' : '';
209
- var mergedWrapClassName = [propsWrapClassName, resizingClass].filter(Boolean).join(' ');
237
+ var resizedClass = hasResized ? 'modal-wrap-resized' : '';
238
+ var mergedWrapClassName = [propsWrapClassName, resizingClass, resizedClass].filter(Boolean).join(' ');
210
239
  return /*#__PURE__*/_react.default.createElement(_antd.Modal, _extends({
211
240
  maskClosable: false,
212
241
  width: width,
@@ -232,18 +261,27 @@ var DraggableModal = function DraggableModal(props) {
232
261
  }, props.title),
233
262
  modalRender: function modalRender(modal) {
234
263
  return /*#__PURE__*/_react.default.createElement(_reactDraggable.default, {
235
- disabled: disabled,
264
+ disabled: disabled || isResizing,
236
265
  bounds: bounds,
266
+ position: dragPosition,
237
267
  nodeRef: draggleRef,
238
268
  onStart: function onStart(event, uiData) {
239
269
  return _onStart(event, uiData);
240
270
  },
271
+ onDrag: function onDrag(_event, uiData) {
272
+ return setDragPosition({
273
+ x: uiData.x,
274
+ y: uiData.y
275
+ });
276
+ },
241
277
  cancel: ".resize-handle"
242
278
  }, /*#__PURE__*/_react.default.createElement("div", {
243
279
  ref: draggleRef,
244
- style: isResizing && modalSize.width > 0 ? {
245
- position: 'relative',
246
- height: '100%',
280
+ style: hasResized && modalSize.width > 0 ? {
281
+ position: 'fixed',
282
+ left: modalPosition.x,
283
+ top: modalPosition.y,
284
+ height: "".concat(modalSize.height, "px"),
247
285
  width: "".concat(modalSize.width, "px"),
248
286
  '--modal-width': "".concat(modalSize.width, "px"),
249
287
  '--modal-height': "".concat(modalSize.height, "px")
@@ -1,6 +1,11 @@
1
1
  :global {
2
- .modal-wrap-resizing {
2
+ .modal-wrap-resizing,
3
+ .modal-wrap-resized {
3
4
  .ant-modal {
5
+ position: absolute;
6
+ left: 0 !important;
7
+ top: 0 !important;
8
+ margin: 0 !important;
4
9
  width: var(--modal-width) !important;
5
10
  transition: none;
6
11
  }
@@ -39,7 +39,8 @@ var ArrayAddBtn = function ArrayAddBtn(props) {
39
39
  rest = _objectWithoutProperties(props, _excluded);
40
40
  var prefixCls = usePrefixCls("formily-array-base");
41
41
  if (!arrayField) return null;
42
- if (arrayField.pattern !== "editable" && arrayField.pattern !== "disabled") return null;
42
+ // 明细处于只读或禁用状态时不显示新增入口,避免出现灰色的无效操作。
43
+ if (arrayField.pattern !== "editable") return null;
43
44
  return /*#__PURE__*/React.createElement(Button, _extends({
44
45
  type: "primary"
45
46
  }, rest, {
@@ -2,19 +2,20 @@ import React from 'react';
2
2
  /**
3
3
  * 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
4
4
  *
5
- * 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
6
- * 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
7
- * 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
5
+ * 用 React 合成的 onFocus/onBlur 判断焦点变化:合成事件会沿 React 树冒泡,穿过 portal。
6
+ * 下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时对本格仍是「格内」。
7
+ * 同时监听指针离开:点击到不可聚焦的文本格或页面空白不会触发 blur,需要按点击位置恢复展示态;
8
+ * portal 内的指针事件仍沿 React 树到达本作用域,不会被当作离开。
8
9
  *
9
- * 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
10
- * blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
11
- * (窗口切走时输入框依旧是 activeElement),同样不算离开。
10
+ * 离开不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
11
+ * blur 之后紧跟着 focus,后者会作废这次判定。单纯 blur 时若 activeElement 仍在格内
12
+ * (如窗口切走),不算离开;指针明确点击到作用域外则按离开处理。
12
13
  *
13
14
  * 用 display: contents,不产生盒子,不改变单元格里原有的布局。
14
15
  *
15
16
  * @author nokecy
16
17
  */
17
- export type CellLeaveHandler = (isStale: () => boolean) => void;
18
+ export type CellLeaveHandler = (isStale: () => boolean, hadFocus: boolean) => void;
18
19
  export declare const CellFocusScope: ({ onLeave, children }: {
19
20
  onLeave: CellLeaveHandler;
20
21
  children: React.ReactNode;