@ekz/lexical-offset 0.40.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/EkzLexicalOffset.dev.js +336 -0
- package/EkzLexicalOffset.dev.mjs +331 -0
- package/EkzLexicalOffset.js +11 -0
- package/EkzLexicalOffset.mjs +15 -0
- package/EkzLexicalOffset.node.mjs +13 -0
- package/EkzLexicalOffset.prod.js +9 -0
- package/EkzLexicalOffset.prod.mjs +9 -0
- package/LICENSE +21 -0
- package/LexicalOffset.js.flow +67 -0
- package/README.md +5 -0
- package/index.d.ts +53 -0
- package/package.json +41 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
var lexical = require('@ekz/lexical');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
15
|
+
*
|
|
16
|
+
* This source code is licensed under the MIT license found in the
|
|
17
|
+
* LICENSE file in the root directory of this source tree.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Do not require this module directly! Use normal `invariant` calls.
|
|
22
|
+
|
|
23
|
+
function formatDevErrorMessage(message) {
|
|
24
|
+
throw new Error(message);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class OffsetView {
|
|
28
|
+
_offsetMap;
|
|
29
|
+
_firstNode;
|
|
30
|
+
_blockOffsetSize;
|
|
31
|
+
constructor(offsetMap, firstNode, blockOffsetSize = 1) {
|
|
32
|
+
this._offsetMap = offsetMap;
|
|
33
|
+
this._firstNode = firstNode;
|
|
34
|
+
this._blockOffsetSize = blockOffsetSize;
|
|
35
|
+
}
|
|
36
|
+
createSelectionFromOffsets(originalStart, originalEnd, diffOffsetView) {
|
|
37
|
+
const firstNode = this._firstNode;
|
|
38
|
+
if (firstNode === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
let start = originalStart;
|
|
42
|
+
let end = originalEnd;
|
|
43
|
+
let startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
|
|
44
|
+
let endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
|
|
45
|
+
if (diffOffsetView !== undefined) {
|
|
46
|
+
start = $getAdjustedOffsetFromDiff(start, startOffsetNode, diffOffsetView, this, this._blockOffsetSize);
|
|
47
|
+
startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
|
|
48
|
+
end = $getAdjustedOffsetFromDiff(end, endOffsetNode, diffOffsetView, this, this._blockOffsetSize);
|
|
49
|
+
endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
|
|
50
|
+
}
|
|
51
|
+
if (startOffsetNode === null || endOffsetNode === null) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
let startKey = startOffsetNode.key;
|
|
55
|
+
let endKey = endOffsetNode.key;
|
|
56
|
+
const startNode = lexical.$getNodeByKey(startKey);
|
|
57
|
+
const endNode = lexical.$getNodeByKey(endKey);
|
|
58
|
+
if (startNode === null || endNode === null) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
let startOffset = 0;
|
|
62
|
+
let endOffset = 0;
|
|
63
|
+
let startType = 'element';
|
|
64
|
+
let endType = 'element';
|
|
65
|
+
if (startOffsetNode.type === 'text') {
|
|
66
|
+
startOffset = start - startOffsetNode.start;
|
|
67
|
+
startType = 'text';
|
|
68
|
+
// If we are at the edge of a text node and we
|
|
69
|
+
// don't have a collapsed selection, then let's
|
|
70
|
+
// try and correct the offset node.
|
|
71
|
+
const sibling = startNode.getNextSibling();
|
|
72
|
+
if (start !== end && startOffset === startNode.getTextContentSize() && lexical.$isTextNode(sibling)) {
|
|
73
|
+
startOffset = 0;
|
|
74
|
+
startKey = sibling.__key;
|
|
75
|
+
}
|
|
76
|
+
} else if (startOffsetNode.type === 'inline') {
|
|
77
|
+
startKey = startNode.getParentOrThrow().getKey();
|
|
78
|
+
startOffset = end > startOffsetNode.start ? startOffsetNode.end : startOffsetNode.start;
|
|
79
|
+
}
|
|
80
|
+
if (endOffsetNode.type === 'text') {
|
|
81
|
+
endOffset = end - endOffsetNode.start;
|
|
82
|
+
endType = 'text';
|
|
83
|
+
} else if (endOffsetNode.type === 'inline') {
|
|
84
|
+
endKey = endNode.getParentOrThrow().getKey();
|
|
85
|
+
endOffset = end > endOffsetNode.start ? endOffsetNode.end : endOffsetNode.start;
|
|
86
|
+
}
|
|
87
|
+
const selection = lexical.$createRangeSelection();
|
|
88
|
+
if (selection === null) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
selection.anchor.set(startKey, startOffset, startType);
|
|
92
|
+
selection.focus.set(endKey, endOffset, endType);
|
|
93
|
+
return selection;
|
|
94
|
+
}
|
|
95
|
+
getOffsetsFromSelection(selection) {
|
|
96
|
+
const anchor = selection.anchor;
|
|
97
|
+
const focus = selection.focus;
|
|
98
|
+
const offsetMap = this._offsetMap;
|
|
99
|
+
const anchorOffset = anchor.offset;
|
|
100
|
+
const focusOffset = focus.offset;
|
|
101
|
+
let start = -1;
|
|
102
|
+
let end = -1;
|
|
103
|
+
if (anchor.type === 'text') {
|
|
104
|
+
const offsetNode = offsetMap.get(anchor.key);
|
|
105
|
+
if (offsetNode !== undefined) {
|
|
106
|
+
start = offsetNode.start + anchorOffset;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
const node = anchor.getNode().getDescendantByIndex(anchorOffset);
|
|
110
|
+
if (node !== null) {
|
|
111
|
+
const offsetNode = offsetMap.get(node.getKey());
|
|
112
|
+
if (offsetNode !== undefined) {
|
|
113
|
+
const isAtEnd = node.getIndexWithinParent() !== anchorOffset;
|
|
114
|
+
start = isAtEnd ? offsetNode.end : offsetNode.start;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (focus.type === 'text') {
|
|
119
|
+
const offsetNode = offsetMap.get(focus.key);
|
|
120
|
+
if (offsetNode !== undefined) {
|
|
121
|
+
end = offsetNode.start + focus.offset;
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
const node = focus.getNode().getDescendantByIndex(focusOffset);
|
|
125
|
+
if (node !== null) {
|
|
126
|
+
const offsetNode = offsetMap.get(node.getKey());
|
|
127
|
+
if (offsetNode !== undefined) {
|
|
128
|
+
const isAtEnd = node.getIndexWithinParent() !== focusOffset;
|
|
129
|
+
end = isAtEnd ? offsetNode.end : offsetNode.start;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return [start, end];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function $getAdjustedOffsetFromDiff(offset, offsetNode, prevOffsetView, offsetView, blockOffsetSize) {
|
|
137
|
+
const prevOffsetMap = prevOffsetView._offsetMap;
|
|
138
|
+
const offsetMap = offsetView._offsetMap;
|
|
139
|
+
const visited = new Set();
|
|
140
|
+
let adjustedOffset = offset;
|
|
141
|
+
let currentNode = offsetNode;
|
|
142
|
+
while (currentNode !== null) {
|
|
143
|
+
const key = currentNode.key;
|
|
144
|
+
const prevNode = prevOffsetMap.get(key);
|
|
145
|
+
const diff = currentNode.end - currentNode.start;
|
|
146
|
+
visited.add(key);
|
|
147
|
+
if (prevNode === undefined) {
|
|
148
|
+
adjustedOffset += diff;
|
|
149
|
+
} else {
|
|
150
|
+
const prevDiff = prevNode.end - prevNode.start;
|
|
151
|
+
if (prevDiff !== diff) {
|
|
152
|
+
adjustedOffset += diff - prevDiff;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const sibling = currentNode.prev;
|
|
156
|
+
if (sibling !== null) {
|
|
157
|
+
currentNode = sibling;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
let parent = currentNode.parent;
|
|
161
|
+
while (parent !== null) {
|
|
162
|
+
let parentSibling = parent.prev;
|
|
163
|
+
if (parentSibling !== null) {
|
|
164
|
+
const parentSiblingKey = parentSibling.key;
|
|
165
|
+
const prevParentSibling = prevOffsetMap.get(parentSiblingKey);
|
|
166
|
+
const parentDiff = parentSibling.end - parentSibling.start;
|
|
167
|
+
visited.add(parentSiblingKey);
|
|
168
|
+
if (prevParentSibling === undefined) {
|
|
169
|
+
adjustedOffset += parentDiff;
|
|
170
|
+
} else {
|
|
171
|
+
const prevParentDiff = prevParentSibling.end - prevParentSibling.start;
|
|
172
|
+
if (prevParentDiff !== parentDiff) {
|
|
173
|
+
adjustedOffset += parentDiff - prevParentDiff;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
parentSibling = parentSibling.prev;
|
|
177
|
+
}
|
|
178
|
+
parent = parent.parent;
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Now traverse through the old offsets nodes and find any nodes we missed
|
|
184
|
+
// above, because they were not in the latest offset node view (they have been
|
|
185
|
+
// deleted).
|
|
186
|
+
const prevFirstNode = prevOffsetView._firstNode;
|
|
187
|
+
if (prevFirstNode !== null) {
|
|
188
|
+
currentNode = $searchForNodeWithOffset(prevFirstNode, offset, blockOffsetSize);
|
|
189
|
+
let alreadyVisitedParentOfCurrentNode = false;
|
|
190
|
+
while (currentNode !== null) {
|
|
191
|
+
if (!visited.has(currentNode.key)) {
|
|
192
|
+
alreadyVisitedParentOfCurrentNode = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
currentNode = currentNode.parent;
|
|
196
|
+
}
|
|
197
|
+
if (!alreadyVisitedParentOfCurrentNode) {
|
|
198
|
+
while (currentNode !== null) {
|
|
199
|
+
const key = currentNode.key;
|
|
200
|
+
if (!visited.has(key)) {
|
|
201
|
+
const node = offsetMap.get(key);
|
|
202
|
+
const prevDiff = currentNode.end - currentNode.start;
|
|
203
|
+
if (node === undefined) {
|
|
204
|
+
adjustedOffset -= prevDiff;
|
|
205
|
+
} else {
|
|
206
|
+
const diff = node.end - node.start;
|
|
207
|
+
if (prevDiff !== diff) {
|
|
208
|
+
adjustedOffset += diff - prevDiff;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
currentNode = currentNode.prev;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return adjustedOffset;
|
|
217
|
+
}
|
|
218
|
+
function $searchForNodeWithOffset(firstNode, offset, blockOffsetSize) {
|
|
219
|
+
let currentNode = firstNode;
|
|
220
|
+
while (currentNode !== null) {
|
|
221
|
+
const end = currentNode.end + (currentNode.type !== 'element' || blockOffsetSize === 0 ? 1 : 0);
|
|
222
|
+
if (offset < end) {
|
|
223
|
+
const child = currentNode.child;
|
|
224
|
+
if (child !== null) {
|
|
225
|
+
currentNode = child;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
return currentNode;
|
|
229
|
+
}
|
|
230
|
+
const sibling = currentNode.next;
|
|
231
|
+
if (sibling === null) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
currentNode = sibling;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
function $createInternalOffsetNode(child, type, start, end, key, parent) {
|
|
239
|
+
return {
|
|
240
|
+
child,
|
|
241
|
+
end,
|
|
242
|
+
key,
|
|
243
|
+
next: null,
|
|
244
|
+
parent,
|
|
245
|
+
prev: null,
|
|
246
|
+
start,
|
|
247
|
+
type
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function $createOffsetNode(state, key, parent, nodeMap, offsetMap, blockOffsetSize) {
|
|
251
|
+
const node = nodeMap.get(key);
|
|
252
|
+
if (node === undefined) {
|
|
253
|
+
{
|
|
254
|
+
formatDevErrorMessage(`createOffsetModel: could not find node by key`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const start = state.offset;
|
|
258
|
+
if (lexical.$isElementNode(node)) {
|
|
259
|
+
const childKeys = $createChildrenArray(node, nodeMap);
|
|
260
|
+
const blockIsEmpty = childKeys.length === 0;
|
|
261
|
+
const child = blockIsEmpty ? null : $createOffsetChild(state, childKeys, null, nodeMap, offsetMap, blockOffsetSize);
|
|
262
|
+
|
|
263
|
+
// If the prev node was not a block or the block is empty, we should
|
|
264
|
+
// account for the user being able to selection the block (due to the \n).
|
|
265
|
+
if (!state.prevIsBlock || blockIsEmpty) {
|
|
266
|
+
state.prevIsBlock = true;
|
|
267
|
+
state.offset += blockOffsetSize;
|
|
268
|
+
}
|
|
269
|
+
const offsetNode = $createInternalOffsetNode(child, 'element', start, start, key, parent);
|
|
270
|
+
if (child !== null) {
|
|
271
|
+
child.parent = offsetNode;
|
|
272
|
+
}
|
|
273
|
+
const end = state.offset;
|
|
274
|
+
offsetNode.end = end;
|
|
275
|
+
offsetMap.set(key, offsetNode);
|
|
276
|
+
return offsetNode;
|
|
277
|
+
}
|
|
278
|
+
state.prevIsBlock = false;
|
|
279
|
+
const isText = lexical.$isTextNode(node);
|
|
280
|
+
const length = isText ? node.__text.length : 1;
|
|
281
|
+
const end = state.offset += length;
|
|
282
|
+
const offsetNode = $createInternalOffsetNode(null, isText ? 'text' : 'inline', start, end, key, parent);
|
|
283
|
+
offsetMap.set(key, offsetNode);
|
|
284
|
+
return offsetNode;
|
|
285
|
+
}
|
|
286
|
+
function $createOffsetChild(state, children, parent, nodeMap, offsetMap, blockOffsetSize) {
|
|
287
|
+
let firstNode = null;
|
|
288
|
+
let currentNode = null;
|
|
289
|
+
const childrenLength = children.length;
|
|
290
|
+
for (let i = 0; i < childrenLength; i++) {
|
|
291
|
+
const childKey = children[i];
|
|
292
|
+
const offsetNode = $createOffsetNode(state, childKey, parent, nodeMap, offsetMap, blockOffsetSize);
|
|
293
|
+
if (currentNode === null) {
|
|
294
|
+
firstNode = offsetNode;
|
|
295
|
+
} else {
|
|
296
|
+
offsetNode.prev = currentNode;
|
|
297
|
+
currentNode.next = offsetNode;
|
|
298
|
+
}
|
|
299
|
+
currentNode = offsetNode;
|
|
300
|
+
}
|
|
301
|
+
return firstNode;
|
|
302
|
+
}
|
|
303
|
+
function $createChildrenArray(element, nodeMap) {
|
|
304
|
+
const children = [];
|
|
305
|
+
let nodeKey = element.__first;
|
|
306
|
+
while (nodeKey !== null) {
|
|
307
|
+
const node = nodeMap === null ? lexical.$getNodeByKey(nodeKey) : nodeMap.get(nodeKey);
|
|
308
|
+
if (node === null || node === undefined) {
|
|
309
|
+
{
|
|
310
|
+
formatDevErrorMessage(`$createChildrenArray: node does not exist in nodeMap`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
children.push(nodeKey);
|
|
314
|
+
nodeKey = node.__next;
|
|
315
|
+
}
|
|
316
|
+
return children;
|
|
317
|
+
}
|
|
318
|
+
/** @deprecated renamed to {@link $createChildrenArray} by @ekz/lexical-eslint-plugin rules-of-lexical */
|
|
319
|
+
const createChildrenArray = $createChildrenArray;
|
|
320
|
+
function $createOffsetView(editor, blockOffsetSize = 1, editorState) {
|
|
321
|
+
const targetEditorState = editorState || editor._pendingEditorState || editor._editorState;
|
|
322
|
+
const nodeMap = targetEditorState._nodeMap;
|
|
323
|
+
const root = nodeMap.get('root');
|
|
324
|
+
const offsetMap = new Map();
|
|
325
|
+
const state = {
|
|
326
|
+
offset: 0,
|
|
327
|
+
prevIsBlock: false
|
|
328
|
+
};
|
|
329
|
+
const node = $createOffsetChild(state, $createChildrenArray(root, nodeMap), null, nodeMap, offsetMap, blockOffsetSize);
|
|
330
|
+
return new OffsetView(offsetMap, node, blockOffsetSize);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
exports.$createChildrenArray = $createChildrenArray;
|
|
334
|
+
exports.$createOffsetView = $createOffsetView;
|
|
335
|
+
exports.OffsetView = OffsetView;
|
|
336
|
+
exports.createChildrenArray = createChildrenArray;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { $getNodeByKey, $isTextNode, $createRangeSelection, $isElementNode } from '@ekz/lexical';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the MIT license found in the
|
|
15
|
+
* LICENSE file in the root directory of this source tree.
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Do not require this module directly! Use normal `invariant` calls.
|
|
20
|
+
|
|
21
|
+
function formatDevErrorMessage(message) {
|
|
22
|
+
throw new Error(message);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class OffsetView {
|
|
26
|
+
_offsetMap;
|
|
27
|
+
_firstNode;
|
|
28
|
+
_blockOffsetSize;
|
|
29
|
+
constructor(offsetMap, firstNode, blockOffsetSize = 1) {
|
|
30
|
+
this._offsetMap = offsetMap;
|
|
31
|
+
this._firstNode = firstNode;
|
|
32
|
+
this._blockOffsetSize = blockOffsetSize;
|
|
33
|
+
}
|
|
34
|
+
createSelectionFromOffsets(originalStart, originalEnd, diffOffsetView) {
|
|
35
|
+
const firstNode = this._firstNode;
|
|
36
|
+
if (firstNode === null) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
let start = originalStart;
|
|
40
|
+
let end = originalEnd;
|
|
41
|
+
let startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
|
|
42
|
+
let endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
|
|
43
|
+
if (diffOffsetView !== undefined) {
|
|
44
|
+
start = $getAdjustedOffsetFromDiff(start, startOffsetNode, diffOffsetView, this, this._blockOffsetSize);
|
|
45
|
+
startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
|
|
46
|
+
end = $getAdjustedOffsetFromDiff(end, endOffsetNode, diffOffsetView, this, this._blockOffsetSize);
|
|
47
|
+
endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
|
|
48
|
+
}
|
|
49
|
+
if (startOffsetNode === null || endOffsetNode === null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
let startKey = startOffsetNode.key;
|
|
53
|
+
let endKey = endOffsetNode.key;
|
|
54
|
+
const startNode = $getNodeByKey(startKey);
|
|
55
|
+
const endNode = $getNodeByKey(endKey);
|
|
56
|
+
if (startNode === null || endNode === null) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
let startOffset = 0;
|
|
60
|
+
let endOffset = 0;
|
|
61
|
+
let startType = 'element';
|
|
62
|
+
let endType = 'element';
|
|
63
|
+
if (startOffsetNode.type === 'text') {
|
|
64
|
+
startOffset = start - startOffsetNode.start;
|
|
65
|
+
startType = 'text';
|
|
66
|
+
// If we are at the edge of a text node and we
|
|
67
|
+
// don't have a collapsed selection, then let's
|
|
68
|
+
// try and correct the offset node.
|
|
69
|
+
const sibling = startNode.getNextSibling();
|
|
70
|
+
if (start !== end && startOffset === startNode.getTextContentSize() && $isTextNode(sibling)) {
|
|
71
|
+
startOffset = 0;
|
|
72
|
+
startKey = sibling.__key;
|
|
73
|
+
}
|
|
74
|
+
} else if (startOffsetNode.type === 'inline') {
|
|
75
|
+
startKey = startNode.getParentOrThrow().getKey();
|
|
76
|
+
startOffset = end > startOffsetNode.start ? startOffsetNode.end : startOffsetNode.start;
|
|
77
|
+
}
|
|
78
|
+
if (endOffsetNode.type === 'text') {
|
|
79
|
+
endOffset = end - endOffsetNode.start;
|
|
80
|
+
endType = 'text';
|
|
81
|
+
} else if (endOffsetNode.type === 'inline') {
|
|
82
|
+
endKey = endNode.getParentOrThrow().getKey();
|
|
83
|
+
endOffset = end > endOffsetNode.start ? endOffsetNode.end : endOffsetNode.start;
|
|
84
|
+
}
|
|
85
|
+
const selection = $createRangeSelection();
|
|
86
|
+
if (selection === null) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
selection.anchor.set(startKey, startOffset, startType);
|
|
90
|
+
selection.focus.set(endKey, endOffset, endType);
|
|
91
|
+
return selection;
|
|
92
|
+
}
|
|
93
|
+
getOffsetsFromSelection(selection) {
|
|
94
|
+
const anchor = selection.anchor;
|
|
95
|
+
const focus = selection.focus;
|
|
96
|
+
const offsetMap = this._offsetMap;
|
|
97
|
+
const anchorOffset = anchor.offset;
|
|
98
|
+
const focusOffset = focus.offset;
|
|
99
|
+
let start = -1;
|
|
100
|
+
let end = -1;
|
|
101
|
+
if (anchor.type === 'text') {
|
|
102
|
+
const offsetNode = offsetMap.get(anchor.key);
|
|
103
|
+
if (offsetNode !== undefined) {
|
|
104
|
+
start = offsetNode.start + anchorOffset;
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
const node = anchor.getNode().getDescendantByIndex(anchorOffset);
|
|
108
|
+
if (node !== null) {
|
|
109
|
+
const offsetNode = offsetMap.get(node.getKey());
|
|
110
|
+
if (offsetNode !== undefined) {
|
|
111
|
+
const isAtEnd = node.getIndexWithinParent() !== anchorOffset;
|
|
112
|
+
start = isAtEnd ? offsetNode.end : offsetNode.start;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (focus.type === 'text') {
|
|
117
|
+
const offsetNode = offsetMap.get(focus.key);
|
|
118
|
+
if (offsetNode !== undefined) {
|
|
119
|
+
end = offsetNode.start + focus.offset;
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
const node = focus.getNode().getDescendantByIndex(focusOffset);
|
|
123
|
+
if (node !== null) {
|
|
124
|
+
const offsetNode = offsetMap.get(node.getKey());
|
|
125
|
+
if (offsetNode !== undefined) {
|
|
126
|
+
const isAtEnd = node.getIndexWithinParent() !== focusOffset;
|
|
127
|
+
end = isAtEnd ? offsetNode.end : offsetNode.start;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return [start, end];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function $getAdjustedOffsetFromDiff(offset, offsetNode, prevOffsetView, offsetView, blockOffsetSize) {
|
|
135
|
+
const prevOffsetMap = prevOffsetView._offsetMap;
|
|
136
|
+
const offsetMap = offsetView._offsetMap;
|
|
137
|
+
const visited = new Set();
|
|
138
|
+
let adjustedOffset = offset;
|
|
139
|
+
let currentNode = offsetNode;
|
|
140
|
+
while (currentNode !== null) {
|
|
141
|
+
const key = currentNode.key;
|
|
142
|
+
const prevNode = prevOffsetMap.get(key);
|
|
143
|
+
const diff = currentNode.end - currentNode.start;
|
|
144
|
+
visited.add(key);
|
|
145
|
+
if (prevNode === undefined) {
|
|
146
|
+
adjustedOffset += diff;
|
|
147
|
+
} else {
|
|
148
|
+
const prevDiff = prevNode.end - prevNode.start;
|
|
149
|
+
if (prevDiff !== diff) {
|
|
150
|
+
adjustedOffset += diff - prevDiff;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const sibling = currentNode.prev;
|
|
154
|
+
if (sibling !== null) {
|
|
155
|
+
currentNode = sibling;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
let parent = currentNode.parent;
|
|
159
|
+
while (parent !== null) {
|
|
160
|
+
let parentSibling = parent.prev;
|
|
161
|
+
if (parentSibling !== null) {
|
|
162
|
+
const parentSiblingKey = parentSibling.key;
|
|
163
|
+
const prevParentSibling = prevOffsetMap.get(parentSiblingKey);
|
|
164
|
+
const parentDiff = parentSibling.end - parentSibling.start;
|
|
165
|
+
visited.add(parentSiblingKey);
|
|
166
|
+
if (prevParentSibling === undefined) {
|
|
167
|
+
adjustedOffset += parentDiff;
|
|
168
|
+
} else {
|
|
169
|
+
const prevParentDiff = prevParentSibling.end - prevParentSibling.start;
|
|
170
|
+
if (prevParentDiff !== parentDiff) {
|
|
171
|
+
adjustedOffset += parentDiff - prevParentDiff;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
parentSibling = parentSibling.prev;
|
|
175
|
+
}
|
|
176
|
+
parent = parent.parent;
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Now traverse through the old offsets nodes and find any nodes we missed
|
|
182
|
+
// above, because they were not in the latest offset node view (they have been
|
|
183
|
+
// deleted).
|
|
184
|
+
const prevFirstNode = prevOffsetView._firstNode;
|
|
185
|
+
if (prevFirstNode !== null) {
|
|
186
|
+
currentNode = $searchForNodeWithOffset(prevFirstNode, offset, blockOffsetSize);
|
|
187
|
+
let alreadyVisitedParentOfCurrentNode = false;
|
|
188
|
+
while (currentNode !== null) {
|
|
189
|
+
if (!visited.has(currentNode.key)) {
|
|
190
|
+
alreadyVisitedParentOfCurrentNode = true;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
currentNode = currentNode.parent;
|
|
194
|
+
}
|
|
195
|
+
if (!alreadyVisitedParentOfCurrentNode) {
|
|
196
|
+
while (currentNode !== null) {
|
|
197
|
+
const key = currentNode.key;
|
|
198
|
+
if (!visited.has(key)) {
|
|
199
|
+
const node = offsetMap.get(key);
|
|
200
|
+
const prevDiff = currentNode.end - currentNode.start;
|
|
201
|
+
if (node === undefined) {
|
|
202
|
+
adjustedOffset -= prevDiff;
|
|
203
|
+
} else {
|
|
204
|
+
const diff = node.end - node.start;
|
|
205
|
+
if (prevDiff !== diff) {
|
|
206
|
+
adjustedOffset += diff - prevDiff;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
currentNode = currentNode.prev;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return adjustedOffset;
|
|
215
|
+
}
|
|
216
|
+
function $searchForNodeWithOffset(firstNode, offset, blockOffsetSize) {
|
|
217
|
+
let currentNode = firstNode;
|
|
218
|
+
while (currentNode !== null) {
|
|
219
|
+
const end = currentNode.end + (currentNode.type !== 'element' || blockOffsetSize === 0 ? 1 : 0);
|
|
220
|
+
if (offset < end) {
|
|
221
|
+
const child = currentNode.child;
|
|
222
|
+
if (child !== null) {
|
|
223
|
+
currentNode = child;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
return currentNode;
|
|
227
|
+
}
|
|
228
|
+
const sibling = currentNode.next;
|
|
229
|
+
if (sibling === null) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
currentNode = sibling;
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
function $createInternalOffsetNode(child, type, start, end, key, parent) {
|
|
237
|
+
return {
|
|
238
|
+
child,
|
|
239
|
+
end,
|
|
240
|
+
key,
|
|
241
|
+
next: null,
|
|
242
|
+
parent,
|
|
243
|
+
prev: null,
|
|
244
|
+
start,
|
|
245
|
+
type
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function $createOffsetNode(state, key, parent, nodeMap, offsetMap, blockOffsetSize) {
|
|
249
|
+
const node = nodeMap.get(key);
|
|
250
|
+
if (node === undefined) {
|
|
251
|
+
{
|
|
252
|
+
formatDevErrorMessage(`createOffsetModel: could not find node by key`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const start = state.offset;
|
|
256
|
+
if ($isElementNode(node)) {
|
|
257
|
+
const childKeys = $createChildrenArray(node, nodeMap);
|
|
258
|
+
const blockIsEmpty = childKeys.length === 0;
|
|
259
|
+
const child = blockIsEmpty ? null : $createOffsetChild(state, childKeys, null, nodeMap, offsetMap, blockOffsetSize);
|
|
260
|
+
|
|
261
|
+
// If the prev node was not a block or the block is empty, we should
|
|
262
|
+
// account for the user being able to selection the block (due to the \n).
|
|
263
|
+
if (!state.prevIsBlock || blockIsEmpty) {
|
|
264
|
+
state.prevIsBlock = true;
|
|
265
|
+
state.offset += blockOffsetSize;
|
|
266
|
+
}
|
|
267
|
+
const offsetNode = $createInternalOffsetNode(child, 'element', start, start, key, parent);
|
|
268
|
+
if (child !== null) {
|
|
269
|
+
child.parent = offsetNode;
|
|
270
|
+
}
|
|
271
|
+
const end = state.offset;
|
|
272
|
+
offsetNode.end = end;
|
|
273
|
+
offsetMap.set(key, offsetNode);
|
|
274
|
+
return offsetNode;
|
|
275
|
+
}
|
|
276
|
+
state.prevIsBlock = false;
|
|
277
|
+
const isText = $isTextNode(node);
|
|
278
|
+
const length = isText ? node.__text.length : 1;
|
|
279
|
+
const end = state.offset += length;
|
|
280
|
+
const offsetNode = $createInternalOffsetNode(null, isText ? 'text' : 'inline', start, end, key, parent);
|
|
281
|
+
offsetMap.set(key, offsetNode);
|
|
282
|
+
return offsetNode;
|
|
283
|
+
}
|
|
284
|
+
function $createOffsetChild(state, children, parent, nodeMap, offsetMap, blockOffsetSize) {
|
|
285
|
+
let firstNode = null;
|
|
286
|
+
let currentNode = null;
|
|
287
|
+
const childrenLength = children.length;
|
|
288
|
+
for (let i = 0; i < childrenLength; i++) {
|
|
289
|
+
const childKey = children[i];
|
|
290
|
+
const offsetNode = $createOffsetNode(state, childKey, parent, nodeMap, offsetMap, blockOffsetSize);
|
|
291
|
+
if (currentNode === null) {
|
|
292
|
+
firstNode = offsetNode;
|
|
293
|
+
} else {
|
|
294
|
+
offsetNode.prev = currentNode;
|
|
295
|
+
currentNode.next = offsetNode;
|
|
296
|
+
}
|
|
297
|
+
currentNode = offsetNode;
|
|
298
|
+
}
|
|
299
|
+
return firstNode;
|
|
300
|
+
}
|
|
301
|
+
function $createChildrenArray(element, nodeMap) {
|
|
302
|
+
const children = [];
|
|
303
|
+
let nodeKey = element.__first;
|
|
304
|
+
while (nodeKey !== null) {
|
|
305
|
+
const node = nodeMap === null ? $getNodeByKey(nodeKey) : nodeMap.get(nodeKey);
|
|
306
|
+
if (node === null || node === undefined) {
|
|
307
|
+
{
|
|
308
|
+
formatDevErrorMessage(`$createChildrenArray: node does not exist in nodeMap`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
children.push(nodeKey);
|
|
312
|
+
nodeKey = node.__next;
|
|
313
|
+
}
|
|
314
|
+
return children;
|
|
315
|
+
}
|
|
316
|
+
/** @deprecated renamed to {@link $createChildrenArray} by @ekz/lexical-eslint-plugin rules-of-lexical */
|
|
317
|
+
const createChildrenArray = $createChildrenArray;
|
|
318
|
+
function $createOffsetView(editor, blockOffsetSize = 1, editorState) {
|
|
319
|
+
const targetEditorState = editorState || editor._pendingEditorState || editor._editorState;
|
|
320
|
+
const nodeMap = targetEditorState._nodeMap;
|
|
321
|
+
const root = nodeMap.get('root');
|
|
322
|
+
const offsetMap = new Map();
|
|
323
|
+
const state = {
|
|
324
|
+
offset: 0,
|
|
325
|
+
prevIsBlock: false
|
|
326
|
+
};
|
|
327
|
+
const node = $createOffsetChild(state, $createChildrenArray(root, nodeMap), null, nodeMap, offsetMap, blockOffsetSize);
|
|
328
|
+
return new OffsetView(offsetMap, node, blockOffsetSize);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export { $createChildrenArray, $createOffsetView, OffsetView, createChildrenArray };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict'
|
|
10
|
+
const EkzLexicalOffset = process.env.NODE_ENV !== 'production' ? require('./EkzLexicalOffset.dev.js') : require('./EkzLexicalOffset.prod.js');
|
|
11
|
+
module.exports = EkzLexicalOffset;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as modDev from './EkzLexicalOffset.dev.mjs';
|
|
10
|
+
import * as modProd from './EkzLexicalOffset.prod.mjs';
|
|
11
|
+
const mod = process.env.NODE_ENV !== 'production' ? modDev : modProd;
|
|
12
|
+
export const $createChildrenArray = mod.$createChildrenArray;
|
|
13
|
+
export const $createOffsetView = mod.$createOffsetView;
|
|
14
|
+
export const OffsetView = mod.OffsetView;
|
|
15
|
+
export const createChildrenArray = mod.createChildrenArray;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const mod = await (process.env.NODE_ENV !== 'production' ? import('./EkzLexicalOffset.dev.mjs') : import('./EkzLexicalOffset.prod.mjs'));
|
|
10
|
+
export const $createChildrenArray = mod.$createChildrenArray;
|
|
11
|
+
export const $createOffsetView = mod.$createOffsetView;
|
|
12
|
+
export const OffsetView = mod.OffsetView;
|
|
13
|
+
export const createChildrenArray = mod.createChildrenArray;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
"use strict";var e=require("@ekz/lexical");function t(e,...t){const n=new URL("https://lexical.dev/docs/error"),s=new URLSearchParams;s.append("code",e);for(const e of t)s.append("v",e);throw n.search=s.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}class n{_offsetMap;_firstNode;_blockOffsetSize;constructor(e,t,n=1){this._offsetMap=e,this._firstNode=t,this._blockOffsetSize=n}createSelectionFromOffsets(t,n,r){const l=this._firstNode;if(null===l)return null;let i=t,f=n,c=o(l,i,this._blockOffsetSize),a=o(l,f,this._blockOffsetSize);if(void 0!==r&&(i=s(i,c,r,this,this._blockOffsetSize),c=o(l,i,this._blockOffsetSize),f=s(f,a,r,this,this._blockOffsetSize),a=o(l,f,this._blockOffsetSize)),null===c||null===a)return null;let u=c.key,d=a.key;const p=e.$getNodeByKey(u),g=e.$getNodeByKey(d);if(null===p||null===g)return null;let h=0,y=0,_="element",x="element";if("text"===c.type){h=i-c.start,_="text";const t=p.getNextSibling();i!==f&&h===p.getTextContentSize()&&e.$isTextNode(t)&&(h=0,u=t.__key)}else"inline"===c.type&&(u=p.getParentOrThrow().getKey(),h=f>c.start?c.end:c.start);"text"===a.type?(y=f-a.start,x="text"):"inline"===a.type&&(d=g.getParentOrThrow().getKey(),y=f>a.start?a.end:a.start);const k=e.$createRangeSelection();return null===k?null:(k.anchor.set(u,h,_),k.focus.set(d,y,x),k)}getOffsetsFromSelection(e){const t=e.anchor,n=e.focus,s=this._offsetMap,o=t.offset,r=n.offset;let l=-1,i=-1;if("text"===t.type){const e=s.get(t.key);void 0!==e&&(l=e.start+o)}else{const e=t.getNode().getDescendantByIndex(o);if(null!==e){const t=s.get(e.getKey());if(void 0!==t){l=e.getIndexWithinParent()!==o?t.end:t.start}}}if("text"===n.type){const e=s.get(n.key);void 0!==e&&(i=e.start+n.offset)}else{const e=n.getNode().getDescendantByIndex(r);if(null!==e){const t=s.get(e.getKey());if(void 0!==t){i=e.getIndexWithinParent()!==r?t.end:t.start}}}return[l,i]}}function s(e,t,n,s,r){const l=n._offsetMap,i=s._offsetMap,f=new Set;let c=e,a=t;for(;null!==a;){const e=a.key,t=l.get(e),n=a.end-a.start;if(f.add(e),void 0===t)c+=n;else{const e=t.end-t.start;e!==n&&(c+=n-e)}const s=a.prev;if(null!==s){a=s;continue}let o=a.parent;for(;null!==o;){let e=o.prev;if(null!==e){const t=e.key,n=l.get(t),s=e.end-e.start;if(f.add(t),void 0===n)c+=s;else{const e=n.end-n.start;e!==s&&(c+=s-e)}e=e.prev}o=o.parent}break}const u=n._firstNode;if(null!==u){a=o(u,e,r);let t=!1;for(;null!==a;){if(!f.has(a.key)){t=!0;break}a=a.parent}if(!t)for(;null!==a;){const e=a.key;if(!f.has(e)){const t=i.get(e),n=a.end-a.start;if(void 0===t)c-=n;else{const e=t.end-t.start;n!==e&&(c+=e-n)}}a=a.prev}}return c}function o(e,t,n){let s=e;for(;null!==s;){if(t<s.end+("element"!==s.type||0===n?1:0)){const e=s.child;if(null!==e){s=e;continue}return s}const e=s.next;if(null===e)break;s=e}return null}function r(e,t,n,s,o,r){return{child:e,end:s,key:o,next:null,parent:r,prev:null,start:n,type:t}}function l(n,s,o,l,c,a){const u=l.get(s);void 0===u&&t(3);const d=n.offset;if(e.$isElementNode(u)){const e=f(u,l),t=0===e.length,p=t?null:i(n,e,null,l,c,a);n.prevIsBlock&&!t||(n.prevIsBlock=!0,n.offset+=a);const g=r(p,"element",d,d,s,o);null!==p&&(p.parent=g);const h=n.offset;return g.end=h,c.set(s,g),g}n.prevIsBlock=!1;const p=e.$isTextNode(u),g=p?u.__text.length:1,h=r(null,p?"text":"inline",d,n.offset+=g,s,o);return c.set(s,h),h}function i(e,t,n,s,o,r){let i=null,f=null;const c=t.length;for(let a=0;a<c;a++){const c=l(e,t[a],n,s,o,r);null===f?i=c:(c.prev=f,f.next=c),f=c}return i}function f(n,s){const o=[];let r=n.__first;for(;null!==r;){const n=null===s?e.$getNodeByKey(r):s.get(r);null==n&&t(174),o.push(r),r=n.__next}return o}const c=f;exports.$createChildrenArray=f,exports.$createOffsetView=function(e,t=1,s){const o=(s||e._pendingEditorState||e._editorState)._nodeMap,r=o.get("root"),l=new Map,c=i({offset:0,prevIsBlock:!1},f(r,o),null,o,l,t);return new n(l,c,t)},exports.OffsetView=n,exports.createChildrenArray=c;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import{$getNodeByKey as t,$isTextNode as e,$createRangeSelection as n,$isElementNode as o}from"@ekz/lexical";function s(t,...e){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",t);for(const t of e)o.append("v",t);throw n.search=o.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}class l{_offsetMap;_firstNode;_blockOffsetSize;constructor(t,e,n=1){this._offsetMap=t,this._firstNode=e,this._blockOffsetSize=n}createSelectionFromOffsets(o,s,l){const f=this._firstNode;if(null===f)return null;let c=o,u=s,a=i(f,c,this._blockOffsetSize),d=i(f,u,this._blockOffsetSize);if(void 0!==l&&(c=r(c,a,l,this,this._blockOffsetSize),a=i(f,c,this._blockOffsetSize),u=r(u,d,l,this,this._blockOffsetSize),d=i(f,u,this._blockOffsetSize)),null===a||null===d)return null;let p=a.key,g=d.key;const h=t(p),_=t(g);if(null===h||null===_)return null;let k=0,v=0,y="element",x="element";if("text"===a.type){k=c-a.start,y="text";const t=h.getNextSibling();c!==u&&k===h.getTextContentSize()&&e(t)&&(k=0,p=t.__key)}else"inline"===a.type&&(p=h.getParentOrThrow().getKey(),k=u>a.start?a.end:a.start);"text"===d.type?(v=u-d.start,x="text"):"inline"===d.type&&(g=_.getParentOrThrow().getKey(),v=u>d.start?d.end:d.start);const S=n();return null===S?null:(S.anchor.set(p,k,y),S.focus.set(g,v,x),S)}getOffsetsFromSelection(t){const e=t.anchor,n=t.focus,o=this._offsetMap,s=e.offset,l=n.offset;let r=-1,i=-1;if("text"===e.type){const t=o.get(e.key);void 0!==t&&(r=t.start+s)}else{const t=e.getNode().getDescendantByIndex(s);if(null!==t){const e=o.get(t.getKey());if(void 0!==e){r=t.getIndexWithinParent()!==s?e.end:e.start}}}if("text"===n.type){const t=o.get(n.key);void 0!==t&&(i=t.start+n.offset)}else{const t=n.getNode().getDescendantByIndex(l);if(null!==t){const e=o.get(t.getKey());if(void 0!==e){i=t.getIndexWithinParent()!==l?e.end:e.start}}}return[r,i]}}function r(t,e,n,o,s){const l=n._offsetMap,r=o._offsetMap,f=new Set;let c=t,u=e;for(;null!==u;){const t=u.key,e=l.get(t),n=u.end-u.start;if(f.add(t),void 0===e)c+=n;else{const t=e.end-e.start;t!==n&&(c+=n-t)}const o=u.prev;if(null!==o){u=o;continue}let s=u.parent;for(;null!==s;){let t=s.prev;if(null!==t){const e=t.key,n=l.get(e),o=t.end-t.start;if(f.add(e),void 0===n)c+=o;else{const t=n.end-n.start;t!==o&&(c+=o-t)}t=t.prev}s=s.parent}break}const a=n._firstNode;if(null!==a){u=i(a,t,s);let e=!1;for(;null!==u;){if(!f.has(u.key)){e=!0;break}u=u.parent}if(!e)for(;null!==u;){const t=u.key;if(!f.has(t)){const e=r.get(t),n=u.end-u.start;if(void 0===e)c-=n;else{const t=e.end-e.start;n!==t&&(c+=t-n)}}u=u.prev}}return c}function i(t,e,n){let o=t;for(;null!==o;){if(e<o.end+("element"!==o.type||0===n?1:0)){const t=o.child;if(null!==t){o=t;continue}return o}const t=o.next;if(null===t)break;o=t}return null}function f(t,e,n,o,s,l){return{child:t,end:o,key:s,next:null,parent:l,prev:null,start:n,type:e}}function c(t,n,l,r,i,c){const d=r.get(n);void 0===d&&s(3);const p=t.offset;if(o(d)){const e=a(d,r),o=0===e.length,s=o?null:u(t,e,null,r,i,c);t.prevIsBlock&&!o||(t.prevIsBlock=!0,t.offset+=c);const g=f(s,"element",p,p,n,l);null!==s&&(s.parent=g);const h=t.offset;return g.end=h,i.set(n,g),g}t.prevIsBlock=!1;const g=e(d),h=g?d.__text.length:1,_=f(null,g?"text":"inline",p,t.offset+=h,n,l);return i.set(n,_),_}function u(t,e,n,o,s,l){let r=null,i=null;const f=e.length;for(let u=0;u<f;u++){const f=c(t,e[u],n,o,s,l);null===i?r=f:(f.prev=i,i.next=f),i=f}return r}function a(e,n){const o=[];let l=e.__first;for(;null!==l;){const e=null===n?t(l):n.get(l);null==e&&s(174),o.push(l),l=e.__next}return o}const d=a;function p(t,e=1,n){const o=(n||t._pendingEditorState||t._editorState)._nodeMap,s=o.get("root"),r=new Map,i=u({offset:0,prevIsBlock:!1},a(s,o),null,o,r,e);return new l(r,i,e)}export{a as $createChildrenArray,p as $createOffsetView,l as OffsetView,d as createChildrenArray};
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict
|
|
8
|
+
*/
|
|
9
|
+
import type {
|
|
10
|
+
EditorState,
|
|
11
|
+
LexicalEditor,
|
|
12
|
+
NodeKey,
|
|
13
|
+
RangeSelection,
|
|
14
|
+
} from '@ekz/lexical';
|
|
15
|
+
type OffsetElementNode = {
|
|
16
|
+
child: null | OffsetNode,
|
|
17
|
+
end: number,
|
|
18
|
+
key: NodeKey,
|
|
19
|
+
next: null | OffsetNode,
|
|
20
|
+
parent: null | OffsetElementNode,
|
|
21
|
+
prev: null | OffsetNode,
|
|
22
|
+
start: number,
|
|
23
|
+
type: 'element',
|
|
24
|
+
};
|
|
25
|
+
type OffsetTextNode = {
|
|
26
|
+
child: null,
|
|
27
|
+
end: number,
|
|
28
|
+
key: NodeKey,
|
|
29
|
+
next: null | OffsetNode,
|
|
30
|
+
parent: null | OffsetElementNode,
|
|
31
|
+
prev: null | OffsetNode,
|
|
32
|
+
start: number,
|
|
33
|
+
type: 'text',
|
|
34
|
+
};
|
|
35
|
+
type OffsetInlineNode = {
|
|
36
|
+
child: null,
|
|
37
|
+
end: number,
|
|
38
|
+
key: NodeKey,
|
|
39
|
+
next: null | OffsetNode,
|
|
40
|
+
parent: null | OffsetElementNode,
|
|
41
|
+
prev: null | OffsetNode,
|
|
42
|
+
start: number,
|
|
43
|
+
type: 'inline',
|
|
44
|
+
};
|
|
45
|
+
type OffsetNode = OffsetElementNode | OffsetTextNode | OffsetInlineNode;
|
|
46
|
+
type OffsetMap = Map<NodeKey, OffsetNode>;
|
|
47
|
+
declare export class OffsetView {
|
|
48
|
+
_offsetMap: OffsetMap;
|
|
49
|
+
_firstNode: null | OffsetNode;
|
|
50
|
+
_blockOffsetSize: number;
|
|
51
|
+
constructor(
|
|
52
|
+
offsetMap: OffsetMap,
|
|
53
|
+
firstNode: null | OffsetNode,
|
|
54
|
+
blockOffsetSize: number,
|
|
55
|
+
): void;
|
|
56
|
+
createSelectionFromOffsets(
|
|
57
|
+
originalStart: number,
|
|
58
|
+
originalEnd: number,
|
|
59
|
+
diffOffsetView?: OffsetView,
|
|
60
|
+
): null | RangeSelection;
|
|
61
|
+
getOffsetsFromSelection(selection: RangeSelection): [number, number];
|
|
62
|
+
}
|
|
63
|
+
declare export function $createOffsetView(
|
|
64
|
+
editor: LexicalEditor,
|
|
65
|
+
blockOffsetSize?: number,
|
|
66
|
+
editorState?: EditorState,
|
|
67
|
+
): OffsetView;
|
package/README.md
ADDED
package/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import type { EditorState, ElementNode, LexicalEditor, NodeKey, NodeMap, RangeSelection } from '@ekz/lexical';
|
|
9
|
+
type OffsetElementNode = {
|
|
10
|
+
child: null | OffsetNode;
|
|
11
|
+
end: number;
|
|
12
|
+
key: NodeKey;
|
|
13
|
+
next: null | OffsetNode;
|
|
14
|
+
parent: null | OffsetElementNode;
|
|
15
|
+
prev: null | OffsetNode;
|
|
16
|
+
start: number;
|
|
17
|
+
type: 'element';
|
|
18
|
+
};
|
|
19
|
+
type OffsetTextNode = {
|
|
20
|
+
child: null;
|
|
21
|
+
end: number;
|
|
22
|
+
key: NodeKey;
|
|
23
|
+
next: null | OffsetNode;
|
|
24
|
+
parent: null | OffsetElementNode;
|
|
25
|
+
prev: null | OffsetNode;
|
|
26
|
+
start: number;
|
|
27
|
+
type: 'text';
|
|
28
|
+
};
|
|
29
|
+
type OffsetInlineNode = {
|
|
30
|
+
child: null;
|
|
31
|
+
end: number;
|
|
32
|
+
key: NodeKey;
|
|
33
|
+
next: null | OffsetNode;
|
|
34
|
+
parent: null | OffsetElementNode;
|
|
35
|
+
prev: null | OffsetNode;
|
|
36
|
+
start: number;
|
|
37
|
+
type: 'inline';
|
|
38
|
+
};
|
|
39
|
+
type OffsetNode = OffsetElementNode | OffsetTextNode | OffsetInlineNode;
|
|
40
|
+
type OffsetMap = Map<NodeKey, OffsetNode>;
|
|
41
|
+
export declare class OffsetView {
|
|
42
|
+
_offsetMap: OffsetMap;
|
|
43
|
+
_firstNode: null | OffsetNode;
|
|
44
|
+
_blockOffsetSize: number;
|
|
45
|
+
constructor(offsetMap: OffsetMap, firstNode: null | OffsetNode, blockOffsetSize?: number);
|
|
46
|
+
createSelectionFromOffsets(originalStart: number, originalEnd: number, diffOffsetView?: OffsetView): null | RangeSelection;
|
|
47
|
+
getOffsetsFromSelection(selection: RangeSelection): [number, number];
|
|
48
|
+
}
|
|
49
|
+
export declare function $createChildrenArray(element: ElementNode, nodeMap: null | NodeMap): Array<NodeKey>;
|
|
50
|
+
/** @deprecated renamed to {@link $createChildrenArray} by @ekz/lexical-eslint-plugin rules-of-lexical */
|
|
51
|
+
export declare const createChildrenArray: typeof $createChildrenArray;
|
|
52
|
+
export declare function $createOffsetView(editor: LexicalEditor, blockOffsetSize?: number, editorState?: EditorState | null): OffsetView;
|
|
53
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekz/lexical-offset",
|
|
3
|
+
"description": "This package contains selection offset helpers for Lexical.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"lexical",
|
|
6
|
+
"editor",
|
|
7
|
+
"rich-text",
|
|
8
|
+
"offset"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"version": "0.40.0",
|
|
12
|
+
"main": "LexicalOffset.js",
|
|
13
|
+
"types": "index.d.ts",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/facebook/lexical.git",
|
|
17
|
+
"directory": "packages/lexical-offset"
|
|
18
|
+
},
|
|
19
|
+
"module": "LexicalOffset.mjs",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./index.d.ts",
|
|
25
|
+
"development": "./LexicalOffset.dev.mjs",
|
|
26
|
+
"production": "./LexicalOffset.prod.mjs",
|
|
27
|
+
"node": "./LexicalOffset.node.mjs",
|
|
28
|
+
"default": "./LexicalOffset.mjs"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./index.d.ts",
|
|
32
|
+
"development": "./LexicalOffset.dev.js",
|
|
33
|
+
"production": "./LexicalOffset.prod.js",
|
|
34
|
+
"default": "./LexicalOffset.js"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@ekz/lexical": "0.40.0"
|
|
40
|
+
}
|
|
41
|
+
}
|