one-design-next 0.0.25 → 0.0.26
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/artifact/index.d.ts +3 -12
- package/dist/artifact/index.js +100 -31
- package/dist/artifact/style/index.css +99 -48
- package/dist/composer/caret.d.ts +19 -0
- package/dist/composer/caret.js +302 -0
- package/dist/composer/editor.js +121 -134
- package/dist/composer/hooks/useChipManager.d.ts +1 -1
- package/dist/composer/hooks/useChipManager.js +202 -35
- package/dist/composer/style/index.css +30 -6
- package/dist/composer/utils.d.ts +34 -52
- package/dist/composer/utils.js +170 -350
- package/dist/invocation/style/index.css +3 -3
- package/dist/mention/style/index.css +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { createLineRoot, ensureLineRoots as ensureLineRootsInUtils, getCurrentLineRoot, isLineRoot, LINE_ATTR_NAME, stripInvisibleChars, ZWSP } from "./utils";
|
|
2
|
+
var CHIP_SELECTOR = '[data-odn-composer-chip-host],[data-mention-id]';
|
|
3
|
+
var isChipHost = function isChipHost(node) {
|
|
4
|
+
return !!node && node.nodeType === Node.ELEMENT_NODE && node instanceof HTMLElement && (node.hasAttribute('data-odn-composer-chip-host') || node.dataset.mentionId !== undefined);
|
|
5
|
+
};
|
|
6
|
+
var isSkippableAnchorText = function isSkippableAnchorText(node) {
|
|
7
|
+
var _node$textContent;
|
|
8
|
+
return !!node && node.nodeType === Node.TEXT_NODE && stripInvisibleChars((_node$textContent = node.textContent) !== null && _node$textContent !== void 0 ? _node$textContent : '') === '';
|
|
9
|
+
};
|
|
10
|
+
var prevNonAnchorSibling = function prevNonAnchorSibling(node) {
|
|
11
|
+
var cur = node.previousSibling;
|
|
12
|
+
while (isSkippableAnchorText(cur)) cur = cur.previousSibling;
|
|
13
|
+
return cur;
|
|
14
|
+
};
|
|
15
|
+
var nextNonAnchorSibling = function nextNonAnchorSibling(node) {
|
|
16
|
+
var cur = node.nextSibling;
|
|
17
|
+
while (isSkippableAnchorText(cur)) cur = cur.nextSibling;
|
|
18
|
+
return cur;
|
|
19
|
+
};
|
|
20
|
+
var childIndex = function childIndex(node) {
|
|
21
|
+
var idx = 0;
|
|
22
|
+
var cur = node.previousSibling;
|
|
23
|
+
while (cur) {
|
|
24
|
+
idx += 1;
|
|
25
|
+
cur = cur.previousSibling;
|
|
26
|
+
}
|
|
27
|
+
return idx;
|
|
28
|
+
};
|
|
29
|
+
var createTextCaretAtBoundary = function createTextCaretAtBoundary(parent, offset, prefer) {
|
|
30
|
+
var _parent$childNodes, _parent$childNodes$sa;
|
|
31
|
+
var len = parent.childNodes.length;
|
|
32
|
+
var safeOffset = Math.max(0, Math.min(offset, len));
|
|
33
|
+
var left = (_parent$childNodes = parent.childNodes[safeOffset - 1]) !== null && _parent$childNodes !== void 0 ? _parent$childNodes : null;
|
|
34
|
+
var right = (_parent$childNodes$sa = parent.childNodes[safeOffset]) !== null && _parent$childNodes$sa !== void 0 ? _parent$childNodes$sa : null;
|
|
35
|
+
var isUsableTextNode = function isUsableTextNode(n) {
|
|
36
|
+
var _n$textContent;
|
|
37
|
+
return !!n && n.nodeType === Node.TEXT_NODE && ((_n$textContent = n.textContent) !== null && _n$textContent !== void 0 ? _n$textContent : '') !== '';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// 在边界两侧选 text 落点时,优先跳过纯锚点文本(ZWSP/SEP-only),
|
|
41
|
+
// 避免箭头键停在不可见字符上造成“卡一拍”。
|
|
42
|
+
var pickTextNode = function pickTextNode(start, dir) {
|
|
43
|
+
var cur = start;
|
|
44
|
+
var fallbackAnchor = null;
|
|
45
|
+
while (cur && isUsableTextNode(cur)) {
|
|
46
|
+
if (!isSkippableAnchorText(cur)) return cur;
|
|
47
|
+
if (!fallbackAnchor) fallbackAnchor = cur;
|
|
48
|
+
cur = dir === 'left' ? cur.previousSibling : cur.nextSibling;
|
|
49
|
+
}
|
|
50
|
+
return fallbackAnchor;
|
|
51
|
+
};
|
|
52
|
+
var textOffset = function textOffset(text, side, from) {
|
|
53
|
+
var _text$textContent;
|
|
54
|
+
var end = ((_text$textContent = text.textContent) !== null && _text$textContent !== void 0 ? _text$textContent : '').length;
|
|
55
|
+
if (!isSkippableAnchorText(text)) return side === 'start' ? 0 : end;
|
|
56
|
+
// 纯锚点文本使用“外侧边界”偏移,减少 0/1 内部来回抖动。
|
|
57
|
+
if (from === 'right') return end;
|
|
58
|
+
return 0;
|
|
59
|
+
};
|
|
60
|
+
var fromLeft = function fromLeft() {
|
|
61
|
+
var leftText = pickTextNode(left, 'left');
|
|
62
|
+
if (leftText) {
|
|
63
|
+
return {
|
|
64
|
+
node: leftText,
|
|
65
|
+
offset: textOffset(leftText, 'end', 'left')
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
var rightText = pickTextNode(right, 'right');
|
|
69
|
+
if (rightText) {
|
|
70
|
+
return {
|
|
71
|
+
node: rightText,
|
|
72
|
+
offset: textOffset(rightText, 'start', 'left')
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
77
|
+
var fromRight = function fromRight() {
|
|
78
|
+
var rightText = pickTextNode(right, 'right');
|
|
79
|
+
if (rightText) {
|
|
80
|
+
return {
|
|
81
|
+
node: rightText,
|
|
82
|
+
offset: textOffset(rightText, 'start', 'right')
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
var leftText = pickTextNode(left, 'left');
|
|
86
|
+
if (leftText) {
|
|
87
|
+
return {
|
|
88
|
+
node: leftText,
|
|
89
|
+
offset: textOffset(leftText, 'end', 'right')
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
var anchored = prefer === 'left' ? fromLeft() : fromRight();
|
|
95
|
+
if (anchored) return anchored;
|
|
96
|
+
if (parent.nodeType === Node.ELEMENT_NODE || parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
97
|
+
var _parent$childNodes$sa2;
|
|
98
|
+
var anchor = document.createTextNode(ZWSP);
|
|
99
|
+
var ref = (_parent$childNodes$sa2 = parent.childNodes[safeOffset]) !== null && _parent$childNodes$sa2 !== void 0 ? _parent$childNodes$sa2 : null;
|
|
100
|
+
parent.insertBefore(anchor, ref);
|
|
101
|
+
return {
|
|
102
|
+
node: anchor,
|
|
103
|
+
offset: prefer === 'right' ? 1 : 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
node: parent,
|
|
108
|
+
offset: safeOffset
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
var caretBeforeNode = function caretBeforeNode(target) {
|
|
112
|
+
var parent = target.parentNode;
|
|
113
|
+
if (!parent) return null;
|
|
114
|
+
var idx = childIndex(target);
|
|
115
|
+
return createTextCaretAtBoundary(parent, idx, 'left');
|
|
116
|
+
};
|
|
117
|
+
var caretAfterNode = function caretAfterNode(target) {
|
|
118
|
+
var parent = target.parentNode;
|
|
119
|
+
if (!parent) return null;
|
|
120
|
+
var idx = childIndex(target);
|
|
121
|
+
return createTextCaretAtBoundary(parent, idx + 1, 'right');
|
|
122
|
+
};
|
|
123
|
+
var lineStartCaret = function lineStartCaret(line) {
|
|
124
|
+
return createTextCaretAtBoundary(line, 0, 'right');
|
|
125
|
+
};
|
|
126
|
+
var lineEndCaret = function lineEndCaret(line) {
|
|
127
|
+
return createTextCaretAtBoundary(line, line.childNodes.length, 'left');
|
|
128
|
+
};
|
|
129
|
+
var hasContentBeforeCaretInLine = function hasContentBeforeCaretInLine(range, line) {
|
|
130
|
+
var prefix = document.createRange();
|
|
131
|
+
prefix.selectNodeContents(line);
|
|
132
|
+
prefix.setEnd(range.startContainer, range.startOffset);
|
|
133
|
+
var hasVisibleText = stripInvisibleChars(prefix.toString()).replace(/\n/g, '') !== '';
|
|
134
|
+
if (hasVisibleText) return true;
|
|
135
|
+
return prefix.cloneContents().querySelector(CHIP_SELECTOR) !== null;
|
|
136
|
+
};
|
|
137
|
+
var hasContentAfterCaretInLine = function hasContentAfterCaretInLine(range, line) {
|
|
138
|
+
var suffix = document.createRange();
|
|
139
|
+
suffix.selectNodeContents(line);
|
|
140
|
+
suffix.setStart(range.startContainer, range.startOffset);
|
|
141
|
+
var hasVisibleText = stripInvisibleChars(suffix.toString()).replace(/\n/g, '') !== '';
|
|
142
|
+
if (hasVisibleText) return true;
|
|
143
|
+
return suffix.cloneContents().querySelector(CHIP_SELECTOR) !== null;
|
|
144
|
+
};
|
|
145
|
+
export function getCurrentLine(node, editor) {
|
|
146
|
+
return getCurrentLineRoot(node, editor);
|
|
147
|
+
}
|
|
148
|
+
export function findChipHostBeforeCaret(range) {
|
|
149
|
+
var node = range.startContainer,
|
|
150
|
+
offset = range.startOffset;
|
|
151
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
152
|
+
var _node$textContent2;
|
|
153
|
+
if (offset === 0) {
|
|
154
|
+
var prev = prevNonAnchorSibling(node);
|
|
155
|
+
return isChipHost(prev) ? prev : null;
|
|
156
|
+
}
|
|
157
|
+
var text = (_node$textContent2 = node.textContent) !== null && _node$textContent2 !== void 0 ? _node$textContent2 : '';
|
|
158
|
+
if (offset === 1 && text[0] === ZWSP) {
|
|
159
|
+
var _prev = prevNonAnchorSibling(node);
|
|
160
|
+
return isChipHost(_prev) ? _prev : null;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
165
|
+
var _childNodes;
|
|
166
|
+
var _prev2 = (_childNodes = node.childNodes[offset - 1]) !== null && _childNodes !== void 0 ? _childNodes : null;
|
|
167
|
+
while (isSkippableAnchorText(_prev2)) _prev2 = _prev2.previousSibling;
|
|
168
|
+
return isChipHost(_prev2) ? _prev2 : null;
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
export function findChipHostAfterCaret(range) {
|
|
173
|
+
var node = range.startContainer,
|
|
174
|
+
offset = range.startOffset;
|
|
175
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
176
|
+
var _node$textContent3;
|
|
177
|
+
var text = (_node$textContent3 = node.textContent) !== null && _node$textContent3 !== void 0 ? _node$textContent3 : '';
|
|
178
|
+
if (offset === text.length) {
|
|
179
|
+
var next = nextNonAnchorSibling(node);
|
|
180
|
+
return isChipHost(next) ? next : null;
|
|
181
|
+
}
|
|
182
|
+
if (offset === 0 && text[0] === ZWSP) {
|
|
183
|
+
var _next = nextNonAnchorSibling(node);
|
|
184
|
+
return isChipHost(_next) ? _next : null;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
189
|
+
var _childNodes$offset;
|
|
190
|
+
var _next2 = (_childNodes$offset = node.childNodes[offset]) !== null && _childNodes$offset !== void 0 ? _childNodes$offset : null;
|
|
191
|
+
while (isSkippableAnchorText(_next2)) _next2 = _next2.nextSibling;
|
|
192
|
+
return isChipHost(_next2) ? _next2 : null;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
export function normalizeCollapsedCaretAtChipBoundary(range, editor) {
|
|
197
|
+
var _node$childNodes, _node$childNodes$safe;
|
|
198
|
+
if (!range.collapsed) return null;
|
|
199
|
+
if (!editor.contains(range.startContainer)) return null;
|
|
200
|
+
var node = range.startContainer;
|
|
201
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
202
|
+
var _node$textContent4;
|
|
203
|
+
// 浏览器可能把 caret 放进纯锚点文本(ZWSP/SEP-only),
|
|
204
|
+
// 这里把它规范化回边界外,避免在锚点 0/1 之间“卡一拍”。
|
|
205
|
+
if (!isSkippableAnchorText(node)) return null;
|
|
206
|
+
var parent = node.parentNode;
|
|
207
|
+
if (!parent) return null;
|
|
208
|
+
if (parent.nodeType !== Node.ELEMENT_NODE && parent.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
var idx = childIndex(node);
|
|
212
|
+
var _len = ((_node$textContent4 = node.textContent) !== null && _node$textContent4 !== void 0 ? _node$textContent4 : '').length;
|
|
213
|
+
var caret = Math.max(0, Math.min(range.startOffset, _len));
|
|
214
|
+
if (caret <= 0) return createTextCaretAtBoundary(parent, idx, 'left');
|
|
215
|
+
if (caret >= _len) return createTextCaretAtBoundary(parent, idx + 1, 'right');
|
|
216
|
+
|
|
217
|
+
// 理论上纯锚点文本长度通常为 1,这里保留通用处理。
|
|
218
|
+
var towardLeft = caret <= _len - caret;
|
|
219
|
+
return towardLeft ? createTextCaretAtBoundary(parent, idx, 'left') : createTextCaretAtBoundary(parent, idx + 1, 'right');
|
|
220
|
+
}
|
|
221
|
+
if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
var len = node.childNodes.length;
|
|
225
|
+
var safeOffset = Math.max(0, Math.min(range.startOffset, len));
|
|
226
|
+
var left = (_node$childNodes = node.childNodes[safeOffset - 1]) !== null && _node$childNodes !== void 0 ? _node$childNodes : null;
|
|
227
|
+
var right = (_node$childNodes$safe = node.childNodes[safeOffset]) !== null && _node$childNodes$safe !== void 0 ? _node$childNodes$safe : null;
|
|
228
|
+
var leftChip = isChipHost(left);
|
|
229
|
+
var rightChip = isChipHost(right);
|
|
230
|
+
if (!leftChip && !rightChip) return null;
|
|
231
|
+
var prefer = leftChip && !rightChip ? 'right' : 'left';
|
|
232
|
+
return createTextCaretAtBoundary(node, safeOffset, prefer);
|
|
233
|
+
}
|
|
234
|
+
export function resolveCaretMove(range, direction, editor) {
|
|
235
|
+
if (!editor.contains(range.startContainer)) return null;
|
|
236
|
+
if (direction === 'left') {
|
|
237
|
+
var chipBefore = findChipHostBeforeCaret(range);
|
|
238
|
+
if (chipBefore) return caretBeforeNode(chipBefore);
|
|
239
|
+
var line = getCurrentLine(range.startContainer, editor);
|
|
240
|
+
if (!line || hasContentBeforeCaretInLine(range, line)) return null;
|
|
241
|
+
var prevLine = line.previousElementSibling;
|
|
242
|
+
if (!isLineRoot(prevLine)) return null;
|
|
243
|
+
return lineEndCaret(prevLine);
|
|
244
|
+
}
|
|
245
|
+
if (direction === 'right') {
|
|
246
|
+
var chipAfter = findChipHostAfterCaret(range);
|
|
247
|
+
if (chipAfter) return caretAfterNode(chipAfter);
|
|
248
|
+
var _line = getCurrentLine(range.startContainer, editor);
|
|
249
|
+
if (!_line || hasContentAfterCaretInLine(range, _line)) return null;
|
|
250
|
+
var nextLine = _line.nextElementSibling;
|
|
251
|
+
if (!isLineRoot(nextLine)) return null;
|
|
252
|
+
return lineStartCaret(nextLine);
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
export function splitLineAt(range, editor) {
|
|
257
|
+
var _currentLine$lastChil, _currentLine$parentNo;
|
|
258
|
+
if (!editor.contains(range.startContainer)) return null;
|
|
259
|
+
range.deleteContents();
|
|
260
|
+
ensureLineRootsInUtils(editor);
|
|
261
|
+
var currentLine = getCurrentLine(range.startContainer, editor);
|
|
262
|
+
if (!currentLine) {
|
|
263
|
+
var firstLine = editor.querySelector(":scope > [".concat(LINE_ATTR_NAME, "]"));
|
|
264
|
+
if (!firstLine) return null;
|
|
265
|
+
currentLine = firstLine;
|
|
266
|
+
range.setStart(currentLine, 0);
|
|
267
|
+
}
|
|
268
|
+
var tailRange = document.createRange();
|
|
269
|
+
tailRange.setStart(range.startContainer, range.startOffset);
|
|
270
|
+
tailRange.setEndAfter((_currentLine$lastChil = currentLine.lastChild) !== null && _currentLine$lastChil !== void 0 ? _currentLine$lastChil : currentLine);
|
|
271
|
+
if (!currentLine.contains(tailRange.endContainer)) {
|
|
272
|
+
tailRange.selectNodeContents(currentLine);
|
|
273
|
+
tailRange.setStart(range.startContainer, range.startOffset);
|
|
274
|
+
}
|
|
275
|
+
var tailFrag = tailRange.extractContents();
|
|
276
|
+
var newLine = createLineRoot();
|
|
277
|
+
newLine.appendChild(tailFrag);
|
|
278
|
+
(_currentLine$parentNo = currentLine.parentNode) === null || _currentLine$parentNo === void 0 || _currentLine$parentNo.insertBefore(newLine, currentLine.nextSibling);
|
|
279
|
+
return {
|
|
280
|
+
newLine: newLine,
|
|
281
|
+
caret: lineStartCaret(newLine)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
export function mergeLineIntoPreviousAtCaretStart(range, editor, sel) {
|
|
285
|
+
if (!editor.contains(range.startContainer)) return false;
|
|
286
|
+
var currentLine = getCurrentLine(range.startContainer, editor);
|
|
287
|
+
var prevLine = currentLine === null || currentLine === void 0 ? void 0 : currentLine.previousElementSibling;
|
|
288
|
+
if (!currentLine || !isLineRoot(prevLine)) return false;
|
|
289
|
+
if (hasContentBeforeCaretInLine(range, currentLine)) return false;
|
|
290
|
+
var targetLine = prevLine;
|
|
291
|
+
var mergeBoundaryOffset = targetLine.childNodes.length;
|
|
292
|
+
while (currentLine.firstChild) {
|
|
293
|
+
targetLine.appendChild(currentLine.firstChild);
|
|
294
|
+
}
|
|
295
|
+
currentLine.remove();
|
|
296
|
+
var r = document.createRange();
|
|
297
|
+
r.setStart(targetLine, Math.min(mergeBoundaryOffset, targetLine.childNodes.length));
|
|
298
|
+
r.collapse(true);
|
|
299
|
+
sel.removeAllRanges();
|
|
300
|
+
sel.addRange(r);
|
|
301
|
+
return true;
|
|
302
|
+
}
|