@zhenliang/sheet 0.2.4 → 0.2.5-beta.0

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,14 @@
1
+ import type { SheetType } from "../../../type";
2
+ import './index.less';
3
+ export interface OptionItem {
4
+ label: string;
5
+ value: string;
6
+ info?: string;
7
+ children?: OptionItem[];
8
+ }
9
+ export interface FormulaInputProps {
10
+ options: OptionItem[];
11
+ value?: string;
12
+ }
13
+ export declare const getFormulaInputEditor: (options: OptionItem[]) => SheetType.CellEditor;
14
+ export default getFormulaInputEditor;
@@ -0,0 +1,645 @@
1
+ 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); }
2
+ 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; }
3
+ 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; }
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
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
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
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
9
+ 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
+ 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
+ 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
+ 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
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
14
+ import { Dropdown, Menu, Tooltip } from 'antd';
15
+ import { head, isEmpty, isNil } from 'lodash';
16
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
17
+ import { InfoCircleOutlined } from '@ant-design/icons';
18
+ import { flattenOptions, getStringDiff, replaceLabelsWithValues, replaceLongestDiff, replaceValuesWithLabels, tokenize, validateVariables, getCursorPositionInSpan } from "./utils";
19
+ import validateCalculationExpr from "./vaildFormula";
20
+ import "./index.less";
21
+ import { jsx as _jsx } from "react/jsx-runtime";
22
+ import { jsxs as _jsxs } from "react/jsx-runtime";
23
+ var OPERATORS = ['=', '+', '-', '*', '/'];
24
+
25
+ // 获取光标所在的 span
26
+ var getSpanAtCursor = function getSpanAtCursor(startContainer) {
27
+ if (startContainer.nodeType === Node.TEXT_NODE) {
28
+ return startContainer.parentElement;
29
+ }
30
+ if (startContainer.nodeType === Node.ELEMENT_NODE) {
31
+ return startContainer;
32
+ }
33
+ return null;
34
+ };
35
+ export var getFormulaInputEditor = function getFormulaInputEditor(options) {
36
+ var FormulaInputEditor = function FormulaInputEditor(props) {
37
+ var value = props.value,
38
+ onChange = props.onChange,
39
+ isEditing = props.isEditing;
40
+ var _useState = useState(value),
41
+ _useState2 = _slicedToArray(_useState, 2),
42
+ inputValue = _useState2[0],
43
+ setInputValue = _useState2[1];
44
+ var _useState3 = useState(0),
45
+ _useState4 = _slicedToArray(_useState3, 2),
46
+ offsetX = _useState4[0],
47
+ setOffsetX = _useState4[1];
48
+ // 为了不重新render,再写一个
49
+ var _useState5 = useState(value),
50
+ _useState6 = _slicedToArray(_useState5, 2),
51
+ containerValue = _useState6[0],
52
+ setContainerValue = _useState6[1];
53
+ var _useState7 = useState(false),
54
+ _useState8 = _slicedToArray(_useState7, 2),
55
+ open = _useState8[0],
56
+ setOpen = _useState8[1];
57
+ var containerValueSnapShot = useRef('');
58
+ var _useState9 = useState(''),
59
+ _useState10 = _slicedToArray(_useState9, 2),
60
+ inputAfterDropDownShow = _useState10[0],
61
+ setInputAfterDropDownShow = _useState10[1];
62
+ var containerRef = useRef(null);
63
+ var inputOperatorPosition = useRef(0);
64
+
65
+ // 扁平化所有选项
66
+ var flatOptions = useMemo(function () {
67
+ return flattenOptions(options);
68
+ }, [options]);
69
+
70
+ // 解析表达式为 token 数组(内部自动将 value 转换为 label)
71
+ var tokens = useMemo(function () {
72
+ return tokenize(inputValue, flatOptions);
73
+ }, [inputValue, flatOptions]);
74
+ // 根据搜索文本获取筛选后的层级选项(保持层级结构)
75
+ var filteredHierarchyOptions = useMemo(function () {
76
+ var searchText = inputAfterDropDownShow;
77
+ // 如果搜索文本为空,显示所有选项(保持层级)
78
+ if (!searchText) {
79
+ return options;
80
+ }
81
+
82
+ // 根据搜索文本筛选,保持层级结构
83
+ var filterWithHierarchy = function filterWithHierarchy(items) {
84
+ var result = [];
85
+ var _iterator = _createForOfIteratorHelper(items),
86
+ _step;
87
+ try {
88
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
89
+ var item = _step.value;
90
+ var labelMatch = item.label.includes(searchText);
91
+ // 筛选 children
92
+ var filteredChildren = item.children ? filterWithHierarchy(item.children) : [];
93
+ if (labelMatch || filteredChildren.length > 0) {
94
+ result.push(_objectSpread(_objectSpread({}, item), {}, {
95
+ children: filteredChildren.length > 0 ? filteredChildren : item.children
96
+ }));
97
+ }
98
+ }
99
+ } catch (err) {
100
+ _iterator.e(err);
101
+ } finally {
102
+ _iterator.f();
103
+ }
104
+ return result;
105
+ };
106
+ return filterWithHierarchy(options);
107
+ }, [inputAfterDropDownShow]);
108
+ useEffect(function () {
109
+ if (!open) {
110
+ containerValueSnapShot.current = '';
111
+ }
112
+ setInputAfterDropDownShow('');
113
+ }, [open]);
114
+
115
+ // 处理选项点击:删除用户刚输入的值,将选中的 label 放到运算符后面
116
+ var handleOptionClick = useCallback(function (opt) {
117
+ var container = containerRef.current;
118
+ if (!container) return;
119
+ var newValue = replaceLongestDiff(containerValueSnapShot.current, opt.label, inputOperatorPosition.current);
120
+ setOpen(false);
121
+ containerValueSnapShot.current = '';
122
+ var expr = newValue;
123
+ var valueExpr = replaceLabelsWithValues(expr, flatOptions);
124
+ setContainerValue(valueExpr);
125
+ onChange(valueExpr);
126
+
127
+ // 获取当前光标所在的 span
128
+ var selection = window.getSelection();
129
+ if (!selection || selection.rangeCount === 0) return;
130
+ var range = selection.getRangeAt(0);
131
+ var startContainer = range.startContainer;
132
+ var currentSpan = startContainer.nodeType === Node.TEXT_NODE ? startContainer.parentElement : startContainer;
133
+ if (!currentSpan || currentSpan === container) return;
134
+
135
+ // 删除 inputAfterDropDownShow
136
+ var spanText = currentSpan.textContent || '';
137
+ var deleteText = inputAfterDropDownShow;
138
+ var deleteIndex = spanText.indexOf(deleteText);
139
+ if (deleteIndex !== -1) {
140
+ currentSpan.textContent = spanText.slice(0, deleteIndex) + spanText.slice(deleteIndex + deleteText.length);
141
+ }
142
+
143
+ // 新建 label span,内容为 opt.label,插入到当前 span 后面
144
+ var labelSpan = document.createElement('span');
145
+ labelSpan.className = 'formula-editor-token-label';
146
+ labelSpan.textContent = opt.label;
147
+ currentSpan.after(labelSpan);
148
+
149
+ // 将光标移到下一个 span 的开头
150
+ var allSpans = Array.from(container.querySelectorAll('span'));
151
+ var labelSpanIndex = allSpans.indexOf(labelSpan);
152
+ var targetSpan = allSpans[labelSpanIndex + 1];
153
+
154
+ // 如果下一个 span 是 label 类型或没有下一个 span,则插入一个空 span
155
+ if (!targetSpan || targetSpan.classList.contains('formula-editor-token-label')) {
156
+ targetSpan = document.createElement('span');
157
+ targetSpan.className = 'formula-editor-token-other';
158
+ targetSpan.textContent = '';
159
+ labelSpan.after(targetSpan);
160
+ }
161
+
162
+ // 将光标移到目标 span 的开头
163
+ var newRange = document.createRange();
164
+ newRange.selectNodeContents(targetSpan);
165
+ newRange.collapse(true);
166
+ selection.removeAllRanges();
167
+ selection.addRange(newRange);
168
+ }, [inputAfterDropDownShow]);
169
+
170
+ // 处理输入变化
171
+ var handleInput = useCallback(function () {
172
+ var container = containerRef.current;
173
+ if (!container) return;
174
+ var newValue = container.textContent || '';
175
+ var processedValue = newValue;
176
+ var expr = processedValue;
177
+ var valueExpr = replaceLabelsWithValues(expr, flatOptions);
178
+ var oldLabelText = replaceValuesWithLabels(containerValue, flatOptions);
179
+ var firstDiffPosition = 0;
180
+ for (var i = 0; i < newValue.length; i++) {
181
+ if (newValue[i] !== oldLabelText[i]) {
182
+ firstDiffPosition = i;
183
+ break;
184
+ }
185
+ }
186
+ var _getStringDiff = getStringDiff(containerValue, valueExpr),
187
+ added = _getStringDiff.added,
188
+ removed = _getStringDiff.removed;
189
+ setContainerValue(valueExpr);
190
+ var pressAOperator = added.length === 1 && OPERATORS.includes(head(added));
191
+ var deleteAOperator = removed.length === 1 && OPERATORS.includes(head(removed));
192
+ var isBeginWithNumber;
193
+ var inputStringAfterDropDownShow = '';
194
+ if (open) {
195
+ var _getStringDiff2 = getStringDiff(containerValueSnapShot.current, newValue),
196
+ addedOnSnapShot = _getStringDiff2.added;
197
+ isBeginWithNumber = /^\d$/.test(head(addedOnSnapShot));
198
+ inputStringAfterDropDownShow = addedOnSnapShot.join('');
199
+ }
200
+ if (pressAOperator) {
201
+ // 按下了运算符,下拉框刚打开,重置输入记录
202
+ containerValueSnapShot.current = expr;
203
+ inputStringAfterDropDownShow = '';
204
+ inputOperatorPosition.current = firstDiffPosition;
205
+
206
+ // 获取当前光标距离 containerRef 最左边的距离
207
+ var selection = window.getSelection();
208
+ var cursorDistance = 0;
209
+ if (selection && selection.rangeCount > 0) {
210
+ var _containerRef$current;
211
+ var range = selection.getRangeAt(0);
212
+ var containerRect = (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.getBoundingClientRect();
213
+ var cursorRect = range.getBoundingClientRect();
214
+ if (containerRect && cursorRect) {
215
+ cursorDistance = cursorRect.left - containerRect.left;
216
+ }
217
+ }
218
+ setOffsetX(cursorDistance < 150 ? 0 : cursorDistance - 8);
219
+ setOpen(true);
220
+ } else if (open && deleteAOperator || isBeginWithNumber) {
221
+ setOpen(false);
222
+ }
223
+ setInputAfterDropDownShow(inputStringAfterDropDownShow);
224
+ onChange(valueExpr);
225
+ }, [containerValue, open]);
226
+
227
+ // 编辑状态时自动聚焦
228
+ useEffect(function () {
229
+ if (isEditing) {
230
+ var _containerRef$current2;
231
+ (_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 || _containerRef$current2.focus();
232
+ }
233
+ }, [isEditing]);
234
+ var closeDorpDown = useCallback(function () {
235
+ // 点击div内直接关闭
236
+ setOpen(false);
237
+ }, []);
238
+
239
+ // 鼠标释放时,如果光标在 label span 内,移动到后一个 span 的开头
240
+ var handleMouseUp = useCallback(function () {
241
+ var div = containerRef.current;
242
+ if (!div) return;
243
+ closeDorpDown();
244
+ var selection = window.getSelection();
245
+ if (!selection || selection.rangeCount === 0) return;
246
+ var range = selection.getRangeAt(0);
247
+ var startContainer = range.startContainer;
248
+ var currentSpan = getSpanAtCursor(startContainer);
249
+ if (!currentSpan || currentSpan === div) return;
250
+ var isLabelSpan = currentSpan.classList.contains('formula-editor-token-label');
251
+
252
+ // 如果在 label span 内,移动到下一个 span 的开头
253
+ if (isLabelSpan) {
254
+ var allSpans = Array.from(div.querySelectorAll('span'));
255
+ var currentIndex = allSpans.indexOf(currentSpan);
256
+ if (currentIndex < allSpans.length - 1) {
257
+ var nextSpan = allSpans[currentIndex + 1];
258
+ var newRange = document.createRange();
259
+ newRange.selectNodeContents(nextSpan);
260
+ newRange.collapse(true);
261
+ selection.removeAllRanges();
262
+ selection.addRange(newRange);
263
+ }
264
+ }
265
+ }, [closeDorpDown]);
266
+ var handleKeyDown = useCallback(function (e) {
267
+ var _currentSpan$textCont, _currentSpan$textCont2;
268
+ var div = containerRef.current;
269
+ if (!div) return;
270
+ var selection = window.getSelection();
271
+ if (!selection || selection.rangeCount === 0) return;
272
+ var range = selection.getRangeAt(0);
273
+ var startContainer = range.startContainer,
274
+ startOffset = range.startOffset;
275
+ var currentSpan = getSpanAtCursor(startContainer);
276
+ if (!currentSpan || currentSpan === div) return;
277
+ var allSpans = Array.from(div.querySelectorAll('span'));
278
+ var currentIndex = allSpans.indexOf(currentSpan);
279
+ 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;
280
+
281
+ // 方向键:跳过 label span
282
+ if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
283
+ closeDorpDown();
284
+ var position = getCursorPositionInSpan(currentSpan);
285
+ var isAtEnd = startOffset >= textLen;
286
+ var isAtStart = startOffset === 0;
287
+ var isAlmostEnd = startOffset >= textLen - 1;
288
+ var isAlmostStart = position === 1;
289
+ if (e.key === 'ArrowRight' && isAtEnd) {
290
+ // 向右 + 在末尾 -> 跳到下一个 span 的开头(跳过 label span)
291
+ var targetIndex = currentIndex + 1;
292
+ while (targetIndex < allSpans.length && allSpans[targetIndex].classList.contains('formula-editor-token-label')) {
293
+ targetIndex++;
294
+ }
295
+ if (targetIndex < allSpans.length && targetIndex !== currentIndex + 1) {
296
+ e.preventDefault();
297
+ var targetSpan = allSpans[targetIndex];
298
+ var newRange = document.createRange();
299
+ newRange.selectNodeContents(targetSpan);
300
+ newRange.collapse(true);
301
+ selection.removeAllRanges();
302
+ selection.addRange(newRange);
303
+ }
304
+ } else if (e.key === 'ArrowLeft' && isAtStart) {
305
+ // 向左 + 在开头 -> 跳到上一个 span 的末尾(跳过 label span)
306
+ var _targetIndex = currentIndex - 1;
307
+ while (_targetIndex >= 0 && allSpans[_targetIndex].classList.contains('formula-editor-token-label')) {
308
+ _targetIndex--;
309
+ }
310
+ if (_targetIndex >= 0 && _targetIndex !== currentIndex - 1) {
311
+ e.preventDefault();
312
+ var _targetSpan = allSpans[_targetIndex];
313
+ var _newRange = document.createRange();
314
+ _newRange.selectNodeContents(_targetSpan);
315
+ _newRange.collapse(false);
316
+ selection.removeAllRanges();
317
+ selection.addRange(_newRange);
318
+ }
319
+ } else if (e.key === 'ArrowRight' && isAlmostEnd) {
320
+ var _targetIndex2 = currentIndex + 1;
321
+ if (_targetIndex2 < allSpans.length && !allSpans[_targetIndex2].classList.contains('formula-editor-token-label')) {
322
+ e.preventDefault();
323
+ var _targetSpan2 = allSpans[_targetIndex2];
324
+ var _newRange2 = document.createRange();
325
+ _newRange2.selectNodeContents(_targetSpan2);
326
+ _newRange2.collapse(true);
327
+ selection.removeAllRanges();
328
+ selection.addRange(_newRange2);
329
+ }
330
+ } else if (e.key === 'ArrowLeft' && isAlmostStart) {
331
+ var _targetIndex3 = currentIndex - 1;
332
+ if (_targetIndex3 >= 0 && !allSpans[_targetIndex3].classList.contains('formula-editor-token-label')) {
333
+ e.preventDefault();
334
+ var _targetSpan3 = allSpans[_targetIndex3];
335
+ var _newRange3 = document.createRange();
336
+ _newRange3.selectNodeContents(_targetSpan3);
337
+ _newRange3.collapse(false);
338
+ selection.removeAllRanges();
339
+ selection.addRange(_newRange3);
340
+ }
341
+ }
342
+ return;
343
+ }
344
+
345
+ // 删除键逻辑
346
+ if (e.key !== 'Delete' && e.key !== 'Backspace') return;
347
+
348
+ // 不在 label span 内,在 span 开头且前一个是 label 时,删除整个 label span
349
+ if (startOffset === 0 && e.key === 'Backspace' && currentIndex > 0) {
350
+ var prevSpan = allSpans[currentIndex - 1];
351
+ if (prevSpan.classList.contains('formula-editor-token-label')) {
352
+ e.preventDefault();
353
+ e.stopPropagation();
354
+ prevSpan.remove();
355
+ var _newRange4 = document.createRange();
356
+
357
+ // 如果当前 span 有内容,光标放在开头;否则找最近的有内容的 span
358
+ if (currentSpan.textContent !== '') {
359
+ _newRange4.selectNodeContents(currentSpan);
360
+ _newRange4.collapse(true);
361
+ } else {
362
+ // 找前一个最近的有内容的 span
363
+ var _targetSpan4;
364
+ for (var i = currentIndex - 2; i >= 0; i--) {
365
+ if (allSpans[i].textContent !== '') {
366
+ _targetSpan4 = allSpans[i];
367
+ break;
368
+ }
369
+ }
370
+ if (_targetSpan4) {
371
+ _newRange4.selectNodeContents(_targetSpan4);
372
+ _newRange4.collapse(false);
373
+ } else {
374
+ _newRange4.selectNodeContents(div);
375
+ _newRange4.collapse(true);
376
+ }
377
+ }
378
+ selection.removeAllRanges();
379
+ selection.addRange(_newRange4);
380
+ return;
381
+ }
382
+ }
383
+
384
+ // 在 span 开头 + Delete 键,光标移到前一个 span 的末尾
385
+ if (startOffset === 0 && e.key === 'Backspace' && currentIndex > 0) {
386
+ var _prevSpan = allSpans[currentIndex - 1];
387
+ // 前一个 span 是非 label span 时,光标移到其末尾
388
+ if (!_prevSpan.classList.contains('formula-editor-token-label')) {
389
+ e.preventDefault();
390
+ e.stopPropagation();
391
+ var prevText = _prevSpan.textContent || '';
392
+ if (prevText.length > 0) {
393
+ _prevSpan.textContent = prevText.slice(0, -1);
394
+ }
395
+ var _newRange5 = document.createRange();
396
+ _newRange5.selectNodeContents(_prevSpan);
397
+ _newRange5.collapse(false);
398
+ selection.removeAllRanges();
399
+ selection.addRange(_newRange5);
400
+ }
401
+ }
402
+
403
+ // 其他情况:等待浏览器完成默认删除后,检查当前 span 是否为空
404
+ setTimeout(function () {
405
+ var currentSpanNow = getSpanAtCursor(startContainer);
406
+ var allSpansNow = Array.from(div.querySelectorAll('span'));
407
+
408
+ // 如果 currentSpanNow 不在 allSpansNow 里(被浏览器删除了)
409
+ // 且还有 span 存在,用按下删除键时的 currentSpan 位置往前找
410
+ if (allSpansNow.length > 0 && !allSpansNow.includes(currentSpanNow)) {
411
+ // 如果光标在 span 开头 + Delete 键,currentIndex 指向的是被删 span 的后一个,所以要 -1
412
+ var searchIndex = startOffset === 0 ? currentIndex - 1 : currentIndex;
413
+ // 从 searchIndex 位置往前找
414
+ var _targetSpan5;
415
+ for (var _i = searchIndex; _i >= 0; _i--) {
416
+ var span = allSpansNow[_i];
417
+ if (span.textContent !== '') {
418
+ _targetSpan5 = span;
419
+ break;
420
+ }
421
+ }
422
+ // 如果找到的有内容 span 是 label,则把光标放在 label span 的后一个 span 的开头
423
+ if (_targetSpan5 && _targetSpan5.classList.contains('formula-editor-token-label')) {
424
+ var labelIndex = allSpansNow.indexOf(_targetSpan5);
425
+ if (labelIndex < allSpansNow.length - 1) {
426
+ var nextSpan = allSpansNow[labelIndex + 1];
427
+ var _newRange6 = document.createRange();
428
+ _newRange6.selectNodeContents(nextSpan);
429
+ _newRange6.collapse(true);
430
+ selection.removeAllRanges();
431
+ selection.addRange(_newRange6);
432
+ return;
433
+ }
434
+ }
435
+
436
+ // 否则把光标放在找到的 span 的末尾
437
+ if (_targetSpan5) {
438
+ var _newRange7 = document.createRange();
439
+ _newRange7.selectNodeContents(_targetSpan5);
440
+ _newRange7.collapse(false);
441
+ selection.removeAllRanges();
442
+ selection.addRange(_newRange7);
443
+ return;
444
+ }
445
+ }
446
+ if (!currentSpanNow || currentSpanNow === div) return;
447
+ if (currentSpanNow.textContent === '') {
448
+ var currentIndexNow = allSpansNow.indexOf(currentSpanNow);
449
+ var _newRange8 = document.createRange();
450
+ if (currentIndexNow > 0) {
451
+ // 移到前一个 span 的末尾,但如果前一个 span 是 label,则移到后一个 span 的开头
452
+ var _targetSpan6 = allSpansNow[currentIndexNow - 1];
453
+ if (_targetSpan6.classList.contains('formula-editor-token-label')) {
454
+ // 移到 label span 的后一个 span 的开头
455
+ if (currentIndexNow < allSpansNow.length - 1) {
456
+ var _nextSpan = allSpansNow[currentIndexNow + 1];
457
+ _newRange8.selectNodeContents(_nextSpan);
458
+ _newRange8.collapse(true);
459
+ } else {
460
+ _newRange8.selectNodeContents(div);
461
+ _newRange8.collapse(false);
462
+ }
463
+ } else {
464
+ _newRange8.selectNodeContents(_targetSpan6);
465
+ _newRange8.collapse(false);
466
+ }
467
+ currentSpanNow.remove();
468
+ } else if (allSpansNow.length > 1) {
469
+ // 第一个 span 为空,移到下一个 span 的开头
470
+ var _nextSpan2 = allSpansNow[currentIndexNow + 1];
471
+ _newRange8.selectNodeContents(_nextSpan2);
472
+ _newRange8.collapse(true);
473
+ currentSpanNow.remove();
474
+ } else {
475
+ // 只有一个空 span,保持空
476
+ _newRange8.selectNodeContents(div);
477
+ _newRange8.collapse(true);
478
+ }
479
+ selection.removeAllRanges();
480
+ selection.addRange(_newRange8);
481
+ }
482
+ }, 0);
483
+ }, []);
484
+ var dropdownContent = /*#__PURE__*/_jsx(Menu, {
485
+ className: "formula-editor-dropdown",
486
+ style: {
487
+ transform: "translateX(".concat(offsetX, "px)")
488
+ },
489
+ onMouseDown: function onMouseDown(e) {
490
+ e.preventDefault();
491
+ e.stopPropagation();
492
+ },
493
+ children: filteredHierarchyOptions.length === 0 ? /*#__PURE__*/_jsx(Menu.Item, {
494
+ disabled: true,
495
+ children: /*#__PURE__*/_jsx("span", {
496
+ className: "formula-editor-dropdown-no-data",
497
+ children: "\u6682\u65E0\u6570\u636E"
498
+ })
499
+ }, "no-data") : filteredHierarchyOptions.map(function (opt) {
500
+ return opt.children && opt.children.length > 0 ? /*#__PURE__*/_jsx(Menu.SubMenu, {
501
+ title: opt.label,
502
+ className: "formula-editor-dropdown-submenu",
503
+ children: opt.children.map(function (child) {
504
+ return /*#__PURE__*/_jsx(Menu.Item, {
505
+ className: "formula-editor-dropdown-item",
506
+ onClick: function onClick() {
507
+ return handleOptionClick(child);
508
+ },
509
+ children: child.label
510
+ }, child.value);
511
+ })
512
+ }, opt.value) : /*#__PURE__*/_jsxs(Menu.Item, {
513
+ className: "formula-editor-dropdown-item",
514
+ onClick: function onClick() {
515
+ return handleOptionClick(opt);
516
+ },
517
+ children: [opt.label, opt.info ? /*#__PURE__*/_jsx(Tooltip, {
518
+ title: opt.info,
519
+ children: /*#__PURE__*/_jsx(InfoCircleOutlined, {
520
+ style: {
521
+ color: '#FF9900',
522
+ marginLeft: 4
523
+ }
524
+ })
525
+ }) : null]
526
+ }, opt.value);
527
+ })
528
+ });
529
+ return /*#__PURE__*/_jsx(Dropdown, {
530
+ overlayClassName: "dropDownOffset",
531
+ overlay: dropdownContent,
532
+ open: open,
533
+ children: /*#__PURE__*/_jsx("div", {
534
+ ref: containerRef,
535
+ className: "formula-editor",
536
+ contentEditable: true,
537
+ suppressContentEditableWarning: true,
538
+ onInput: handleInput,
539
+ onMouseDown: function onMouseDown(e) {
540
+ return e.stopPropagation();
541
+ },
542
+ onMouseUp: handleMouseUp,
543
+ onKeyDown: handleKeyDown,
544
+ spellCheck: false,
545
+ children: isEmpty(tokens) ? /*#__PURE__*/_jsx("span", {
546
+ style: {
547
+ minWidth: 8,
548
+ height: 40
549
+ }
550
+ }) : tokens.map(function (token, index) {
551
+ return token.type === 'end' ? /*#__PURE__*/_jsx("span", {
552
+ style: {
553
+ minWidth: 8,
554
+ height: 40
555
+ }
556
+ }, index) : /*#__PURE__*/_jsx("span", {
557
+ className: "formula-editor-token-".concat(token.type),
558
+ children: token.text
559
+ }, index);
560
+ })
561
+ })
562
+ });
563
+ };
564
+
565
+ // 扁平化选项用于校验和格式化
566
+ var flatOptions = flattenOptions(options);
567
+
568
+ // checker:使用 validateCalculationExpr 校验语法 + 校验变量是否都在 options 中
569
+ FormulaInputEditor.checker = function (value) {
570
+ if (isNil(value)) return true;
571
+ var strValue = String(value);
572
+
573
+ // 如果以 = 开头,去掉 = 后校验
574
+ var expr = strValue.startsWith('=') ? strValue.slice(1) : strValue;
575
+ var parsedExpr = replaceLabelsWithValues(expr, flatOptions);
576
+
577
+ // 空表达式不校验
578
+ if (!parsedExpr.trim()) return true;
579
+
580
+ // 1. 语法校验
581
+ if (!validateCalculationExpr(parsedExpr)) {
582
+ return false;
583
+ }
584
+
585
+ // 2. 变量校验:所有变量必须在 options 中
586
+ if (!validateVariables(parsedExpr, flatOptions)) {
587
+ return false;
588
+ }
589
+ return true;
590
+ };
591
+
592
+ // formula:校验公式,校验成功返回 currentValue
593
+ FormulaInputEditor.formula = function (value, currentValue) {
594
+ if (isNil(value)) {
595
+ return null;
596
+ }
597
+ var strValue = String(value);
598
+ var hasEqualSign = strValue.startsWith('=');
599
+ var expr = hasEqualSign ? strValue.slice(1) : strValue;
600
+
601
+ // 空表达式返回 null
602
+ if (!expr.trim()) {
603
+ return null;
604
+ }
605
+ if (!hasEqualSign) {
606
+ return Infinity;
607
+ }
608
+ // 语法校验
609
+ if (!validateCalculationExpr(expr)) {
610
+ return Infinity;
611
+ }
612
+
613
+ // 变量校验
614
+ if (!validateVariables(expr, flatOptions)) {
615
+ return Infinity;
616
+ }
617
+ return currentValue;
618
+ };
619
+
620
+ // parser:粘贴时处理,将 label 转换为 value
621
+ FormulaInputEditor.parser = function (value) {
622
+ if (isNil(value)) return null;
623
+ var strValue = String(value);
624
+ var hasEqualSign = strValue.startsWith('=');
625
+ var expr = hasEqualSign ? strValue.slice(1) : strValue;
626
+
627
+ // 将 label 替换为 value
628
+ var parsedExpr = replaceLabelsWithValues(expr, flatOptions);
629
+ return '=' + parsedExpr;
630
+ };
631
+
632
+ // formatter:复制时触发,将 value 转换为 label
633
+ FormulaInputEditor.formatter = function (value) {
634
+ if (isNil(value)) return null;
635
+ var strValue = String(value);
636
+ var hasEqualSign = strValue.startsWith('=');
637
+ var expr = hasEqualSign ? strValue.slice(1) : strValue;
638
+
639
+ // 将 value 替换为 label
640
+ var displayExpr = replaceValuesWithLabels(expr, flatOptions);
641
+ return hasEqualSign ? '=' + displayExpr : displayExpr;
642
+ };
643
+ return FormulaInputEditor;
644
+ };
645
+ export default getFormulaInputEditor;