@mx-space/cli 0.7.8 → 0.9.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/README.md +30 -0
- package/ROADMAP.md +1 -0
- package/dist/{Lexical.dev-rcxzwpir.mjs → Lexical.dev-By5dOKBN.mjs} +1486 -356
- package/dist/{Lexical.node-BUsI9gWr.mjs → Lexical.node-fXM8Jp7p.mjs} +39 -17
- package/dist/{Lexical.prod-C8HtkSD6.mjs → Lexical.prod-BlCvXPGa.mjs} +2279 -1768
- package/dist/LexicalClipboard.dev--HxGoSkO.mjs +915 -0
- package/dist/{LexicalClipboard.node-CO-MCKx5.mjs → LexicalClipboard.node-C3mRbrmW.mjs} +10 -3
- package/dist/LexicalClipboard.prod-DNJzFMcT.mjs +371 -0
- package/dist/LexicalCodeCore.dev-DjSrd4A4.mjs +1109 -0
- package/dist/LexicalCodeCore.prod-DTDShkR3.mjs +642 -0
- package/dist/{LexicalDragon.dev-gBP8WSRS.mjs → LexicalDragon.dev-DwprRH0z.mjs} +3 -3
- package/dist/{LexicalDragon.node-OG6G55nl.mjs → LexicalDragon.node-BVhw4C7s.mjs} +2 -2
- package/dist/{LexicalDragon.prod-DP-_Q8cv.mjs → LexicalDragon.prod-nh4J-8vn.mjs} +3 -3
- package/dist/{LexicalExtension.dev-DVbjljrm.mjs → LexicalExtension.dev-BPNyvGqe.mjs} +335 -6
- package/dist/{LexicalExtension.node-CF3EAPN7.mjs → LexicalExtension.node-BVlcrgOx.mjs} +15 -8
- package/dist/{LexicalExtension.prod-DklHHErZ.mjs → LexicalExtension.prod-DFKbiNt-.mjs} +375 -265
- package/dist/{LexicalHeadless.dev-DdWLM7Nl.mjs → LexicalHeadless.dev-B-cj7Pr5.mjs} +2 -2
- package/dist/{LexicalHeadless.prod-CoiiTDzR.mjs → LexicalHeadless.prod-Byp1vNzT.mjs} +2 -2
- package/dist/LexicalHtml.dev-prBAwszN.mjs +2424 -0
- package/dist/LexicalHtml.node-BvNF96jZ.mjs +56 -0
- package/dist/LexicalHtml.prod-Sz3CW1ht.mjs +1207 -0
- package/dist/{LexicalLink.dev-BS1Bf-yD.mjs → LexicalLink.dev-NySTYhVR.mjs} +42 -8
- package/dist/{LexicalLink.prod-B_crkoHP.mjs → LexicalLink.prod-BuCM8gts.mjs} +209 -191
- package/dist/{LexicalList.dev-OMqmcnQ-.mjs → LexicalList.dev-DIMOvSFD.mjs} +252 -80
- package/dist/{LexicalList.prod-CpL1s8vS.mjs → LexicalList.prod-MVqfA6Vo.mjs} +364 -278
- package/dist/{LexicalMarkdown.dev-BHSdx_Gr.mjs → LexicalMarkdown.dev-C7aylvCG.mjs} +729 -552
- package/dist/LexicalMarkdown.prod-CfQSM406.mjs +868 -0
- package/dist/{LexicalRichText.dev-Bz9A_WbM.mjs → LexicalRichText.dev-DfICq6lU.mjs} +272 -77
- package/dist/{LexicalRichText.node-DDpwfE-P.mjs → LexicalRichText.node-Bg9jND3Q.mjs} +21 -8
- package/dist/LexicalRichText.prod-GsFzCK-o.mjs +550 -0
- package/dist/{LexicalSelection.dev-CQ380fnD.mjs → LexicalSelection.dev-DGjj12wW.mjs} +50 -38
- package/dist/{LexicalSelection.node-DesBMjIE.mjs → LexicalSelection.node-BU5L4193.mjs} +4 -4
- package/dist/{LexicalSelection.prod-D2jivCDB.mjs → LexicalSelection.prod-D-PwRbyK.mjs} +158 -140
- package/dist/{LexicalTable.dev-Ca7mQ7Jk.mjs → LexicalTable.dev-ChP7cugd.mjs} +215 -53
- package/dist/{LexicalTable.prod-DyPAcC7y.mjs → LexicalTable.prod-Bpr9YRXp.mjs} +668 -573
- package/dist/{LexicalUtils.dev-HxM8F0g6.mjs → LexicalUtils.dev-BiFU3789.mjs} +10 -47
- package/dist/{LexicalUtils.node-lCWjRjoG.mjs → LexicalUtils.node-j3Llm7Y3.mjs} +2 -2
- package/dist/{LexicalUtils.prod-Bti5w4ki.mjs → LexicalUtils.prod-DCvrA78p.mjs} +103 -105
- package/dist/bin/mxs.d.mts +1 -1
- package/dist/bin/mxs.mjs +1 -1
- package/dist/esm-By7trG9K.mjs +2 -0
- package/dist/{esm-CK9m5JKX.mjs → esm-Dg-WhPCe.mjs} +989 -989
- package/dist/index.d.mts +11 -2
- package/dist/index.mjs +1 -1
- package/dist/{mxs-BZt1cLW9.mjs → mxs-BR5Y5ash.mjs} +25141 -24672
- package/package.json +15 -14
- package/skills/commands-auth.md +1 -1
- package/skills/commands-category.md +1 -1
- package/skills/commands-comment.md +1 -1
- package/skills/commands-config.md +1 -1
- package/skills/commands-preview.md +1 -1
- package/skills/commands-profile.md +1 -1
- package/skills/commands-project.md +57 -0
- package/skills/commands-topic.md +1 -1
- package/skills/output-modes.md +1 -1
- package/skills/overview.md +2 -1
- package/skills/safety.md +10 -0
- package/dist/LexicalClipboard.dev-BpqRwTLP.mjs +0 -494
- package/dist/LexicalClipboard.prod-B_xdeiAC.mjs +0 -291
- package/dist/LexicalCodeCore.dev-7_cexm39.mjs +0 -537
- package/dist/LexicalCodeCore.prod-Bd6WFHcO.mjs +0 -381
- package/dist/LexicalHtml.dev-C3ZSWBFZ.mjs +0 -684
- package/dist/LexicalHtml.node-D0TD152E.mjs +0 -24
- package/dist/LexicalHtml.prod-CX47X0zl.mjs +0 -430
- package/dist/LexicalMarkdown.prod-C-S7Ii6-.mjs +0 -754
- package/dist/LexicalRichText.prod-C8qXACDL.mjs +0 -427
- package/dist/esm-DRbkkY3r.mjs +0 -2
- /package/dist/{mxs-CM8-6uAr.d.mts → mxs-iqJBopvE.d.mts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/lexical@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/lexical@0.45.0/node_modules/lexical/dist/Lexical.dev.mjs
|
|
2
2
|
/**
|
|
3
3
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
4
|
*
|
|
@@ -24,13 +24,6 @@ function formatDevErrorMessage(message) {
|
|
|
24
24
|
*
|
|
25
25
|
*/
|
|
26
26
|
const CAN_USE_DOM = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
27
|
-
/**
|
|
28
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
29
|
-
*
|
|
30
|
-
* This source code is licensed under the MIT license found in the
|
|
31
|
-
* LICENSE file in the root directory of this source tree.
|
|
32
|
-
*
|
|
33
|
-
*/
|
|
34
27
|
const documentMode = CAN_USE_DOM && "documentMode" in document ? document.documentMode : null;
|
|
35
28
|
const IS_APPLE = CAN_USE_DOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
36
29
|
const IS_FIREFOX = CAN_USE_DOM && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
@@ -82,9 +75,8 @@ const NON_BREAKING_SPACE = "\xA0";
|
|
|
82
75
|
const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS || IS_APPLE_WEBKIT ? NON_BREAKING_SPACE : "";
|
|
83
76
|
const DOUBLE_LINE_BREAK = "\n\n";
|
|
84
77
|
const COMPOSITION_START_CHAR = IS_FIREFOX ? NON_BREAKING_SPACE : COMPOSITION_SUFFIX;
|
|
85
|
-
const RTL = "֑-߿יִ-﷽ﹰ-ﻼ";
|
|
86
78
|
const RTL_REGEX = /* @__PURE__ */ new RegExp("^[^A-Za-zÀ-ÖØ-öø-ʸ̀-ࠀ-Ⰰ-︀--]*[֑-߿יִ-﷽ﹰ-ﻼ]");
|
|
87
|
-
const LTR_REGEX = new RegExp("^[
|
|
79
|
+
const LTR_REGEX = /* @__PURE__ */ new RegExp("^[^֑-߿יִ-﷽ﹰ-ﻼ]*[A-Za-zÀ-ÖØ-öø-ʸ̀-ࠀ-Ⰰ-︀--]");
|
|
88
80
|
const TEXT_TYPE_TO_FORMAT = {
|
|
89
81
|
bold: 1,
|
|
90
82
|
capitalize: IS_CAPITALIZE,
|
|
@@ -131,6 +123,321 @@ const TEXT_TYPE_TO_MODE = {
|
|
|
131
123
|
const NODE_STATE_KEY = "$";
|
|
132
124
|
const PROTOTYPE_CONFIG_METHOD = "$config";
|
|
133
125
|
/**
|
|
126
|
+
* The editor has at most one block cursor element
|
|
127
|
+
* ({@link LexicalEditor._blockCursorElement}) — a transient, non-lexical
|
|
128
|
+
* element the selection layer inserts among an ElementNode's children when a
|
|
129
|
+
* collapsed element selection is adjacent to a node that can't host the caret
|
|
130
|
+
* (a block decorator, or a non-empty-capable block). Slots must skip it so it
|
|
131
|
+
* is never mistaken for managed content. There is only ever one, read from the
|
|
132
|
+
* active editor.
|
|
133
|
+
*/
|
|
134
|
+
function $getActiveBlockCursorElement() {
|
|
135
|
+
return $getEditor()._blockCursorElement;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Base class for DOM slots — a pointer to the content-bearing element of a
|
|
139
|
+
* node's DOM, plus optional `before` / `after` boundaries marking where the
|
|
140
|
+
* lexical-managed content sits inside that element.
|
|
141
|
+
*
|
|
142
|
+
* For ElementNode children management see {@link ElementDOMSlot}. For
|
|
143
|
+
* non-Element nodes (TextNode, LineBreakNode, DecoratorNode) the slot still
|
|
144
|
+
* supports an internal `before` / `after` so subclasses can prepend or
|
|
145
|
+
* append non-lexical siblings around the content node and the reconciler /
|
|
146
|
+
* `setTextContent` route the actual content through the slot.
|
|
147
|
+
*
|
|
148
|
+
* @experimental
|
|
149
|
+
*/
|
|
150
|
+
var DOMSlot = class DOMSlot {
|
|
151
|
+
/** The content-bearing element of the node's DOM. */
|
|
152
|
+
element;
|
|
153
|
+
/** Upper boundary: the lexical-managed range ends before this node. */
|
|
154
|
+
before;
|
|
155
|
+
/** Lower boundary: the lexical-managed range starts after this node. */
|
|
156
|
+
after;
|
|
157
|
+
constructor(element, before, after) {
|
|
158
|
+
this.element = element;
|
|
159
|
+
this.before = before || null;
|
|
160
|
+
this.after = after || null;
|
|
161
|
+
}
|
|
162
|
+
/** Return a new slot with `before` updated. */
|
|
163
|
+
withBefore(before) {
|
|
164
|
+
return new DOMSlot(this.element, before, this.after);
|
|
165
|
+
}
|
|
166
|
+
/** Return a new slot with `after` updated. */
|
|
167
|
+
withAfter(after) {
|
|
168
|
+
return new DOMSlot(this.element, this.before, after);
|
|
169
|
+
}
|
|
170
|
+
/** Return a new slot with `element` updated. */
|
|
171
|
+
withElement(element) {
|
|
172
|
+
if (this.element === element) return this;
|
|
173
|
+
return new DOMSlot(element, this.before, this.after);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Insert the given node before `this.before` (if defined) or append it to
|
|
177
|
+
* `this.element` otherwise. Subclasses may override to respect additional
|
|
178
|
+
* boundaries (e.g. `ElementDOMSlot` also keeps the managed line break at
|
|
179
|
+
* the end).
|
|
180
|
+
*/
|
|
181
|
+
insertChild(dom) {
|
|
182
|
+
const before = this.getInsertionAnchor();
|
|
183
|
+
if (!(before === null || before.parentElement === this.element)) formatDevErrorMessage(`DOMSlot.insertChild: before is not in element`);
|
|
184
|
+
this.element.insertBefore(dom, before);
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Remove the given child from `this.element`. Throws if it was not a child.
|
|
189
|
+
*/
|
|
190
|
+
removeChild(dom) {
|
|
191
|
+
if (!(dom.parentElement === this.element)) formatDevErrorMessage(`DOMSlot.removeChild: dom is not in element`);
|
|
192
|
+
this.element.removeChild(dom);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Replace `prevDom` with `dom`. Throws if `prevDom` is not a child.
|
|
197
|
+
*/
|
|
198
|
+
replaceChild(dom, prevDom) {
|
|
199
|
+
if (!(prevDom.parentElement === this.element)) formatDevErrorMessage(`DOMSlot.replaceChild: prevDom is not in element`);
|
|
200
|
+
this.element.replaceChild(dom, prevDom);
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Returns the first managed child (the first node in
|
|
205
|
+
* `this.element` that is not a non-lexical prelude / decoration), or
|
|
206
|
+
* `null` if there is none. Subclasses may override to also skip
|
|
207
|
+
* reconciler-managed scaffolding such as the managed line break.
|
|
208
|
+
*/
|
|
209
|
+
getFirstChild() {
|
|
210
|
+
const anchor = this.getFirstChildAnchor();
|
|
211
|
+
const firstChild = anchor ? anchor.nextSibling : this.element.firstChild;
|
|
212
|
+
return firstChild === this.getInsertionAnchor() ? null : firstChild;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* @internal
|
|
216
|
+
*
|
|
217
|
+
* The leading-boundary counterpart to {@link getInsertionAnchor}: the node
|
|
218
|
+
* the lexical-managed range starts immediately after (its `nextSibling` is
|
|
219
|
+
* the first managed child), or `null` when managed children begin at
|
|
220
|
+
* `this.element.firstChild`. The base slot uses `this.after`; subclasses
|
|
221
|
+
* extend it to skip leading non-lexical scaffolding (e.g. the block cursor).
|
|
222
|
+
*/
|
|
223
|
+
getFirstChildAnchor() {
|
|
224
|
+
return this.after;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Map a DOM selection point landing at or inside `leafDOM` (the node's
|
|
228
|
+
* keyed DOM) to whether the caret is positioned BEFORE or AFTER the
|
|
229
|
+
* node in document order. The default implementation derives the
|
|
230
|
+
* boundary from `this.element`'s index inside `leafDOM`:
|
|
231
|
+
*
|
|
232
|
+
* - When `this.element === leafDOM` (no wrap exposed an inner content
|
|
233
|
+
* element via `withElement`): only a DOM caret directly on
|
|
234
|
+
* `leafDOM` at offset 0 counts as "before". Matches the historical
|
|
235
|
+
* decorator rule.
|
|
236
|
+
* - When `this.element !== leafDOM` (wrap pattern that exposed the
|
|
237
|
+
* inner content element via `withElement`, e.g. a `<br>` inside a
|
|
238
|
+
* decoration `<span>`): caret positions at or before the content
|
|
239
|
+
* element are "before", later positions are "after". Handles
|
|
240
|
+
* nested wraps by walking each side up to its top-level child of
|
|
241
|
+
* `leafDOM`.
|
|
242
|
+
*
|
|
243
|
+
* Symmetric with {@link ElementDOMSlot.resolveChildIndex}, which
|
|
244
|
+
* performs the analogous mapping for ElementNode children. Together
|
|
245
|
+
* they let the slot abstraction own all DOM-offset to lexical-offset
|
|
246
|
+
* translation.
|
|
247
|
+
*
|
|
248
|
+
* @internal
|
|
249
|
+
*/
|
|
250
|
+
resolveLeafPosition(leafDOM, initialDOM, initialOffset) {
|
|
251
|
+
if (this.element === leafDOM) return initialDOM === leafDOM && initialOffset === 0 ? "before" : "after";
|
|
252
|
+
const innerChild = $topLevelChildOf(leafDOM, this.element);
|
|
253
|
+
if (innerChild === null) return "after";
|
|
254
|
+
const innerIndex = Array.prototype.indexOf.call(leafDOM.childNodes, innerChild);
|
|
255
|
+
if (innerIndex < 0) return "after";
|
|
256
|
+
if (initialDOM === leafDOM) return initialOffset <= innerIndex ? "before" : "after";
|
|
257
|
+
const initialChild = $topLevelChildOf(leafDOM, initialDOM);
|
|
258
|
+
if (initialChild === null) return "after";
|
|
259
|
+
const childIndex = Array.prototype.indexOf.call(leafDOM.childNodes, initialChild);
|
|
260
|
+
return childIndex >= 0 && childIndex <= innerIndex ? "before" : "after";
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* @internal
|
|
264
|
+
*
|
|
265
|
+
* The node managed children are inserted before, or `null` to append.
|
|
266
|
+
* Subclasses widen this to reserve trailing scaffolding (e.g.
|
|
267
|
+
* {@link ElementDOMSlot} keeps the managed line break last).
|
|
268
|
+
*/
|
|
269
|
+
getInsertionAnchor() {
|
|
270
|
+
return this.before;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
function $topLevelChildOf(parent, descendant) {
|
|
274
|
+
let node = descendant;
|
|
275
|
+
while (node !== null && node.parentNode !== parent) node = node.parentNode;
|
|
276
|
+
return node;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* A utility class for managing the DOM children of an ElementNode.
|
|
280
|
+
*
|
|
281
|
+
* Extends {@link DOMSlot} with ElementNode-specific scaffolding — the
|
|
282
|
+
* reconciler-managed line break that keeps empty elements selectable, and
|
|
283
|
+
* the offset / index resolution helpers needed when mapping DOM selections
|
|
284
|
+
* onto lexical positions. The base `before` / `after` boundaries and the
|
|
285
|
+
* children mutation helpers (`insertChild`, `removeChild`, …) live on
|
|
286
|
+
* {@link DOMSlot}.
|
|
287
|
+
*/
|
|
288
|
+
var ElementDOMSlot = class ElementDOMSlot extends DOMSlot {
|
|
289
|
+
/** Return a new slot with `before` updated, preserving subclass type. */
|
|
290
|
+
withBefore(before) {
|
|
291
|
+
return new ElementDOMSlot(this.element, before, this.after);
|
|
292
|
+
}
|
|
293
|
+
/** Return a new slot with `after` updated, preserving subclass type. */
|
|
294
|
+
withAfter(after) {
|
|
295
|
+
return new ElementDOMSlot(this.element, this.before, after);
|
|
296
|
+
}
|
|
297
|
+
/** Return a new slot with `element` updated, preserving subclass type. */
|
|
298
|
+
withElement(element) {
|
|
299
|
+
if (this.element === element) return this;
|
|
300
|
+
return new ElementDOMSlot(element, this.before, this.after);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* @internal
|
|
304
|
+
*/
|
|
305
|
+
getInsertionAnchor() {
|
|
306
|
+
return super.getInsertionAnchor() || this.getManagedLineBreak();
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* @internal
|
|
310
|
+
*
|
|
311
|
+
* Extends the leading boundary to skip the editor's transient block cursor
|
|
312
|
+
* when it sits at the head of the managed range (a collapsed element
|
|
313
|
+
* selection at offset 0), mirroring how {@link getInsertionAnchor} extends
|
|
314
|
+
* the trailing boundary past the managed line break. Only ElementNodes host
|
|
315
|
+
* a block cursor among their children, so the base slot stays editor-free.
|
|
316
|
+
*/
|
|
317
|
+
getFirstChildAnchor() {
|
|
318
|
+
const after = super.getFirstChildAnchor();
|
|
319
|
+
const firstChild = after ? after.nextSibling : this.element.firstChild;
|
|
320
|
+
return firstChild !== null && firstChild === $getActiveBlockCursorElement() ? firstChild : after;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* @internal
|
|
324
|
+
*/
|
|
325
|
+
getManagedLineBreak() {
|
|
326
|
+
return this.element.__lexicalLineBreak || null;
|
|
327
|
+
}
|
|
328
|
+
/** @internal */
|
|
329
|
+
setManagedLineBreak(lineBreakType) {
|
|
330
|
+
const element = this.element;
|
|
331
|
+
element.__lexicalLastChildKind = lineBreakType;
|
|
332
|
+
if (lineBreakType === null) this.removeManagedLineBreak();
|
|
333
|
+
else {
|
|
334
|
+
const webkitHack = lineBreakType === "decorator" && (IS_APPLE_WEBKIT || IS_IOS || IS_SAFARI);
|
|
335
|
+
this.insertManagedLineBreak(webkitHack);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** @internal */
|
|
339
|
+
removeManagedLineBreak() {
|
|
340
|
+
const br = this.getManagedLineBreak();
|
|
341
|
+
if (br) {
|
|
342
|
+
const element = this.element;
|
|
343
|
+
const sibling = br.nodeName === "IMG" ? br.nextSibling : null;
|
|
344
|
+
if (sibling) element.removeChild(sibling);
|
|
345
|
+
element.removeChild(br);
|
|
346
|
+
element.__lexicalLineBreak = void 0;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/** @internal */
|
|
350
|
+
insertManagedLineBreak(webkitHack) {
|
|
351
|
+
const prevBreak = this.getManagedLineBreak();
|
|
352
|
+
if (prevBreak) {
|
|
353
|
+
if (webkitHack === (prevBreak.nodeName === "IMG")) return;
|
|
354
|
+
this.removeManagedLineBreak();
|
|
355
|
+
}
|
|
356
|
+
const element = this.element;
|
|
357
|
+
const before = this.before;
|
|
358
|
+
const br = document.createElement("br");
|
|
359
|
+
element.insertBefore(br, before);
|
|
360
|
+
if (webkitHack) {
|
|
361
|
+
const img = document.createElement("img");
|
|
362
|
+
img.setAttribute("data-lexical-linebreak", "true");
|
|
363
|
+
img.style.setProperty("display", "inline", "important");
|
|
364
|
+
img.style.setProperty("border", "0px", "important");
|
|
365
|
+
img.style.setProperty("margin", "0px", "important");
|
|
366
|
+
img.alt = "";
|
|
367
|
+
element.insertBefore(img, br);
|
|
368
|
+
element.__lexicalLineBreak = img;
|
|
369
|
+
} else element.__lexicalLineBreak = br;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* @internal
|
|
373
|
+
*
|
|
374
|
+
* The DOM child index at which the first managed child appears — i.e. the
|
|
375
|
+
* count of leading non-lexical nodes (the `this.after` region, plus the
|
|
376
|
+
* block cursor when it sits at the head). Walks forward from the start,
|
|
377
|
+
* stopping at the first managed child, or at the trailing boundary
|
|
378
|
+
* (`this.before` / the managed line break via {@link getInsertionAnchor})
|
|
379
|
+
* when there are no managed children.
|
|
380
|
+
*/
|
|
381
|
+
getFirstChildOffset() {
|
|
382
|
+
const firstChild = this.getFirstChild();
|
|
383
|
+
const insertionAnchor = this.getInsertionAnchor();
|
|
384
|
+
let i = 0;
|
|
385
|
+
for (let node = this.element.firstChild; node !== null && node !== firstChild && node !== insertionAnchor; node = node.nextSibling) i++;
|
|
386
|
+
return i;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* @internal
|
|
390
|
+
*/
|
|
391
|
+
resolveChildIndex(element, elementDOM, initialDOM, initialOffset) {
|
|
392
|
+
if (initialDOM === this.element) {
|
|
393
|
+
const firstChildOffset = this.getFirstChildOffset();
|
|
394
|
+
const blockCursor = $getActiveBlockCursorElement();
|
|
395
|
+
const childNodes = this.element.childNodes;
|
|
396
|
+
const limit = Math.min(initialOffset, childNodes.length);
|
|
397
|
+
let idx = 0;
|
|
398
|
+
for (let i = firstChildOffset; i < limit; i++) if (childNodes[i] !== blockCursor) idx++;
|
|
399
|
+
return [element, Math.min(idx, element.getChildrenSize())];
|
|
400
|
+
}
|
|
401
|
+
const initialPath = indexPath(elementDOM, initialDOM);
|
|
402
|
+
initialPath.push(initialOffset);
|
|
403
|
+
const elementPath = indexPath(elementDOM, this.element);
|
|
404
|
+
let offset = element.getIndexWithinParent();
|
|
405
|
+
for (let i = 0; i < elementPath.length; i++) {
|
|
406
|
+
const target = initialPath[i];
|
|
407
|
+
const source = elementPath[i];
|
|
408
|
+
if (target === void 0 || target < source) break;
|
|
409
|
+
else if (target > source) {
|
|
410
|
+
offset += 1;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return [element.getParentOrThrow(), offset];
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
function indexPath(root, child) {
|
|
418
|
+
const path = [];
|
|
419
|
+
let node = child;
|
|
420
|
+
for (; node !== root && node !== null; node = node.parentNode) {
|
|
421
|
+
let i = 0;
|
|
422
|
+
for (let sibling = node.previousSibling; sibling !== null; sibling = sibling.previousSibling) i++;
|
|
423
|
+
path.push(i);
|
|
424
|
+
}
|
|
425
|
+
if (!(node === root)) formatDevErrorMessage(`indexPath: root is not a parent of child`);
|
|
426
|
+
return path.reverse();
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
430
|
+
*
|
|
431
|
+
* This source code is licensed under the MIT license found in the
|
|
432
|
+
* LICENSE file in the root directory of this source tree.
|
|
433
|
+
*
|
|
434
|
+
*/
|
|
435
|
+
let envLexicalVersion;
|
|
436
|
+
try {
|
|
437
|
+
envLexicalVersion = "0.45.0+dev.esm";
|
|
438
|
+
} catch (_unused) {}
|
|
439
|
+
const LEXICAL_VERSION = envLexicalVersion ?? "0.45.0+source";
|
|
440
|
+
/**
|
|
134
441
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
135
442
|
*
|
|
136
443
|
* This source code is licensed under the MIT license found in the
|
|
@@ -178,6 +485,191 @@ var DequeSet = class {
|
|
|
178
485
|
* LICENSE file in the root directory of this source tree.
|
|
179
486
|
*
|
|
180
487
|
*/
|
|
488
|
+
const TOMBSTONE = null;
|
|
489
|
+
const GEN_MAP_SIZE_THRESHOLD = 1e3;
|
|
490
|
+
/**
|
|
491
|
+
* @internal
|
|
492
|
+
*
|
|
493
|
+
* Create a copy of the given Map, returning either a fresh Map or a clone
|
|
494
|
+
* of a copy-on-write GenMap depending on the source type and size.
|
|
495
|
+
*
|
|
496
|
+
* - If the source is already a GenMap, returns `map.clone()` (O(1)).
|
|
497
|
+
* - If the source is a plain Map below the threshold, returns
|
|
498
|
+
* `new Map(map)` to avoid the GenMap overhead on small docs.
|
|
499
|
+
* - Otherwise wraps a fresh GenMap around the source.
|
|
500
|
+
*/
|
|
501
|
+
function cloneMap(map, minGenMapSize = GEN_MAP_SIZE_THRESHOLD) {
|
|
502
|
+
if (map instanceof GenMap) return map.clone();
|
|
503
|
+
if (map.size < minGenMapSize) return new Map(map);
|
|
504
|
+
return new GenMap().init(new Map(map), void 0, map.size);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* @internal
|
|
508
|
+
*
|
|
509
|
+
* A copy-on-write Map suitable for cloning large collections cheaply.
|
|
510
|
+
*
|
|
511
|
+
* Before being written to, a GenMap shares its `_old` and `_nursery` Maps
|
|
512
|
+
* with the GenMap it was cloned from. On first write it either compacts
|
|
513
|
+
* (folds `_nursery` into a new `_old`) or shallow-copies `_nursery`,
|
|
514
|
+
* isolating subsequent writes from sibling clones.
|
|
515
|
+
*
|
|
516
|
+
* `_old` is the immutable snapshot from the most recent compaction;
|
|
517
|
+
* `_nursery` holds writes since the last compaction (deletions stored as
|
|
518
|
+
* `TOMBSTONE`). `_mutable` tracks whether `_nursery` may be written to
|
|
519
|
+
* directly or must first be cloned.
|
|
520
|
+
*
|
|
521
|
+
* Implements the full `Map<K, V>` interface; methods not documented
|
|
522
|
+
* individually behave as their native `Map` counterparts.
|
|
523
|
+
*/
|
|
524
|
+
var GenMap = class GenMap {
|
|
525
|
+
_mutable = false;
|
|
526
|
+
_old = void 0;
|
|
527
|
+
_nursery = void 0;
|
|
528
|
+
_size = 0;
|
|
529
|
+
/**
|
|
530
|
+
* Returns a new GenMap that initially shares `_old` and `_nursery`
|
|
531
|
+
* with this one. Marks both as not-mutable so the next write on either
|
|
532
|
+
* side triggers a copy-on-write of the nursery before mutating.
|
|
533
|
+
*/
|
|
534
|
+
clone() {
|
|
535
|
+
this._mutable = false;
|
|
536
|
+
return new GenMap().init(this._old, this._nursery, this._size);
|
|
537
|
+
}
|
|
538
|
+
init(old, nursery, size) {
|
|
539
|
+
this._old = old;
|
|
540
|
+
this._nursery = nursery;
|
|
541
|
+
this._size = size;
|
|
542
|
+
return this;
|
|
543
|
+
}
|
|
544
|
+
get size() {
|
|
545
|
+
return this._size;
|
|
546
|
+
}
|
|
547
|
+
has(key) {
|
|
548
|
+
return this.get(key) !== void 0;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Returns the raw value for `key`, including TOMBSTONE for keys deleted
|
|
552
|
+
* since the last compaction. Used internally to distinguish "missing"
|
|
553
|
+
* from "deleted" without doing a second lookup.
|
|
554
|
+
*/
|
|
555
|
+
getWithTombstone(key) {
|
|
556
|
+
const v = this._nursery && this._nursery.get(key);
|
|
557
|
+
if (v !== void 0) return v;
|
|
558
|
+
return this._old && this._old.get(key);
|
|
559
|
+
}
|
|
560
|
+
get(key) {
|
|
561
|
+
const v = this.getWithTombstone(key);
|
|
562
|
+
return v === TOMBSTONE ? void 0 : v;
|
|
563
|
+
}
|
|
564
|
+
shouldCompact() {
|
|
565
|
+
return this._nursery !== void 0 && this._nursery.size * 2 > this._size;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Returns the nursery for in-place writes. If this GenMap is currently
|
|
569
|
+
* sharing its nursery with an ancestor clone, this either compacts (if
|
|
570
|
+
* the nursery has grown large enough) or makes a shallow copy.
|
|
571
|
+
*/
|
|
572
|
+
getNursery() {
|
|
573
|
+
if (!this._mutable || !this._nursery) {
|
|
574
|
+
this.compact();
|
|
575
|
+
this._nursery = new Map(this._nursery);
|
|
576
|
+
this._mutable = true;
|
|
577
|
+
}
|
|
578
|
+
return this._nursery;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Fold the nursery into a new `_old` snapshot when it has grown large
|
|
582
|
+
* enough that lookup overhead outweighs the savings from sharing.
|
|
583
|
+
* Triggered automatically from `getNursery` once `_nursery.size * 2 >
|
|
584
|
+
* _size`; can be forced via `compact(true)`.
|
|
585
|
+
*/
|
|
586
|
+
compact(force = false) {
|
|
587
|
+
if (this._nursery && this._nursery.size > 0 && (force || this.shouldCompact())) {
|
|
588
|
+
const compact = new Map(this._old);
|
|
589
|
+
for (const [k, v] of this._nursery) if (v !== TOMBSTONE) compact.set(k, v);
|
|
590
|
+
else compact.delete(k);
|
|
591
|
+
this._old = compact;
|
|
592
|
+
this._nursery = void 0;
|
|
593
|
+
}
|
|
594
|
+
this._mutable = false;
|
|
595
|
+
return this;
|
|
596
|
+
}
|
|
597
|
+
set(key, value) {
|
|
598
|
+
const v = this.getWithTombstone(key);
|
|
599
|
+
if (v === value) return this;
|
|
600
|
+
const nursery = this.getNursery();
|
|
601
|
+
if (v === TOMBSTONE || v === void 0) {
|
|
602
|
+
this._size++;
|
|
603
|
+
if (v === TOMBSTONE) nursery.delete(key);
|
|
604
|
+
}
|
|
605
|
+
nursery.set(key, value);
|
|
606
|
+
return this;
|
|
607
|
+
}
|
|
608
|
+
delete(key) {
|
|
609
|
+
const deleted = this.has(key);
|
|
610
|
+
if (deleted) {
|
|
611
|
+
this.getNursery().set(key, TOMBSTONE);
|
|
612
|
+
this._size--;
|
|
613
|
+
}
|
|
614
|
+
return deleted;
|
|
615
|
+
}
|
|
616
|
+
getOrInsert(key, defaultValue) {
|
|
617
|
+
const existing = this.get(key);
|
|
618
|
+
if (existing !== void 0) return existing;
|
|
619
|
+
this.set(key, defaultValue);
|
|
620
|
+
return defaultValue;
|
|
621
|
+
}
|
|
622
|
+
getOrInsertComputed(key, computer) {
|
|
623
|
+
const existing = this.get(key);
|
|
624
|
+
if (existing !== void 0) return existing;
|
|
625
|
+
const value = computer(key);
|
|
626
|
+
this.set(key, value);
|
|
627
|
+
return value;
|
|
628
|
+
}
|
|
629
|
+
clear() {
|
|
630
|
+
this._mutable = false;
|
|
631
|
+
this._old = void 0;
|
|
632
|
+
this._nursery = void 0;
|
|
633
|
+
this._size = 0;
|
|
634
|
+
}
|
|
635
|
+
*keys() {
|
|
636
|
+
for (const pair of this.entries()) yield pair[0];
|
|
637
|
+
}
|
|
638
|
+
*values() {
|
|
639
|
+
for (const pair of this.entries()) yield pair[1];
|
|
640
|
+
}
|
|
641
|
+
*entries() {
|
|
642
|
+
const nursery = this._nursery;
|
|
643
|
+
const old = this._old;
|
|
644
|
+
if (old) for (const pair of old) {
|
|
645
|
+
const k = pair[0];
|
|
646
|
+
const v = nursery ? nursery.get(k) : void 0;
|
|
647
|
+
if (v === TOMBSTONE) continue;
|
|
648
|
+
else if (v !== void 0) pair[1] = v;
|
|
649
|
+
yield pair;
|
|
650
|
+
}
|
|
651
|
+
if (nursery) {
|
|
652
|
+
for (const pair of nursery) if (pair[1] !== TOMBSTONE && !(old && old.has(pair[0]))) yield pair;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
forEach(callbackfn, thisArg) {
|
|
656
|
+
if (thisArg !== void 0) callbackfn = callbackfn.bind(thisArg);
|
|
657
|
+
for (const [k, v] of this.entries()) callbackfn(v, k, this);
|
|
658
|
+
}
|
|
659
|
+
get [Symbol.toStringTag]() {
|
|
660
|
+
return "GenMap";
|
|
661
|
+
}
|
|
662
|
+
[Symbol.iterator]() {
|
|
663
|
+
return this.entries();
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
/**
|
|
667
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
668
|
+
*
|
|
669
|
+
* This source code is licensed under the MIT license found in the
|
|
670
|
+
* LICENSE file in the root directory of this source tree.
|
|
671
|
+
*
|
|
672
|
+
*/
|
|
181
673
|
function $garbageCollectDetachedDecorators(editor, pendingEditorState) {
|
|
182
674
|
const currentDecorators = editor._decorators;
|
|
183
675
|
let decorators = editor._pendingDecorators || currentDecorators;
|
|
@@ -308,7 +800,7 @@ function flushMutations(editor, mutations, observer) {
|
|
|
308
800
|
const addedDOM = addedDOMs[s];
|
|
309
801
|
const node = $getNodeFromDOMNode(addedDOM);
|
|
310
802
|
const parentDOM = addedDOM.parentNode;
|
|
311
|
-
if (parentDOM != null && addedDOM !== blockCursorElement && node === null && !isManagedLineBreak(addedDOM, parentDOM, editor)) {
|
|
803
|
+
if (parentDOM != null && addedDOM !== blockCursorElement && node === null && !isManagedLineBreak(addedDOM, parentDOM, editor) && !isDOMUnmanaged(addedDOM)) {
|
|
312
804
|
if (IS_FIREFOX) {
|
|
313
805
|
const possibleText = (isHTMLElement(addedDOM) ? addedDOM.innerText : null) || addedDOM.nodeValue;
|
|
314
806
|
if (possibleText) possibleTextForFirefoxPaste += possibleText;
|
|
@@ -587,7 +1079,7 @@ function $setState(node, stateConfig, valueOrUpdater) {
|
|
|
587
1079
|
* @internal
|
|
588
1080
|
*
|
|
589
1081
|
* Register the config to this node's sharedConfigMap and throw an exception in
|
|
590
|
-
* `
|
|
1082
|
+
* `__DEV__` when a collision is detected.
|
|
591
1083
|
*/
|
|
592
1084
|
function $checkCollision(node, stateConfig, state) {
|
|
593
1085
|
{
|
|
@@ -1055,9 +1547,112 @@ function $normalizePoint(point) {
|
|
|
1055
1547
|
point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getChildrenSize() : 0, "element", true);
|
|
1056
1548
|
}
|
|
1057
1549
|
}
|
|
1550
|
+
/**
|
|
1551
|
+
* @internal
|
|
1552
|
+
*
|
|
1553
|
+
* A reconcile-managed cache of `getTextContentSize()` for leaf nodes.
|
|
1554
|
+
*
|
|
1555
|
+
* Stored as a Symbol-keyed property on the node instance itself so that
|
|
1556
|
+
* read/write are direct slot access. The slot is pre-allocated to
|
|
1557
|
+
* `undefined` as a non-enumerable property in the LexicalNode constructor
|
|
1558
|
+
* so all instances share the same V8 hidden-class shape and the setter is
|
|
1559
|
+
* a stable inline cache hit instead of a per-instance shape transition.
|
|
1560
|
+
*
|
|
1561
|
+
* ElementNodes are NOT stored here: an element can be dirty without being
|
|
1562
|
+
* cloned (a descendant edit marks ancestors dirty via
|
|
1563
|
+
* `internalMarkParentElementsAsDirty` but does not `getWritable()` them), so
|
|
1564
|
+
* the same — DEV-frozen — instance would need its size rewritten when its
|
|
1565
|
+
* text changes, which the skip-if-set guard cannot do. Element sizes come
|
|
1566
|
+
* from `dom.__lexicalTextContent` instead (see `$prevSuffixTextSize`).
|
|
1567
|
+
*
|
|
1568
|
+
* Leaf writes are skipped when the slot is already not `undefined`. The
|
|
1569
|
+
* setter is only re-entered for the same instance via cross-parent moves
|
|
1570
|
+
* (where the leaf is reused in a new parent without going through
|
|
1571
|
+
* `getWritable` — text is unchanged, so the prior cycle's value is still
|
|
1572
|
+
* correct). A leaf whose text actually changed went through
|
|
1573
|
+
* `getWritable()` and produced a fresh clone via `static clone(node)` ->
|
|
1574
|
+
* ctor -> fresh `undefined` slot, so the setter writes through normally.
|
|
1575
|
+
*
|
|
1576
|
+
* The reconciler sets this on every reconciled leaf at the end of
|
|
1577
|
+
* `$reconcileNode` (and on every newly-created leaf in `$createNode`), so
|
|
1578
|
+
* the previous editor state's leaves always carry a valid cached size from
|
|
1579
|
+
* the cycle that just committed.
|
|
1580
|
+
*
|
|
1581
|
+
* Suffix-incremental fast path reads this off the previous-state instance
|
|
1582
|
+
* to get the pre-reconcile size of dirty children in O(1), avoiding both
|
|
1583
|
+
* the `getLatest()` -> next-state trap and a recursive prev-tree walk.
|
|
1584
|
+
*/
|
|
1585
|
+
const CACHED_TEXT_SIZE_KEY = Symbol.for("@lexical/CachedTextSize");
|
|
1586
|
+
function $prevSuffixTextSize(startKey, count) {
|
|
1587
|
+
return activePrevEditorState.read(() => {
|
|
1588
|
+
let size = 0;
|
|
1589
|
+
let cur = startKey;
|
|
1590
|
+
for (let i = 0; i < count && cur !== null; i++) {
|
|
1591
|
+
const prevNode = activePrevNodeMap.get(cur);
|
|
1592
|
+
if (!(prevNode !== void 0)) formatDevErrorMessage(`prevSuffixTextSize: missing prev node for key ${cur}`);
|
|
1593
|
+
if ($isElementNode(prevNode)) {
|
|
1594
|
+
const nextNode = activeNextNodeMap.get(cur);
|
|
1595
|
+
if (nextNode !== void 0 && $isElementNode(nextNode) && nextNode.__parent !== prevNode.__parent) size += prevNode.getTextContentSize();
|
|
1596
|
+
else {
|
|
1597
|
+
const keyedDom = activePrevKeyToDOMMap.get(cur);
|
|
1598
|
+
const cached = keyedDom && keyedDom.__lexicalTextContent;
|
|
1599
|
+
if (!(typeof cached === "string")) formatDevErrorMessage(`prevSuffixTextSize: missing __lexicalTextContent for ElementNode of type ${prevNode.getType()}`);
|
|
1600
|
+
size += cached.length;
|
|
1601
|
+
}
|
|
1602
|
+
if (i < count - 1 && !prevNode.isInline()) size += 2;
|
|
1603
|
+
} else {
|
|
1604
|
+
const cached = prevNode[CACHED_TEXT_SIZE_KEY];
|
|
1605
|
+
if (!(cached !== void 0)) formatDevErrorMessage(`prevSuffixTextSize: missing cached size for leaf ${prevNode.getType()} key ${cur}`);
|
|
1606
|
+
size += cached;
|
|
1607
|
+
}
|
|
1608
|
+
cur = prevNode.__next;
|
|
1609
|
+
}
|
|
1610
|
+
return size;
|
|
1611
|
+
}, { editor: activeEditor$1 });
|
|
1612
|
+
}
|
|
1613
|
+
function $setCachedTextSize(node) {
|
|
1614
|
+
if ($isElementNode(node)) return;
|
|
1615
|
+
if (node[CACHED_TEXT_SIZE_KEY] !== void 0) return;
|
|
1616
|
+
node[CACHED_TEXT_SIZE_KEY] = $isTextNode(node) ? node.__text.length : node.getTextContentSize();
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Minimum children count for the suffix-incremental fast path to engage.
|
|
1620
|
+
* The fast path adds bookkeeping (cache lookups, suffix walks, splice) that
|
|
1621
|
+
* a few-children parent's general walk would beat — gate by a threshold so
|
|
1622
|
+
* the overhead only kicks in where the prefix preservation pays for it.
|
|
1623
|
+
* Tuned via `editorCycle.bench`.
|
|
1624
|
+
*/
|
|
1625
|
+
const MIN_FAST_PATH_CHILDREN = 4;
|
|
1058
1626
|
let subTreeTextContent = "";
|
|
1059
1627
|
let subTreeTextFormat = null;
|
|
1060
1628
|
let subTreeTextStyle = null;
|
|
1629
|
+
let subTreeFirstTextKey = null;
|
|
1630
|
+
function $beginCaptureGuard() {
|
|
1631
|
+
return {
|
|
1632
|
+
firstTextKey: subTreeFirstTextKey,
|
|
1633
|
+
format: subTreeTextFormat,
|
|
1634
|
+
style: subTreeTextStyle
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function $endCaptureGuard(saved) {
|
|
1638
|
+
if (saved.firstTextKey !== null) {
|
|
1639
|
+
subTreeTextFormat = saved.format;
|
|
1640
|
+
subTreeTextStyle = saved.style;
|
|
1641
|
+
subTreeFirstTextKey = saved.firstTextKey;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function $bubbleChildFirstText(childKeyedDom) {
|
|
1645
|
+
if (subTreeFirstTextKey !== null) return;
|
|
1646
|
+
const childFirstKey = childKeyedDom.__lexicalFirstTextKey;
|
|
1647
|
+
if (!(childFirstKey !== void 0)) formatDevErrorMessage(`$bubbleChildFirstText: missing __lexicalFirstTextKey on element keyed DOM`);
|
|
1648
|
+
if (childFirstKey === null) return;
|
|
1649
|
+
const textNode = activeNextNodeMap.get(childFirstKey);
|
|
1650
|
+
if ($isTextNode(textNode)) {
|
|
1651
|
+
subTreeTextFormat = textNode.getFormat();
|
|
1652
|
+
subTreeTextStyle = textNode.getStyle();
|
|
1653
|
+
subTreeFirstTextKey = childFirstKey;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1061
1656
|
let activeEditorConfig;
|
|
1062
1657
|
let activeEditor$1;
|
|
1063
1658
|
let activeEditorNodes;
|
|
@@ -1067,17 +1662,21 @@ let activeMutationListeners;
|
|
|
1067
1662
|
let activeDirtyElements;
|
|
1068
1663
|
let activeDirtyLeaves;
|
|
1069
1664
|
let activePrevNodeMap;
|
|
1665
|
+
let activePrevEditorState;
|
|
1070
1666
|
let activeNextNodeMap;
|
|
1071
1667
|
let activePrevKeyToDOMMap;
|
|
1668
|
+
let activeDirtyChildrenByParent;
|
|
1072
1669
|
let mutatedNodes;
|
|
1073
1670
|
let activeEditorDOMRenderConfig;
|
|
1074
1671
|
function $destroyNode(key, parentDOM) {
|
|
1075
1672
|
const node = activePrevNodeMap.get(key);
|
|
1673
|
+
const isMoved = activeNextNodeMap.has(key);
|
|
1076
1674
|
if (parentDOM !== null) {
|
|
1077
1675
|
const dom = getPrevElementByKeyOrThrow(key);
|
|
1078
1676
|
if (dom.parentNode === parentDOM) parentDOM.removeChild(dom);
|
|
1079
1677
|
}
|
|
1080
|
-
if (
|
|
1678
|
+
if (isMoved) return;
|
|
1679
|
+
activeEditor$1._keyToDOMMap.delete(key);
|
|
1081
1680
|
if ($isElementNode(node)) {
|
|
1082
1681
|
const children = $createChildrenArray(node, activePrevNodeMap);
|
|
1083
1682
|
$destroyChildren(children, 0, children.length - 1, null);
|
|
@@ -1101,12 +1700,7 @@ function setElementIndent(dom, indent) {
|
|
|
1101
1700
|
if (indent > 0 && !elementHasClassName) dom.classList.add(indentClassName);
|
|
1102
1701
|
else if (indent < 1 && elementHasClassName) dom.classList.remove(indentClassName);
|
|
1103
1702
|
}
|
|
1104
|
-
|
|
1105
|
-
dom.style.setProperty("padding-inline-start", "");
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
const indentationBaseValue = getComputedStyle(activeEditor$1._rootElement || dom).getPropertyValue("--lexical-indent-base-value") || DEFAULT_INDENT_VALUE;
|
|
1109
|
-
dom.style.setProperty("padding-inline-start", `calc(${indent} * ${indentationBaseValue})`);
|
|
1703
|
+
dom.style.setProperty("padding-inline-start", indent === 0 ? "" : `calc(${indent} * var(--lexical-indent-base-value, ${DEFAULT_INDENT_VALUE}))`);
|
|
1110
1704
|
}
|
|
1111
1705
|
function setElementFormat(dom, format) {
|
|
1112
1706
|
const domStyle = dom.style;
|
|
@@ -1123,7 +1717,7 @@ function $getReconciledDirection(node) {
|
|
|
1123
1717
|
if (direction !== null) return direction;
|
|
1124
1718
|
if ($isRootNode(node)) return null;
|
|
1125
1719
|
const parent = node.getParentOrThrow();
|
|
1126
|
-
if (!$
|
|
1720
|
+
if (!$isRootOrShadowRoot(parent) || parent.__dir !== null) return null;
|
|
1127
1721
|
return "auto";
|
|
1128
1722
|
}
|
|
1129
1723
|
function $setElementDirection(dom, node) {
|
|
@@ -1134,6 +1728,16 @@ function $setElementDirection(dom, node) {
|
|
|
1134
1728
|
function $createNode(key, slot) {
|
|
1135
1729
|
const node = activeNextNodeMap.get(key);
|
|
1136
1730
|
if (node === void 0) formatDevErrorMessage(`createNode: node does not exist in nodeMap`);
|
|
1731
|
+
if (slot !== null) {
|
|
1732
|
+
const prevNode = activePrevNodeMap.get(key);
|
|
1733
|
+
if (prevNode !== void 0 && prevNode.__parent !== node.__parent) {
|
|
1734
|
+
const existingDOM = activePrevKeyToDOMMap.get(key);
|
|
1735
|
+
if (existingDOM !== void 0) {
|
|
1736
|
+
slot.insertChild(existingDOM);
|
|
1737
|
+
return $reconcileNode(key, slot.element);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1137
1741
|
const dom = activeEditorDOMRenderConfig.$createDOM(node, activeEditor$1);
|
|
1138
1742
|
storeDOMWithKey(key, dom, activeEditor$1);
|
|
1139
1743
|
if ($isTextNode(node)) dom.setAttribute("data-lexical-text", "true");
|
|
@@ -1143,9 +1747,12 @@ function $createNode(key, slot) {
|
|
|
1143
1747
|
const childrenSize = node.__size;
|
|
1144
1748
|
$setElementDirection(dom, node);
|
|
1145
1749
|
if (indent !== 0) setElementIndent(dom, indent);
|
|
1146
|
-
if (childrenSize
|
|
1750
|
+
if (childrenSize === 0) {
|
|
1751
|
+
dom.__lexicalTextContent = "";
|
|
1752
|
+
dom.__lexicalFirstTextKey = null;
|
|
1753
|
+
} else {
|
|
1147
1754
|
const endIndex = childrenSize - 1;
|
|
1148
|
-
$createChildren($createChildrenArray(node, activeNextNodeMap), node, 0, endIndex,
|
|
1755
|
+
$createChildren($createChildrenArray(node, activeNextNodeMap), node, 0, endIndex, $getDOMSlot(node, dom, activeEditor$1));
|
|
1149
1756
|
}
|
|
1150
1757
|
const format = node.__format;
|
|
1151
1758
|
if (format !== 0) setElementFormat(dom, format);
|
|
@@ -1161,27 +1768,38 @@ function $createNode(key, slot) {
|
|
|
1161
1768
|
}
|
|
1162
1769
|
if (slot !== null) slot.insertChild(dom);
|
|
1163
1770
|
activeEditorDOMRenderConfig.$decorateDOM(node, null, dom, activeEditor$1);
|
|
1771
|
+
$setCachedTextSize(node);
|
|
1164
1772
|
Object.freeze(node);
|
|
1165
1773
|
setMutatedNode(mutatedNodes, activeEditorNodes, activeMutationListeners, node, "created");
|
|
1166
1774
|
return dom;
|
|
1167
1775
|
}
|
|
1168
1776
|
function $createChildren(children, element, _startIndex, endIndex, slot) {
|
|
1169
1777
|
const previousSubTreeTextContent = subTreeTextContent;
|
|
1778
|
+
const outerSaved = $beginCaptureGuard();
|
|
1170
1779
|
subTreeTextContent = "";
|
|
1780
|
+
subTreeTextFormat = null;
|
|
1781
|
+
subTreeTextStyle = null;
|
|
1782
|
+
subTreeFirstTextKey = null;
|
|
1171
1783
|
let startIndex = _startIndex;
|
|
1172
1784
|
for (; startIndex <= endIndex; ++startIndex) {
|
|
1785
|
+
const saved = $beginCaptureGuard();
|
|
1173
1786
|
$createNode(children[startIndex], slot);
|
|
1174
1787
|
const node = activeNextNodeMap.get(children[startIndex]);
|
|
1175
1788
|
if (node !== null && $isTextNode(node)) {
|
|
1176
1789
|
if (subTreeTextFormat === null) {
|
|
1177
1790
|
subTreeTextFormat = node.getFormat();
|
|
1178
1791
|
subTreeTextStyle = node.getStyle();
|
|
1792
|
+
subTreeFirstTextKey = node.__key;
|
|
1179
1793
|
}
|
|
1180
1794
|
} else if ($isElementNode(node) && startIndex < endIndex && !node.isInline()) subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
1795
|
+
$endCaptureGuard(saved);
|
|
1181
1796
|
}
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1797
|
+
const cacheDom = activeEditor$1._keyToDOMMap.get(element.__key);
|
|
1798
|
+
if (!(cacheDom !== void 0)) formatDevErrorMessage(`$createChildren: Element with key ${element.__key} missing from keyToDOMMap`);
|
|
1799
|
+
cacheDom.__lexicalTextContent = subTreeTextContent;
|
|
1800
|
+
cacheDom.__lexicalFirstTextKey = subTreeFirstTextKey;
|
|
1184
1801
|
subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
|
|
1802
|
+
$endCaptureGuard(outerSaved);
|
|
1185
1803
|
}
|
|
1186
1804
|
function isLastChildLineBreakOrDecorator(element, nodeMap) {
|
|
1187
1805
|
if (element) {
|
|
@@ -1195,9 +1813,10 @@ function isLastChildLineBreakOrDecorator(element, nodeMap) {
|
|
|
1195
1813
|
return null;
|
|
1196
1814
|
}
|
|
1197
1815
|
function $reconcileElementTerminatingLineBreak(prevElement, nextElement, dom) {
|
|
1198
|
-
const
|
|
1816
|
+
const slot = $getDOMSlot(nextElement, dom, activeEditor$1);
|
|
1817
|
+
const prevLineBreak = slot.element.__lexicalLastChildKind ?? null;
|
|
1199
1818
|
const nextLineBreak = isLastChildLineBreakOrDecorator(nextElement, activeNextNodeMap);
|
|
1200
|
-
if (prevLineBreak !== nextLineBreak)
|
|
1819
|
+
if (prevLineBreak !== nextLineBreak) slot.setManagedLineBreak(nextLineBreak);
|
|
1201
1820
|
}
|
|
1202
1821
|
function reconcileTextFormat(element) {
|
|
1203
1822
|
if (subTreeTextFormat != null && subTreeTextFormat !== element.__textFormat && !activeEditorStateReadOnly) element.setTextFormat(subTreeTextFormat);
|
|
@@ -1208,9 +1827,210 @@ function reconcileTextStyle(element) {
|
|
|
1208
1827
|
function $reconcileChildrenWithDirection(prevElement, nextElement, dom) {
|
|
1209
1828
|
subTreeTextFormat = null;
|
|
1210
1829
|
subTreeTextStyle = null;
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1830
|
+
subTreeFirstTextKey = null;
|
|
1831
|
+
$reconcileChildren(prevElement, nextElement, $getDOMSlot(nextElement, dom, activeEditor$1));
|
|
1832
|
+
if (!$isRootOrShadowRoot(nextElement)) {
|
|
1833
|
+
reconcileTextFormat(nextElement);
|
|
1834
|
+
reconcileTextStyle(nextElement);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
function $buildDirtyChildrenByParent() {
|
|
1838
|
+
const map = /* @__PURE__ */ new Map();
|
|
1839
|
+
const addKeysToMap = (keys) => {
|
|
1840
|
+
for (const key of keys) {
|
|
1841
|
+
const node = activeNextNodeMap.get(key);
|
|
1842
|
+
if (node === void 0) continue;
|
|
1843
|
+
const parentKey = node.__parent;
|
|
1844
|
+
if (parentKey === null) continue;
|
|
1845
|
+
let set = map.get(parentKey);
|
|
1846
|
+
if (set === void 0) {
|
|
1847
|
+
set = /* @__PURE__ */ new Set();
|
|
1848
|
+
map.set(parentKey, set);
|
|
1849
|
+
}
|
|
1850
|
+
set.add(key);
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
addKeysToMap(activeDirtyElements.keys());
|
|
1854
|
+
addKeysToMap(activeDirtyLeaves);
|
|
1855
|
+
return map;
|
|
1856
|
+
}
|
|
1857
|
+
function $suffixStartIfContiguous(parent, dirty) {
|
|
1858
|
+
const k = dirty.size;
|
|
1859
|
+
if (k === 0 || k >= parent.__size) return null;
|
|
1860
|
+
let cur = parent.__last;
|
|
1861
|
+
let suffixStart = null;
|
|
1862
|
+
let i = 0;
|
|
1863
|
+
while (cur !== null && i < k) {
|
|
1864
|
+
if (!dirty.has(cur)) return null;
|
|
1865
|
+
suffixStart = cur;
|
|
1866
|
+
const node = activeNextNodeMap.get(cur);
|
|
1867
|
+
if (node === void 0) return null;
|
|
1868
|
+
cur = node.__prev;
|
|
1869
|
+
i++;
|
|
1870
|
+
}
|
|
1871
|
+
if (i !== k) return null;
|
|
1872
|
+
if (cur !== null && dirty.has(cur)) return null;
|
|
1873
|
+
return suffixStart;
|
|
1874
|
+
}
|
|
1875
|
+
function $tryReconcileSuffixWithSizeDelta(prevElement, nextElement, slot, cacheDom, cachedParentText, suffixStartKey, k, sizeDelta) {
|
|
1876
|
+
if (sizeDelta !== 1 && sizeDelta !== -1) return false;
|
|
1877
|
+
if (k !== (sizeDelta === 1 ? 2 : 1)) return false;
|
|
1878
|
+
const kPrime = k - sizeDelta;
|
|
1879
|
+
let prevSuffixStartKey = prevElement.__last;
|
|
1880
|
+
for (let i = 0; i < kPrime - 1; i++) {
|
|
1881
|
+
if (prevSuffixStartKey === null) return false;
|
|
1882
|
+
const node = activePrevNodeMap.get(prevSuffixStartKey);
|
|
1883
|
+
if (node === void 0) return false;
|
|
1884
|
+
prevSuffixStartKey = node.__prev;
|
|
1885
|
+
}
|
|
1886
|
+
if (prevSuffixStartKey === null) return false;
|
|
1887
|
+
const nextStartNode = activeNextNodeMap.get(suffixStartKey);
|
|
1888
|
+
const prevStartNode = activePrevNodeMap.get(prevSuffixStartKey);
|
|
1889
|
+
if (nextStartNode === void 0 || prevStartNode === void 0) return false;
|
|
1890
|
+
if (nextStartNode.__prev !== prevStartNode.__prev) return false;
|
|
1891
|
+
const nextSuffixKeys = [];
|
|
1892
|
+
let cur = suffixStartKey;
|
|
1893
|
+
for (let i = 0; i < k; i++) {
|
|
1894
|
+
if (cur === null) return false;
|
|
1895
|
+
nextSuffixKeys.push(cur);
|
|
1896
|
+
const node = activeNextNodeMap.get(cur);
|
|
1897
|
+
cur = node ? node.__next : null;
|
|
1898
|
+
}
|
|
1899
|
+
const prevSuffixKeys = [];
|
|
1900
|
+
cur = prevSuffixStartKey;
|
|
1901
|
+
for (let i = 0; i < kPrime; i++) {
|
|
1902
|
+
if (cur === null) return false;
|
|
1903
|
+
prevSuffixKeys.push(cur);
|
|
1904
|
+
const node = activePrevNodeMap.get(cur);
|
|
1905
|
+
cur = node ? node.__next : null;
|
|
1906
|
+
}
|
|
1907
|
+
const prevSet = new Set(prevSuffixKeys);
|
|
1908
|
+
const nextSet = new Set(nextSuffixKeys);
|
|
1909
|
+
const ops = [];
|
|
1910
|
+
let pi = 0;
|
|
1911
|
+
let ni = 0;
|
|
1912
|
+
while (pi < kPrime && ni < k) if (nextSuffixKeys[ni] === prevSuffixKeys[pi]) {
|
|
1913
|
+
ops.push({
|
|
1914
|
+
key: nextSuffixKeys[ni],
|
|
1915
|
+
kind: "reconcile"
|
|
1916
|
+
});
|
|
1917
|
+
pi++;
|
|
1918
|
+
ni++;
|
|
1919
|
+
} else if (!nextSet.has(prevSuffixKeys[pi])) {
|
|
1920
|
+
ops.push({
|
|
1921
|
+
key: prevSuffixKeys[pi],
|
|
1922
|
+
kind: "destroy"
|
|
1923
|
+
});
|
|
1924
|
+
pi++;
|
|
1925
|
+
} else if (!prevSet.has(nextSuffixKeys[ni])) {
|
|
1926
|
+
ops.push({
|
|
1927
|
+
key: nextSuffixKeys[ni],
|
|
1928
|
+
kind: "create",
|
|
1929
|
+
nextIndex: ni
|
|
1930
|
+
});
|
|
1931
|
+
ni++;
|
|
1932
|
+
} else return false;
|
|
1933
|
+
while (pi < kPrime) ops.push({
|
|
1934
|
+
key: prevSuffixKeys[pi++],
|
|
1935
|
+
kind: "destroy"
|
|
1936
|
+
});
|
|
1937
|
+
while (ni < k) {
|
|
1938
|
+
ops.push({
|
|
1939
|
+
key: nextSuffixKeys[ni],
|
|
1940
|
+
kind: "create",
|
|
1941
|
+
nextIndex: ni
|
|
1942
|
+
});
|
|
1943
|
+
ni++;
|
|
1944
|
+
}
|
|
1945
|
+
const oldSuffixLength = $prevSuffixTextSize(prevSuffixStartKey, kPrime);
|
|
1946
|
+
for (const op of ops) {
|
|
1947
|
+
const saved = $beginCaptureGuard();
|
|
1948
|
+
if (op.kind === "reconcile") $reconcileNode(op.key, slot.element);
|
|
1949
|
+
else if (op.kind === "destroy") $destroyNode(op.key, slot.element);
|
|
1950
|
+
else {
|
|
1951
|
+
let beforeDOM = null;
|
|
1952
|
+
for (let j = op.nextIndex + 1; j < k; j++) {
|
|
1953
|
+
const siblingDOM = activeEditor$1._keyToDOMMap.get(nextSuffixKeys[j]);
|
|
1954
|
+
if (siblingDOM !== void 0) {
|
|
1955
|
+
beforeDOM = siblingDOM;
|
|
1956
|
+
break;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
$createNode(op.key, slot.withBefore(beforeDOM ?? slot.before));
|
|
1960
|
+
}
|
|
1961
|
+
if (op.kind !== "destroy") {
|
|
1962
|
+
const opNode = activeNextNodeMap.get(op.key);
|
|
1963
|
+
if (opNode && $isTextNode(opNode) && subTreeTextFormat === null) {
|
|
1964
|
+
subTreeTextFormat = opNode.getFormat();
|
|
1965
|
+
subTreeTextStyle = opNode.getStyle();
|
|
1966
|
+
subTreeFirstTextKey = opNode.__key;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
$endCaptureGuard(saved);
|
|
1970
|
+
}
|
|
1971
|
+
let newSuffix = "";
|
|
1972
|
+
for (let i = 0; i < k; i++) {
|
|
1973
|
+
const node = activeNextNodeMap.get(nextSuffixKeys[i]);
|
|
1974
|
+
if (node === void 0) return false;
|
|
1975
|
+
let text;
|
|
1976
|
+
if ($isElementNode(node)) {
|
|
1977
|
+
const childKeyedDom = activeEditor$1._keyToDOMMap.get(nextSuffixKeys[i]);
|
|
1978
|
+
const cached = childKeyedDom && childKeyedDom.__lexicalTextContent;
|
|
1979
|
+
if (!(typeof cached === "string")) formatDevErrorMessage(`tryReconcileSuffixWithSizeDelta: missing __lexicalTextContent on child of type ${node.getType()} after suffix reconcile`);
|
|
1980
|
+
text = cached;
|
|
1981
|
+
} else text = node.getTextContent();
|
|
1982
|
+
newSuffix += text;
|
|
1983
|
+
if (i < k - 1 && $isElementNode(node) && !node.isInline()) newSuffix += DOUBLE_LINE_BREAK;
|
|
1984
|
+
}
|
|
1985
|
+
cacheDom.__lexicalTextContent = cachedParentText.slice(0, cachedParentText.length - oldSuffixLength) + newSuffix;
|
|
1986
|
+
return true;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Decide whether the post-suffix-walk values of `subTreeTextFormat` /
|
|
1990
|
+
* `subTreeTextStyle` should be kept (the prefix has no text descendant
|
|
1991
|
+
* and the suffix carries the canonical first text) or replaced with the
|
|
1992
|
+
* prev-cycle's canonical values (the prefix is still authoritative).
|
|
1993
|
+
*
|
|
1994
|
+
* The cached `__lexicalFirstTextKey` on `dom` is the deep TextNode key
|
|
1995
|
+
* recorded when this element's children were last walked. We climb its
|
|
1996
|
+
* ancestor chain in next-state until we reach a direct child of
|
|
1997
|
+
* `nextElement`, then probe `dirtyChildren`: if that direct child is
|
|
1998
|
+
* dirty (or the cached key is missing from the next map), the cached
|
|
1999
|
+
* key has been moved into the suffix's subtree or destroyed, so the
|
|
2000
|
+
* suffix-derived values are authoritative. Otherwise the prefix is
|
|
2001
|
+
* canonical and we recover format/style from the live text node, which
|
|
2002
|
+
* lets `reconcileTextFormat` / `reconcileTextStyle` no-op via their
|
|
2003
|
+
* existing equality check against the parent's `__textFormat` /
|
|
2004
|
+
* `__textStyle`.
|
|
2005
|
+
*
|
|
2006
|
+
* Walk depth is bounded by tree depth from the text node to the
|
|
2007
|
+
* reconciled element (typically 1 — text directly under a paragraph).
|
|
2008
|
+
* Always refreshes the cache for the next cycle.
|
|
2009
|
+
*/
|
|
2010
|
+
function $resolveSuffixPathFormat(nextElement, dom, dirtyChildren) {
|
|
2011
|
+
const cachedFirstTextKey = dom.__lexicalFirstTextKey;
|
|
2012
|
+
if (cachedFirstTextKey != null) {
|
|
2013
|
+
const parentKey = nextElement.__key;
|
|
2014
|
+
let ancestor = cachedFirstTextKey;
|
|
2015
|
+
while (ancestor !== null) {
|
|
2016
|
+
const node = activeNextNodeMap.get(ancestor);
|
|
2017
|
+
if (node === void 0) {
|
|
2018
|
+
ancestor = null;
|
|
2019
|
+
break;
|
|
2020
|
+
}
|
|
2021
|
+
if (node.__parent === parentKey) break;
|
|
2022
|
+
ancestor = node.__parent;
|
|
2023
|
+
}
|
|
2024
|
+
if (ancestor !== null && !dirtyChildren.has(ancestor)) {
|
|
2025
|
+
const textNode = activeNextNodeMap.get(cachedFirstTextKey);
|
|
2026
|
+
if ($isTextNode(textNode)) {
|
|
2027
|
+
subTreeTextFormat = textNode.getFormat();
|
|
2028
|
+
subTreeTextStyle = textNode.getStyle();
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
dom.__lexicalFirstTextKey = subTreeFirstTextKey;
|
|
1214
2034
|
}
|
|
1215
2035
|
function $reconcileChildren(prevElement, nextElement, slot) {
|
|
1216
2036
|
const previousSubTreeTextContent = subTreeTextContent;
|
|
@@ -1218,6 +2038,105 @@ function $reconcileChildren(prevElement, nextElement, slot) {
|
|
|
1218
2038
|
const nextChildrenSize = nextElement.__size;
|
|
1219
2039
|
subTreeTextContent = "";
|
|
1220
2040
|
const dom = slot.element;
|
|
2041
|
+
const cacheDom = activeEditor$1._keyToDOMMap.get(nextElement.__key);
|
|
2042
|
+
if (!(cacheDom !== void 0)) formatDevErrorMessage(`$reconcileChildren: Element with key ${nextElement.__key} missing from keyToDOMMap`);
|
|
2043
|
+
const sizeDelta = nextChildrenSize - prevChildrenSize;
|
|
2044
|
+
if (!treatAllNodesAsDirty && Math.abs(sizeDelta) <= 1 && prevChildrenSize >= MIN_FAST_PATH_CHILDREN && prevElement.__first === nextElement.__first && (sizeDelta !== 0 || !activeEditor$1._cloneNotNeeded.has(prevElement.__key))) {
|
|
2045
|
+
const cachedParentText = cacheDom.__lexicalTextContent;
|
|
2046
|
+
const dirtyChildren = activeDirtyChildrenByParent.get(prevElement.__key);
|
|
2047
|
+
if (!treatAllNodesAsDirty && typeof cachedParentText === "string" && dirtyChildren !== void 0) {
|
|
2048
|
+
const suffixStartKey = $suffixStartIfContiguous(nextElement, dirtyChildren);
|
|
2049
|
+
if (suffixStartKey !== null) {
|
|
2050
|
+
const k = dirtyChildren.size;
|
|
2051
|
+
if (sizeDelta === 0) {
|
|
2052
|
+
const oldSuffixLength = $prevSuffixTextSize(suffixStartKey, k);
|
|
2053
|
+
let cur = suffixStartKey;
|
|
2054
|
+
let i = 0;
|
|
2055
|
+
while (cur !== null && i < k) {
|
|
2056
|
+
const node = activeNextNodeMap.get(cur);
|
|
2057
|
+
if (node === void 0) break;
|
|
2058
|
+
const saved = $beginCaptureGuard();
|
|
2059
|
+
$reconcileNode(cur, dom);
|
|
2060
|
+
if ($isTextNode(node) && subTreeTextFormat === null) {
|
|
2061
|
+
subTreeTextFormat = node.getFormat();
|
|
2062
|
+
subTreeTextStyle = node.getStyle();
|
|
2063
|
+
subTreeFirstTextKey = node.__key;
|
|
2064
|
+
}
|
|
2065
|
+
$endCaptureGuard(saved);
|
|
2066
|
+
cur = node.__next;
|
|
2067
|
+
i++;
|
|
2068
|
+
}
|
|
2069
|
+
let newSuffix = "";
|
|
2070
|
+
cur = suffixStartKey;
|
|
2071
|
+
i = 0;
|
|
2072
|
+
while (cur !== null && i < k) {
|
|
2073
|
+
const node = activeNextNodeMap.get(cur);
|
|
2074
|
+
if (node === void 0) break;
|
|
2075
|
+
let text;
|
|
2076
|
+
if ($isElementNode(node)) {
|
|
2077
|
+
const childKeyedDom = activeEditor$1._keyToDOMMap.get(cur);
|
|
2078
|
+
const cached = childKeyedDom && childKeyedDom.__lexicalTextContent;
|
|
2079
|
+
if (!(typeof cached === "string")) formatDevErrorMessage(`reconcileChildren same-size suffix: missing __lexicalTextContent on child of type ${node.getType()} after reconcile`);
|
|
2080
|
+
text = cached;
|
|
2081
|
+
} else text = node.getTextContent();
|
|
2082
|
+
newSuffix += text;
|
|
2083
|
+
if (i < k - 1 && $isElementNode(node) && !node.isInline()) newSuffix += DOUBLE_LINE_BREAK;
|
|
2084
|
+
cur = node.__next;
|
|
2085
|
+
i++;
|
|
2086
|
+
}
|
|
2087
|
+
const newParentText = cachedParentText.slice(0, cachedParentText.length - oldSuffixLength) + newSuffix;
|
|
2088
|
+
cacheDom.__lexicalTextContent = newParentText;
|
|
2089
|
+
subTreeTextContent = previousSubTreeTextContent + newParentText;
|
|
2090
|
+
$resolveSuffixPathFormat(nextElement, cacheDom, dirtyChildren);
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
if ($tryReconcileSuffixWithSizeDelta(prevElement, nextElement, slot, cacheDom, cachedParentText, suffixStartKey, k, sizeDelta)) {
|
|
2094
|
+
const newCachedText = cacheDom.__lexicalTextContent;
|
|
2095
|
+
if (!(typeof newCachedText === "string")) formatDevErrorMessage(`reconcileChildren: $tryReconcileSuffixWithSizeDelta returned true without writing __lexicalTextContent`);
|
|
2096
|
+
subTreeTextContent = previousSubTreeTextContent + newCachedText;
|
|
2097
|
+
$resolveSuffixPathFormat(nextElement, cacheDom, dirtyChildren);
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
if (sizeDelta === 0) {
|
|
2103
|
+
let nodeKey = prevElement.__first;
|
|
2104
|
+
let i = 0;
|
|
2105
|
+
while (nodeKey !== null) {
|
|
2106
|
+
const node = activeNextNodeMap.get(nodeKey);
|
|
2107
|
+
if (node === void 0) break;
|
|
2108
|
+
const isDirty = treatAllNodesAsDirty || activeDirtyLeaves.has(nodeKey) || activeDirtyElements.has(nodeKey);
|
|
2109
|
+
const saved = $beginCaptureGuard();
|
|
2110
|
+
if (isDirty) $reconcileNode(nodeKey, dom);
|
|
2111
|
+
else {
|
|
2112
|
+
let text;
|
|
2113
|
+
let childKeyedDom;
|
|
2114
|
+
if ($isElementNode(node)) {
|
|
2115
|
+
childKeyedDom = activePrevKeyToDOMMap.get(nodeKey);
|
|
2116
|
+
const cached = childKeyedDom && childKeyedDom.__lexicalTextContent;
|
|
2117
|
+
if (!(typeof cached === "string")) formatDevErrorMessage(`reconcileChildren structurally-clean walk: missing __lexicalTextContent on non-dirty child of type ${node.getType()}`);
|
|
2118
|
+
text = cached;
|
|
2119
|
+
} else text = node.getTextContent();
|
|
2120
|
+
subTreeTextContent += text;
|
|
2121
|
+
if (childKeyedDom !== void 0) $bubbleChildFirstText(childKeyedDom);
|
|
2122
|
+
}
|
|
2123
|
+
if ($isTextNode(node)) {
|
|
2124
|
+
if (subTreeTextFormat === null) {
|
|
2125
|
+
subTreeTextFormat = node.getFormat();
|
|
2126
|
+
subTreeTextStyle = node.getStyle();
|
|
2127
|
+
subTreeFirstTextKey = node.__key;
|
|
2128
|
+
}
|
|
2129
|
+
} else if ($isElementNode(node) && i < nextChildrenSize - 1 && !node.isInline()) subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
2130
|
+
$endCaptureGuard(saved);
|
|
2131
|
+
nodeKey = node.__next;
|
|
2132
|
+
i++;
|
|
2133
|
+
}
|
|
2134
|
+
cacheDom.__lexicalTextContent = subTreeTextContent;
|
|
2135
|
+
cacheDom.__lexicalFirstTextKey = subTreeFirstTextKey;
|
|
2136
|
+
subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
1221
2140
|
if (prevChildrenSize === 1 && nextChildrenSize === 1) {
|
|
1222
2141
|
const prevFirstChildKey = prevElement.__first;
|
|
1223
2142
|
const nextFirstChildKey = nextElement.__first;
|
|
@@ -1226,7 +2145,8 @@ function $reconcileChildren(prevElement, nextElement, slot) {
|
|
|
1226
2145
|
const lastDOM = getPrevElementByKeyOrThrow(prevFirstChildKey);
|
|
1227
2146
|
const replacementDOM = $createNode(nextFirstChildKey, null);
|
|
1228
2147
|
try {
|
|
1229
|
-
dom.replaceChild(replacementDOM, lastDOM);
|
|
2148
|
+
if (lastDOM.parentNode === dom) dom.replaceChild(replacementDOM, lastDOM);
|
|
2149
|
+
else slot.insertChild(replacementDOM);
|
|
1230
2150
|
} catch (error) {
|
|
1231
2151
|
if (typeof error === "object" && error != null) {
|
|
1232
2152
|
const msg = `${error.toString()} Parent: ${dom.tagName}, new child: {tag: ${replacementDOM.tagName} key: ${nextFirstChildKey}}, old child: {tag: ${lastDOM.tagName}, key: ${prevFirstChildKey}}.`;
|
|
@@ -1240,6 +2160,7 @@ function $reconcileChildren(prevElement, nextElement, slot) {
|
|
|
1240
2160
|
if (subTreeTextFormat === null) {
|
|
1241
2161
|
subTreeTextFormat = nextChildNode.getFormat();
|
|
1242
2162
|
subTreeTextStyle = nextChildNode.getStyle();
|
|
2163
|
+
subTreeFirstTextKey = nextChildNode.__key;
|
|
1243
2164
|
}
|
|
1244
2165
|
}
|
|
1245
2166
|
} else {
|
|
@@ -1257,7 +2178,8 @@ function $reconcileChildren(prevElement, nextElement, slot) {
|
|
|
1257
2178
|
}
|
|
1258
2179
|
} else $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChildrenSize, nextChildrenSize, slot);
|
|
1259
2180
|
}
|
|
1260
|
-
|
|
2181
|
+
cacheDom.__lexicalTextContent = subTreeTextContent;
|
|
2182
|
+
cacheDom.__lexicalFirstTextKey = subTreeFirstTextKey;
|
|
1261
2183
|
subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
|
|
1262
2184
|
}
|
|
1263
2185
|
function $reconcileNode(key, parentDOM) {
|
|
@@ -1270,11 +2192,9 @@ function $reconcileNode(key, parentDOM) {
|
|
|
1270
2192
|
let text;
|
|
1271
2193
|
if ($isElementNode(prevNode)) {
|
|
1272
2194
|
const previousSubTreeTextContent = dom.__lexicalTextContent;
|
|
1273
|
-
if (typeof previousSubTreeTextContent === "string")
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
dom.__lexicalTextContent = text;
|
|
1277
|
-
}
|
|
2195
|
+
if (!(typeof previousSubTreeTextContent === "string")) formatDevErrorMessage(`reconcileNode: missing __lexicalTextContent on non-dirty element of type ${prevNode.getType()}`);
|
|
2196
|
+
text = previousSubTreeTextContent;
|
|
2197
|
+
$bubbleChildFirstText(dom);
|
|
1278
2198
|
} else text = prevNode.getTextContent();
|
|
1279
2199
|
subTreeTextContent += text;
|
|
1280
2200
|
return dom;
|
|
@@ -1298,15 +2218,11 @@ function $reconcileNode(key, parentDOM) {
|
|
|
1298
2218
|
if (!$isRootNode(nextNode) && !nextNode.isInline()) $reconcileElementTerminatingLineBreak(prevNode, nextNode, dom);
|
|
1299
2219
|
} else {
|
|
1300
2220
|
const previousSubTreeTextContent = dom.__lexicalTextContent;
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
text = prevNode.getTextContent();
|
|
1305
|
-
dom.__lexicalTextContent = text;
|
|
1306
|
-
}
|
|
1307
|
-
subTreeTextContent += text;
|
|
2221
|
+
if (!(typeof previousSubTreeTextContent === "string")) formatDevErrorMessage(`reconcileNode: missing __lexicalTextContent on cloned non-dirty element of type ${prevNode.getType()}`);
|
|
2222
|
+
subTreeTextContent += previousSubTreeTextContent;
|
|
2223
|
+
$bubbleChildFirstText(dom);
|
|
1308
2224
|
}
|
|
1309
|
-
if (treatAllNodesAsDirty || nextNode.__dir !== prevNode.__dir) {
|
|
2225
|
+
if (treatAllNodesAsDirty || nextNode.__dir !== prevNode.__dir || nextNode.__parent !== prevNode.__parent) {
|
|
1310
2226
|
$setElementDirection(dom, nextNode);
|
|
1311
2227
|
if ($isRootNode(nextNode) && !treatAllNodesAsDirty) {
|
|
1312
2228
|
for (const child of nextNode.getChildren()) if ($isElementNode(child)) $setElementDirection(getElementByKeyOrThrow(activeEditor$1, child.getKey()), child);
|
|
@@ -1320,12 +2236,16 @@ function $reconcileNode(key, parentDOM) {
|
|
|
1320
2236
|
}
|
|
1321
2237
|
subTreeTextContent += text;
|
|
1322
2238
|
}
|
|
1323
|
-
if (!activeEditorStateReadOnly && $isRootNode(nextNode)
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
2239
|
+
if (!activeEditorStateReadOnly && $isRootNode(nextNode)) {
|
|
2240
|
+
const latestRoot = nextNode.getLatest();
|
|
2241
|
+
if (latestRoot.__cachedText !== subTreeTextContent) {
|
|
2242
|
+
const nextRootNode = latestRoot.getWritable();
|
|
2243
|
+
nextRootNode.__cachedText = subTreeTextContent;
|
|
2244
|
+
nextNode = nextRootNode;
|
|
2245
|
+
}
|
|
1327
2246
|
}
|
|
1328
2247
|
activeEditorDOMRenderConfig.$decorateDOM(nextNode, prevNode, dom, activeEditor$1);
|
|
2248
|
+
$setCachedTextSize(nextNode);
|
|
1329
2249
|
Object.freeze(nextNode);
|
|
1330
2250
|
return dom;
|
|
1331
2251
|
}
|
|
@@ -1359,6 +2279,7 @@ function $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChi
|
|
|
1359
2279
|
while (prevIndex <= prevEndIndex && nextIndex <= nextEndIndex) {
|
|
1360
2280
|
const prevKey = prevChildren[prevIndex];
|
|
1361
2281
|
const nextKey = nextChildren[nextIndex];
|
|
2282
|
+
const saved = $beginCaptureGuard();
|
|
1362
2283
|
if (prevKey === nextKey) {
|
|
1363
2284
|
siblingDOM = getNextSibling($reconcileNode(nextKey, slot.element));
|
|
1364
2285
|
prevIndex++;
|
|
@@ -1368,6 +2289,7 @@ function $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChi
|
|
|
1368
2289
|
if (prevChildrenSet === void 0) prevChildrenSet = childrenSet(prevChildren, prevIndex);
|
|
1369
2290
|
else if (!prevChildrenSet.has(prevKey)) {
|
|
1370
2291
|
prevIndex++;
|
|
2292
|
+
$endCaptureGuard(saved);
|
|
1371
2293
|
continue;
|
|
1372
2294
|
}
|
|
1373
2295
|
if (!nextChildrenSet.has(prevKey)) {
|
|
@@ -1375,14 +2297,15 @@ function $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChi
|
|
|
1375
2297
|
$destroyNode(prevKey, slot.element);
|
|
1376
2298
|
prevIndex++;
|
|
1377
2299
|
prevChildrenSet.delete(prevKey);
|
|
2300
|
+
$endCaptureGuard(saved);
|
|
1378
2301
|
continue;
|
|
1379
2302
|
}
|
|
1380
2303
|
if (!prevChildrenSet.has(nextKey)) {
|
|
1381
|
-
$createNode(nextKey, slot.withBefore(siblingDOM));
|
|
2304
|
+
$createNode(nextKey, slot.withBefore(siblingDOM ?? slot.before));
|
|
1382
2305
|
nextIndex++;
|
|
1383
2306
|
} else {
|
|
1384
2307
|
const childDOM = getElementByKeyOrThrow(activeEditor$1, nextKey);
|
|
1385
|
-
if (childDOM !== siblingDOM) slot.withBefore(siblingDOM).insertChild(childDOM);
|
|
2308
|
+
if (childDOM !== siblingDOM) slot.withBefore(siblingDOM ?? slot.before).insertChild(childDOM);
|
|
1386
2309
|
siblingDOM = getNextSibling($reconcileNode(nextKey, slot.element));
|
|
1387
2310
|
prevIndex++;
|
|
1388
2311
|
nextIndex++;
|
|
@@ -1393,19 +2316,24 @@ function $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChi
|
|
|
1393
2316
|
if (subTreeTextFormat === null) {
|
|
1394
2317
|
subTreeTextFormat = node.getFormat();
|
|
1395
2318
|
subTreeTextStyle = node.getStyle();
|
|
2319
|
+
subTreeFirstTextKey = node.__key;
|
|
1396
2320
|
}
|
|
1397
2321
|
} else if ($isElementNode(node) && nextIndex <= nextEndIndex && !node.isInline()) subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
2322
|
+
$endCaptureGuard(saved);
|
|
1398
2323
|
}
|
|
1399
2324
|
const appendNewChildren = prevIndex > prevEndIndex;
|
|
1400
2325
|
const removeOldChildren = nextIndex > nextEndIndex;
|
|
1401
2326
|
if (appendNewChildren && !removeOldChildren) {
|
|
1402
2327
|
const previousNode = nextChildren[nextEndIndex + 1];
|
|
1403
2328
|
const insertDOM = previousNode === void 0 ? null : activeEditor$1.getElementByKey(previousNode);
|
|
1404
|
-
$createChildren(nextChildren, nextElement, nextIndex, nextEndIndex, slot.withBefore(insertDOM));
|
|
2329
|
+
$createChildren(nextChildren, nextElement, nextIndex, nextEndIndex, slot.withBefore(insertDOM ?? slot.before));
|
|
1405
2330
|
} else if (removeOldChildren && !appendNewChildren) $destroyChildren(prevChildren, prevIndex, prevEndIndex, slot.element);
|
|
1406
2331
|
}
|
|
1407
2332
|
function $reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dirtyElements, dirtyLeaves) {
|
|
1408
2333
|
subTreeTextContent = "";
|
|
2334
|
+
subTreeTextFormat = null;
|
|
2335
|
+
subTreeTextStyle = null;
|
|
2336
|
+
subTreeFirstTextKey = null;
|
|
1409
2337
|
treatAllNodesAsDirty = dirtyType === FULL_RECONCILE;
|
|
1410
2338
|
activeEditor$1 = editor;
|
|
1411
2339
|
activeEditorConfig = editor._config;
|
|
@@ -1415,9 +2343,11 @@ function $reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dir
|
|
|
1415
2343
|
activeDirtyElements = dirtyElements;
|
|
1416
2344
|
activeDirtyLeaves = dirtyLeaves;
|
|
1417
2345
|
activePrevNodeMap = prevEditorState._nodeMap;
|
|
2346
|
+
activePrevEditorState = prevEditorState;
|
|
1418
2347
|
activeNextNodeMap = nextEditorState._nodeMap;
|
|
1419
2348
|
activeEditorStateReadOnly = nextEditorState._readOnly;
|
|
1420
|
-
activePrevKeyToDOMMap =
|
|
2349
|
+
activePrevKeyToDOMMap = cloneMap(editor._keyToDOMMap);
|
|
2350
|
+
activeDirtyChildrenByParent = $buildDirtyChildrenByParent();
|
|
1421
2351
|
const currentMutatedNodes = /* @__PURE__ */ new Map();
|
|
1422
2352
|
mutatedNodes = currentMutatedNodes;
|
|
1423
2353
|
$reconcileNode("root", null);
|
|
@@ -1426,9 +2356,11 @@ function $reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dir
|
|
|
1426
2356
|
activeDirtyElements = void 0;
|
|
1427
2357
|
activeDirtyLeaves = void 0;
|
|
1428
2358
|
activePrevNodeMap = void 0;
|
|
2359
|
+
activePrevEditorState = void 0;
|
|
1429
2360
|
activeNextNodeMap = void 0;
|
|
1430
2361
|
activeEditorConfig = void 0;
|
|
1431
2362
|
activePrevKeyToDOMMap = void 0;
|
|
2363
|
+
activeDirtyChildrenByParent = void 0;
|
|
1432
2364
|
mutatedNodes = void 0;
|
|
1433
2365
|
activeEditorDOMRenderConfig = DEFAULT_EDITOR_DOM_CONFIG;
|
|
1434
2366
|
return currentMutatedNodes;
|
|
@@ -1650,6 +2582,8 @@ let lastKeyDownTimeStamp = 0;
|
|
|
1650
2582
|
let lastKeyCode = null;
|
|
1651
2583
|
let lastBeforeInputInsertTextTimeStamp = 0;
|
|
1652
2584
|
let unprocessedBeforeInputData = null;
|
|
2585
|
+
let isInsertTextAfterHandledSelectionCommand = false;
|
|
2586
|
+
let handledSelectionCommandTimeoutId = null;
|
|
1653
2587
|
const rootElementToDocument = /* @__PURE__ */ new WeakMap();
|
|
1654
2588
|
const rootElementsRegistered = /* @__PURE__ */ new WeakMap();
|
|
1655
2589
|
let isSelectionChangeFromDOMUpdate = false;
|
|
@@ -1676,7 +2610,7 @@ function $shouldPreventDefaultAndInsertText(selection, domTargetRange, text, tim
|
|
|
1676
2610
|
const anchorKey = anchor.key;
|
|
1677
2611
|
const backingAnchorElement = editor.getElementByKey(anchorKey);
|
|
1678
2612
|
const textLength = text.length;
|
|
1679
|
-
return anchorKey !== focus.key || !$isTextNode(anchorNode) || (!isBeforeInput && (!CAN_USE_BEFORE_INPUT || lastBeforeInputInsertTextTimeStamp < timeStamp + 50) || anchorNode.isDirty() && textLength < 2 || doesContainSurrogatePair(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() || $isTokenOrSegmented(anchorNode) || anchorNode.isDirty() && textLength > 1 || (isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== getDOMTextNode(backingAnchorElement) || domSelection !== null && domTargetRange !== null && (!domTargetRange.collapsed || domTargetRange.startContainer !== domSelection.anchorNode || domTargetRange.startOffset !== domSelection.anchorOffset) || !anchorNode.isComposing() && (anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style) || $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
|
|
2613
|
+
return anchorKey !== focus.key || !$isTextNode(anchorNode) || (!isBeforeInput && (!CAN_USE_BEFORE_INPUT || lastBeforeInputInsertTextTimeStamp < timeStamp + 50) || anchorNode.isDirty() && textLength < 2 || doesContainSurrogatePair(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() || $isTokenOrSegmented(anchorNode) || anchorNode.isDirty() && textLength > 1 || (isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== $getDOMTextNode(anchorNode, backingAnchorElement, editor) || domSelection !== null && domTargetRange !== null && (!domTargetRange.collapsed || domTargetRange.startContainer !== domSelection.anchorNode || domTargetRange.startOffset !== domSelection.anchorOffset) || !anchorNode.isComposing() && (anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style) || $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
|
|
1680
2614
|
}
|
|
1681
2615
|
function shouldSkipSelectionChange(domNode, offset) {
|
|
1682
2616
|
return isDOMTextNode(domNode) && domNode.nodeValue !== null && offset !== 0 && offset !== domNode.nodeValue.length;
|
|
@@ -1775,11 +2709,6 @@ function onClick(event, editor) {
|
|
|
1775
2709
|
if (anchor.type === "element" && anchor.offset === 0 && selection.isCollapsed() && !$isRootNode(anchorNode) && $getRoot().getChildrenSize() === 1 && anchorNode.getTopLevelElementOrThrow().isEmpty() && lastSelection !== null && selection.is(lastSelection)) {
|
|
1776
2710
|
domSelection.removeAllRanges();
|
|
1777
2711
|
selection.dirty = true;
|
|
1778
|
-
} else if (event.detail === 3 && !selection.isCollapsed()) {
|
|
1779
|
-
if (anchorNode !== selection.focus.getNode()) {
|
|
1780
|
-
const parentNode = $findMatchingParent(anchorNode, (node) => $isElementNode(node) && !node.isInline());
|
|
1781
|
-
if ($isElementNode(parentNode)) parentNode.select(0);
|
|
1782
|
-
}
|
|
1783
2712
|
}
|
|
1784
2713
|
} else if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
1785
2714
|
const domAnchorNode = domSelection.anchorNode;
|
|
@@ -1802,12 +2731,47 @@ function getTargetRange(event) {
|
|
|
1802
2731
|
if (targetRanges.length === 0) return null;
|
|
1803
2732
|
return targetRanges[0];
|
|
1804
2733
|
}
|
|
2734
|
+
function $maybeMoveSelectionPastTrailingAcceptanceBoundary(insertedText) {
|
|
2735
|
+
if (insertedText == null || insertedText.length <= 1 || lastKeyCode == null) return;
|
|
2736
|
+
const characterToSearchFor = lastKeyCode.length === 1 ? lastKeyCode : lastKeyCode === "Enter" ? "\n" : lastKeyCode === "Tab" ? " " : null;
|
|
2737
|
+
if (!characterToSearchFor) return;
|
|
2738
|
+
const selection = $getSelection();
|
|
2739
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return;
|
|
2740
|
+
const anchorNode = selection.anchor.getNode();
|
|
2741
|
+
if (!$isTextNode(anchorNode)) return;
|
|
2742
|
+
const { offset } = selection.anchor;
|
|
2743
|
+
if (anchorNode.getTextContentSize() === offset) {
|
|
2744
|
+
const nextSibling = anchorNode.getNextSibling();
|
|
2745
|
+
if (characterToSearchFor === "\n") {
|
|
2746
|
+
if ($isLineBreakNode(nextSibling)) nextSibling.selectEnd();
|
|
2747
|
+
else if (!nextSibling) {
|
|
2748
|
+
const block = $findMatchingParent(anchorNode, $isBlockElementNode);
|
|
2749
|
+
const nextBlock = block && block.getNextSibling();
|
|
2750
|
+
if ($isElementNode(nextBlock)) nextBlock.selectStart();
|
|
2751
|
+
}
|
|
2752
|
+
} else if (characterToSearchFor === " ") {
|
|
2753
|
+
if ($isTabNode(nextSibling)) nextSibling.selectEnd();
|
|
2754
|
+
} else if ($isTextNode(nextSibling) && nextSibling.getTextContent()[0] === characterToSearchFor) nextSibling.select(1, 1);
|
|
2755
|
+
} else if (anchorNode.getTextContent()[offset] === characterToSearchFor) anchorNode.select(offset + 1, offset + 1);
|
|
2756
|
+
}
|
|
1805
2757
|
function $canRemoveText(anchorNode, focusNode) {
|
|
1806
2758
|
return anchorNode !== focusNode || $isElementNode(anchorNode) || $isElementNode(focusNode) || !$isTokenOrTab(anchorNode) || !$isTokenOrTab(focusNode);
|
|
1807
2759
|
}
|
|
1808
2760
|
function isPossiblyAndroidKeyPress(timeStamp) {
|
|
1809
2761
|
return lastKeyCode === "MediaLast" && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
|
|
1810
2762
|
}
|
|
2763
|
+
function clearHandledSelectionCommandInsertText() {
|
|
2764
|
+
isInsertTextAfterHandledSelectionCommand = false;
|
|
2765
|
+
if (handledSelectionCommandTimeoutId !== null) {
|
|
2766
|
+
clearTimeout(handledSelectionCommandTimeoutId);
|
|
2767
|
+
handledSelectionCommandTimeoutId = null;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
function markHandledSelectionCommandInsertText() {
|
|
2771
|
+
clearHandledSelectionCommandInsertText();
|
|
2772
|
+
isInsertTextAfterHandledSelectionCommand = true;
|
|
2773
|
+
handledSelectionCommandTimeoutId = setTimeout(clearHandledSelectionCommandInsertText, 0);
|
|
2774
|
+
}
|
|
1811
2775
|
function registerDefaultCommandHandlers(editor) {
|
|
1812
2776
|
editor.registerCommand(BEFORE_INPUT_COMMAND, $handleBeforeInput, 0);
|
|
1813
2777
|
editor.registerCommand(INPUT_COMMAND, $handleInput, 0);
|
|
@@ -1826,6 +2790,16 @@ function $handleBeforeInput(event) {
|
|
|
1826
2790
|
const targetRange = getTargetRange(event);
|
|
1827
2791
|
const editor = getActiveEditor();
|
|
1828
2792
|
const selection = $getSelection();
|
|
2793
|
+
if (inputType === "insertText" && event.data && isInsertTextAfterHandledSelectionCommand) {
|
|
2794
|
+
clearHandledSelectionCommandInsertText();
|
|
2795
|
+
event.preventDefault();
|
|
2796
|
+
if ($isRangeSelection(selection) && !selection.isCollapsed()) {
|
|
2797
|
+
const point = selection.isBackward() ? selection.anchor : selection.focus;
|
|
2798
|
+
selection.anchor.set(point.key, point.offset, point.type);
|
|
2799
|
+
selection.focus.set(point.key, point.offset, point.type);
|
|
2800
|
+
}
|
|
2801
|
+
return true;
|
|
2802
|
+
}
|
|
1829
2803
|
if (inputType === "deleteContentBackward") {
|
|
1830
2804
|
if (selection === null) {
|
|
1831
2805
|
const prevSelection = $getPreviousSelection();
|
|
@@ -1850,6 +2824,14 @@ function $handleBeforeInput(event) {
|
|
|
1850
2824
|
}
|
|
1851
2825
|
} else {
|
|
1852
2826
|
$setCompositionKey(null);
|
|
2827
|
+
if (IS_IOS && targetRange !== null && !targetRange.collapsed) {
|
|
2828
|
+
selection.applyDOMRange(targetRange);
|
|
2829
|
+
if (!selection.isCollapsed()) {
|
|
2830
|
+
event.preventDefault();
|
|
2831
|
+
selection.removeText();
|
|
2832
|
+
return true;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
1853
2835
|
event.preventDefault();
|
|
1854
2836
|
const selectedNode = selection.anchor.getNode();
|
|
1855
2837
|
const selectedNodeText = selectedNode.getTextContent();
|
|
@@ -1892,6 +2874,7 @@ function $handleBeforeInput(event) {
|
|
|
1892
2874
|
} else if (data != null && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, true)) {
|
|
1893
2875
|
event.preventDefault();
|
|
1894
2876
|
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2877
|
+
$maybeMoveSelectionPastTrailingAcceptanceBoundary(data);
|
|
1895
2878
|
} else unprocessedBeforeInputData = data;
|
|
1896
2879
|
lastBeforeInputInsertTextTimeStamp = event.timeStamp;
|
|
1897
2880
|
return true;
|
|
@@ -1902,6 +2885,7 @@ function $handleBeforeInput(event) {
|
|
|
1902
2885
|
case "insertFromDrop":
|
|
1903
2886
|
case "insertReplacementText":
|
|
1904
2887
|
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
|
|
2888
|
+
$maybeMoveSelectionPastTrailingAcceptanceBoundary((event.dataTransfer ? event.dataTransfer.getData("text/plain") : null) ?? event.data);
|
|
1905
2889
|
break;
|
|
1906
2890
|
case "insertFromComposition":
|
|
1907
2891
|
$setCompositionKey(null);
|
|
@@ -1973,6 +2957,7 @@ function $handleBeforeInput(event) {
|
|
|
1973
2957
|
}
|
|
1974
2958
|
function onInput(event, editor) {
|
|
1975
2959
|
event.stopPropagation();
|
|
2960
|
+
clearHandledSelectionCommandInsertText();
|
|
1976
2961
|
updateEditorSync(editor, () => {
|
|
1977
2962
|
editor.dispatchCommand(INPUT_COMMAND, event);
|
|
1978
2963
|
}, { event });
|
|
@@ -1997,7 +2982,11 @@ function $handleInput(event) {
|
|
|
1997
2982
|
const endOffset = isBackward ? selection.focus.offset : selection.anchor.offset;
|
|
1998
2983
|
if (!CAN_USE_BEFORE_INPUT || selection.isCollapsed() || !$isTextNode(anchorNode) || domSelection.anchorNode === null || anchorNode.getTextContent().slice(0, startOffset) + data + anchorNode.getTextContent().slice(startOffset + endOffset) !== getAnchorTextFromDOM(domSelection.anchorNode)) dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
1999
2984
|
const textLength = data.length;
|
|
2000
|
-
if (IS_FIREFOX && textLength > 1 && event.inputType === "insertCompositionText" && !editor.isComposing())
|
|
2985
|
+
if (IS_FIREFOX && textLength > 1 && event.inputType === "insertCompositionText" && !editor.isComposing()) {
|
|
2986
|
+
selection.anchor.offset -= textLength;
|
|
2987
|
+
selection._cachedNodes = null;
|
|
2988
|
+
selection._cachedIsBackward = null;
|
|
2989
|
+
}
|
|
2001
2990
|
if (IS_ANDROID_CHROME && editor.isComposing()) {
|
|
2002
2991
|
lastKeyDownTimeStamp = 0;
|
|
2003
2992
|
$setCompositionKey(null);
|
|
@@ -2038,7 +3027,8 @@ function $onCompositionEndImpl(editor, data) {
|
|
|
2038
3027
|
if (compositionKey !== null && data != null) {
|
|
2039
3028
|
if (data === "") {
|
|
2040
3029
|
const node = $getNodeByKey(compositionKey);
|
|
2041
|
-
const
|
|
3030
|
+
const domElement = editor.getElementByKey(compositionKey);
|
|
3031
|
+
const textNode = domElement !== null && $isTextNode(node) ? $getDOMTextNode(node, domElement, editor) : null;
|
|
2042
3032
|
if (textNode !== null && textNode.nodeValue !== null && $isTextNode(node)) {
|
|
2043
3033
|
const domSelection = getDOMSelection(getWindow(editor));
|
|
2044
3034
|
let anchorOffset = null;
|
|
@@ -2074,6 +3064,7 @@ function onCompositionEnd(event, editor) {
|
|
|
2074
3064
|
function onKeyDown(event, editor) {
|
|
2075
3065
|
lastKeyDownTimeStamp = event.timeStamp;
|
|
2076
3066
|
lastKeyCode = event.key;
|
|
3067
|
+
if (event.key !== "Backspace") clearHandledSelectionCommandInsertText();
|
|
2077
3068
|
if (editor.isComposing()) return;
|
|
2078
3069
|
dispatchCommand(editor, KEY_DOWN_COMMAND, event);
|
|
2079
3070
|
}
|
|
@@ -2109,8 +3100,9 @@ function $handleKeyDown(event) {
|
|
|
2109
3100
|
} else if (isParagraph(event)) {
|
|
2110
3101
|
isInsertLineBreak = false;
|
|
2111
3102
|
dispatchCommand(editor, KEY_ENTER_COMMAND, event);
|
|
2112
|
-
} else if (isDeleteBackward(event)) if (isBackspace(event))
|
|
2113
|
-
|
|
3103
|
+
} else if (isDeleteBackward(event)) if (isBackspace(event)) {
|
|
3104
|
+
if (dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event)) markHandledSelectionCommandInsertText();
|
|
3105
|
+
} else {
|
|
2114
3106
|
event.preventDefault();
|
|
2115
3107
|
dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
|
|
2116
3108
|
}
|
|
@@ -2150,20 +3142,17 @@ function $handleKeyDown(event) {
|
|
|
2150
3142
|
dispatchCommand(editor, REDO_COMMAND, void 0);
|
|
2151
3143
|
} else {
|
|
2152
3144
|
const prevSelection = editor._editorState._selection;
|
|
2153
|
-
if (
|
|
3145
|
+
if (isSelectAll(event)) {
|
|
3146
|
+
event.preventDefault();
|
|
3147
|
+
if (dispatchCommand(editor, SELECT_ALL_COMMAND, event)) markHandledSelectionCommandInsertText();
|
|
3148
|
+
} else if (prevSelection !== null && !$isRangeSelection(prevSelection)) {
|
|
2154
3149
|
if (isCopy(event)) {
|
|
2155
3150
|
event.preventDefault();
|
|
2156
3151
|
dispatchCommand(editor, COPY_COMMAND, event);
|
|
2157
3152
|
} else if (isCut(event)) {
|
|
2158
3153
|
event.preventDefault();
|
|
2159
3154
|
dispatchCommand(editor, CUT_COMMAND, event);
|
|
2160
|
-
} else if (isSelectAll(event)) {
|
|
2161
|
-
event.preventDefault();
|
|
2162
|
-
dispatchCommand(editor, SELECT_ALL_COMMAND, event);
|
|
2163
3155
|
}
|
|
2164
|
-
} else if (isSelectAll(event)) {
|
|
2165
|
-
event.preventDefault();
|
|
2166
|
-
dispatchCommand(editor, SELECT_ALL_COMMAND, event);
|
|
2167
3156
|
}
|
|
2168
3157
|
}
|
|
2169
3158
|
if (isModifier(event)) editor.dispatchCommand(KEY_MODIFIER_COMMAND, event);
|
|
@@ -2201,6 +3190,7 @@ function onDocumentSelectionChange(event) {
|
|
|
2201
3190
|
if (nextActiveEditor !== rootEditor) activeNestedEditorsMap.set(rootEditorKey, nextActiveEditor);
|
|
2202
3191
|
else if (activeNestedEditor) activeNestedEditorsMap.delete(rootEditorKey);
|
|
2203
3192
|
}
|
|
3193
|
+
/** @internal */
|
|
2204
3194
|
function stopLexicalPropagation(event) {
|
|
2205
3195
|
event._lexicalHandled = true;
|
|
2206
3196
|
}
|
|
@@ -2393,6 +3383,13 @@ function $markEphemeral(node) {
|
|
|
2393
3383
|
node[EPHEMERAL] = true;
|
|
2394
3384
|
return node;
|
|
2395
3385
|
}
|
|
3386
|
+
/** @internal */
|
|
3387
|
+
const NON_ENUMERABLE_PROP_DESC = {
|
|
3388
|
+
configurable: true,
|
|
3389
|
+
enumerable: false,
|
|
3390
|
+
value: void 0,
|
|
3391
|
+
writable: true
|
|
3392
|
+
};
|
|
2396
3393
|
var LexicalNode = class {
|
|
2397
3394
|
/** @internal Allow us to look up the type including static props */
|
|
2398
3395
|
/** @internal */
|
|
@@ -2407,6 +3404,8 @@ var LexicalNode = class {
|
|
|
2407
3404
|
__next;
|
|
2408
3405
|
/** @internal */
|
|
2409
3406
|
__state;
|
|
3407
|
+
/** @internal */
|
|
3408
|
+
[CACHED_TEXT_SIZE_KEY];
|
|
2410
3409
|
/**
|
|
2411
3410
|
* Returns the string type of this node. Every node must
|
|
2412
3411
|
* implement this and it MUST BE UNIQUE amongst nodes registered
|
|
@@ -2530,12 +3529,8 @@ var LexicalNode = class {
|
|
|
2530
3529
|
this.__parent = null;
|
|
2531
3530
|
this.__prev = null;
|
|
2532
3531
|
this.__next = null;
|
|
2533
|
-
Object.defineProperty(this, "__state",
|
|
2534
|
-
|
|
2535
|
-
enumerable: false,
|
|
2536
|
-
value: void 0,
|
|
2537
|
-
writable: true
|
|
2538
|
-
});
|
|
3532
|
+
Object.defineProperty(this, "__state", NON_ENUMERABLE_PROP_DESC);
|
|
3533
|
+
Object.defineProperty(this, CACHED_TEXT_SIZE_KEY, NON_ENUMERABLE_PROP_DESC);
|
|
2539
3534
|
$setNodeKey(this, key);
|
|
2540
3535
|
if (this.__type !== "root") errorOnTypeKlassMismatch(this.__type, this.constructor);
|
|
2541
3536
|
}
|
|
@@ -2916,6 +3911,23 @@ var LexicalNode = class {
|
|
|
2916
3911
|
formatDevErrorMessage(`updateDOM: base method not extended`);
|
|
2917
3912
|
}
|
|
2918
3913
|
/**
|
|
3914
|
+
* Returns a {@link DOMSlot} pointing at the content-bearing element of this
|
|
3915
|
+
* node's DOM. The default returns a slot wrapping the keyed DOM as-is.
|
|
3916
|
+
*
|
|
3917
|
+
* Override this when {@link createDOM} returns a wrapper around the
|
|
3918
|
+
* content-bearing element (e.g. `<span><br/></span>` for a styled line
|
|
3919
|
+
* break), so selection / reconciliation logic can target the inner element.
|
|
3920
|
+
*
|
|
3921
|
+
* {@link ElementNode} overrides this to return an {@link ElementDOMSlot}
|
|
3922
|
+
* with children-management semantics (used by the reconciler to place
|
|
3923
|
+
* managed children).
|
|
3924
|
+
*
|
|
3925
|
+
* @experimental
|
|
3926
|
+
*/
|
|
3927
|
+
getDOMSlot(element) {
|
|
3928
|
+
return new DOMSlot(element);
|
|
3929
|
+
}
|
|
3930
|
+
/**
|
|
2919
3931
|
* Controls how the this node is serialized to HTML. This is important for
|
|
2920
3932
|
* copy and paste between Lexical and non-Lexical editors, or Lexical editors with different namespaces,
|
|
2921
3933
|
* in which case the primary transfer format is HTML. It's also important if you're serializing
|
|
@@ -3023,7 +4035,10 @@ var LexicalNode = class {
|
|
|
3023
4035
|
const writableReplaceWith = replaceWith.getWritable();
|
|
3024
4036
|
const writableParent = this.getParentOrThrow().getWritable();
|
|
3025
4037
|
const size = writableParent.__size;
|
|
4038
|
+
const replaceWithOldParent = writableReplaceWith.getParent();
|
|
4039
|
+
const replaceWithOldIndex = replaceWithOldParent !== null ? writableReplaceWith.getIndexWithinParent() : -1;
|
|
3026
4040
|
removeFromParent(writableReplaceWith);
|
|
4041
|
+
if (replaceWithOldParent !== null && $isRangeSelection(selection)) $updateElementSelectionOnCreateDeleteNode(selection, replaceWithOldParent, replaceWithOldIndex, -1);
|
|
3027
4042
|
const prevSibling = self.getPreviousSibling();
|
|
3028
4043
|
const nextSibling = self.getNextSibling();
|
|
3029
4044
|
const prevKey = self.__prev;
|
|
@@ -3044,18 +4059,20 @@ var LexicalNode = class {
|
|
|
3044
4059
|
writableReplaceWith.__next = nextKey;
|
|
3045
4060
|
writableReplaceWith.__parent = parentKey;
|
|
3046
4061
|
writableParent.__size = size;
|
|
4062
|
+
let prevSizeBeforeChildrenTransfer = 0;
|
|
3047
4063
|
if (includeChildren) {
|
|
3048
4064
|
if (!($isElementNode(this) && $isElementNode(writableReplaceWith))) formatDevErrorMessage(`includeChildren should only be true for ElementNodes`);
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
});
|
|
4065
|
+
prevSizeBeforeChildrenTransfer = writableReplaceWith.getChildrenSize();
|
|
4066
|
+
writableReplaceWith.splice(prevSizeBeforeChildrenTransfer, 0, this.getChildren());
|
|
3052
4067
|
}
|
|
3053
4068
|
if ($isRangeSelection(selection)) {
|
|
3054
4069
|
$setSelection(selection);
|
|
3055
4070
|
const anchor = selection.anchor;
|
|
3056
4071
|
const focus = selection.focus;
|
|
3057
|
-
if (anchor.key === toReplaceKey)
|
|
3058
|
-
|
|
4072
|
+
if (anchor.key === toReplaceKey) if (includeChildren && anchor.type === "element") anchor.set(writableReplaceWith.__key, prevSizeBeforeChildrenTransfer + anchor.offset, "element");
|
|
4073
|
+
else $moveSelectionPointToEnd(anchor, writableReplaceWith);
|
|
4074
|
+
if (focus.key === toReplaceKey) if (includeChildren && focus.type === "element") focus.set(writableReplaceWith.__key, prevSizeBeforeChildrenTransfer + focus.offset, "element");
|
|
4075
|
+
else $moveSelectionPointToEnd(focus, writableReplaceWith);
|
|
3059
4076
|
}
|
|
3060
4077
|
if ($getCompositionKey() === toReplaceKey) $setCompositionKey(key);
|
|
3061
4078
|
return writableReplaceWith;
|
|
@@ -3078,7 +4095,6 @@ var LexicalNode = class {
|
|
|
3078
4095
|
let elementFocusSelectionOnNode = false;
|
|
3079
4096
|
if (oldParent !== null) {
|
|
3080
4097
|
const oldIndex = nodeToInsert.getIndexWithinParent();
|
|
3081
|
-
removeFromParent(writableNodeToInsert);
|
|
3082
4098
|
if ($isRangeSelection(selection)) {
|
|
3083
4099
|
const oldParentKey = oldParent.__key;
|
|
3084
4100
|
const anchor = selection.anchor;
|
|
@@ -3086,6 +4102,8 @@ var LexicalNode = class {
|
|
|
3086
4102
|
elementAnchorSelectionOnNode = anchor.type === "element" && anchor.key === oldParentKey && anchor.offset === oldIndex + 1;
|
|
3087
4103
|
elementFocusSelectionOnNode = focus.type === "element" && focus.key === oldParentKey && focus.offset === oldIndex + 1;
|
|
3088
4104
|
}
|
|
4105
|
+
removeFromParent(writableNodeToInsert);
|
|
4106
|
+
if (restoreSelection && $isRangeSelection(selection)) $updateElementSelectionOnCreateDeleteNode(selection, oldParent, oldIndex, -1);
|
|
3089
4107
|
}
|
|
3090
4108
|
const nextSibling = this.getNextSibling();
|
|
3091
4109
|
const writableParent = this.getParentOrThrow().getWritable();
|
|
@@ -3123,7 +4141,11 @@ var LexicalNode = class {
|
|
|
3123
4141
|
const writableSelf = this.getWritable();
|
|
3124
4142
|
const writableNodeToInsert = nodeToInsert.getWritable();
|
|
3125
4143
|
const insertKey = writableNodeToInsert.__key;
|
|
4144
|
+
const selection = $getSelection();
|
|
4145
|
+
const insertOldParent = writableNodeToInsert.getParent();
|
|
4146
|
+
const insertOldIndex = insertOldParent !== null ? writableNodeToInsert.getIndexWithinParent() : -1;
|
|
3126
4147
|
removeFromParent(writableNodeToInsert);
|
|
4148
|
+
if (insertOldParent !== null && restoreSelection && $isRangeSelection(selection)) $updateElementSelectionOnCreateDeleteNode(selection, insertOldParent, insertOldIndex, -1);
|
|
3127
4149
|
const prevSibling = this.getPreviousSibling();
|
|
3128
4150
|
const writableParent = this.getParentOrThrow().getWritable();
|
|
3129
4151
|
const prevKey = writableSelf.__prev;
|
|
@@ -3138,7 +4160,6 @@ var LexicalNode = class {
|
|
|
3138
4160
|
writableNodeToInsert.__prev = prevKey;
|
|
3139
4161
|
writableNodeToInsert.__next = writableSelf.__key;
|
|
3140
4162
|
writableNodeToInsert.__parent = writableSelf.__parent;
|
|
3141
|
-
const selection = $getSelection();
|
|
3142
4163
|
if (restoreSelection && $isRangeSelection(selection)) $updateElementSelectionOnCreateDeleteNode(selection, this.getParentOrThrow(), index);
|
|
3143
4164
|
return nodeToInsert;
|
|
3144
4165
|
}
|
|
@@ -3570,22 +4591,27 @@ function diffComposedText(a, b) {
|
|
|
3570
4591
|
b.slice(left, bLength - right)
|
|
3571
4592
|
];
|
|
3572
4593
|
}
|
|
3573
|
-
function setTextContent(nextText, dom, node) {
|
|
3574
|
-
const firstChild = dom.firstChild;
|
|
4594
|
+
function $setTextContent(nextText, dom, node) {
|
|
3575
4595
|
const isComposing = node.isComposing();
|
|
3576
4596
|
const text = nextText + (isComposing ? COMPOSITION_SUFFIX : "");
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
firstChild.insertData(index, insert);
|
|
3584
|
-
} else firstChild.nodeValue = text;
|
|
4597
|
+
const editor = $getEditor();
|
|
4598
|
+
const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot(node, dom, editor);
|
|
4599
|
+
const firstChild = slot.getFirstChild();
|
|
4600
|
+
if (firstChild === null || firstChild.nodeType !== Node.TEXT_NODE) {
|
|
4601
|
+
slot.insertChild(document.createTextNode(text));
|
|
4602
|
+
return;
|
|
3585
4603
|
}
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
4604
|
+
const textChild = firstChild;
|
|
4605
|
+
const nodeValue = textChild.nodeValue;
|
|
4606
|
+
if (nodeValue === text) return;
|
|
4607
|
+
if (isComposing || IS_FIREFOX) {
|
|
4608
|
+
const [index, remove, insert] = diffComposedText(nodeValue, text);
|
|
4609
|
+
if (remove !== 0) textChild.deleteData(index, remove);
|
|
4610
|
+
textChild.insertData(index, insert);
|
|
4611
|
+
} else textChild.nodeValue = text;
|
|
4612
|
+
}
|
|
4613
|
+
function $createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
|
|
4614
|
+
$setTextContent(text, innerDOM, node);
|
|
3589
4615
|
const textClassNames = config.theme.text;
|
|
3590
4616
|
if (textClassNames !== void 0) setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
|
|
3591
4617
|
}
|
|
@@ -3770,7 +4796,7 @@ var TextNode = class TextNode extends LexicalNode {
|
|
|
3770
4796
|
dom.appendChild(innerDOM);
|
|
3771
4797
|
}
|
|
3772
4798
|
const text = this.__text;
|
|
3773
|
-
createTextInnerDOM(innerDOM, this, innerTag, format, text, config);
|
|
4799
|
+
$createTextInnerDOM(innerDOM, this, innerTag, format, text, config);
|
|
3774
4800
|
const style = this.__style;
|
|
3775
4801
|
if (style !== "") setDOMStyleFromCSS(dom.style, style);
|
|
3776
4802
|
return dom;
|
|
@@ -3788,7 +4814,7 @@ var TextNode = class TextNode extends LexicalNode {
|
|
|
3788
4814
|
const prevInnerDOM = dom.firstChild;
|
|
3789
4815
|
if (prevInnerDOM == null) formatDevErrorMessage(`updateDOM: prevInnerDOM is null or undefined`);
|
|
3790
4816
|
const nextInnerDOM = document.createElement(nextInnerTag);
|
|
3791
|
-
createTextInnerDOM(nextInnerDOM, this, nextInnerTag, nextFormat, nextText, config);
|
|
4817
|
+
$createTextInnerDOM(nextInnerDOM, this, nextInnerTag, nextFormat, nextText, config);
|
|
3792
4818
|
dom.replaceChild(nextInnerDOM, prevInnerDOM);
|
|
3793
4819
|
return false;
|
|
3794
4820
|
}
|
|
@@ -3799,7 +4825,7 @@ var TextNode = class TextNode extends LexicalNode {
|
|
|
3799
4825
|
if (innerDOM == null) formatDevErrorMessage(`updateDOM: innerDOM is null or undefined`);
|
|
3800
4826
|
}
|
|
3801
4827
|
}
|
|
3802
|
-
setTextContent(nextText, innerDOM, this);
|
|
4828
|
+
$setTextContent(nextText, innerDOM, this);
|
|
3803
4829
|
const textClassNames = config.theme.text;
|
|
3804
4830
|
if (textClassNames !== void 0 && prevFormat !== nextFormat) setTextThemeClassNames(nextInnerTag, prevFormat, nextFormat, innerDOM, textClassNames);
|
|
3805
4831
|
const prevStyle = prevNode.__style;
|
|
@@ -4447,6 +5473,7 @@ var Point = class {
|
|
|
4447
5473
|
if ($getCompositionKey() === oldKey) $setCompositionKey(key);
|
|
4448
5474
|
if (selection !== null) {
|
|
4449
5475
|
selection.setCachedNodes(null);
|
|
5476
|
+
if ($isRangeSelection(selection)) selection._cachedIsBackward = null;
|
|
4450
5477
|
selection.dirty = true;
|
|
4451
5478
|
}
|
|
4452
5479
|
}
|
|
@@ -4611,6 +5638,8 @@ var RangeSelection = class RangeSelection {
|
|
|
4611
5638
|
anchor;
|
|
4612
5639
|
focus;
|
|
4613
5640
|
_cachedNodes;
|
|
5641
|
+
/** @internal */
|
|
5642
|
+
_cachedIsBackward;
|
|
4614
5643
|
dirty;
|
|
4615
5644
|
constructor(anchor, focus, format, style) {
|
|
4616
5645
|
this.anchor = anchor;
|
|
@@ -4618,6 +5647,7 @@ var RangeSelection = class RangeSelection {
|
|
|
4618
5647
|
anchor._selection = this;
|
|
4619
5648
|
focus._selection = this;
|
|
4620
5649
|
this._cachedNodes = null;
|
|
5650
|
+
this._cachedIsBackward = null;
|
|
4621
5651
|
this.format = format;
|
|
4622
5652
|
this.style = style;
|
|
4623
5653
|
this.dirty = false;
|
|
@@ -4677,6 +5707,7 @@ var RangeSelection = class RangeSelection {
|
|
|
4677
5707
|
setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset) {
|
|
4678
5708
|
this.anchor.set(anchorNode.__key, anchorOffset, "text");
|
|
4679
5709
|
this.focus.set(focusNode.__key, focusOffset, "text");
|
|
5710
|
+
return this;
|
|
4680
5711
|
}
|
|
4681
5712
|
/**
|
|
4682
5713
|
* Gets the (plain) text content of all the nodes in the selection.
|
|
@@ -4725,9 +5756,10 @@ var RangeSelection = class RangeSelection {
|
|
|
4725
5756
|
const lastSelection = editor.getEditorState()._selection;
|
|
4726
5757
|
const resolvedSelectionPoints = $internalResolveSelectionPoints(range.startContainer, range.startOffset, range.endContainer, range.endOffset, editor, lastSelection);
|
|
4727
5758
|
if (resolvedSelectionPoints === null) return;
|
|
4728
|
-
const [anchorPoint, focusPoint] = resolvedSelectionPoints;
|
|
5759
|
+
const [anchorPoint, focusPoint, dirty] = resolvedSelectionPoints;
|
|
4729
5760
|
this.anchor.set(anchorPoint.key, anchorPoint.offset, anchorPoint.type, true);
|
|
4730
5761
|
this.focus.set(focusPoint.key, focusPoint.offset, focusPoint.type, true);
|
|
5762
|
+
if (dirty) this.dirty = true;
|
|
4731
5763
|
$normalizeSelection(this);
|
|
4732
5764
|
}
|
|
4733
5765
|
/**
|
|
@@ -4785,16 +5817,7 @@ var RangeSelection = class RangeSelection {
|
|
|
4785
5817
|
* @param text the text to insert into the Selection
|
|
4786
5818
|
*/
|
|
4787
5819
|
insertRawText(text) {
|
|
4788
|
-
|
|
4789
|
-
const nodes = [];
|
|
4790
|
-
const length = parts.length;
|
|
4791
|
-
for (let i = 0; i < length; i++) {
|
|
4792
|
-
const part = parts[i];
|
|
4793
|
-
if (part === "\n" || part === "\r\n") nodes.push($createLineBreakNode());
|
|
4794
|
-
else if (part === " ") nodes.push($createTabNode());
|
|
4795
|
-
else nodes.push($createTextNode(part));
|
|
4796
|
-
}
|
|
4797
|
-
this.insertNodes(nodes);
|
|
5820
|
+
this.insertNodes($generateNodesFromRawText(text));
|
|
4798
5821
|
}
|
|
4799
5822
|
/**
|
|
4800
5823
|
* Insert the provided text into the EditorState at the current Selection.
|
|
@@ -4892,7 +5915,11 @@ var RangeSelection = class RangeSelection {
|
|
|
4892
5915
|
const [targetNode] = firstNode.splitText(startOffset);
|
|
4893
5916
|
targetNode.insertAfter(textNode, false);
|
|
4894
5917
|
}
|
|
4895
|
-
if (textNode.isComposing() && this.anchor.type === "text")
|
|
5918
|
+
if (textNode.isComposing() && this.anchor.type === "text") {
|
|
5919
|
+
this.anchor.offset -= text.length;
|
|
5920
|
+
this._cachedNodes = null;
|
|
5921
|
+
this._cachedIsBackward = null;
|
|
5922
|
+
}
|
|
4896
5923
|
return;
|
|
4897
5924
|
}
|
|
4898
5925
|
else if ($isTabNode(firstNode)) {
|
|
@@ -4909,7 +5936,11 @@ var RangeSelection = class RangeSelection {
|
|
|
4909
5936
|
else if (this.anchor.type === "text") {
|
|
4910
5937
|
this.format = firstNodeFormat;
|
|
4911
5938
|
this.style = firstNodeStyle;
|
|
4912
|
-
if (firstNode.isComposing())
|
|
5939
|
+
if (firstNode.isComposing()) {
|
|
5940
|
+
this.anchor.offset -= text.length;
|
|
5941
|
+
this._cachedNodes = null;
|
|
5942
|
+
this._cachedIsBackward = null;
|
|
5943
|
+
}
|
|
4913
5944
|
}
|
|
4914
5945
|
} else {
|
|
4915
5946
|
const markedNodeKeysForKeep = new Set([...firstNode.getParentKeys(), ...lastNode.getParentKeys()]);
|
|
@@ -4964,7 +5995,11 @@ var RangeSelection = class RangeSelection {
|
|
|
4964
5995
|
else if (this.anchor.type === "text") {
|
|
4965
5996
|
this.format = firstNode.getFormat();
|
|
4966
5997
|
this.style = firstNode.getStyle();
|
|
4967
|
-
if (firstNode.isComposing())
|
|
5998
|
+
if (firstNode.isComposing()) {
|
|
5999
|
+
this.anchor.offset -= text.length;
|
|
6000
|
+
this._cachedNodes = null;
|
|
6001
|
+
this._cachedIsBackward = null;
|
|
6002
|
+
}
|
|
4968
6003
|
}
|
|
4969
6004
|
} else if (startOffset === firstNodeTextLength) firstNode.select();
|
|
4970
6005
|
else {
|
|
@@ -5238,10 +6273,18 @@ var RangeSelection = class RangeSelection {
|
|
|
5238
6273
|
const focusNode = this.focus.getNode();
|
|
5239
6274
|
if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
|
|
5240
6275
|
if (this.dirty) {
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
6276
|
+
const anchorKeyedDOM = getElementByKeyOrThrow(editor, this.anchor.key);
|
|
6277
|
+
const focusKeyedDOM = getElementByKeyOrThrow(editor, this.focus.key);
|
|
6278
|
+
let nextAnchorDOM = anchorKeyedDOM;
|
|
6279
|
+
let nextFocusDOM = focusKeyedDOM;
|
|
6280
|
+
if (this.anchor.type === "text") {
|
|
6281
|
+
const node = this.anchor.getNode();
|
|
6282
|
+
nextAnchorDOM = $isTextNode(node) ? $getDOMTextNode(node, anchorKeyedDOM, editor) : null;
|
|
6283
|
+
}
|
|
6284
|
+
if (this.focus.type === "text") {
|
|
6285
|
+
const node = this.focus.getNode();
|
|
6286
|
+
nextFocusDOM = $isTextNode(node) ? $getDOMTextNode(node, focusKeyedDOM, editor) : null;
|
|
6287
|
+
}
|
|
5245
6288
|
if (nextAnchorDOM && nextFocusDOM) setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, this.anchor.offset, nextFocusDOM, this.focus.offset);
|
|
5246
6289
|
}
|
|
5247
6290
|
moveNativeSelection(domSelection, alter, isBackward ? "backward" : "forward", granularity);
|
|
@@ -5343,6 +6386,10 @@ var RangeSelection = class RangeSelection {
|
|
|
5343
6386
|
}
|
|
5344
6387
|
if (state.type === "merge-block") {
|
|
5345
6388
|
const { caret, block } = state;
|
|
6389
|
+
if (caret.origin.isEmpty() && !block.isEmpty() && caret.origin.getParent() === block.getParent()) {
|
|
6390
|
+
caret.origin.remove(true);
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
5346
6393
|
$updateRangeSelectionFromCaretRange(this, $getCaretRange(!caret.origin.isEmpty() && block.isEmpty() ? $rewindSiblingCaret($getSiblingCaret(block, caret.direction)) : initialRange.anchor, caret));
|
|
5347
6394
|
return this.removeText();
|
|
5348
6395
|
}
|
|
@@ -5411,7 +6458,11 @@ var RangeSelection = class RangeSelection {
|
|
|
5411
6458
|
* @returns true if the Selection is backwards, false otherwise.
|
|
5412
6459
|
*/
|
|
5413
6460
|
isBackward() {
|
|
5414
|
-
|
|
6461
|
+
const cached = this._cachedIsBackward;
|
|
6462
|
+
if (cached !== null) return cached;
|
|
6463
|
+
const isBackward = this.focus.isBefore(this.anchor);
|
|
6464
|
+
if (!isCurrentlyReadOnlyMode()) this._cachedIsBackward = isBackward;
|
|
6465
|
+
return isBackward;
|
|
5415
6466
|
}
|
|
5416
6467
|
getStartEndPoints() {
|
|
5417
6468
|
return [this.anchor, this.focus];
|
|
@@ -5593,15 +6644,17 @@ function shouldResolveAncestor(resolvedElement, resolvedOffset, lastPoint) {
|
|
|
5593
6644
|
function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
|
|
5594
6645
|
let resolvedOffset = offset;
|
|
5595
6646
|
let resolvedNode;
|
|
6647
|
+
let dirty = false;
|
|
5596
6648
|
if (isHTMLElement(dom)) {
|
|
5597
6649
|
let moveSelectionToEnd = false;
|
|
5598
6650
|
const childNodes = dom.childNodes;
|
|
5599
6651
|
const childNodesLength = childNodes.length;
|
|
5600
6652
|
const blockCursorElement = editor._blockCursorElement;
|
|
5601
|
-
if (resolvedOffset === childNodesLength) {
|
|
6653
|
+
if (resolvedOffset === childNodesLength && childNodesLength > 0) {
|
|
5602
6654
|
moveSelectionToEnd = true;
|
|
5603
6655
|
resolvedOffset = childNodesLength - 1;
|
|
5604
6656
|
}
|
|
6657
|
+
if (getNodeKeyFromDOMNode(dom, editor) === void 0 && dom !== editor.getRootElement() && !$isSelectionCapturedInDecorator(dom)) dirty = true;
|
|
5605
6658
|
let childDOM = childNodes[resolvedOffset];
|
|
5606
6659
|
let hasBlockCursor = false;
|
|
5607
6660
|
if (childDOM === blockCursorElement) {
|
|
@@ -5621,7 +6674,7 @@ function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
|
|
|
5621
6674
|
if ($isElementNode(resolvedElement)) {
|
|
5622
6675
|
const elementDOM = editor.getElementByKey(resolvedElement.getKey());
|
|
5623
6676
|
if (!(elementDOM !== null)) formatDevErrorMessage(`$internalResolveSelectionPoint: node in DOM but not keyToDOMMap`);
|
|
5624
|
-
const slot = $
|
|
6677
|
+
const slot = $getDOMSlot(resolvedElement, elementDOM, editor);
|
|
5625
6678
|
[resolvedElement, resolvedOffset] = slot.resolveChildIndex(resolvedElement, elementDOM, dom, offset);
|
|
5626
6679
|
if (!$isElementNode(resolvedElement)) formatDevErrorMessage(`$internalResolveSelectionPoint: resolvedElement is not an ElementNode`);
|
|
5627
6680
|
if (moveSelectionToEnd && resolvedOffset >= resolvedElement.getChildrenSize()) resolvedOffset = Math.max(0, resolvedElement.getChildrenSize() - 1);
|
|
@@ -5645,15 +6698,21 @@ function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
|
|
|
5645
6698
|
}
|
|
5646
6699
|
} else {
|
|
5647
6700
|
const index = resolvedElement.getIndexWithinParent();
|
|
5648
|
-
|
|
5649
|
-
|
|
6701
|
+
const elementDOM = editor.getElementByKey(resolvedElement.getKey());
|
|
6702
|
+
let position = "after";
|
|
6703
|
+
if (elementDOM !== null && $getNodeFromDOM(dom) === resolvedElement) {
|
|
6704
|
+
const slot = $getDOMSlot(resolvedElement, elementDOM, editor);
|
|
6705
|
+
if (slot.element !== elementDOM) position = slot.resolveLeafPosition(elementDOM, dom, offset);
|
|
6706
|
+
else if (offset === 0 && $isDecoratorNode(resolvedElement)) position = "before";
|
|
6707
|
+
}
|
|
6708
|
+
resolvedOffset = position === "before" ? index : index + 1;
|
|
5650
6709
|
resolvedElement = resolvedElement.getParentOrThrow();
|
|
5651
6710
|
}
|
|
5652
|
-
if ($isElementNode(resolvedElement)) return $createPoint(resolvedElement.__key, resolvedOffset, "element");
|
|
6711
|
+
if ($isElementNode(resolvedElement)) return [$createPoint(resolvedElement.__key, resolvedOffset, "element"), dirty];
|
|
5653
6712
|
}
|
|
5654
6713
|
} else resolvedNode = $getNodeFromDOM(dom);
|
|
5655
6714
|
if (!$isTextNode(resolvedNode)) return null;
|
|
5656
|
-
return $createPoint(resolvedNode.__key, $getTextNodeOffset(resolvedNode, resolvedOffset, "clamp"), "text");
|
|
6715
|
+
return [$createPoint(resolvedNode.__key, $getTextNodeOffset(resolvedNode, resolvedOffset, "clamp"), "text"), dirty];
|
|
5657
6716
|
}
|
|
5658
6717
|
function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
|
|
5659
6718
|
const offset = point.offset;
|
|
@@ -5689,10 +6748,12 @@ function $normalizeSelectionPointsForBoundaries(anchor, focus, lastSelection) {
|
|
|
5689
6748
|
}
|
|
5690
6749
|
function $internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focusOffset, editor, lastSelection) {
|
|
5691
6750
|
if (anchorDOM === null || focusDOM === null || !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) return null;
|
|
5692
|
-
const
|
|
5693
|
-
if (
|
|
5694
|
-
const
|
|
5695
|
-
if (
|
|
6751
|
+
const resolvedAnchor = $internalResolveSelectionPoint(anchorDOM, anchorOffset, $isRangeSelection(lastSelection) ? lastSelection.anchor : null, editor);
|
|
6752
|
+
if (resolvedAnchor === null) return null;
|
|
6753
|
+
const resolvedFocus = $internalResolveSelectionPoint(focusDOM, focusOffset, $isRangeSelection(lastSelection) ? lastSelection.focus : null, editor);
|
|
6754
|
+
if (resolvedFocus === null) return null;
|
|
6755
|
+
const [resolvedAnchorPoint, anchorDirty] = resolvedAnchor;
|
|
6756
|
+
const [resolvedFocusPoint, focusDirty] = resolvedFocus;
|
|
5696
6757
|
$validatePoint("anchor", resolvedAnchorPoint);
|
|
5697
6758
|
$validatePoint("focus", resolvedFocusPoint);
|
|
5698
6759
|
if (resolvedAnchorPoint.type === "element" && resolvedFocusPoint.type === "element") {
|
|
@@ -5701,7 +6762,11 @@ function $internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focu
|
|
|
5701
6762
|
if ($isDecoratorNode(anchorNode) && $isDecoratorNode(focusNode)) return null;
|
|
5702
6763
|
}
|
|
5703
6764
|
$normalizeSelectionPointsForBoundaries(resolvedAnchorPoint, resolvedFocusPoint);
|
|
5704
|
-
return [
|
|
6765
|
+
return [
|
|
6766
|
+
resolvedAnchorPoint,
|
|
6767
|
+
resolvedFocusPoint,
|
|
6768
|
+
anchorDirty || focusDirty
|
|
6769
|
+
];
|
|
5705
6770
|
}
|
|
5706
6771
|
function $isBlockElementNode(node) {
|
|
5707
6772
|
return $isElementNode(node) && !node.isInline();
|
|
@@ -5746,7 +6811,7 @@ function $internalCreateRangeSelection(lastSelection, domSelection, editor, even
|
|
|
5746
6811
|
} else return lastSelection.clone();
|
|
5747
6812
|
const resolvedSelectionPoints = $internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focusOffset, editor, lastSelection);
|
|
5748
6813
|
if (resolvedSelectionPoints === null) return null;
|
|
5749
|
-
const [resolvedAnchorPoint, resolvedFocusPoint] = resolvedSelectionPoints;
|
|
6814
|
+
const [resolvedAnchorPoint, resolvedFocusPoint, dirty] = resolvedSelectionPoints;
|
|
5750
6815
|
let format = 0;
|
|
5751
6816
|
let style = "";
|
|
5752
6817
|
if ($isRangeSelection(lastSelection)) {
|
|
@@ -5765,7 +6830,9 @@ function $internalCreateRangeSelection(lastSelection, domSelection, editor, even
|
|
|
5765
6830
|
}
|
|
5766
6831
|
}
|
|
5767
6832
|
}
|
|
5768
|
-
|
|
6833
|
+
const newSelection = new RangeSelection(resolvedAnchorPoint, resolvedFocusPoint, format, style);
|
|
6834
|
+
if (dirty) newSelection.dirty = true;
|
|
6835
|
+
return newSelection;
|
|
5769
6836
|
}
|
|
5770
6837
|
function $validatePoint(name, point) {
|
|
5771
6838
|
const node = $getNodeByKey(point.key);
|
|
@@ -5915,20 +6982,17 @@ function setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, nextAnchorOff
|
|
|
5915
6982
|
function $getElementAndOffsetForPoint(editor, node, offset) {
|
|
5916
6983
|
const element = getElementByKeyOrThrow(editor, node.getKey());
|
|
5917
6984
|
if ($isElementNode(node)) {
|
|
5918
|
-
const slot = $
|
|
6985
|
+
const slot = $getDOMSlot(node, element, editor);
|
|
5919
6986
|
return [slot.element, offset + slot.getFirstChildOffset()];
|
|
5920
6987
|
}
|
|
5921
6988
|
return [element, offset];
|
|
5922
6989
|
}
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
const focusDOMNode = domSelection.focusNode;
|
|
5926
|
-
const anchorOffset = domSelection.anchorOffset;
|
|
5927
|
-
const focusOffset = domSelection.focusOffset;
|
|
6990
|
+
/** @internal */
|
|
6991
|
+
function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection, tags, rootElement) {
|
|
5928
6992
|
const activeElement = document.activeElement;
|
|
5929
6993
|
if (tags.has("collaboration") && activeElement !== rootElement || activeElement !== null && isSelectionCapturedInDecoratorInput(activeElement)) return;
|
|
5930
6994
|
if (!$isRangeSelection(nextSelection)) {
|
|
5931
|
-
if (prevSelection !== null && isSelectionWithinEditor(editor,
|
|
6995
|
+
if (prevSelection !== null && isSelectionWithinEditor(editor, domSelection.anchorNode, domSelection.focusNode)) domSelection.removeAllRanges();
|
|
5932
6996
|
return;
|
|
5933
6997
|
}
|
|
5934
6998
|
const anchor = nextSelection.anchor;
|
|
@@ -5944,13 +7008,13 @@ function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5944
7008
|
let nextFocusNode = focusDOM;
|
|
5945
7009
|
let anchorFormatOrStyleChanged = false;
|
|
5946
7010
|
if (anchor.type === "text") {
|
|
5947
|
-
nextAnchorNode = getDOMTextNode(anchorDOM);
|
|
7011
|
+
nextAnchorNode = $isTextNode(anchorNode) ? $getDOMTextNode(anchorNode, anchorDOM, editor) : null;
|
|
5948
7012
|
anchorFormatOrStyleChanged = anchorNode.getFormat() !== nextFormat || anchorNode.getStyle() !== nextStyle;
|
|
5949
7013
|
} else if ($isRangeSelection(prevSelection) && prevSelection.anchor.type === "text") anchorFormatOrStyleChanged = true;
|
|
5950
|
-
if (focus.type === "text") nextFocusNode = getDOMTextNode(focusDOM);
|
|
7014
|
+
if (focus.type === "text") nextFocusNode = $isTextNode(focusNode) ? $getDOMTextNode(focusNode, focusDOM, editor) : null;
|
|
5951
7015
|
if (nextAnchorNode === null || nextFocusNode === null) return;
|
|
5952
7016
|
if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) markCollapsedSelectionFormat(nextFormat, nextStyle, nextAnchorOffset, anchor.key, performance.now());
|
|
5953
|
-
if (
|
|
7017
|
+
if (!(domSelection.type === "Range" && isCollapsed) && domSelection.anchorOffset === nextAnchorOffset && domSelection.focusOffset === nextFocusOffset && domSelection.anchorNode === nextAnchorNode && domSelection.focusNode === nextFocusNode) {
|
|
5954
7018
|
if (activeElement === null || !rootElement.contains(activeElement)) {
|
|
5955
7019
|
if (!tags.has("skip-selection-focus")) rootElement.focus({ preventScroll: true });
|
|
5956
7020
|
}
|
|
@@ -5977,6 +7041,46 @@ function $insertNodes(nodes) {
|
|
|
5977
7041
|
if (selection === null) selection = $getRoot().selectEnd();
|
|
5978
7042
|
selection.insertNodes(nodes);
|
|
5979
7043
|
}
|
|
7044
|
+
/**
|
|
7045
|
+
* Push-lexer visitor passed to {@link tokenizeRawText}. The tokenizer
|
|
7046
|
+
* invokes one callback per token it emits; empty text runs are
|
|
7047
|
+
* suppressed, so `text` is only invoked with a non-empty string.
|
|
7048
|
+
*/
|
|
7049
|
+
/**
|
|
7050
|
+
* Push-lex a raw text string into `linebreak` (`\n` / `\r\n`), `tab`
|
|
7051
|
+
* (`\t`), and `text` (everything else) tokens, dispatching each to the
|
|
7052
|
+
* matching callback on `visitor` in source order.
|
|
7053
|
+
*
|
|
7054
|
+
* Shared by {@link $generateNodesFromRawText} (which builds
|
|
7055
|
+
* `LineBreakNode` / `TabNode` / `TextNode` siblings) and by
|
|
7056
|
+
* `@lexical/clipboard`'s default `text/plain` clipboard importer
|
|
7057
|
+
* (which maps `linebreak` to a real paragraph break via
|
|
7058
|
+
* `insertParagraph` so multi-line plain text becomes multi-paragraph
|
|
7059
|
+
* rich text). Empty text runs are dropped so callers don't need to
|
|
7060
|
+
* special-case them.
|
|
7061
|
+
*/
|
|
7062
|
+
function tokenizeRawText(text, visitor) {
|
|
7063
|
+
for (const part of text.split(/(\r?\n|\t)/)) if (part === "\n" || part === "\r\n") visitor.linebreak();
|
|
7064
|
+
else if (part === " ") visitor.tab();
|
|
7065
|
+
else if (part !== "") visitor.text(part);
|
|
7066
|
+
}
|
|
7067
|
+
/**
|
|
7068
|
+
* Convert a raw text string into a flat array of `TextNode`,
|
|
7069
|
+
* `LineBreakNode`, and `TabNode` siblings, splitting on `\n`, `\r\n`,
|
|
7070
|
+
* and `\t`. Use this when you need the same `\n` / `\t` → real-node
|
|
7071
|
+
* conversion that {@link RangeSelection.insertRawText} performs but
|
|
7072
|
+
* without a selection — e.g. when building a `CodeNode`'s children
|
|
7073
|
+
* inside a DOM-import rule.
|
|
7074
|
+
*/
|
|
7075
|
+
function $generateNodesFromRawText(text) {
|
|
7076
|
+
const nodes = [];
|
|
7077
|
+
tokenizeRawText(text, {
|
|
7078
|
+
linebreak: () => nodes.push($createLineBreakNode()),
|
|
7079
|
+
tab: () => nodes.push($createTabNode()),
|
|
7080
|
+
text: (part) => nodes.push($createTextNode(part))
|
|
7081
|
+
});
|
|
7082
|
+
return nodes;
|
|
7083
|
+
}
|
|
5980
7084
|
function $getTextContent() {
|
|
5981
7085
|
const selection = $getSelection();
|
|
5982
7086
|
if (selection === null) return "";
|
|
@@ -6161,6 +7265,18 @@ function getActiveEditor() {
|
|
|
6161
7265
|
if (activeEditor === null) formatDevErrorMessage(`Unable to find an active editor. This method can only be used synchronously during the callback of editor.update(), editor.read(), or editor.getEditorState().read(..., {editor}).${collectBuildInformation()}`);
|
|
6162
7266
|
return activeEditor;
|
|
6163
7267
|
}
|
|
7268
|
+
/**
|
|
7269
|
+
* Schedule a full reconcile of the active editor, so that every node is
|
|
7270
|
+
* re-rendered through the current {@link EditorDOMRenderConfig} on the next
|
|
7271
|
+
* commit. Unlike {@link LexicalNode.markDirty}, this does not clone or
|
|
7272
|
+
* otherwise mutate the node map, so no mutation/collaboration listeners
|
|
7273
|
+
* observe a change. Must be called within an `editor.update`.
|
|
7274
|
+
*
|
|
7275
|
+
* @internal
|
|
7276
|
+
*/
|
|
7277
|
+
function $fullReconcile() {
|
|
7278
|
+
getActiveEditor()._dirtyType = FULL_RECONCILE;
|
|
7279
|
+
}
|
|
6164
7280
|
function collectBuildInformation() {
|
|
6165
7281
|
let compatibleEditors = 0;
|
|
6166
7282
|
const incompatibleEditors = /* @__PURE__ */ new Set();
|
|
@@ -6302,7 +7418,7 @@ function parseEditorState(serializedEditorState, editor, updateFn) {
|
|
|
6302
7418
|
editor._dirtyElements = /* @__PURE__ */ new Map();
|
|
6303
7419
|
editor._dirtyLeaves = /* @__PURE__ */ new Set();
|
|
6304
7420
|
editor._cloneNotNeeded = /* @__PURE__ */ new Set();
|
|
6305
|
-
editor._dirtyType =
|
|
7421
|
+
editor._dirtyType = NO_DIRTY_NODES;
|
|
6306
7422
|
activeEditorState = editorState;
|
|
6307
7423
|
isReadOnlyMode = false;
|
|
6308
7424
|
activeEditor = editor;
|
|
@@ -6438,7 +7554,7 @@ function $commitPendingUpdates(editor, recoveryEditorState) {
|
|
|
6438
7554
|
if (blockCursorElement !== null) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
|
|
6439
7555
|
$updateDOMSelection(currentSelection, pendingSelection, editor, domSelection, tags, rootElement);
|
|
6440
7556
|
}
|
|
6441
|
-
updateDOMBlockCursorElement(editor, rootElement, pendingSelection);
|
|
7557
|
+
$updateDOMBlockCursorElement(editor, rootElement, pendingSelection);
|
|
6442
7558
|
} finally {
|
|
6443
7559
|
if (observer !== null) observer.observe(rootElement, observerOptions);
|
|
6444
7560
|
activeEditor = previousActiveEditor;
|
|
@@ -6536,12 +7652,24 @@ function triggerCommandListeners(editor, type, payload, fromEditor) {
|
|
|
6536
7652
|
}
|
|
6537
7653
|
function $triggerEnqueuedUpdates(editor) {
|
|
6538
7654
|
const queuedUpdates = editor._updates;
|
|
6539
|
-
if (queuedUpdates.length
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
7655
|
+
if (queuedUpdates.length === 0) {
|
|
7656
|
+
editor._cascadeCount = 0;
|
|
7657
|
+
return;
|
|
7658
|
+
}
|
|
7659
|
+
if (editor._cascadeCount++ > 99) {
|
|
7660
|
+
editor._updates = [];
|
|
7661
|
+
editor._cascadeCount = 0;
|
|
7662
|
+
try {
|
|
7663
|
+
formatDevErrorMessage(`One or more update listeners are endlessly enqueueing more updates. May have encountered infinite recursion caused by update listeners that trigger additional updates without a stop condition.`);
|
|
7664
|
+
} catch (error) {
|
|
7665
|
+
if (error instanceof Error) editor._onError(error);
|
|
6544
7666
|
}
|
|
7667
|
+
return;
|
|
7668
|
+
}
|
|
7669
|
+
const queuedUpdate = queuedUpdates.shift();
|
|
7670
|
+
if (queuedUpdate) {
|
|
7671
|
+
const [updateFn, options] = queuedUpdate;
|
|
7672
|
+
$beginUpdate(editor, updateFn, options);
|
|
6545
7673
|
}
|
|
6546
7674
|
}
|
|
6547
7675
|
function triggerDeferredUpdateCallbacks(editor, deferred) {
|
|
@@ -6680,167 +7808,6 @@ function updateEditor(editor, updateFn, options) {
|
|
|
6680
7808
|
if (editor._updating) editor._updates.push([updateFn, options]);
|
|
6681
7809
|
else $beginUpdate(editor, updateFn, options);
|
|
6682
7810
|
}
|
|
6683
|
-
/**
|
|
6684
|
-
* A utility class for managing the DOM children of an ElementNode
|
|
6685
|
-
*/
|
|
6686
|
-
var ElementDOMSlot = class ElementDOMSlot {
|
|
6687
|
-
element;
|
|
6688
|
-
before;
|
|
6689
|
-
after;
|
|
6690
|
-
constructor(element, before, after) {
|
|
6691
|
-
this.element = element;
|
|
6692
|
-
this.before = before || null;
|
|
6693
|
-
this.after = after || null;
|
|
6694
|
-
}
|
|
6695
|
-
/**
|
|
6696
|
-
* Return a new ElementDOMSlot where all managed children will be inserted before this node
|
|
6697
|
-
*/
|
|
6698
|
-
withBefore(before) {
|
|
6699
|
-
return new ElementDOMSlot(this.element, before, this.after);
|
|
6700
|
-
}
|
|
6701
|
-
/**
|
|
6702
|
-
* Return a new ElementDOMSlot where all managed children will be inserted after this node
|
|
6703
|
-
*/
|
|
6704
|
-
withAfter(after) {
|
|
6705
|
-
return new ElementDOMSlot(this.element, this.before, after);
|
|
6706
|
-
}
|
|
6707
|
-
/**
|
|
6708
|
-
* Return a new ElementDOMSlot with an updated root element
|
|
6709
|
-
*/
|
|
6710
|
-
withElement(element) {
|
|
6711
|
-
if (this.element === element) return this;
|
|
6712
|
-
return new ElementDOMSlot(element, this.before, this.after);
|
|
6713
|
-
}
|
|
6714
|
-
/**
|
|
6715
|
-
* Insert the given child before this.before and any reconciler managed line break node,
|
|
6716
|
-
* or append it if this.before is not defined
|
|
6717
|
-
*/
|
|
6718
|
-
insertChild(dom) {
|
|
6719
|
-
const before = this.before || this.getManagedLineBreak();
|
|
6720
|
-
if (!(before === null || before.parentElement === this.element)) formatDevErrorMessage(`ElementDOMSlot.insertChild: before is not in element`);
|
|
6721
|
-
this.element.insertBefore(dom, before);
|
|
6722
|
-
return this;
|
|
6723
|
-
}
|
|
6724
|
-
/**
|
|
6725
|
-
* Remove the managed child from this container, will throw if it was not already there
|
|
6726
|
-
*/
|
|
6727
|
-
removeChild(dom) {
|
|
6728
|
-
if (!(dom.parentElement === this.element)) formatDevErrorMessage(`ElementDOMSlot.removeChild: dom is not in element`);
|
|
6729
|
-
this.element.removeChild(dom);
|
|
6730
|
-
return this;
|
|
6731
|
-
}
|
|
6732
|
-
/**
|
|
6733
|
-
* Replace managed child prevDom with dom. Will throw if prevDom is not a child
|
|
6734
|
-
*
|
|
6735
|
-
* @param dom The new node to replace prevDom
|
|
6736
|
-
* @param prevDom the node that will be replaced
|
|
6737
|
-
*/
|
|
6738
|
-
replaceChild(dom, prevDom) {
|
|
6739
|
-
if (!(prevDom.parentElement === this.element)) formatDevErrorMessage(`ElementDOMSlot.replaceChild: prevDom is not in element`);
|
|
6740
|
-
this.element.replaceChild(dom, prevDom);
|
|
6741
|
-
return this;
|
|
6742
|
-
}
|
|
6743
|
-
/**
|
|
6744
|
-
* Returns the first managed child of this node,
|
|
6745
|
-
* which will either be this.after.nextSibling or this.element.firstChild,
|
|
6746
|
-
* and will never be this.before if it is defined.
|
|
6747
|
-
*/
|
|
6748
|
-
getFirstChild() {
|
|
6749
|
-
const firstChild = this.after ? this.after.nextSibling : this.element.firstChild;
|
|
6750
|
-
return firstChild === this.before || firstChild === this.getManagedLineBreak() ? null : firstChild;
|
|
6751
|
-
}
|
|
6752
|
-
/**
|
|
6753
|
-
* @internal
|
|
6754
|
-
*/
|
|
6755
|
-
getManagedLineBreak() {
|
|
6756
|
-
return this.element.__lexicalLineBreak || null;
|
|
6757
|
-
}
|
|
6758
|
-
/** @internal */
|
|
6759
|
-
setManagedLineBreak(lineBreakType) {
|
|
6760
|
-
if (lineBreakType === null) this.removeManagedLineBreak();
|
|
6761
|
-
else {
|
|
6762
|
-
const webkitHack = lineBreakType === "decorator" && (IS_APPLE_WEBKIT || IS_IOS || IS_SAFARI);
|
|
6763
|
-
this.insertManagedLineBreak(webkitHack);
|
|
6764
|
-
}
|
|
6765
|
-
}
|
|
6766
|
-
/** @internal */
|
|
6767
|
-
removeManagedLineBreak() {
|
|
6768
|
-
const br = this.getManagedLineBreak();
|
|
6769
|
-
if (br) {
|
|
6770
|
-
const element = this.element;
|
|
6771
|
-
const sibling = br.nodeName === "IMG" ? br.nextSibling : null;
|
|
6772
|
-
if (sibling) element.removeChild(sibling);
|
|
6773
|
-
element.removeChild(br);
|
|
6774
|
-
element.__lexicalLineBreak = void 0;
|
|
6775
|
-
}
|
|
6776
|
-
}
|
|
6777
|
-
/** @internal */
|
|
6778
|
-
insertManagedLineBreak(webkitHack) {
|
|
6779
|
-
const prevBreak = this.getManagedLineBreak();
|
|
6780
|
-
if (prevBreak) {
|
|
6781
|
-
if (webkitHack === (prevBreak.nodeName === "IMG")) return;
|
|
6782
|
-
this.removeManagedLineBreak();
|
|
6783
|
-
}
|
|
6784
|
-
const element = this.element;
|
|
6785
|
-
const before = this.before;
|
|
6786
|
-
const br = document.createElement("br");
|
|
6787
|
-
element.insertBefore(br, before);
|
|
6788
|
-
if (webkitHack) {
|
|
6789
|
-
const img = document.createElement("img");
|
|
6790
|
-
img.setAttribute("data-lexical-linebreak", "true");
|
|
6791
|
-
img.style.setProperty("display", "inline", "important");
|
|
6792
|
-
img.style.setProperty("border", "0px", "important");
|
|
6793
|
-
img.style.setProperty("margin", "0px", "important");
|
|
6794
|
-
img.alt = "";
|
|
6795
|
-
element.insertBefore(img, br);
|
|
6796
|
-
element.__lexicalLineBreak = img;
|
|
6797
|
-
} else element.__lexicalLineBreak = br;
|
|
6798
|
-
}
|
|
6799
|
-
/**
|
|
6800
|
-
* @internal
|
|
6801
|
-
*
|
|
6802
|
-
* Returns the offset of the first child
|
|
6803
|
-
*/
|
|
6804
|
-
getFirstChildOffset() {
|
|
6805
|
-
let i = 0;
|
|
6806
|
-
for (let node = this.after; node !== null; node = node.previousSibling) i++;
|
|
6807
|
-
return i;
|
|
6808
|
-
}
|
|
6809
|
-
/**
|
|
6810
|
-
* @internal
|
|
6811
|
-
*/
|
|
6812
|
-
resolveChildIndex(element, elementDOM, initialDOM, initialOffset) {
|
|
6813
|
-
if (initialDOM === this.element) {
|
|
6814
|
-
const firstChildOffset = this.getFirstChildOffset();
|
|
6815
|
-
return [element, Math.min(firstChildOffset + element.getChildrenSize(), Math.max(firstChildOffset, initialOffset))];
|
|
6816
|
-
}
|
|
6817
|
-
const initialPath = indexPath(elementDOM, initialDOM);
|
|
6818
|
-
initialPath.push(initialOffset);
|
|
6819
|
-
const elementPath = indexPath(elementDOM, this.element);
|
|
6820
|
-
let offset = element.getIndexWithinParent();
|
|
6821
|
-
for (let i = 0; i < elementPath.length; i++) {
|
|
6822
|
-
const target = initialPath[i];
|
|
6823
|
-
const source = elementPath[i];
|
|
6824
|
-
if (target === void 0 || target < source) break;
|
|
6825
|
-
else if (target > source) {
|
|
6826
|
-
offset += 1;
|
|
6827
|
-
break;
|
|
6828
|
-
}
|
|
6829
|
-
}
|
|
6830
|
-
return [element.getParentOrThrow(), offset];
|
|
6831
|
-
}
|
|
6832
|
-
};
|
|
6833
|
-
function indexPath(root, child) {
|
|
6834
|
-
const path = [];
|
|
6835
|
-
let node = child;
|
|
6836
|
-
for (; node !== root && node !== null; node = node.parentNode) {
|
|
6837
|
-
let i = 0;
|
|
6838
|
-
for (let sibling = node.previousSibling; sibling !== null; sibling = sibling.previousSibling) i++;
|
|
6839
|
-
path.push(i);
|
|
6840
|
-
}
|
|
6841
|
-
if (!(node === root)) formatDevErrorMessage(`indexPath: root is not a parent of child`);
|
|
6842
|
-
return path.reverse();
|
|
6843
|
-
}
|
|
6844
7811
|
/** @noInheritDoc */
|
|
6845
7812
|
var ElementNode = class extends LexicalNode {
|
|
6846
7813
|
/** @internal */
|
|
@@ -7214,12 +8181,12 @@ var ElementNode = class extends LexicalNode {
|
|
|
7214
8181
|
return writableSelf;
|
|
7215
8182
|
}
|
|
7216
8183
|
/**
|
|
7217
|
-
* @
|
|
8184
|
+
* @experimental
|
|
7218
8185
|
*
|
|
7219
|
-
* An
|
|
7220
|
-
*
|
|
7221
|
-
*
|
|
7222
|
-
*
|
|
8186
|
+
* An ElementNode subclass can override this to control where its children
|
|
8187
|
+
* are inserted into the DOM, e.g. to add a wrapping node or accessory nodes
|
|
8188
|
+
* before or after the children. The root of the node returned by createDOM
|
|
8189
|
+
* must still be exactly one HTMLElement.
|
|
7223
8190
|
*/
|
|
7224
8191
|
getDOMSlot(element) {
|
|
7225
8192
|
return new ElementDOMSlot(element);
|
|
@@ -7228,7 +8195,10 @@ var ElementNode = class extends LexicalNode {
|
|
|
7228
8195
|
const { element } = super.exportDOM(editor);
|
|
7229
8196
|
if (isHTMLElement(element)) {
|
|
7230
8197
|
const indent = this.getIndent();
|
|
7231
|
-
if (indent > 0)
|
|
8198
|
+
if (indent > 0) {
|
|
8199
|
+
element.style.paddingInlineStart = `${indent * 40}px`;
|
|
8200
|
+
element.setAttribute("data-lexical-indent", String(indent));
|
|
8201
|
+
}
|
|
7232
8202
|
const direction = this.getDirection();
|
|
7233
8203
|
if (direction) element.dir = direction;
|
|
7234
8204
|
}
|
|
@@ -7282,6 +8252,10 @@ var ElementNode = class extends LexicalNode {
|
|
|
7282
8252
|
canInsertTextAfter() {
|
|
7283
8253
|
return true;
|
|
7284
8254
|
}
|
|
8255
|
+
/**
|
|
8256
|
+
* If the method is overridden and returns true, ensure that `canBeEmpty()`
|
|
8257
|
+
* returns false for the inline node to work correctly
|
|
8258
|
+
*/
|
|
7285
8259
|
isInline() {
|
|
7286
8260
|
return false;
|
|
7287
8261
|
}
|
|
@@ -7313,7 +8287,7 @@ var ElementNode = class extends LexicalNode {
|
|
|
7313
8287
|
}
|
|
7314
8288
|
/** @internal */
|
|
7315
8289
|
reconcileObservedMutation(dom, editor) {
|
|
7316
|
-
const slot = $
|
|
8290
|
+
const slot = $getDOMSlot(this, dom, editor);
|
|
7317
8291
|
let currentDOM = slot.getFirstChild();
|
|
7318
8292
|
for (let currentNode = this.getFirstChild(); currentNode; currentNode = currentNode.getNextSibling()) {
|
|
7319
8293
|
const correctDOM = editor.getElementByKey(currentNode.getKey());
|
|
@@ -7386,10 +8360,7 @@ var RootNode = class RootNode extends ElementNode {
|
|
|
7386
8360
|
}
|
|
7387
8361
|
getTextContent() {
|
|
7388
8362
|
const cachedText = this.__cachedText;
|
|
7389
|
-
|
|
7390
|
-
if (cachedText !== null) return cachedText;
|
|
7391
|
-
}
|
|
7392
|
-
return super.getTextContent();
|
|
8363
|
+
return cachedText !== null && (isCurrentlyReadOnlyMode() || getActiveEditor()._dirtyType === NO_DIRTY_NODES) ? cachedText : super.getTextContent();
|
|
7393
8364
|
}
|
|
7394
8365
|
remove() {
|
|
7395
8366
|
formatDevErrorMessage(`remove: cannot be called on root nodes`);
|
|
@@ -7432,7 +8403,7 @@ function editorStateHasDirtySelection(editorState, editor) {
|
|
|
7432
8403
|
return false;
|
|
7433
8404
|
}
|
|
7434
8405
|
function cloneEditorState(current) {
|
|
7435
|
-
return new EditorState(
|
|
8406
|
+
return new EditorState(cloneMap(current._nodeMap));
|
|
7436
8407
|
}
|
|
7437
8408
|
function createEmptyEditorState() {
|
|
7438
8409
|
return new EditorState(new Map([["root", $createRootNode()]]));
|
|
@@ -7588,16 +8559,15 @@ var ParagraphNode = class ParagraphNode extends ElementNode {
|
|
|
7588
8559
|
};
|
|
7589
8560
|
function $convertParagraphElement(element) {
|
|
7590
8561
|
const node = $createParagraphNode();
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
setNodeIndentFromDOM(element, node);
|
|
7594
|
-
}
|
|
8562
|
+
$setFormatFromDOM(node, element);
|
|
8563
|
+
setNodeIndentFromDOM(element, node);
|
|
7595
8564
|
if (node.getFormatType() === "") {
|
|
7596
8565
|
const align = element.getAttribute("align");
|
|
7597
8566
|
if (align) {
|
|
7598
8567
|
if (align && align in ELEMENT_TYPE_TO_FORMAT) node.setFormat(align);
|
|
7599
8568
|
}
|
|
7600
8569
|
}
|
|
8570
|
+
$setDirectionFromDOM(node, element);
|
|
7601
8571
|
return { node };
|
|
7602
8572
|
}
|
|
7603
8573
|
function $createParagraphNode() {
|
|
@@ -7607,8 +8577,24 @@ function $isParagraphNode(node) {
|
|
|
7607
8577
|
return node instanceof ParagraphNode;
|
|
7608
8578
|
}
|
|
7609
8579
|
/**
|
|
8580
|
+
* Configuration entry passed in {@link CreateEditorArgs.nodes} to substitute
|
|
8581
|
+
* a core node class with a custom subclass. The replacement class itself
|
|
8582
|
+
* must also appear in `nodes`.
|
|
8583
|
+
*
|
|
8584
|
+
* See [Node Replacement](https://lexical.dev/docs/concepts/node-replacement).
|
|
8585
|
+
*/
|
|
8586
|
+
/**
|
|
7610
8587
|
* A LexicalNode class or LexicalNodeReplacement configuration
|
|
7611
8588
|
*/
|
|
8589
|
+
/**
|
|
8590
|
+
* @experimental
|
|
8591
|
+
*
|
|
8592
|
+
* The slot type produced by `$getDOMSlot` for a given node, narrowed via
|
|
8593
|
+
* the node's static class: `ElementNode` resolves to {@link ElementDOMSlot}
|
|
8594
|
+
* (with children-management methods), other nodes to the base
|
|
8595
|
+
* {@link DOMSlot}. Callers passing a known node type get the narrowed slot
|
|
8596
|
+
* without manual `instanceof` checks.
|
|
8597
|
+
*/
|
|
7612
8598
|
/** @internal @experimental */
|
|
7613
8599
|
const DEFAULT_SKIP_INITIALIZATION = false;
|
|
7614
8600
|
/**
|
|
@@ -7690,7 +8676,16 @@ function normalizePriority(priority) {
|
|
|
7690
8676
|
* }
|
|
7691
8677
|
* ```
|
|
7692
8678
|
*/
|
|
7693
|
-
|
|
8679
|
+
/** @internal */
|
|
8680
|
+
/**
|
|
8681
|
+
* @internal
|
|
8682
|
+
*
|
|
8683
|
+
* Resets the editor's transient state — DOM mappings, dirty tracking,
|
|
8684
|
+
* composition, and (by default) the queued updates and tags — while
|
|
8685
|
+
* applying the given pendingEditorState. Used during root element
|
|
8686
|
+
* transitions and reconciler error recovery.
|
|
8687
|
+
*/
|
|
8688
|
+
function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorState, options) {
|
|
7694
8689
|
const keyNodeMap = editor._keyToDOMMap;
|
|
7695
8690
|
keyNodeMap.clear();
|
|
7696
8691
|
editor._editorState = createEmptyEditorState();
|
|
@@ -7701,8 +8696,11 @@ function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorStat
|
|
|
7701
8696
|
editor._dirtyLeaves = /* @__PURE__ */ new Set();
|
|
7702
8697
|
editor._dirtyElements.clear();
|
|
7703
8698
|
editor._normalizedNodes = /* @__PURE__ */ new Set();
|
|
7704
|
-
|
|
7705
|
-
|
|
8699
|
+
if (!options || !options.preserveUpdateQueue) {
|
|
8700
|
+
editor._updateTags = /* @__PURE__ */ new Set();
|
|
8701
|
+
editor._updates = [];
|
|
8702
|
+
editor._cascadeCount = 0;
|
|
8703
|
+
}
|
|
7706
8704
|
editor._blockCursorElement = null;
|
|
7707
8705
|
const observer = editor._observer;
|
|
7708
8706
|
if (observer !== null) {
|
|
@@ -7771,10 +8769,7 @@ const DEFAULT_EDITOR_DOM_CONFIG = {
|
|
|
7771
8769
|
return registeredNode && registeredNode.exportDOM !== void 0 ? registeredNode.exportDOM(editor, node) : node.exportDOM(editor);
|
|
7772
8770
|
},
|
|
7773
8771
|
$extractWithChild: (node, childNode, selection, destination, _editor) => $isElementNode(node) && node.extractWithChild(childNode, selection, destination),
|
|
7774
|
-
$getDOMSlot: (node, dom, _editor) =>
|
|
7775
|
-
if (!$isElementNode(node)) formatDevErrorMessage(`$getDOMSlot called on a non-ElementNode (key ${node.getKey()} type ${node.getType()})`);
|
|
7776
|
-
return node.getDOMSlot(dom);
|
|
7777
|
-
},
|
|
8772
|
+
$getDOMSlot: (node, dom, _editor) => node.getDOMSlot(dom),
|
|
7778
8773
|
$shouldExclude: (node, _selection, _editor) => $isElementNode(node) && node.excludeFromCopy("html"),
|
|
7779
8774
|
$shouldInclude: (node, selection, _editor) => selection ? node.isSelected(selection) : true,
|
|
7780
8775
|
$updateDOM: (nextNode, prevNode, dom, editor) => nextNode.updateDOM(prevNode, dom, editor._config)
|
|
@@ -7902,6 +8897,8 @@ var LexicalEditor = class {
|
|
|
7902
8897
|
/** @internal */
|
|
7903
8898
|
_updating;
|
|
7904
8899
|
/** @internal */
|
|
8900
|
+
_cascadeCount;
|
|
8901
|
+
/** @internal */
|
|
7905
8902
|
_listeners;
|
|
7906
8903
|
/** @internal */
|
|
7907
8904
|
_commands;
|
|
@@ -7950,9 +8947,10 @@ var LexicalEditor = class {
|
|
|
7950
8947
|
this._pendingEditorState = null;
|
|
7951
8948
|
this._compositionKey = null;
|
|
7952
8949
|
this._deferred = [];
|
|
7953
|
-
this._keyToDOMMap =
|
|
8950
|
+
this._keyToDOMMap = new GenMap();
|
|
7954
8951
|
this._updates = [];
|
|
7955
8952
|
this._updating = false;
|
|
8953
|
+
this._cascadeCount = 0;
|
|
7956
8954
|
this._listeners = {
|
|
7957
8955
|
decorator: /* @__PURE__ */ new Map(),
|
|
7958
8956
|
editable: /* @__PURE__ */ new Map(),
|
|
@@ -8247,7 +9245,7 @@ var LexicalEditor = class {
|
|
|
8247
9245
|
const classNames = getCachedClassNameArray(this._config.theme, "root");
|
|
8248
9246
|
const pendingEditorState = this._pendingEditorState || this._editorState;
|
|
8249
9247
|
this._rootElement = nextRootElement;
|
|
8250
|
-
resetEditor(this, prevRootElement, nextRootElement, pendingEditorState);
|
|
9248
|
+
resetEditor(this, prevRootElement, nextRootElement, pendingEditorState, { preserveUpdateQueue: true });
|
|
8251
9249
|
if (prevRootElement !== null) {
|
|
8252
9250
|
if (!this._config.disableEvents) removeRootElementEvents(prevRootElement);
|
|
8253
9251
|
if (classNames != null) prevRootElement.classList.remove(...classNames);
|
|
@@ -8421,7 +9419,7 @@ var LexicalEditor = class {
|
|
|
8421
9419
|
return { editorState: this._editorState.toJSON() };
|
|
8422
9420
|
}
|
|
8423
9421
|
};
|
|
8424
|
-
LexicalEditor.version =
|
|
9422
|
+
LexicalEditor.version = LEXICAL_VERSION;
|
|
8425
9423
|
let pendingNodeToClone = null;
|
|
8426
9424
|
function setPendingNodeToClone(pendingNode) {
|
|
8427
9425
|
pendingNodeToClone = pendingNode;
|
|
@@ -8569,7 +9567,7 @@ function $setNodeKey(node, existingKey) {
|
|
|
8569
9567
|
if ($isElementNode(node)) editor._dirtyElements.set(key, true);
|
|
8570
9568
|
else editor._dirtyLeaves.add(key);
|
|
8571
9569
|
editor._cloneNotNeeded.add(key);
|
|
8572
|
-
editor._dirtyType = HAS_DIRTY_NODES;
|
|
9570
|
+
if (editor._dirtyType === NO_DIRTY_NODES) editor._dirtyType = HAS_DIRTY_NODES;
|
|
8573
9571
|
node.__key = key;
|
|
8574
9572
|
}
|
|
8575
9573
|
function errorOnNodeKeyConstructorMismatch(node, existingKey, pendingNode) {
|
|
@@ -8596,6 +9594,12 @@ function internalMarkParentElementsAsDirty(parentKey, nodeMap, dirtyElements) {
|
|
|
8596
9594
|
* Removes a node from its parent, updating all necessary pointers and links.
|
|
8597
9595
|
* @internal
|
|
8598
9596
|
*
|
|
9597
|
+
* This function does not adjust the editor's current selection. Callers
|
|
9598
|
+
* that need element-anchored offsets in the old parent to track the child
|
|
9599
|
+
* count change must call `$updateElementSelectionOnCreateDeleteNode` (with
|
|
9600
|
+
* `times = -1`) after invoking this — see `$removeNode`, `replace`,
|
|
9601
|
+
* `insertBefore`, and `insertAfter` for the pattern.
|
|
9602
|
+
*
|
|
8599
9603
|
* This function is for internal use of the library.
|
|
8600
9604
|
* Please do not use it as it may change in the future.
|
|
8601
9605
|
*/
|
|
@@ -8631,7 +9635,7 @@ function internalMarkNodeAsDirty(node) {
|
|
|
8631
9635
|
const dirtyElements = editor._dirtyElements;
|
|
8632
9636
|
if (parent !== null) internalMarkParentElementsAsDirty(parent, nodeMap, dirtyElements);
|
|
8633
9637
|
const key = latest.__key;
|
|
8634
|
-
editor._dirtyType = HAS_DIRTY_NODES;
|
|
9638
|
+
if (editor._dirtyType === NO_DIRTY_NODES) editor._dirtyType = HAS_DIRTY_NODES;
|
|
8635
9639
|
if ($isElementNode(node)) dirtyElements.set(key, true);
|
|
8636
9640
|
else editor._dirtyLeaves.add(key);
|
|
8637
9641
|
}
|
|
@@ -8847,9 +9851,17 @@ function $setTextContentWithSelection(node, textContent, selection) {
|
|
|
8847
9851
|
node.setTextContent(textContent);
|
|
8848
9852
|
if ($isRangeSelection(selection)) {
|
|
8849
9853
|
const key = node.getKey();
|
|
9854
|
+
let pointMutated = false;
|
|
8850
9855
|
for (const k of ["anchor", "focus"]) {
|
|
8851
9856
|
const pt = selection[k];
|
|
8852
|
-
if (pt.type === "text" && pt.key === key)
|
|
9857
|
+
if (pt.type === "text" && pt.key === key) {
|
|
9858
|
+
pt.offset = $getTextNodeOffset(node, pt.offset, "clamp");
|
|
9859
|
+
pointMutated = true;
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
if (pointMutated) {
|
|
9863
|
+
selection._cachedNodes = null;
|
|
9864
|
+
selection._cachedIsBackward = null;
|
|
8853
9865
|
}
|
|
8854
9866
|
}
|
|
8855
9867
|
}
|
|
@@ -8988,13 +10000,19 @@ function isMoveBackward(event) {
|
|
|
8988
10000
|
return isExactShortcutMatch(event, "ArrowLeft", { shiftKey: "any" });
|
|
8989
10001
|
}
|
|
8990
10002
|
function isMoveToStart(event) {
|
|
8991
|
-
return isExactShortcutMatch(event, "ArrowLeft",
|
|
10003
|
+
return isExactShortcutMatch(event, "ArrowLeft", {
|
|
10004
|
+
...CONTROL_OR_META,
|
|
10005
|
+
shiftKey: "any"
|
|
10006
|
+
});
|
|
8992
10007
|
}
|
|
8993
10008
|
function isMoveForward(event) {
|
|
8994
10009
|
return isExactShortcutMatch(event, "ArrowRight", { shiftKey: "any" });
|
|
8995
10010
|
}
|
|
8996
10011
|
function isMoveToEnd(event) {
|
|
8997
|
-
return isExactShortcutMatch(event, "ArrowRight",
|
|
10012
|
+
return isExactShortcutMatch(event, "ArrowRight", {
|
|
10013
|
+
...CONTROL_OR_META,
|
|
10014
|
+
shiftKey: "any"
|
|
10015
|
+
});
|
|
8998
10016
|
}
|
|
8999
10017
|
function isMoveUp(event) {
|
|
9000
10018
|
return isExactShortcutMatch(event, "ArrowUp", {
|
|
@@ -9145,8 +10163,15 @@ function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
|
|
|
9145
10163
|
while (element !== null) {
|
|
9146
10164
|
const isBodyElement = element === doc.body;
|
|
9147
10165
|
if (isBodyElement) {
|
|
9148
|
-
|
|
9149
|
-
|
|
10166
|
+
const visualViewport = defaultView.visualViewport;
|
|
10167
|
+
if (visualViewport) {
|
|
10168
|
+
const offsetTop = visualViewport.offsetTop;
|
|
10169
|
+
targetTop = offsetTop;
|
|
10170
|
+
targetBottom = offsetTop + visualViewport.height;
|
|
10171
|
+
} else {
|
|
10172
|
+
targetTop = 0;
|
|
10173
|
+
targetBottom = getWindow(editor).innerHeight;
|
|
10174
|
+
}
|
|
9150
10175
|
const computedStyle = defaultView.getComputedStyle(doc.documentElement);
|
|
9151
10176
|
const scrollPaddingTop = parseFloat(computedStyle.scrollPaddingTop);
|
|
9152
10177
|
const scrollPaddingBottom = parseFloat(computedStyle.scrollPaddingBottom);
|
|
@@ -9295,7 +10320,7 @@ function removeDOMBlockCursorElement(blockCursorElement, editor, rootElement) {
|
|
|
9295
10320
|
const parentElement = blockCursorElement.parentElement;
|
|
9296
10321
|
if (parentElement !== null) parentElement.removeChild(blockCursorElement);
|
|
9297
10322
|
}
|
|
9298
|
-
function updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
|
|
10323
|
+
function $updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
|
|
9299
10324
|
let blockCursorElement = editor._blockCursorElement;
|
|
9300
10325
|
if ($isRangeSelection(nextSelection) && nextSelection.isCollapsed() && nextSelection.anchor.type === "element" && rootElement.contains(document.activeElement)) {
|
|
9301
10326
|
const anchor = nextSelection.anchor;
|
|
@@ -9317,7 +10342,7 @@ function updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
|
|
|
9317
10342
|
}
|
|
9318
10343
|
}
|
|
9319
10344
|
if (isBlockCursor) {
|
|
9320
|
-
const elementDOM = editor.getElementByKey(elementNode.__key);
|
|
10345
|
+
const elementDOM = $getDOMSlot(elementNode, editor.getElementByKey(elementNode.__key), editor).element;
|
|
9321
10346
|
if (blockCursorElement === null) editor._blockCursorElement = blockCursorElement = createBlockCursorElement(editor._config);
|
|
9322
10347
|
rootElement.style.caretColor = "transparent";
|
|
9323
10348
|
if (insertBeforeElement === null) elementDOM.appendChild(blockCursorElement);
|
|
@@ -9386,6 +10411,21 @@ function isHTMLAnchorElement(x) {
|
|
|
9386
10411
|
}
|
|
9387
10412
|
/**
|
|
9388
10413
|
* @param x - The element being tested
|
|
10414
|
+
* @returns Returns true if x is an HTML `<tr>` element, false otherwise
|
|
10415
|
+
*/
|
|
10416
|
+
function isHTMLTableRowElement(x) {
|
|
10417
|
+
return isHTMLElement(x) && x.tagName === "TR";
|
|
10418
|
+
}
|
|
10419
|
+
/**
|
|
10420
|
+
* @param x - The element being tested
|
|
10421
|
+
* @returns Returns true if x is an HTML `<td>` or `<th>` element, false
|
|
10422
|
+
* otherwise
|
|
10423
|
+
*/
|
|
10424
|
+
function isHTMLTableCellElement(x) {
|
|
10425
|
+
return isHTMLElement(x) && (x.tagName === "TD" || x.tagName === "TH");
|
|
10426
|
+
}
|
|
10427
|
+
/**
|
|
10428
|
+
* @param x - The element being tested
|
|
9389
10429
|
* @returns Returns true if x is an HTML element, false otherwise.
|
|
9390
10430
|
*/
|
|
9391
10431
|
function isHTMLElement(x) {
|
|
@@ -9405,18 +10445,23 @@ function isDOMNode(x) {
|
|
|
9405
10445
|
function isDocumentFragment(x) {
|
|
9406
10446
|
return isDOMNode(x) && x.nodeType === DOM_DOCUMENT_FRAGMENT_TYPE;
|
|
9407
10447
|
}
|
|
10448
|
+
const INLINE_TAG_RE = /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|mark|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var|#text)$/i;
|
|
9408
10449
|
/**
|
|
9409
10450
|
*
|
|
9410
10451
|
* @param node - the Dom Node to check
|
|
9411
10452
|
* @returns if the Dom Node is an inline node
|
|
9412
10453
|
*/
|
|
9413
10454
|
function isInlineDomNode(node) {
|
|
9414
|
-
|
|
9415
|
-
return node.nodeName.match(inlineNodes) !== null;
|
|
10455
|
+
return isHTMLElement(node) && node.style.display.startsWith("inline") ? true : INLINE_TAG_RE.test(node.nodeName);
|
|
9416
10456
|
}
|
|
10457
|
+
const BLOCK_TAG_RE = /^(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|td|tfoot|ul|video)$/i;
|
|
10458
|
+
/**
|
|
10459
|
+
*
|
|
10460
|
+
* @param node - the Dom Node to check
|
|
10461
|
+
* @returns if the Dom Node is a block node
|
|
10462
|
+
*/
|
|
9417
10463
|
function isBlockDomNode(node) {
|
|
9418
|
-
|
|
9419
|
-
return node.nodeName.match(blockNodes) !== null;
|
|
10464
|
+
return isHTMLElement(node) && node.style.display.startsWith("inline") ? false : BLOCK_TAG_RE.test(node.nodeName);
|
|
9420
10465
|
}
|
|
9421
10466
|
/**
|
|
9422
10467
|
* @internal
|
|
@@ -9446,11 +10491,57 @@ function $getEditor() {
|
|
|
9446
10491
|
return getActiveEditor();
|
|
9447
10492
|
}
|
|
9448
10493
|
/**
|
|
9449
|
-
* @
|
|
10494
|
+
* @experimental
|
|
10495
|
+
*
|
|
10496
|
+
* Read the editor's `$getDOMSlot` configuration (defaulting to the base
|
|
10497
|
+
* implementation when no override is registered via {@link DOMRenderExtension}).
|
|
10498
|
+
* Cross-package consumers (`@lexical/utils`, `@lexical/react`) use this to
|
|
10499
|
+
* route selection / DOM lookups through extension-configured slots.
|
|
9450
10500
|
*/
|
|
9451
10501
|
function $getEditorDOMRenderConfig(editor = $getEditor()) {
|
|
9452
10502
|
return editor._config.dom || DEFAULT_EDITOR_DOM_CONFIG;
|
|
9453
10503
|
}
|
|
10504
|
+
/**
|
|
10505
|
+
* @experimental
|
|
10506
|
+
*
|
|
10507
|
+
* Resolve the DOM slot for a node through the configured `$getDOMSlot` hook,
|
|
10508
|
+
* narrowing the return type via {@link DOMSlotForNode}: for an `ElementNode`
|
|
10509
|
+
* the result is an {@link ElementDOMSlot} (with children-management methods),
|
|
10510
|
+
* for non-Element nodes the base {@link DOMSlot} pointing at the keyed DOM.
|
|
10511
|
+
*
|
|
10512
|
+
* Invariants if an extension override returns a slot that doesn't match the
|
|
10513
|
+
* expected narrow type for the node (extension contract violation).
|
|
10514
|
+
*/
|
|
10515
|
+
function $getDOMSlot(node, dom, editor = $getEditor()) {
|
|
10516
|
+
const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot(node, dom, editor);
|
|
10517
|
+
if ($isElementNode(node)) {
|
|
10518
|
+
if (!$isElementDOMSlot(slot)) formatDevErrorMessage(`$getDOMSlot: expected ElementDOMSlot for ElementNode (key ${node.getKey()} type ${node.getType()})`);
|
|
10519
|
+
}
|
|
10520
|
+
return slot;
|
|
10521
|
+
}
|
|
10522
|
+
/**
|
|
10523
|
+
* @experimental
|
|
10524
|
+
*
|
|
10525
|
+
* Type guard narrowing a {@link DOMSlot} to an {@link ElementDOMSlot}, which
|
|
10526
|
+
* exposes children-management methods like `insertChild` and the managed
|
|
10527
|
+
* line-break helpers.
|
|
10528
|
+
*/
|
|
10529
|
+
function $isElementDOMSlot(slot) {
|
|
10530
|
+
return slot instanceof ElementDOMSlot;
|
|
10531
|
+
}
|
|
10532
|
+
/**
|
|
10533
|
+
* @experimental
|
|
10534
|
+
*
|
|
10535
|
+
* Resolve the actual text DOM (`Text`) for a `TextNode` through the
|
|
10536
|
+
* configured `$getDOMSlot` hook. Unlike the plain {@link getDOMTextNode}
|
|
10537
|
+
* which descends the first child chain from a raw element, this routes
|
|
10538
|
+
* through the slot so an extension wrapping the text node's keyed DOM
|
|
10539
|
+
* (e.g. one that injects a `contentEditable=false` sibling before the
|
|
10540
|
+
* text) still points at the correct content element.
|
|
10541
|
+
*/
|
|
10542
|
+
function $getDOMTextNode(node, dom, editor = $getEditor()) {
|
|
10543
|
+
return getDOMTextNode($getDOMSlot(node, dom, editor).element);
|
|
10544
|
+
}
|
|
9454
10545
|
/** @internal */
|
|
9455
10546
|
/**
|
|
9456
10547
|
* @internal
|
|
@@ -9522,24 +10613,63 @@ function $cloneWithPropertiesEphemeral(latestNode) {
|
|
|
9522
10613
|
return $markEphemeral($cloneWithProperties(latestNode));
|
|
9523
10614
|
}
|
|
9524
10615
|
function setNodeIndentFromDOM(elementDom, elementNode) {
|
|
10616
|
+
const indentAttr = elementDom.getAttribute("data-lexical-indent");
|
|
10617
|
+
if (indentAttr !== null) {
|
|
10618
|
+
const parsed = parseInt(indentAttr, 10);
|
|
10619
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
10620
|
+
elementNode.setIndent(parsed);
|
|
10621
|
+
return;
|
|
10622
|
+
}
|
|
10623
|
+
}
|
|
9525
10624
|
const indentSize = parseInt(elementDom.style.paddingInlineStart, 10) || 0;
|
|
9526
10625
|
const indent = Math.round(indentSize / 40);
|
|
9527
10626
|
elementNode.setIndent(indent);
|
|
9528
10627
|
}
|
|
9529
10628
|
/**
|
|
9530
|
-
*
|
|
10629
|
+
* Reads the `dir` attribute from a DOM element and applies it to the given
|
|
10630
|
+
* ElementNode via {@link ElementNode.setDirection} when it is a valid direction
|
|
10631
|
+
* value (`'ltr'` or `'rtl'`). Other values, including missing or empty `dir`,
|
|
10632
|
+
* leave the node unchanged. Useful inside `importDOM` converters to preserve
|
|
10633
|
+
* explicit text direction from imported HTML.
|
|
10634
|
+
*
|
|
10635
|
+
* @param node - The ElementNode to update.
|
|
10636
|
+
* @param domNode - The source HTMLElement whose `dir` attribute is read.
|
|
10637
|
+
* @returns The node, with its direction set when the source `dir` was valid.
|
|
10638
|
+
*/
|
|
10639
|
+
function $setDirectionFromDOM(node, domNode) {
|
|
10640
|
+
const dir = domNode.getAttribute("dir");
|
|
10641
|
+
return dir === "ltr" || dir === "rtl" ? node.setDirection(dir) : node;
|
|
10642
|
+
}
|
|
10643
|
+
/**
|
|
10644
|
+
* Reads the `style` and CSS `textAlign` property from a DOM element
|
|
10645
|
+
* and set format to the given ElementNode via {@link ElementNode.setFormat}
|
|
10646
|
+
* when it is a valid alignment value {@link ElementFormatType}
|
|
10647
|
+
* Other values, including missing or empty, leave the node unchanged.
|
|
10648
|
+
* Useful inside `importDOM` converters to preserve explicit alignment from imported HTML.
|
|
10649
|
+
*
|
|
10650
|
+
* @param node - The ElementNode to update.
|
|
10651
|
+
* @param domNode - The source HTMLElement whose `style` property is read.
|
|
10652
|
+
* @returns The node, with its align format set when the source `style.textAlign` was valid.
|
|
10653
|
+
*/
|
|
10654
|
+
function $setFormatFromDOM(node, domNode) {
|
|
10655
|
+
const alignment = domNode.style.textAlign;
|
|
10656
|
+
return alignment && alignment in ELEMENT_TYPE_TO_FORMAT ? node.setFormat(alignment) : node;
|
|
10657
|
+
}
|
|
10658
|
+
/**
|
|
10659
|
+
* Mark this DOM element as unmanaged by lexical's mutation observer (like
|
|
10660
|
+
* decorator nodes are). Extensions that inject non-lexical decoration
|
|
10661
|
+
* elements into a node's DOM should mark them so the mutation observer
|
|
10662
|
+
* doesn't evict them as "unknown DOM children" during cleanup.
|
|
9531
10663
|
*
|
|
9532
|
-
*
|
|
9533
|
-
* decorator nodes
|
|
10664
|
+
* @experimental
|
|
9534
10665
|
*/
|
|
9535
10666
|
function setDOMUnmanaged(elementDom) {
|
|
9536
|
-
|
|
9537
|
-
el.__lexicalUnmanaged = true;
|
|
10667
|
+
elementDom.__lexicalUnmanaged = true;
|
|
9538
10668
|
}
|
|
9539
10669
|
/**
|
|
9540
|
-
* @
|
|
10670
|
+
* True if this DOM node was marked with {@link setDOMUnmanaged}.
|
|
9541
10671
|
*
|
|
9542
|
-
*
|
|
10672
|
+
* @experimental
|
|
9543
10673
|
*/
|
|
9544
10674
|
function isDOMUnmanaged(elementDom) {
|
|
9545
10675
|
return elementDom.__lexicalUnmanaged === true;
|
|
@@ -10987,4 +12117,4 @@ function mergeRegister(...func) {
|
|
|
10987
12117
|
};
|
|
10988
12118
|
}
|
|
10989
12119
|
//#endregion
|
|
10990
|
-
export { $addUpdateTag, $applyNodeReplacement, $caretFromPoint, $caretRangeFromSelection, $cloneWithProperties, $cloneWithPropertiesEphemeral, $comparePointCaretNext, $copyNode, $create, $createChildrenArray, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createPoint, $createRangeSelection, $createRangeSelectionFromDom, $createTabNode, $createTextNode, $extendCaretToRange, $findMatchingParent, $getAdjacentChildCaret, $getAdjacentNode, $getAdjacentSiblingOrParentSiblingCaret, $getCaretInDirection, $getCaretRange, $getCaretRangeInDirection, $getCharacterOffsets, $getChildCaret, $getChildCaretAtIndex, $getChildCaretOrSelf, $getCollapsedCaretRange, $getCommonAncestor, $getCommonAncestorResultBranchOrder, $getEditor, $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getNearestRootOrShadowRoot, $getNodeByKey, $getNodeByKeyOrThrow, $getNodeFromDOMNode, $getPreviousSelection, $getRoot, $getSelection, $getSiblingCaret, $getState, $getStateChange, $getTextContent, $getTextNodeOffset, $getTextPointCaret, $getTextPointCaretSlice, $getWritableNodeState, $hasAncestor, $hasUpdateTag, $insertNodes, $isBlockElementNode, $isChildCaret, $isDecoratorNode, $isEditorState, $isElementNode, $isExtendableTextPointCaret, $isInlineElementOrDecoratorNode, $isLeafNode, $isLexicalNode, $isLineBreakNode, $isNodeCaret, $isNodeSelection, $isParagraphNode, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isSiblingCaret, $isTabNode, $isTextNode, $isTextPointCaret, $isTextPointCaretSlice, $isTokenOrSegmented, $isTokenOrTab, $nodesOfType, $normalizeCaret, $normalizeSelection as $normalizeSelection__EXPERIMENTAL, $onUpdate, $parseSerializedNode, $removeTextFromCaretRange, $rewindSiblingCaret, $selectAll, $setCompositionKey, $setPointFromCaret, $setSelection, $setSelectionFromCaretRange, $setState, $splitAtPointCaretNext, $splitNode, $updateRangeSelectionFromCaretRange, ArtificialNode__DO_NOT_USE, BEFORE_INPUT_COMMAND, BLUR_COMMAND, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, CLICK_COMMAND, COLLABORATION_TAG, COMMAND_PRIORITY_BEFORE_CRITICAL, COMMAND_PRIORITY_BEFORE_EDITOR, COMMAND_PRIORITY_BEFORE_HIGH, COMMAND_PRIORITY_BEFORE_LOW, COMMAND_PRIORITY_BEFORE_NORMAL, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, COMPOSITION_END_COMMAND, COMPOSITION_END_TAG, COMPOSITION_START_COMMAND, COMPOSITION_START_TAG, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, CUT_COMMAND, DEFAULT_EDITOR_DOM_CONFIG, DELETE_CHARACTER_COMMAND, DELETE_LINE_COMMAND, DELETE_WORD_COMMAND, DRAGEND_COMMAND, DRAGOVER_COMMAND, DRAGSTART_COMMAND, DROP_COMMAND, DecoratorNode, ElementNode, FOCUS_COMMAND, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INPUT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, INTERNAL_$isBlock, IS_ALL_FORMATTING, IS_BOLD, IS_CODE, IS_HIGHLIGHT, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_MODIFIER_COMMAND, KEY_SPACE_COMMAND, KEY_TAB_COMMAND, LineBreakNode, MOVE_TO_END, MOVE_TO_START, NODE_STATE_KEY, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, PASTE_TAG, ParagraphNode, REDO_COMMAND, REMOVE_TEXT_COMMAND, RootNode, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, SKIP_COLLAB_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, SKIP_SELECTION_FOCUS_TAG, TEXT_TYPE_TO_FORMAT, TabNode, TextNode, UNDO_COMMAND, addClassNamesToElement, buildImportMap, configExtension, createCommand, createEditor, createSharedNodeState, createState, declarePeerDependency, defineExtension, flipDirection, getDOMOwnerDocument, getDOMSelection, getDOMSelectionFromTarget, getDOMTextNode, getEditorPropertyFromDOMNode, getNearestEditorFromDOMNode, getRegisteredNode, getRegisteredNodeOrThrow, getStaticNodeConfig, getStyleObjectFromCSS, getTextDirection, getTransformSetFromKlass, isBlockDomNode, isCurrentlyReadOnlyMode, isDOMDocumentNode, isDOMNode, isDOMTextNode, isDOMUnmanaged, isDocumentFragment, isExactShortcutMatch, isHTMLAnchorElement, isHTMLElement, isInlineDomNode, isLexicalEditor, isModifierMatch, isSelectionCapturedInDecoratorInput, isSelectionWithinEditor, makeStepwiseIterator, mergeRegister, normalizeClassNames, removeClassNamesFromElement, removeFromParent, resetRandomKey, safeCast, setDOMStyleFromCSS, setDOMStyleObject, setDOMUnmanaged, setNodeIndentFromDOM, shallowMergeConfig, toggleTextFormatType };
|
|
12120
|
+
export { $addUpdateTag, $applyNodeReplacement, $caretFromPoint, $caretRangeFromSelection, $cloneWithProperties, $cloneWithPropertiesEphemeral, $comparePointCaretNext, $copyNode, $create, $createChildrenArray, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createPoint, $createRangeSelection, $createRangeSelectionFromDom, $createTabNode, $createTextNode, $extendCaretToRange, $findMatchingParent, $fullReconcile, $generateNodesFromRawText, $getAdjacentChildCaret, $getAdjacentNode, $getAdjacentSiblingOrParentSiblingCaret, $getCaretInDirection, $getCaretRange, $getCaretRangeInDirection, $getCharacterOffsets, $getChildCaret, $getChildCaretAtIndex, $getChildCaretOrSelf, $getCollapsedCaretRange, $getCommonAncestor, $getCommonAncestorResultBranchOrder, $getDOMSlot, $getDOMTextNode, $getEditor, $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getNearestRootOrShadowRoot, $getNodeByKey, $getNodeByKeyOrThrow, $getNodeFromDOMNode, $getPreviousSelection, $getRoot, $getSelection, $getSiblingCaret, $getState, $getStateChange, $getTextContent, $getTextNodeOffset, $getTextPointCaret, $getTextPointCaretSlice, $getWritableNodeState, $hasAncestor, $hasUpdateTag, $insertNodes, $isBlockElementNode, $isChildCaret, $isDecoratorNode, $isEditorState, $isElementDOMSlot, $isElementNode, $isExtendableTextPointCaret, $isInlineElementOrDecoratorNode, $isLeafNode, $isLexicalNode, $isLineBreakNode, $isNodeCaret, $isNodeSelection, $isParagraphNode, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isSiblingCaret, $isTabNode, $isTextNode, $isTextPointCaret, $isTextPointCaretSlice, $isTokenOrSegmented, $isTokenOrTab, $nodesOfType, $normalizeCaret, $normalizeSelection as $normalizeSelection__EXPERIMENTAL, $onUpdate, $parseSerializedNode, $removeTextFromCaretRange, $rewindSiblingCaret, $selectAll, $setCompositionKey, $setDirectionFromDOM, $setFormatFromDOM, $setPointFromCaret, $setSelection, $setSelectionFromCaretRange, $setState, $splitAtPointCaretNext, $splitNode, $updateDOMSelection, $updateRangeSelectionFromCaretRange, ArtificialNode__DO_NOT_USE, BEFORE_INPUT_COMMAND, BLUR_COMMAND, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, CAN_USE_BEFORE_INPUT, CAN_USE_DOM, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, CLICK_COMMAND, COLLABORATION_TAG, COMMAND_PRIORITY_BEFORE_CRITICAL, COMMAND_PRIORITY_BEFORE_EDITOR, COMMAND_PRIORITY_BEFORE_HIGH, COMMAND_PRIORITY_BEFORE_LOW, COMMAND_PRIORITY_BEFORE_NORMAL, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, COMPOSITION_END_COMMAND, COMPOSITION_END_TAG, COMPOSITION_START_COMMAND, COMPOSITION_START_TAG, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, CUT_COMMAND, DEFAULT_EDITOR_DOM_CONFIG, DELETE_CHARACTER_COMMAND, DELETE_LINE_COMMAND, DELETE_WORD_COMMAND, DRAGEND_COMMAND, DRAGOVER_COMMAND, DRAGSTART_COMMAND, DROP_COMMAND, DecoratorNode, ElementNode, FOCUS_COMMAND, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INPUT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, INTERNAL_$isBlock, IS_ALL_FORMATTING, IS_ANDROID, IS_ANDROID_CHROME, IS_APPLE, IS_APPLE_WEBKIT, IS_BOLD, IS_CHROME, IS_CODE, IS_FIREFOX, IS_HIGHLIGHT, IS_IOS, IS_ITALIC, IS_SAFARI, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_MODIFIER_COMMAND, KEY_SPACE_COMMAND, KEY_TAB_COMMAND, LineBreakNode, MOVE_TO_END, MOVE_TO_START, NODE_STATE_KEY, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, PASTE_TAG, ParagraphNode, REDO_COMMAND, REMOVE_TEXT_COMMAND, RootNode, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, SKIP_COLLAB_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, SKIP_SELECTION_FOCUS_TAG, TEXT_TYPE_TO_FORMAT, TabNode, TextNode, UNDO_COMMAND, addClassNamesToElement, buildImportMap, configExtension, createCommand, createEditor, createSharedNodeState, createState, declarePeerDependency, defineExtension, flipDirection, getDOMOwnerDocument, getDOMSelection, getDOMSelectionFromTarget, getDOMTextNode, getEditorPropertyFromDOMNode, getNearestEditorFromDOMNode, getRegisteredNode, getRegisteredNodeOrThrow, getStaticNodeConfig, getStyleObjectFromCSS, getTextDirection, getTransformSetFromKlass, isBlockDomNode, isCurrentlyReadOnlyMode, isDOMDocumentNode, isDOMNode, isDOMTextNode, isDOMUnmanaged, isDocumentFragment, isExactShortcutMatch, isHTMLAnchorElement, isHTMLElement, isHTMLTableCellElement, isHTMLTableRowElement, isInlineDomNode, isLexicalEditor, isModifierMatch, isSelectionCapturedInDecoratorInput, isSelectionWithinEditor, makeStepwiseIterator, mergeRegister, normalizeClassNames, removeClassNamesFromElement, removeFromParent, resetRandomKey, safeCast, setDOMStyleFromCSS, setDOMStyleObject, setDOMUnmanaged, setNodeIndentFromDOM, shallowMergeConfig, stopLexicalPropagation, toggleTextFormatType, tokenizeRawText };
|