@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.
- package/dist/core/editor/InputOptionsEditor/index.d.ts +14 -0
- package/dist/core/editor/InputOptionsEditor/index.js +645 -0
- package/dist/core/editor/InputOptionsEditor/index.less +57 -0
- package/dist/core/editor/InputOptionsEditor/utils.d.ts +59 -0
- package/dist/core/editor/InputOptionsEditor/utils.js +335 -0
- package/dist/core/editor/InputOptionsEditor/vaildFormula.js +333 -0
- package/dist/core/editor/index.d.ts +1 -0
- package/dist/core/editor/index.js +2 -1
- package/dist/example/basic.d.ts +1 -0
- package/dist/example/basic.js +52 -5
- package/dist/example/index.less +7 -4
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
.formula-editor {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
white-space: nowrap;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
outline: none;
|
|
7
|
+
min-width: 95%;
|
|
8
|
+
max-height: 100%;
|
|
9
|
+
box-shadow: inset 0 -100px 0 rgba(33, 133, 208, 15%);
|
|
10
|
+
position: absolute;
|
|
11
|
+
top: 0;
|
|
12
|
+
background: #fff;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.formula-editor-equal {
|
|
16
|
+
color: #000;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.formula-editor-token-label {
|
|
20
|
+
color: #0078e0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.formula-editor-token-other {
|
|
24
|
+
color: #000;
|
|
25
|
+
min-height: 40px;
|
|
26
|
+
min-width: 1px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.formula-editor-dropdown {
|
|
30
|
+
max-height: 300px;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
background: #fff;
|
|
33
|
+
border-radius: 4px;
|
|
34
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
35
|
+
|
|
36
|
+
.formula-editor-dropdown-item {
|
|
37
|
+
padding: 8px 12px;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
transition: background 0.2s;
|
|
40
|
+
|
|
41
|
+
&:hover {
|
|
42
|
+
background: #f5f5f5;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.formula-editor-dropdown-no-data {
|
|
47
|
+
color: #999;
|
|
48
|
+
padding: 8px 12px;
|
|
49
|
+
display: block;
|
|
50
|
+
text-align: center;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.dropDownOffset {
|
|
55
|
+
min-width: 100px !important;
|
|
56
|
+
max-width: 200px !important;
|
|
57
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export declare const OPERATORS: string[];
|
|
2
|
+
export declare const VARIABLE_REGEX: RegExp;
|
|
3
|
+
export declare const NUMBER_REGEX: RegExp;
|
|
4
|
+
export interface Token {
|
|
5
|
+
text: string;
|
|
6
|
+
type: 'label' | 'operator' | 'other' | 'end';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 将表达式中的 value 替换为对应的 label
|
|
10
|
+
*/
|
|
11
|
+
export declare const replaceValuesWithLabels: (expr: string, flatOptions: {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}[]) => string;
|
|
15
|
+
/**
|
|
16
|
+
* 从表达式中提取所有变量名
|
|
17
|
+
*/
|
|
18
|
+
export declare const extractVariables: (expr: string) => string[];
|
|
19
|
+
/**
|
|
20
|
+
* 将表达式解析为 token 数组,用于渲染带颜色的文本
|
|
21
|
+
* 自动将 value 转换为对应的 label
|
|
22
|
+
* 有效 label 为 type 'label',其他为 'other'
|
|
23
|
+
*/
|
|
24
|
+
export declare const tokenize: (text: string, flatOptions: {
|
|
25
|
+
label: string;
|
|
26
|
+
value: string;
|
|
27
|
+
}[]) => Token[];
|
|
28
|
+
/**
|
|
29
|
+
* 将表达式中的 label 替换为对应的 value
|
|
30
|
+
*/
|
|
31
|
+
export declare const replaceLabelsWithValues: (expr: string, flatOptions: {
|
|
32
|
+
label: string;
|
|
33
|
+
value: string;
|
|
34
|
+
}[]) => string;
|
|
35
|
+
/**
|
|
36
|
+
* 校验表达式中的所有变量是否都在 options 中
|
|
37
|
+
*/
|
|
38
|
+
export declare const validateVariables: (expr: string, flatOptions: {
|
|
39
|
+
label: string;
|
|
40
|
+
value: string;
|
|
41
|
+
}[]) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 扁平化选项列表(将 children 展开)
|
|
44
|
+
*/
|
|
45
|
+
export declare const flattenOptions: (options: {
|
|
46
|
+
label: string;
|
|
47
|
+
value: string;
|
|
48
|
+
children?: {
|
|
49
|
+
label: string;
|
|
50
|
+
value: string;
|
|
51
|
+
}[];
|
|
52
|
+
}[]) => {
|
|
53
|
+
label: string;
|
|
54
|
+
value: string;
|
|
55
|
+
}[];
|
|
56
|
+
export declare const longestCommonSubsequence: (arr1: string, arr2: string) => string[];
|
|
57
|
+
export declare const getStringDiff: (str1: any, str2: any, mode?: string) => any;
|
|
58
|
+
export declare const replaceLongestDiff: (a: string, str: string, position: number) => string;
|
|
59
|
+
export declare const getCursorPositionInSpan: (span: any) => any;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
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; } } }; }
|
|
2
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
3
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
4
|
+
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); }
|
|
5
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
6
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
7
|
+
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; }
|
|
8
|
+
// 运算符列表
|
|
9
|
+
export var OPERATORS = ['+', '-', '*', '/'];
|
|
10
|
+
|
|
11
|
+
// 变量名正则:匹配字母开头的标识符
|
|
12
|
+
export var VARIABLE_REGEX = /[a-zA-Z_][a-zA-Z0-9_]*/g;
|
|
13
|
+
|
|
14
|
+
// 数字正则
|
|
15
|
+
export var NUMBER_REGEX = /^\d+(\.\d+)?/;
|
|
16
|
+
|
|
17
|
+
// Token 类型
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 将表达式中的 value 替换为对应的 label
|
|
21
|
+
*/
|
|
22
|
+
export var replaceValuesWithLabels = function replaceValuesWithLabels(expr, flatOptions) {
|
|
23
|
+
// 按 value 长度降序排序,避免短 value 先匹配导致问题
|
|
24
|
+
var sortedOptions = _toConsumableArray(flatOptions).sort(function (a, b) {
|
|
25
|
+
return b.value.length - a.value.length;
|
|
26
|
+
});
|
|
27
|
+
var result = expr;
|
|
28
|
+
var _iterator = _createForOfIteratorHelper(sortedOptions),
|
|
29
|
+
_step;
|
|
30
|
+
try {
|
|
31
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
32
|
+
var opt = _step.value;
|
|
33
|
+
// 全局替换 value 为 label
|
|
34
|
+
result = result.split(opt.value).join(opt.label);
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
_iterator.e(err);
|
|
38
|
+
} finally {
|
|
39
|
+
_iterator.f();
|
|
40
|
+
}
|
|
41
|
+
return result.replace(/\s/g, '');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 从表达式中提取所有变量名
|
|
46
|
+
*/
|
|
47
|
+
export var extractVariables = function extractVariables(expr) {
|
|
48
|
+
var matches = new Set(expr.match(VARIABLE_REGEX));
|
|
49
|
+
return matches ? _toConsumableArray(Array.from(matches)) : [];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 将混合 segment 按有效 label 拆分成多个 token
|
|
54
|
+
*/
|
|
55
|
+
var splitSegmentByLabels = function splitSegmentByLabels(segment, sortedLabels) {
|
|
56
|
+
var tokens = [];
|
|
57
|
+
var remaining = segment;
|
|
58
|
+
while (remaining.length > 0) {
|
|
59
|
+
// 找到剩余文本中第一个有效 label 的位置
|
|
60
|
+
var labelStart = -1;
|
|
61
|
+
var matchedLabel = '';
|
|
62
|
+
var _iterator2 = _createForOfIteratorHelper(sortedLabels),
|
|
63
|
+
_step2;
|
|
64
|
+
try {
|
|
65
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
66
|
+
var label = _step2.value;
|
|
67
|
+
var idx = remaining.indexOf(label);
|
|
68
|
+
if (idx !== -1 && (labelStart === -1 || idx < labelStart)) {
|
|
69
|
+
labelStart = idx;
|
|
70
|
+
matchedLabel = label;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
_iterator2.e(err);
|
|
75
|
+
} finally {
|
|
76
|
+
_iterator2.f();
|
|
77
|
+
}
|
|
78
|
+
if (labelStart === -1) {
|
|
79
|
+
// 没有有效 label,整个为 'other'
|
|
80
|
+
if (remaining.trim()) {
|
|
81
|
+
tokens.push({
|
|
82
|
+
text: remaining.trim(),
|
|
83
|
+
type: 'other'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// labelStart 前的部分为 'other'
|
|
90
|
+
if (labelStart > 0) {
|
|
91
|
+
var before = remaining.slice(0, labelStart);
|
|
92
|
+
if (before.trim()) {
|
|
93
|
+
tokens.push({
|
|
94
|
+
text: before.trim(),
|
|
95
|
+
type: 'other'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 有效 label
|
|
101
|
+
tokens.push({
|
|
102
|
+
text: matchedLabel,
|
|
103
|
+
type: 'label'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 剩余部分继续处理
|
|
107
|
+
remaining = remaining.slice(labelStart + matchedLabel.length);
|
|
108
|
+
}
|
|
109
|
+
return tokens;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 将表达式解析为 token 数组,用于渲染带颜色的文本
|
|
114
|
+
* 自动将 value 转换为对应的 label
|
|
115
|
+
* 有效 label 为 type 'label',其他为 'other'
|
|
116
|
+
*/
|
|
117
|
+
export var tokenize = function tokenize(text, flatOptions) {
|
|
118
|
+
var tokens = [];
|
|
119
|
+
|
|
120
|
+
// 将 value 转换为 label
|
|
121
|
+
var labelExpr = replaceValuesWithLabels(text.slice(1), flatOptions);
|
|
122
|
+
tokens.push({
|
|
123
|
+
text: '=',
|
|
124
|
+
type: 'operator'
|
|
125
|
+
});
|
|
126
|
+
if (!labelExpr) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 按长度降序排序,用于匹配
|
|
131
|
+
var sortedLabels = flatOptions.map(function (opt) {
|
|
132
|
+
return opt.label;
|
|
133
|
+
}).sort(function (a, b) {
|
|
134
|
+
return b.length - a.length;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// 按运算符分割文本
|
|
138
|
+
var segments = labelExpr.split(/([=+\-*/])/);
|
|
139
|
+
var _iterator3 = _createForOfIteratorHelper(segments),
|
|
140
|
+
_step3;
|
|
141
|
+
try {
|
|
142
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
143
|
+
var segment = _step3.value;
|
|
144
|
+
if (!segment) continue;
|
|
145
|
+
if (OPERATORS.includes(segment)) {
|
|
146
|
+
tokens.push({
|
|
147
|
+
text: segment,
|
|
148
|
+
type: 'operator'
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
// 拆分混合 segment,如 "123苹果456" 拆成 ["123", "苹果", "456"]
|
|
152
|
+
var subTokens = splitSegmentByLabels(segment, sortedLabels);
|
|
153
|
+
tokens.push.apply(tokens, _toConsumableArray(subTokens));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
_iterator3.e(err);
|
|
158
|
+
} finally {
|
|
159
|
+
_iterator3.f();
|
|
160
|
+
}
|
|
161
|
+
tokens.push({
|
|
162
|
+
text: '',
|
|
163
|
+
type: 'end'
|
|
164
|
+
});
|
|
165
|
+
return tokens;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 将表达式中的 label 替换为对应的 value
|
|
170
|
+
*/
|
|
171
|
+
export var replaceLabelsWithValues = function replaceLabelsWithValues(expr, flatOptions) {
|
|
172
|
+
// 按 label 长度降序排序,避免短 label 先匹配导致问题
|
|
173
|
+
var sortedOptions = _toConsumableArray(flatOptions).sort(function (a, b) {
|
|
174
|
+
return b.label.length - a.label.length;
|
|
175
|
+
});
|
|
176
|
+
var result = expr;
|
|
177
|
+
var _iterator4 = _createForOfIteratorHelper(sortedOptions),
|
|
178
|
+
_step4;
|
|
179
|
+
try {
|
|
180
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
181
|
+
var opt = _step4.value;
|
|
182
|
+
// 全局替换 label 为 value
|
|
183
|
+
result = result.split(opt.label).join(opt.value);
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
_iterator4.e(err);
|
|
187
|
+
} finally {
|
|
188
|
+
_iterator4.f();
|
|
189
|
+
}
|
|
190
|
+
return result.replace(/\s/g, '');
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 校验表达式中的所有变量是否都在 options 中
|
|
195
|
+
*/
|
|
196
|
+
export var validateVariables = function validateVariables(expr, flatOptions) {
|
|
197
|
+
var variables = extractVariables(expr);
|
|
198
|
+
var validValues = new Set(flatOptions.map(function (opt) {
|
|
199
|
+
return opt.value;
|
|
200
|
+
}));
|
|
201
|
+
return variables.every(function (v) {
|
|
202
|
+
return validValues.has(v);
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 扁平化选项列表(将 children 展开)
|
|
208
|
+
*/
|
|
209
|
+
export var flattenOptions = function flattenOptions(options) {
|
|
210
|
+
var result = [];
|
|
211
|
+
var _iterator5 = _createForOfIteratorHelper(options),
|
|
212
|
+
_step5;
|
|
213
|
+
try {
|
|
214
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
215
|
+
var opt = _step5.value;
|
|
216
|
+
result.push({
|
|
217
|
+
label: opt.label,
|
|
218
|
+
value: opt.value
|
|
219
|
+
});
|
|
220
|
+
if (opt.children && opt.children.length > 0) {
|
|
221
|
+
result.push.apply(result, _toConsumableArray(flattenOptions(opt.children)));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
_iterator5.e(err);
|
|
226
|
+
} finally {
|
|
227
|
+
_iterator5.f();
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
};
|
|
231
|
+
export var longestCommonSubsequence = function longestCommonSubsequence(arr1, arr2) {
|
|
232
|
+
var m = arr1.length;
|
|
233
|
+
var n = arr2.length;
|
|
234
|
+
var dp = Array(m + 1).fill(null).map(function () {
|
|
235
|
+
return Array(n + 1).fill(0);
|
|
236
|
+
});
|
|
237
|
+
for (var _i = 1; _i <= m; _i++) {
|
|
238
|
+
for (var _j = 1; _j <= n; _j++) {
|
|
239
|
+
if (arr1[_i - 1] === arr2[_j - 1]) {
|
|
240
|
+
dp[_i][_j] = dp[_i - 1][_j - 1] + 1;
|
|
241
|
+
} else {
|
|
242
|
+
dp[_i][_j] = Math.max(dp[_i - 1][_j], dp[_i][_j - 1]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 回溯找出 LCS
|
|
248
|
+
var lcs = [];
|
|
249
|
+
var i = m,
|
|
250
|
+
j = n;
|
|
251
|
+
while (i > 0 && j > 0) {
|
|
252
|
+
if (arr1[i - 1] === arr2[j - 1]) {
|
|
253
|
+
lcs.unshift(arr1[i - 1]);
|
|
254
|
+
i--;
|
|
255
|
+
j--;
|
|
256
|
+
} else if (dp[i - 1][j] > dp[i][j - 1]) {
|
|
257
|
+
i--;
|
|
258
|
+
} else {
|
|
259
|
+
j--;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return lcs;
|
|
263
|
+
};
|
|
264
|
+
export var getStringDiff = function getStringDiff(str1, str2) {
|
|
265
|
+
var mode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'char';
|
|
266
|
+
var arr1, arr2;
|
|
267
|
+
if (mode === 'word') {
|
|
268
|
+
arr1 = str1.split(/\s+/);
|
|
269
|
+
arr2 = str2.split(/\s+/);
|
|
270
|
+
} else {
|
|
271
|
+
arr1 = str1.split('');
|
|
272
|
+
arr2 = str2.split('');
|
|
273
|
+
}
|
|
274
|
+
var result = {
|
|
275
|
+
added: [],
|
|
276
|
+
// 新增部分
|
|
277
|
+
removed: [],
|
|
278
|
+
// 删除部分
|
|
279
|
+
unchanged: [] // 未变化部分
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// 使用动态规划计算最长公共子序列
|
|
283
|
+
var lcs = longestCommonSubsequence(arr1, arr2);
|
|
284
|
+
var i = 0,
|
|
285
|
+
j = 0,
|
|
286
|
+
k = 0;
|
|
287
|
+
while (i < arr1.length || j < arr2.length) {
|
|
288
|
+
if (k < lcs.length) {
|
|
289
|
+
// 处理删除的部分
|
|
290
|
+
while (i < arr1.length && arr1[i] !== lcs[k]) {
|
|
291
|
+
result.removed.push(arr1[i]);
|
|
292
|
+
i++;
|
|
293
|
+
}
|
|
294
|
+
// 处理新增的部分
|
|
295
|
+
while (j < arr2.length && arr2[j] !== lcs[k]) {
|
|
296
|
+
result.added.push(arr2[j]);
|
|
297
|
+
j++;
|
|
298
|
+
}
|
|
299
|
+
// 相等的部分
|
|
300
|
+
if (i < arr1.length && j < arr2.length) {
|
|
301
|
+
result.unchanged.push(arr1[i]);
|
|
302
|
+
i++;
|
|
303
|
+
j++;
|
|
304
|
+
k++;
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
// 剩余的删除部分
|
|
308
|
+
while (i < arr1.length) {
|
|
309
|
+
result.removed.push(arr1[i]);
|
|
310
|
+
i++;
|
|
311
|
+
}
|
|
312
|
+
// 剩余的新增部分
|
|
313
|
+
while (j < arr2.length) {
|
|
314
|
+
result.added.push(arr2[j]);
|
|
315
|
+
j++;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
};
|
|
321
|
+
export var replaceLongestDiff = function replaceLongestDiff(a, str, position) {
|
|
322
|
+
// 分割字符串:前半部分 + 替换内容 + 后半部分
|
|
323
|
+
var before = a.substring(0, position + 1);
|
|
324
|
+
var after = a.substring(position + 1);
|
|
325
|
+
return before + str + after;
|
|
326
|
+
};
|
|
327
|
+
export var getCursorPositionInSpan = function getCursorPositionInSpan(span) {
|
|
328
|
+
var selection = window.getSelection();
|
|
329
|
+
if (selection.rangeCount === 0) return 0;
|
|
330
|
+
var range = selection.getRangeAt(0);
|
|
331
|
+
var preCaretRange = range.cloneRange();
|
|
332
|
+
preCaretRange.selectNodeContents(span);
|
|
333
|
+
preCaretRange.setEnd(range.startContainer, range.startOffset);
|
|
334
|
+
return preCaretRange.toString().length;
|
|
335
|
+
};
|