@zhenliang/sheet 0.2.5-beta.1 → 0.2.5-beta.11
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/core/editor/InputOptionsEditor/DropMenu.d.ts +10 -0
- package/dist/core/editor/InputOptionsEditor/DropMenu.js +52 -0
- package/dist/core/editor/InputOptionsEditor/index.d.ts +2 -11
- package/dist/core/editor/InputOptionsEditor/index.js +176 -395
- package/dist/core/editor/InputOptionsEditor/index.less +2 -1
- package/dist/core/editor/InputOptionsEditor/keyEventEffect.d.ts +16 -0
- package/dist/core/editor/InputOptionsEditor/keyEventEffect.js +305 -0
- package/dist/core/editor/InputOptionsEditor/utils.d.ts +22 -1
- package/dist/core/editor/InputOptionsEditor/utils.js +51 -6
- package/dist/core/editor/InputOptionsEditor/vaildFormula.js +14 -2
- package/dist/core/editor/numberEditor/index.d.ts +1 -0
- package/dist/core/editor/numberEditor/index.js +2 -2
- package/dist/core/sheet/Cell.js +1 -1
- package/dist/example/basic.js +8 -0
- package/package.json +1 -1
|
@@ -4,41 +4,45 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
4
4
|
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; }
|
|
5
5
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
6
|
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); }
|
|
7
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
8
7
|
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
9
8
|
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."); }
|
|
10
9
|
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); }
|
|
11
10
|
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; }
|
|
12
11
|
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; } }
|
|
13
12
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
14
|
-
|
|
15
|
-
import {
|
|
13
|
+
function _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError("Cannot destructure " + obj); }
|
|
14
|
+
import { Dropdown, Input } from 'antd';
|
|
15
|
+
import { get, head, isEmpty, isNil } from 'lodash';
|
|
16
16
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import validateCalculationExpr from "./vaildFormula";
|
|
17
|
+
import { formulaString } from "../numberEditor";
|
|
18
|
+
import DropMenu from "./DropMenu";
|
|
20
19
|
import "./index.less";
|
|
20
|
+
import { useKeyEventEffect } from "./keyEventEffect";
|
|
21
|
+
import { createSpan, flattenOptions, getStringDiff, moveCursorToSpan, replaceLabelsWithValues, replaceLongestDiff, replaceValuesWithLabels, SpanType, tokenize, validateVariables } from "./utils";
|
|
22
|
+
import validateCalculationExpr from "./vaildFormula";
|
|
21
23
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
};
|
|
35
|
-
export var getFormulaInputEditor = function getFormulaInputEditor(options, replaceIndex) {
|
|
24
|
+
var OPERATORS = ['=', '+', '-', '*', '/', '('];
|
|
25
|
+
var LABEL = SpanType.LABEL,
|
|
26
|
+
OTHER = SpanType.OTHER;
|
|
27
|
+
export var getFormulaInputEditor = function getFormulaInputEditor(options, replaceIndex, extraProps, getExtraProps, choseEditor) {
|
|
28
|
+
var _ref = extraProps || {},
|
|
29
|
+
warnMethod = _ref.warnMethod,
|
|
30
|
+
rangeMethod = _ref.rangeMethod,
|
|
31
|
+
precision = _ref.precision,
|
|
32
|
+
max = _ref.max,
|
|
33
|
+
min = _ref.min;
|
|
36
34
|
var FormulaInputEditor = function FormulaInputEditor(props) {
|
|
35
|
+
var _get;
|
|
37
36
|
var originValue = props.value,
|
|
38
37
|
onChange = props.onChange,
|
|
39
38
|
isEditing = props.isEditing,
|
|
40
39
|
record = props.record;
|
|
41
|
-
|
|
40
|
+
// 兼容number
|
|
41
|
+
var inputNumberRef = useRef(null);
|
|
42
|
+
var _ref2 = getExtraProps ? getExtraProps(props) : extraProps !== null && extraProps !== void 0 ? extraProps : {},
|
|
43
|
+
inputArgs = Object.assign({}, (_objectDestructuringEmpty(_ref2), _ref2));
|
|
44
|
+
var replaceValue = replaceIndex ? (_get = get(record, [replaceIndex])) !== null && _get !== void 0 ? _get : '' : originValue;
|
|
45
|
+
var calcValue = replaceValue.startsWith('=') ? replaceValue : "=".concat(replaceValue);
|
|
42
46
|
var _useState = useState(calcValue),
|
|
43
47
|
_useState2 = _slicedToArray(_useState, 1),
|
|
44
48
|
inputValue = _useState2[0];
|
|
@@ -47,7 +51,7 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
47
51
|
offsetX = _useState4[0],
|
|
48
52
|
setOffsetX = _useState4[1];
|
|
49
53
|
// 为了不重新render,再写一个
|
|
50
|
-
var _useState5 = useState(
|
|
54
|
+
var _useState5 = useState(replaceValue),
|
|
51
55
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
52
56
|
containerValue = _useState6[0],
|
|
53
57
|
setContainerValue = _useState6[1];
|
|
@@ -60,6 +64,7 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
60
64
|
_useState10 = _slicedToArray(_useState9, 2),
|
|
61
65
|
inputAfterDropDownShow = _useState10[0],
|
|
62
66
|
setInputAfterDropDownShow = _useState10[1];
|
|
67
|
+
var tempSpanRef = useRef(null);
|
|
63
68
|
var containerRef = useRef(null);
|
|
64
69
|
var inputOperatorPosition = useRef(0);
|
|
65
70
|
|
|
@@ -73,61 +78,57 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
73
78
|
return tokenize(inputValue, flatOptions);
|
|
74
79
|
}, [inputValue, flatOptions]);
|
|
75
80
|
// 根据搜索文本获取筛选后的层级选项(保持层级结构)
|
|
76
|
-
var filteredHierarchyOptions = useMemo(function () {
|
|
77
|
-
var searchText = inputAfterDropDownShow;
|
|
78
|
-
// 如果搜索文本为空,显示所有选项(保持层级)
|
|
79
|
-
if (!searchText) {
|
|
80
|
-
return options;
|
|
81
|
-
}
|
|
82
81
|
|
|
83
|
-
// 根据搜索文本筛选,保持层级结构
|
|
84
|
-
var filterWithHierarchy = function filterWithHierarchy(items) {
|
|
85
|
-
var result = [];
|
|
86
|
-
var _iterator = _createForOfIteratorHelper(items),
|
|
87
|
-
_step;
|
|
88
|
-
try {
|
|
89
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
90
|
-
var item = _step.value;
|
|
91
|
-
var labelMatch = item.label.includes(searchText);
|
|
92
|
-
// 筛选 children
|
|
93
|
-
var filteredChildren = item.children ? filterWithHierarchy(item.children) : [];
|
|
94
|
-
if (labelMatch || filteredChildren.length > 0) {
|
|
95
|
-
result.push(_objectSpread(_objectSpread({}, item), {}, {
|
|
96
|
-
children: filteredChildren.length > 0 ? filteredChildren : item.children
|
|
97
|
-
}));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
} catch (err) {
|
|
101
|
-
_iterator.e(err);
|
|
102
|
-
} finally {
|
|
103
|
-
_iterator.f();
|
|
104
|
-
}
|
|
105
|
-
return result;
|
|
106
|
-
};
|
|
107
|
-
return filterWithHierarchy(options);
|
|
108
|
-
}, [inputAfterDropDownShow]);
|
|
109
82
|
useEffect(function () {
|
|
110
83
|
if (!open) {
|
|
111
84
|
containerValueSnapShot.current = '';
|
|
85
|
+
tempSpanRef.current = null;
|
|
112
86
|
}
|
|
113
87
|
setInputAfterDropDownShow('');
|
|
114
88
|
}, [open]);
|
|
89
|
+
var closeDorpDown = useCallback(function () {
|
|
90
|
+
setInputAfterDropDownShow('');
|
|
91
|
+
tempSpanRef.current = null;
|
|
92
|
+
// 点击div内直接关闭
|
|
93
|
+
setOpen(false);
|
|
94
|
+
}, []);
|
|
115
95
|
|
|
116
|
-
//
|
|
96
|
+
// 处理选项点击
|
|
117
97
|
var handleOptionClick = useCallback(function (opt) {
|
|
118
98
|
var container = containerRef.current;
|
|
119
99
|
if (!container) return;
|
|
120
|
-
var newValue = replaceLongestDiff(containerValueSnapShot.current, opt.label, inputOperatorPosition.current);
|
|
121
|
-
setOpen(false);
|
|
122
|
-
containerValueSnapShot.current = '';
|
|
123
|
-
var expr = newValue;
|
|
124
|
-
var valueExpr = replaceLabelsWithValues(expr, flatOptions);
|
|
125
|
-
setContainerValue(valueExpr);
|
|
126
|
-
onChange(valueExpr);
|
|
127
|
-
|
|
128
|
-
// 获取当前光标所在的 span
|
|
129
100
|
var selection = window.getSelection();
|
|
130
101
|
if (!selection || selection.rangeCount === 0) return;
|
|
102
|
+
|
|
103
|
+
// 如果有 tempSpanRef,用新逻辑:直接替换该 span 的内容
|
|
104
|
+
if (tempSpanRef.current) {
|
|
105
|
+
tempSpanRef.current.textContent = opt.label;
|
|
106
|
+
var replacedSpan = tempSpanRef.current;
|
|
107
|
+
tempSpanRef.current = null;
|
|
108
|
+
|
|
109
|
+
// 光标移到替换后的 span 后面
|
|
110
|
+
var _allSpans = Array.from(container.querySelectorAll('span'));
|
|
111
|
+
var replacedIndex = _allSpans.indexOf(replacedSpan);
|
|
112
|
+
var _targetSpan = _allSpans[replacedIndex + 1];
|
|
113
|
+
if (!_targetSpan || _targetSpan.classList.contains('formula-editor-token-label')) {
|
|
114
|
+
_targetSpan = createSpan(OTHER, '');
|
|
115
|
+
replacedSpan.after(_targetSpan);
|
|
116
|
+
}
|
|
117
|
+
moveCursorToSpan(_targetSpan, true, selection);
|
|
118
|
+
// 更新 containerValue
|
|
119
|
+
var newValue = container.textContent || '';
|
|
120
|
+
var valueExpr = replaceLabelsWithValues(newValue, flatOptions);
|
|
121
|
+
setContainerValue(valueExpr);
|
|
122
|
+
onChange(valueExpr);
|
|
123
|
+
setOpen(false);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// 原逻辑:删除 inputAfterDropDownShow,插入新 label
|
|
127
|
+
var newValueUser = replaceLongestDiff(containerValueSnapShot.current, opt.label, inputOperatorPosition.current);
|
|
128
|
+
var expr = newValueUser;
|
|
129
|
+
var valueExprUser = replaceLabelsWithValues(expr, flatOptions);
|
|
130
|
+
setContainerValue(valueExprUser);
|
|
131
|
+
onChange(valueExprUser);
|
|
131
132
|
var range = selection.getRangeAt(0);
|
|
132
133
|
var startContainer = range.startContainer;
|
|
133
134
|
var currentSpan = startContainer.nodeType === Node.TEXT_NODE ? startContainer.parentElement : startContainer;
|
|
@@ -145,23 +146,17 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
145
146
|
var afterPart = spanText.slice(deleteIndex + deleteText.length);
|
|
146
147
|
|
|
147
148
|
// 删除当前 span,用三个 span 替换:other(前)、label、other(后)
|
|
148
|
-
var labelSpan =
|
|
149
|
-
labelSpan.className = 'formula-editor-token-label';
|
|
150
|
-
labelSpan.textContent = opt.label;
|
|
149
|
+
var labelSpan = createSpan(LABEL, opt.label);
|
|
151
150
|
|
|
152
151
|
// 创建待插入的 span 列表
|
|
153
152
|
var spansToInsert = [];
|
|
154
153
|
if (beforePart) {
|
|
155
|
-
var beforeSpan =
|
|
156
|
-
beforeSpan.className = 'formula-editor-token-other';
|
|
157
|
-
beforeSpan.textContent = beforePart;
|
|
154
|
+
var beforeSpan = createSpan(OTHER, beforePart);
|
|
158
155
|
spansToInsert.push(beforeSpan);
|
|
159
156
|
}
|
|
160
157
|
spansToInsert.push(labelSpan);
|
|
161
158
|
if (afterPart) {
|
|
162
|
-
var afterSpan =
|
|
163
|
-
afterSpan.className = 'formula-editor-token-other';
|
|
164
|
-
afterSpan.textContent = afterPart;
|
|
159
|
+
var afterSpan = createSpan(OTHER, afterPart);
|
|
165
160
|
spansToInsert.push(afterSpan);
|
|
166
161
|
}
|
|
167
162
|
// 插入所有 span
|
|
@@ -175,24 +170,20 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
175
170
|
|
|
176
171
|
// 如果下一个 span 是 label 类型或没有下一个 span,则插入一个空 span
|
|
177
172
|
if (!targetSpan || targetSpan.classList.contains('formula-editor-token-label')) {
|
|
178
|
-
targetSpan =
|
|
179
|
-
targetSpan.className = 'formula-editor-token-other';
|
|
180
|
-
targetSpan.textContent = '';
|
|
173
|
+
targetSpan = createSpan(OTHER, '');
|
|
181
174
|
labelSpan.after(targetSpan);
|
|
182
175
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
var newRange = document.createRange();
|
|
186
|
-
newRange.selectNodeContents(targetSpan);
|
|
187
|
-
newRange.collapse(true);
|
|
188
|
-
selection.removeAllRanges();
|
|
189
|
-
selection.addRange(newRange);
|
|
176
|
+
moveCursorToSpan(targetSpan, true, selection);
|
|
177
|
+
setOpen(false);
|
|
190
178
|
}, [inputAfterDropDownShow]);
|
|
191
179
|
|
|
192
180
|
// 处理输入变化
|
|
193
181
|
var handleInput = useCallback(function () {
|
|
194
182
|
var container = containerRef.current;
|
|
195
183
|
if (!container) return;
|
|
184
|
+
if (tempSpanRef.current) {
|
|
185
|
+
closeDorpDown();
|
|
186
|
+
}
|
|
196
187
|
var newValue = container.textContent || '';
|
|
197
188
|
var processedValue = newValue;
|
|
198
189
|
var expr = processedValue;
|
|
@@ -216,7 +207,7 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
216
207
|
if (open) {
|
|
217
208
|
var _getStringDiff2 = getStringDiff(containerValueSnapShot.current, newValue),
|
|
218
209
|
addedOnSnapShot = _getStringDiff2.added;
|
|
219
|
-
isBeginWithNumber = /^\d$/.test(head(addedOnSnapShot));
|
|
210
|
+
isBeginWithNumber = /^\d$/.test(head(addedOnSnapShot.join('').trim()));
|
|
220
211
|
inputStringAfterDropDownShow = addedOnSnapShot.join('');
|
|
221
212
|
}
|
|
222
213
|
if (pressAOperator) {
|
|
@@ -245,6 +236,18 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
245
236
|
setInputAfterDropDownShow(inputStringAfterDropDownShow);
|
|
246
237
|
onChange(valueExpr);
|
|
247
238
|
}, [containerValue, open]);
|
|
239
|
+
var _useKeyEventEffect = useKeyEventEffect({
|
|
240
|
+
closeDorpDown: closeDorpDown,
|
|
241
|
+
setOffsetX: setOffsetX,
|
|
242
|
+
setOpen: setOpen,
|
|
243
|
+
containerRef: containerRef,
|
|
244
|
+
tempSpanRef: tempSpanRef,
|
|
245
|
+
inputAfterDropDownShow: inputAfterDropDownShow,
|
|
246
|
+
options: options
|
|
247
|
+
}),
|
|
248
|
+
handleMouseUp = _useKeyEventEffect.handleMouseUp,
|
|
249
|
+
handleKeyDown = _useKeyEventEffect.handleKeyDown,
|
|
250
|
+
filteredHierarchyOptions = _useKeyEventEffect.filteredHierarchyOptions;
|
|
248
251
|
|
|
249
252
|
// 编辑状态时自动聚焦
|
|
250
253
|
useEffect(function () {
|
|
@@ -253,309 +256,46 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
253
256
|
(_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 || _containerRef$current2.focus();
|
|
254
257
|
}
|
|
255
258
|
}, [isEditing]);
|
|
256
|
-
var
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
var div = containerRef.current;
|
|
264
|
-
if (!div) return;
|
|
265
|
-
closeDorpDown();
|
|
266
|
-
var selection = window.getSelection();
|
|
267
|
-
if (!selection || selection.rangeCount === 0) return;
|
|
268
|
-
var range = selection.getRangeAt(0);
|
|
269
|
-
var startContainer = range.startContainer;
|
|
270
|
-
var currentSpan = getSpanAtCursor(startContainer);
|
|
271
|
-
if (!currentSpan || currentSpan === div) return;
|
|
272
|
-
var isLabelSpan = currentSpan.classList.contains('formula-editor-token-label');
|
|
273
|
-
|
|
274
|
-
// 如果在 label span 内,移动到下一个 span 的开头
|
|
275
|
-
if (isLabelSpan) {
|
|
276
|
-
var allSpans = Array.from(div.querySelectorAll('span'));
|
|
277
|
-
var currentIndex = allSpans.indexOf(currentSpan);
|
|
278
|
-
if (currentIndex < allSpans.length - 1) {
|
|
279
|
-
var nextSpan = allSpans[currentIndex + 1];
|
|
280
|
-
var newRange = document.createRange();
|
|
281
|
-
newRange.selectNodeContents(nextSpan);
|
|
282
|
-
newRange.collapse(true);
|
|
283
|
-
selection.removeAllRanges();
|
|
284
|
-
selection.addRange(newRange);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}, [closeDorpDown]);
|
|
288
|
-
var handleKeyDown = useCallback(function (e) {
|
|
289
|
-
var _currentSpan$textCont, _currentSpan$textCont2;
|
|
290
|
-
if (e.key === 'ArrowUp' || e.key === "ArrowDown") {
|
|
291
|
-
e.preventDefault();
|
|
292
|
-
e.stopPropagation();
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
var div = containerRef.current;
|
|
296
|
-
if (!div) return;
|
|
297
|
-
var selection = window.getSelection();
|
|
298
|
-
if (!selection || selection.rangeCount === 0) return;
|
|
299
|
-
var range = selection.getRangeAt(0);
|
|
300
|
-
var startContainer = range.startContainer,
|
|
301
|
-
startOffset = range.startOffset;
|
|
302
|
-
var currentSpan = getSpanAtCursor(startContainer);
|
|
303
|
-
if (!currentSpan || currentSpan === div) return;
|
|
304
|
-
var allSpans = Array.from(div.querySelectorAll('span'));
|
|
305
|
-
var currentIndex = allSpans.indexOf(currentSpan);
|
|
306
|
-
var textLen = (_currentSpan$textCont = (_currentSpan$textCont2 = currentSpan.textContent) === null || _currentSpan$textCont2 === void 0 ? void 0 : _currentSpan$textCont2.length) !== null && _currentSpan$textCont !== void 0 ? _currentSpan$textCont : 0;
|
|
307
|
-
|
|
308
|
-
// 方向键:跳过 label span
|
|
309
|
-
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
|
|
310
|
-
closeDorpDown();
|
|
311
|
-
var position = getCursorPositionInSpan(currentSpan);
|
|
312
|
-
var isAtEnd = startOffset >= textLen;
|
|
313
|
-
var isAtStart = startOffset === 0;
|
|
314
|
-
var isAlmostEnd = startOffset >= textLen - 1;
|
|
315
|
-
var isAlmostStart = position === 1;
|
|
316
|
-
if (e.key === 'ArrowRight' && isAtEnd) {
|
|
317
|
-
// 向右 + 在末尾 -> 跳到下一个 span 的开头(跳过 label span)
|
|
318
|
-
var targetIndex = currentIndex + 1;
|
|
319
|
-
while (targetIndex < allSpans.length && allSpans[targetIndex].classList.contains('formula-editor-token-label')) {
|
|
320
|
-
targetIndex++;
|
|
321
|
-
}
|
|
322
|
-
if (targetIndex < allSpans.length && targetIndex !== currentIndex + 1) {
|
|
323
|
-
e.preventDefault();
|
|
324
|
-
var targetSpan = allSpans[targetIndex];
|
|
325
|
-
var newRange = document.createRange();
|
|
326
|
-
newRange.selectNodeContents(targetSpan);
|
|
327
|
-
newRange.collapse(true);
|
|
328
|
-
selection.removeAllRanges();
|
|
329
|
-
selection.addRange(newRange);
|
|
330
|
-
}
|
|
331
|
-
} else if (e.key === 'ArrowLeft' && isAtStart) {
|
|
332
|
-
// 向左 + 在开头 -> 跳到上一个 span 的末尾(跳过 label span)
|
|
333
|
-
var _targetIndex = currentIndex - 1;
|
|
334
|
-
while (_targetIndex >= 0 && allSpans[_targetIndex].classList.contains('formula-editor-token-label')) {
|
|
335
|
-
_targetIndex--;
|
|
336
|
-
}
|
|
337
|
-
if (_targetIndex >= 0 && _targetIndex !== currentIndex - 1) {
|
|
338
|
-
e.preventDefault();
|
|
339
|
-
var _targetSpan = allSpans[_targetIndex];
|
|
340
|
-
var _newRange = document.createRange();
|
|
341
|
-
_newRange.selectNodeContents(_targetSpan);
|
|
342
|
-
_newRange.collapse(false);
|
|
343
|
-
selection.removeAllRanges();
|
|
344
|
-
selection.addRange(_newRange);
|
|
345
|
-
}
|
|
346
|
-
} else if (e.key === 'ArrowRight' && isAlmostEnd) {
|
|
347
|
-
var _targetIndex2 = currentIndex + 1;
|
|
348
|
-
if (_targetIndex2 < allSpans.length && !allSpans[_targetIndex2].classList.contains('formula-editor-token-label')) {
|
|
349
|
-
e.preventDefault();
|
|
350
|
-
var _targetSpan2 = allSpans[_targetIndex2];
|
|
351
|
-
var _newRange2 = document.createRange();
|
|
352
|
-
_newRange2.selectNodeContents(_targetSpan2);
|
|
353
|
-
_newRange2.collapse(true);
|
|
354
|
-
selection.removeAllRanges();
|
|
355
|
-
selection.addRange(_newRange2);
|
|
356
|
-
}
|
|
357
|
-
} else if (e.key === 'ArrowLeft' && isAlmostStart) {
|
|
358
|
-
var _targetIndex3 = currentIndex - 1;
|
|
359
|
-
if (_targetIndex3 >= 0 && !allSpans[_targetIndex3].classList.contains('formula-editor-token-label')) {
|
|
360
|
-
e.preventDefault();
|
|
361
|
-
var _targetSpan3 = allSpans[_targetIndex3];
|
|
362
|
-
var _newRange3 = document.createRange();
|
|
363
|
-
_newRange3.selectNodeContents(_targetSpan3);
|
|
364
|
-
_newRange3.collapse(false);
|
|
365
|
-
selection.removeAllRanges();
|
|
366
|
-
selection.addRange(_newRange3);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// 删除键逻辑
|
|
373
|
-
if (e.key !== 'Delete' && e.key !== 'Backspace') return;
|
|
374
|
-
|
|
375
|
-
// 不在 label span 内,在 span 开头且前一个是 label 时,删除整个 label span
|
|
376
|
-
if (startOffset === 0 && e.key === 'Backspace' && currentIndex > 0) {
|
|
377
|
-
var prevSpan = allSpans[currentIndex - 1];
|
|
378
|
-
if (prevSpan.classList.contains('formula-editor-token-label')) {
|
|
379
|
-
e.preventDefault();
|
|
380
|
-
e.stopPropagation();
|
|
381
|
-
prevSpan.remove();
|
|
382
|
-
var _newRange4 = document.createRange();
|
|
383
|
-
|
|
384
|
-
// 如果当前 span 有内容,光标放在开头;否则找最近的有内容的 span
|
|
385
|
-
if (currentSpan.textContent !== '') {
|
|
386
|
-
_newRange4.selectNodeContents(currentSpan);
|
|
387
|
-
_newRange4.collapse(true);
|
|
388
|
-
} else {
|
|
389
|
-
// 找前一个最近的有内容的 span
|
|
390
|
-
var _targetSpan4;
|
|
391
|
-
for (var i = currentIndex - 2; i >= 0; i--) {
|
|
392
|
-
if (allSpans[i].textContent !== '') {
|
|
393
|
-
_targetSpan4 = allSpans[i];
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (_targetSpan4) {
|
|
398
|
-
_newRange4.selectNodeContents(_targetSpan4);
|
|
399
|
-
_newRange4.collapse(false);
|
|
400
|
-
} else {
|
|
401
|
-
_newRange4.selectNodeContents(div);
|
|
402
|
-
_newRange4.collapse(true);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
selection.removeAllRanges();
|
|
406
|
-
selection.addRange(_newRange4);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
259
|
+
var handleChange = useCallback(function (value) {
|
|
260
|
+
onChange(!isNil(value) ? value : null);
|
|
261
|
+
}, [onChange]);
|
|
262
|
+
useEffect(function () {
|
|
263
|
+
if (isEditing) {
|
|
264
|
+
var _inputNumberRef$curre;
|
|
265
|
+
inputNumberRef === null || inputNumberRef === void 0 || (_inputNumberRef$curre = inputNumberRef.current) === null || _inputNumberRef$curre === void 0 || _inputNumberRef$curre.focus();
|
|
409
266
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
var
|
|
414
|
-
|
|
415
|
-
if (!_prevSpan.classList.contains('formula-editor-token-label')) {
|
|
416
|
-
e.preventDefault();
|
|
417
|
-
e.stopPropagation();
|
|
418
|
-
var prevText = _prevSpan.textContent || '';
|
|
419
|
-
if (prevText.length > 0) {
|
|
420
|
-
_prevSpan.textContent = prevText.slice(0, -1);
|
|
421
|
-
}
|
|
422
|
-
var _newRange5 = document.createRange();
|
|
423
|
-
_newRange5.selectNodeContents(_prevSpan);
|
|
424
|
-
_newRange5.collapse(false);
|
|
425
|
-
selection.removeAllRanges();
|
|
426
|
-
selection.addRange(_newRange5);
|
|
427
|
-
}
|
|
267
|
+
}, [isEditing]);
|
|
268
|
+
var reFocus = useCallback(function () {
|
|
269
|
+
if (isEditing) {
|
|
270
|
+
var _inputNumberRef$curre2;
|
|
271
|
+
inputNumberRef === null || inputNumberRef === void 0 || (_inputNumberRef$curre2 = inputNumberRef.current) === null || _inputNumberRef$curre2 === void 0 || _inputNumberRef$curre2.focus();
|
|
428
272
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
var span = allSpansNow[_i];
|
|
444
|
-
if (span.textContent !== '') {
|
|
445
|
-
_targetSpan5 = span;
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
// 如果找到的有内容 span 是 label,则把光标放在 label span 的后一个 span 的开头
|
|
450
|
-
if (_targetSpan5 && _targetSpan5.classList.contains('formula-editor-token-label')) {
|
|
451
|
-
var labelIndex = allSpansNow.indexOf(_targetSpan5);
|
|
452
|
-
if (labelIndex < allSpansNow.length - 1) {
|
|
453
|
-
var nextSpan = allSpansNow[labelIndex + 1];
|
|
454
|
-
var _newRange6 = document.createRange();
|
|
455
|
-
_newRange6.selectNodeContents(nextSpan);
|
|
456
|
-
_newRange6.collapse(true);
|
|
457
|
-
selection.removeAllRanges();
|
|
458
|
-
selection.addRange(_newRange6);
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// 否则把光标放在找到的 span 的末尾
|
|
464
|
-
if (_targetSpan5) {
|
|
465
|
-
var _newRange7 = document.createRange();
|
|
466
|
-
_newRange7.selectNodeContents(_targetSpan5);
|
|
467
|
-
_newRange7.collapse(false);
|
|
468
|
-
selection.removeAllRanges();
|
|
469
|
-
selection.addRange(_newRange7);
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
if (!currentSpanNow || currentSpanNow === div) return;
|
|
474
|
-
if (currentSpanNow.textContent === '') {
|
|
475
|
-
var currentIndexNow = allSpansNow.indexOf(currentSpanNow);
|
|
476
|
-
var _newRange8 = document.createRange();
|
|
477
|
-
if (currentIndexNow > 0) {
|
|
478
|
-
// 移到前一个 span 的末尾,但如果前一个 span 是 label,则移到后一个 span 的开头
|
|
479
|
-
var _targetSpan6 = allSpansNow[currentIndexNow - 1];
|
|
480
|
-
if (_targetSpan6.classList.contains('formula-editor-token-label')) {
|
|
481
|
-
// 移到 label span 的后一个 span 的开头
|
|
482
|
-
if (currentIndexNow < allSpansNow.length - 1) {
|
|
483
|
-
var _nextSpan = allSpansNow[currentIndexNow + 1];
|
|
484
|
-
_newRange8.selectNodeContents(_nextSpan);
|
|
485
|
-
_newRange8.collapse(true);
|
|
486
|
-
} else {
|
|
487
|
-
_newRange8.selectNodeContents(div);
|
|
488
|
-
_newRange8.collapse(false);
|
|
489
|
-
}
|
|
490
|
-
} else {
|
|
491
|
-
_newRange8.selectNodeContents(_targetSpan6);
|
|
492
|
-
_newRange8.collapse(false);
|
|
493
|
-
}
|
|
494
|
-
currentSpanNow.remove();
|
|
495
|
-
} else if (allSpansNow.length > 1) {
|
|
496
|
-
// 第一个 span 为空,移到下一个 span 的开头
|
|
497
|
-
var _nextSpan2 = allSpansNow[currentIndexNow + 1];
|
|
498
|
-
_newRange8.selectNodeContents(_nextSpan2);
|
|
499
|
-
_newRange8.collapse(true);
|
|
500
|
-
currentSpanNow.remove();
|
|
501
|
-
} else {
|
|
502
|
-
// 只有一个空 span,保持空
|
|
503
|
-
_newRange8.selectNodeContents(div);
|
|
504
|
-
_newRange8.collapse(true);
|
|
505
|
-
}
|
|
506
|
-
selection.removeAllRanges();
|
|
507
|
-
selection.addRange(_newRange8);
|
|
273
|
+
}, [isEditing]);
|
|
274
|
+
if (choseEditor && choseEditor(record)) {
|
|
275
|
+
var _ref3;
|
|
276
|
+
return /*#__PURE__*/_jsx(Input, _objectSpread(_objectSpread({
|
|
277
|
+
ref: inputNumberRef,
|
|
278
|
+
onBlur: reFocus
|
|
279
|
+
}, inputArgs), {}, {
|
|
280
|
+
className: "number-editor",
|
|
281
|
+
onMouseDown: function onMouseDown(e) {
|
|
282
|
+
return e.stopPropagation();
|
|
283
|
+
},
|
|
284
|
+
value: (_ref3 = originValue) !== null && _ref3 !== void 0 ? _ref3 : '',
|
|
285
|
+
onChange: function onChange(e) {
|
|
286
|
+
handleChange(e.target.value);
|
|
508
287
|
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
var dropdownContent = /*#__PURE__*/_jsx(Menu, {
|
|
512
|
-
className: "formula-editor-dropdown",
|
|
513
|
-
style: {
|
|
514
|
-
transform: "translateX(".concat(offsetX, "px)")
|
|
515
|
-
},
|
|
516
|
-
onMouseDown: function onMouseDown(e) {
|
|
517
|
-
e.preventDefault();
|
|
518
|
-
e.stopPropagation();
|
|
519
|
-
},
|
|
520
|
-
children: filteredHierarchyOptions.length === 0 ? /*#__PURE__*/_jsx(Menu.Item, {
|
|
521
|
-
disabled: true,
|
|
522
|
-
children: /*#__PURE__*/_jsx("span", {
|
|
523
|
-
className: "formula-editor-dropdown-no-data",
|
|
524
|
-
children: "\u6682\u65E0\u6570\u636E"
|
|
525
|
-
})
|
|
526
|
-
}, "no-data") : filteredHierarchyOptions.map(function (opt) {
|
|
527
|
-
return opt.children && opt.children.length > 0 ? /*#__PURE__*/_jsx(Menu.SubMenu, {
|
|
528
|
-
title: opt.label,
|
|
529
|
-
className: "formula-editor-dropdown-submenu",
|
|
530
|
-
children: opt.children.map(function (child) {
|
|
531
|
-
return /*#__PURE__*/_jsx(Menu.Item, {
|
|
532
|
-
className: "formula-editor-dropdown-item",
|
|
533
|
-
onClick: function onClick() {
|
|
534
|
-
return handleOptionClick(child);
|
|
535
|
-
},
|
|
536
|
-
children: child.label
|
|
537
|
-
}, child.value);
|
|
538
|
-
})
|
|
539
|
-
}, opt.value) : /*#__PURE__*/_jsxs(Menu.Item, {
|
|
540
|
-
className: "formula-editor-dropdown-item",
|
|
541
|
-
onClick: function onClick() {
|
|
542
|
-
return handleOptionClick(opt);
|
|
543
|
-
},
|
|
544
|
-
children: [opt.label, opt.info ? /*#__PURE__*/_jsx(Tooltip, {
|
|
545
|
-
title: opt.info,
|
|
546
|
-
children: /*#__PURE__*/_jsx(InfoCircleOutlined, {
|
|
547
|
-
style: {
|
|
548
|
-
color: '#FF9900',
|
|
549
|
-
marginLeft: 4
|
|
550
|
-
}
|
|
551
|
-
})
|
|
552
|
-
}) : null]
|
|
553
|
-
}, opt.value);
|
|
554
|
-
})
|
|
555
|
-
});
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
556
290
|
return /*#__PURE__*/_jsx(Dropdown, {
|
|
557
291
|
overlayClassName: "dropDownOffset",
|
|
558
|
-
overlay:
|
|
292
|
+
overlay: /*#__PURE__*/_jsx(DropMenu, {
|
|
293
|
+
options: filteredHierarchyOptions,
|
|
294
|
+
onOptionClick: handleOptionClick,
|
|
295
|
+
style: {
|
|
296
|
+
transform: "translateX(".concat(offsetX, "px)")
|
|
297
|
+
}
|
|
298
|
+
}),
|
|
559
299
|
open: open,
|
|
560
300
|
children: /*#__PURE__*/_jsx("div", {
|
|
561
301
|
ref: containerRef,
|
|
@@ -571,13 +311,13 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
571
311
|
spellCheck: false,
|
|
572
312
|
children: isEmpty(tokens) ? /*#__PURE__*/_jsx("span", {
|
|
573
313
|
style: {
|
|
574
|
-
minWidth:
|
|
314
|
+
minWidth: 1,
|
|
575
315
|
height: 40
|
|
576
316
|
}
|
|
577
317
|
}) : tokens.map(function (token, index) {
|
|
578
318
|
return token.type === 'end' ? /*#__PURE__*/_jsx("span", {
|
|
579
319
|
style: {
|
|
580
|
-
minWidth:
|
|
320
|
+
minWidth: 1,
|
|
581
321
|
height: 40
|
|
582
322
|
}
|
|
583
323
|
}, index) : /*#__PURE__*/_jsx("span", {
|
|
@@ -593,22 +333,40 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
593
333
|
var flatOptions = flattenOptions(options);
|
|
594
334
|
|
|
595
335
|
// checker:使用 validateCalculationExpr 校验语法 + 校验变量是否都在 options 中
|
|
596
|
-
FormulaInputEditor.checker = function (value) {
|
|
336
|
+
FormulaInputEditor.checker = function (value, record, currentValue) {
|
|
597
337
|
if (isNil(value)) return true;
|
|
338
|
+
if (choseEditor && choseEditor(record)) {
|
|
339
|
+
var _String;
|
|
340
|
+
var result = (_String = String(value)) === null || _String === void 0 ? void 0 : _String.replace(/,/g, '');
|
|
341
|
+
result = formulaString(result, currentValue, precision);
|
|
342
|
+
// parse number with thousands separator
|
|
343
|
+
var range = rangeMethod === null || rangeMethod === void 0 ? void 0 : rangeMethod(record);
|
|
344
|
+
if (!isNil(range === null || range === void 0 ? void 0 : range.max) && !isNil(range === null || range === void 0 ? void 0 : range.min)) {
|
|
345
|
+
var _ref4 = range || {},
|
|
346
|
+
_ref4$max = _ref4.max,
|
|
347
|
+
_max = _ref4$max === void 0 ? 0 : _ref4$max,
|
|
348
|
+
_ref4$min = _ref4.min,
|
|
349
|
+
_min = _ref4$min === void 0 ? 0 : _ref4$min;
|
|
350
|
+
if (result < _min || result > _max) {
|
|
351
|
+
warnMethod === null || warnMethod === void 0 || warnMethod(record);
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (isNaN(result)) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
598
360
|
var strValue = String(value);
|
|
599
|
-
|
|
600
361
|
// 如果以 = 开头,去掉 = 后校验
|
|
601
362
|
var expr = strValue.startsWith('=') ? strValue.slice(1) : strValue;
|
|
602
363
|
var parsedExpr = replaceLabelsWithValues(expr, flatOptions);
|
|
603
|
-
|
|
604
364
|
// 空表达式不校验
|
|
605
365
|
if (!parsedExpr.trim()) return true;
|
|
606
|
-
|
|
607
366
|
// 1. 语法校验
|
|
608
367
|
if (!validateCalculationExpr(parsedExpr)) {
|
|
609
368
|
return false;
|
|
610
369
|
}
|
|
611
|
-
|
|
612
370
|
// 2. 变量校验:所有变量必须在 options 中
|
|
613
371
|
if (!validateVariables(parsedExpr, flatOptions)) {
|
|
614
372
|
return false;
|
|
@@ -617,10 +375,15 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
617
375
|
};
|
|
618
376
|
|
|
619
377
|
// formula:校验公式,校验成功返回 currentValue
|
|
620
|
-
FormulaInputEditor.formula = function (value, currentValue) {
|
|
378
|
+
FormulaInputEditor.formula = function (value, record, currentValue) {
|
|
621
379
|
if (isNil(value)) {
|
|
622
380
|
return null;
|
|
623
381
|
}
|
|
382
|
+
if (choseEditor && choseEditor(record)) {
|
|
383
|
+
var _String2;
|
|
384
|
+
var formatValue = (_String2 = String(value)) === null || _String2 === void 0 ? void 0 : _String2.replace(/,/g, '');
|
|
385
|
+
return formulaString(formatValue, currentValue, precision, max, min, true);
|
|
386
|
+
}
|
|
624
387
|
var strValue = String(value);
|
|
625
388
|
var hasEqualSign = strValue.startsWith('=');
|
|
626
389
|
var expr = hasEqualSign ? strValue.slice(1) : strValue;
|
|
@@ -645,8 +408,17 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
645
408
|
};
|
|
646
409
|
|
|
647
410
|
// parser:粘贴时处理,将 label 转换为 value
|
|
648
|
-
FormulaInputEditor.parser = function (value) {
|
|
411
|
+
FormulaInputEditor.parser = function (value, record, currentValue) {
|
|
649
412
|
if (isNil(value)) return null;
|
|
413
|
+
if (choseEditor && choseEditor(record)) {
|
|
414
|
+
var _String3;
|
|
415
|
+
var result = (_String3 = String(value)) === null || _String3 === void 0 ? void 0 : _String3.replace(/[,|%]/g, '');
|
|
416
|
+
result = formulaString(result, currentValue, precision);
|
|
417
|
+
if (isNil(result) || isNaN(result)) {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
650
422
|
var strValue = String(value);
|
|
651
423
|
var hasEqualSign = strValue.startsWith('=');
|
|
652
424
|
var expr = hasEqualSign ? strValue.slice(1) : strValue;
|
|
@@ -657,15 +429,24 @@ export var getFormulaInputEditor = function getFormulaInputEditor(options, repla
|
|
|
657
429
|
};
|
|
658
430
|
|
|
659
431
|
// formatter:复制时触发,将 value 转换为 label
|
|
660
|
-
FormulaInputEditor.formatter = function (value) {
|
|
661
|
-
if (
|
|
662
|
-
|
|
432
|
+
FormulaInputEditor.formatter = function (value, record) {
|
|
433
|
+
if (choseEditor && choseEditor(record)) {
|
|
434
|
+
var _String4;
|
|
435
|
+
if (isNil(value) || isNaN(value)) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
var result = parseFloat((_String4 = String(value)) === null || _String4 === void 0 ? void 0 : _String4.replace(/,/g, ''));
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
var replaceValue = replaceIndex ? get(record, [replaceIndex]) : value;
|
|
442
|
+
if (isNil(replaceValue)) return null;
|
|
443
|
+
var strValue = String(replaceValue);
|
|
663
444
|
var hasEqualSign = strValue.startsWith('=');
|
|
664
445
|
var expr = hasEqualSign ? strValue.slice(1) : strValue;
|
|
665
446
|
|
|
666
447
|
// 将 value 替换为 label
|
|
667
448
|
var displayExpr = replaceValuesWithLabels(expr, flatOptions);
|
|
668
|
-
return hasEqualSign ? '=' + displayExpr
|
|
449
|
+
return hasEqualSign ? displayExpr : '=' + displayExpr;
|
|
669
450
|
};
|
|
670
451
|
return FormulaInputEditor;
|
|
671
452
|
};
|