@lexical/selection 0.8.1 → 0.9.1

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.
@@ -24,23 +24,41 @@ const CSS_TO_STYLES = new Map();
24
24
  * LICENSE file in the root directory of this source tree.
25
25
  *
26
26
  */
27
+
27
28
  function getDOMTextNode(element) {
28
29
  let node = element;
30
+
29
31
  while (node != null) {
30
32
  if (node.nodeType === Node.TEXT_NODE) {
31
33
  return node;
32
34
  }
35
+
33
36
  node = node.firstChild;
34
37
  }
38
+
35
39
  return null;
36
40
  }
41
+
37
42
  function getDOMIndexWithinParent(node) {
38
43
  const parent = node.parentNode;
44
+
39
45
  if (parent == null) {
40
46
  throw new Error('Should never happen');
41
47
  }
48
+
42
49
  return [parent, Array.from(parent.childNodes).indexOf(node)];
43
50
  }
51
+ /**
52
+ * Creates a selection range for the DOM.
53
+ * @param editor - The lexical editor.
54
+ * @param anchorNode - The anchor node of a selection.
55
+ * @param _anchorOffset - The amount of space offset from the anchor to the focus.
56
+ * @param focusNode - The current focus.
57
+ * @param _focusOffset - The amount of space offset from the focus to the anchor.
58
+ * @returns The range of selection for the DOM that was created.
59
+ */
60
+
61
+
44
62
  function createDOMRange(editor, anchorNode, _anchorOffset, focusNode, _focusOffset) {
45
63
  const anchorKey = anchorNode.getKey();
46
64
  const focusKey = focusNode.getKey();
@@ -49,94 +67,141 @@ function createDOMRange(editor, anchorNode, _anchorOffset, focusNode, _focusOffs
49
67
  let focusDOM = editor.getElementByKey(focusKey);
50
68
  let anchorOffset = _anchorOffset;
51
69
  let focusOffset = _focusOffset;
70
+
52
71
  if (lexical.$isTextNode(anchorNode)) {
53
72
  anchorDOM = getDOMTextNode(anchorDOM);
54
73
  }
74
+
55
75
  if (lexical.$isTextNode(focusNode)) {
56
76
  focusDOM = getDOMTextNode(focusDOM);
57
77
  }
78
+
58
79
  if (anchorNode === undefined || focusNode === undefined || anchorDOM === null || focusDOM === null) {
59
80
  return null;
60
81
  }
82
+
61
83
  if (anchorDOM.nodeName === 'BR') {
62
84
  [anchorDOM, anchorOffset] = getDOMIndexWithinParent(anchorDOM);
63
85
  }
86
+
64
87
  if (focusDOM.nodeName === 'BR') {
65
88
  [focusDOM, focusOffset] = getDOMIndexWithinParent(focusDOM);
66
89
  }
90
+
67
91
  const firstChild = anchorDOM.firstChild;
92
+
68
93
  if (anchorDOM === focusDOM && firstChild != null && firstChild.nodeName === 'BR' && anchorOffset === 0 && focusOffset === 0) {
69
94
  focusOffset = 1;
70
95
  }
96
+
71
97
  try {
72
98
  range.setStart(anchorDOM, anchorOffset);
73
99
  range.setEnd(focusDOM, focusOffset);
74
100
  } catch (e) {
75
101
  return null;
76
102
  }
103
+
77
104
  if (range.collapsed && (anchorOffset !== focusOffset || anchorKey !== focusKey)) {
78
105
  // Range is backwards, we need to reverse it
79
106
  range.setStart(focusDOM, focusOffset);
80
107
  range.setEnd(anchorDOM, anchorOffset);
81
108
  }
109
+
82
110
  return range;
83
111
  }
112
+ /**
113
+ * Creates DOMRects, generally used to help the editor find a specific location on the screen.
114
+ * @param editor - The lexical editor
115
+ * @param range - A fragment of a document that can contain nodes and parts of text nodes.
116
+ * @returns The selectionRects as an array.
117
+ */
118
+
84
119
  function createRectsFromDOMRange(editor, range) {
85
120
  const rootElement = editor.getRootElement();
121
+
86
122
  if (rootElement === null) {
87
123
  return [];
88
124
  }
125
+
89
126
  const rootRect = rootElement.getBoundingClientRect();
90
127
  const computedStyle = getComputedStyle(rootElement);
91
128
  const rootPadding = parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight);
92
129
  const selectionRects = Array.from(range.getClientRects());
93
130
  let selectionRectsLength = selectionRects.length;
94
131
  let prevRect;
132
+
95
133
  for (let i = 0; i < selectionRectsLength; i++) {
96
- const selectionRect = selectionRects[i];
97
- // Exclude a rect that is the exact same as the last rect. getClientRects() can return
134
+ const selectionRect = selectionRects[i]; // Exclude a rect that is the exact same as the last rect. getClientRects() can return
98
135
  // the same rect twice for some elements. A more sophisticated thing to do here is to
99
136
  // merge all the rects together into a set of rects that don't overlap, so we don't
100
137
  // generate backgrounds that are too dark.
101
- const isDuplicateRect = prevRect && prevRect.top === selectionRect.top && prevRect.left === selectionRect.left && prevRect.width === selectionRect.width && prevRect.height === selectionRect.height;
102
138
 
103
- // Exclude selections that span the entire element
139
+ const isDuplicateRect = prevRect && prevRect.top === selectionRect.top && prevRect.left === selectionRect.left && prevRect.width === selectionRect.width && prevRect.height === selectionRect.height; // Exclude selections that span the entire element
140
+
104
141
  const selectionSpansElement = selectionRect.width + rootPadding === rootRect.width;
142
+
105
143
  if (isDuplicateRect || selectionSpansElement) {
106
144
  selectionRects.splice(i--, 1);
107
145
  selectionRectsLength--;
108
146
  continue;
109
147
  }
148
+
110
149
  prevRect = selectionRect;
111
150
  }
151
+
112
152
  return selectionRects;
113
153
  }
154
+ /**
155
+ * Creates an object containing all the styles and their values provided in the CSS string.
156
+ * @param css - The CSS string of styles and their values.
157
+ * @returns The styleObject containing all the styles and their values.
158
+ */
159
+
114
160
  function getStyleObjectFromRawCSS(css) {
115
161
  const styleObject = {};
116
162
  const styles = css.split(';');
163
+
117
164
  for (const style of styles) {
118
165
  if (style !== '') {
119
166
  const [key, value] = style.split(/:([^]+)/); // split on first colon
167
+
120
168
  styleObject[key.trim()] = value.trim();
121
169
  }
122
170
  }
171
+
123
172
  return styleObject;
124
173
  }
174
+ /**
175
+ * Given a CSS string, returns an object from the style cache.
176
+ * @param css - The CSS property as a string.
177
+ * @returns The value of the given CSS property.
178
+ */
179
+
125
180
  function getStyleObjectFromCSS(css) {
126
181
  let value = CSS_TO_STYLES.get(css);
182
+
127
183
  if (value === undefined) {
128
184
  value = getStyleObjectFromRawCSS(css);
129
185
  CSS_TO_STYLES.set(css, value);
130
186
  }
187
+
131
188
  return value;
132
189
  }
190
+ /**
191
+ * Gets the CSS styles from the style object.
192
+ * @param styles - The style object containing the styles to get.
193
+ * @returns A string containing the CSS styles and their values.
194
+ */
195
+
133
196
  function getCSSFromStyleObject(styles) {
134
197
  let css = '';
198
+
135
199
  for (const style in styles) {
136
200
  if (style) {
137
201
  css += `${style}: ${styles[style]};`;
138
202
  }
139
203
  }
204
+
140
205
  return css;
141
206
  }
142
207
 
@@ -147,6 +212,7 @@ function getCSSFromStyleObject(styles) {
147
212
  * LICENSE file in the root directory of this source tree.
148
213
  *
149
214
  */
215
+
150
216
  function $updateElementNodeProperties(target, source) {
151
217
  target.__first = source.__first;
152
218
  target.__last = source.__last;
@@ -156,6 +222,7 @@ function $updateElementNodeProperties(target, source) {
156
222
  target.__dir = source.__dir;
157
223
  return target;
158
224
  }
225
+
159
226
  function $updateTextNodeProperties(target, source) {
160
227
  target.__format = source.__format;
161
228
  target.__style = source.__style;
@@ -163,28 +230,47 @@ function $updateTextNodeProperties(target, source) {
163
230
  target.__detail = source.__detail;
164
231
  return target;
165
232
  }
233
+ /**
234
+ * Returns a copy of a node, but generates a new key for the copy.
235
+ * @param node - The node to be cloned.
236
+ * @returns The clone of the node.
237
+ */
238
+
239
+
166
240
  function $cloneWithProperties(node) {
167
241
  const latest = node.getLatest();
168
- const constructor = latest.constructor;
169
- // @ts-expect-error
242
+ const constructor = latest.constructor; // @ts-expect-error
243
+
170
244
  const clone = constructor.clone(latest);
171
245
  clone.__parent = latest.__parent;
172
246
  clone.__next = latest.__next;
173
247
  clone.__prev = latest.__prev;
248
+
174
249
  if (lexical.$isElementNode(latest) && lexical.$isElementNode(clone)) {
175
250
  return $updateElementNodeProperties(clone, latest);
176
251
  }
252
+
177
253
  if (lexical.$isTextNode(latest) && lexical.$isTextNode(clone)) {
178
254
  return $updateTextNodeProperties(clone, latest);
179
255
  }
256
+
180
257
  return clone;
181
258
  }
259
+ /**
260
+ * Generally used to append text content to HTML and JSON. Grabs the text content and "slices"
261
+ * it to be generated into the new TextNode.
262
+ * @param selection - The selection containing the node whose TextNode is to be edited.
263
+ * @param textNode - The TextNode to be edited.
264
+ * @returns The updated TextNode.
265
+ */
266
+
182
267
  function $sliceSelectedTextNodeContent(selection, textNode) {
183
268
  if (textNode.isSelected() && !textNode.isSegmented() && !textNode.isToken() && (lexical.$isRangeSelection(selection) || lexical.DEPRECATED_$isGridSelection(selection))) {
184
269
  const anchorNode = selection.anchor.getNode();
185
270
  const focusNode = selection.focus.getNode();
186
271
  const isAnchor = textNode.is(anchorNode);
187
272
  const isFocus = textNode.is(focusNode);
273
+
188
274
  if (isAnchor || isFocus) {
189
275
  const isBackward = selection.isBackward();
190
276
  const [anchorOffset, focusOffset] = selection.getCharacterOffsets();
@@ -193,6 +279,7 @@ function $sliceSelectedTextNodeContent(selection, textNode) {
193
279
  const isLast = textNode.is(isBackward ? anchorNode : focusNode);
194
280
  let startOffset = 0;
195
281
  let endOffset = undefined;
282
+
196
283
  if (isSame) {
197
284
  startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
198
285
  endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
@@ -205,44 +292,71 @@ function $sliceSelectedTextNodeContent(selection, textNode) {
205
292
  startOffset = 0;
206
293
  endOffset = offset;
207
294
  }
295
+
208
296
  textNode.__text = textNode.__text.slice(startOffset, endOffset);
209
297
  return textNode;
210
298
  }
211
299
  }
300
+
212
301
  return textNode;
213
302
  }
303
+ /**
304
+ * Determines if the current selection is at the end of the node.
305
+ * @param point - The point of the selection to test.
306
+ * @returns true if the provided point offset is in the last possible position, false otherwise.
307
+ */
308
+
214
309
  function $isAtNodeEnd(point) {
215
310
  if (point.type === 'text') {
216
311
  return point.offset === point.getNode().getTextContentSize();
217
312
  }
313
+
218
314
  return point.offset === point.getNode().getChildrenSize();
219
315
  }
316
+ /**
317
+ * Trims text from a node in order to shorten it, eg. to enforce a text's max length. If it deletes text
318
+ * that is an ancestor of the anchor then it will leave 2 indents, otherwise, if no text content exists, it deletes
319
+ * the TextNode. It will move the focus to either the end of any left over text or beginning of a new TextNode.
320
+ * @param editor - The lexical editor.
321
+ * @param anchor - The anchor of the current selection, where the selection should be pointing.
322
+ * @param delCount - The amount of characters to delete. Useful as a dynamic variable eg. textContentSize - maxLength;
323
+ */
324
+
220
325
  function trimTextContentFromAnchor(editor, anchor, delCount) {
221
326
  // Work from the current selection anchor point
222
327
  let currentNode = anchor.getNode();
223
328
  let remaining = delCount;
329
+
224
330
  if (lexical.$isElementNode(currentNode)) {
225
331
  const descendantNode = currentNode.getDescendantByIndex(anchor.offset);
332
+
226
333
  if (descendantNode !== null) {
227
334
  currentNode = descendantNode;
228
335
  }
229
336
  }
337
+
230
338
  while (remaining > 0 && currentNode !== null) {
231
339
  let nextNode = currentNode.getPreviousSibling();
232
340
  let additionalElementWhitespace = 0;
341
+
233
342
  if (nextNode === null) {
234
343
  let parent = currentNode.getParentOrThrow();
235
344
  let parentSibling = parent.getPreviousSibling();
345
+
236
346
  while (parentSibling === null) {
237
347
  parent = parent.getParent();
348
+
238
349
  if (parent === null) {
239
350
  nextNode = null;
240
351
  break;
241
352
  }
353
+
242
354
  parentSibling = parent.getPreviousSibling();
243
355
  }
356
+
244
357
  if (parent !== null) {
245
358
  additionalElementWhitespace = parent.isInline() ? 0 : 2;
359
+
246
360
  if (lexical.$isElementNode(parentSibling)) {
247
361
  nextNode = parentSibling.getLastDescendant();
248
362
  } else {
@@ -250,37 +364,46 @@ function trimTextContentFromAnchor(editor, anchor, delCount) {
250
364
  }
251
365
  }
252
366
  }
253
- let text = currentNode.getTextContent();
254
- // If the text is empty, we need to consider adding in two line breaks to match
367
+
368
+ let text = currentNode.getTextContent(); // If the text is empty, we need to consider adding in two line breaks to match
255
369
  // the content if we were to get it from its parent.
370
+
256
371
  if (text === '' && lexical.$isElementNode(currentNode) && !currentNode.isInline()) {
257
372
  // TODO: should this be handled in core?
258
373
  text = '\n\n';
259
374
  }
260
- const textNodeSize = text.length;
261
- const offset = textNodeSize - remaining;
262
- const slicedText = text.slice(0, offset);
263
- if (!lexical.$isTextNode(currentNode) || remaining >= textNodeSize) {
375
+
376
+ const currentNodeSize = currentNode.getTextContentSize();
377
+
378
+ if (!lexical.$isTextNode(currentNode) || remaining >= currentNodeSize) {
264
379
  const parent = currentNode.getParent();
265
380
  currentNode.remove();
266
- if (parent != null && parent.getChildrenSize() === 0) {
381
+
382
+ if (parent != null && parent.getChildrenSize() === 0 && !lexical.$isRootNode(parent)) {
267
383
  parent.remove();
268
384
  }
269
- remaining -= textNodeSize + additionalElementWhitespace;
385
+
386
+ remaining -= currentNodeSize + additionalElementWhitespace;
270
387
  currentNode = nextNode;
271
388
  } else {
272
- const key = currentNode.getKey();
273
- // See if we can just revert it to what was in the last editor state
389
+ const key = currentNode.getKey(); // See if we can just revert it to what was in the last editor state
390
+
274
391
  const prevTextContent = editor.getEditorState().read(() => {
275
392
  const prevNode = lexical.$getNodeByKey(key);
393
+
276
394
  if (lexical.$isTextNode(prevNode) && prevNode.isSimpleText()) {
277
395
  return prevNode.getTextContent();
278
396
  }
397
+
279
398
  return null;
280
399
  });
400
+ const offset = currentNodeSize - remaining;
401
+ const slicedText = text.slice(0, offset);
402
+
281
403
  if (prevTextContent !== null && prevTextContent !== text) {
282
404
  const prevSelection = lexical.$getPreviousSelection();
283
405
  let target = currentNode;
406
+
284
407
  if (!currentNode.isSimpleText()) {
285
408
  const textNode = lexical.$createTextNode(prevTextContent);
286
409
  currentNode.replace(textNode);
@@ -288,6 +411,7 @@ function trimTextContentFromAnchor(editor, anchor, delCount) {
288
411
  } else {
289
412
  currentNode.setTextContent(prevTextContent);
290
413
  }
414
+
291
415
  if (lexical.$isRangeSelection(prevSelection) && prevSelection.isCollapsed()) {
292
416
  const prevOffset = prevSelection.anchor.offset;
293
417
  target.select(prevOffset, prevOffset);
@@ -295,14 +419,16 @@ function trimTextContentFromAnchor(editor, anchor, delCount) {
295
419
  } else if (currentNode.isSimpleText()) {
296
420
  // Split text
297
421
  const isSelected = anchor.key === key;
298
- let anchorOffset = anchor.offset;
299
- // Move offset to end if it's less than the remaniing number, otherwise
422
+ let anchorOffset = anchor.offset; // Move offset to end if it's less than the remaining number, otherwise
300
423
  // we'll have a negative splitStart.
424
+
301
425
  if (anchorOffset < remaining) {
302
- anchorOffset = textNodeSize;
426
+ anchorOffset = currentNodeSize;
303
427
  }
428
+
304
429
  const splitStart = isSelected ? anchorOffset - remaining : 0;
305
430
  const splitEnd = isSelected ? anchorOffset : offset;
431
+
306
432
  if (isSelected && splitStart === 0) {
307
433
  const [excessNode] = currentNode.splitText(splitStart, splitEnd);
308
434
  excessNode.remove();
@@ -314,15 +440,22 @@ function trimTextContentFromAnchor(editor, anchor, delCount) {
314
440
  const textNode = lexical.$createTextNode(slicedText);
315
441
  currentNode.replace(textNode);
316
442
  }
443
+
317
444
  remaining = 0;
318
445
  }
319
446
  }
320
447
  }
448
+ /**
449
+ * Gets the TextNode's style object and adds the styles to the CSS.
450
+ * @param node - The TextNode to add styles to.
451
+ */
452
+
321
453
  function $addNodeStyle(node) {
322
454
  const CSSText = node.getStyle();
323
455
  const styles = getStyleObjectFromRawCSS(CSSText);
324
456
  CSS_TO_STYLES.set(CSSText, styles);
325
457
  }
458
+
326
459
  function $patchStyle(target, patch) {
327
460
  const prevStyles = getStyleObjectFromCSS('getStyle' in target ? target.getStyle() : target.style);
328
461
  const newStyles = Object.entries(patch).reduce((styles, [key, value]) => {
@@ -331,24 +464,35 @@ function $patchStyle(target, patch) {
331
464
  } else {
332
465
  styles[key] = value;
333
466
  }
467
+
334
468
  return styles;
335
- }, {
336
- ...prevStyles
469
+ }, { ...prevStyles
337
470
  } || {});
338
471
  const newCSSText = getCSSFromStyleObject(newStyles);
339
472
  target.setStyle(newCSSText);
340
473
  CSS_TO_STYLES.set(newCSSText, newStyles);
341
474
  }
475
+ /**
476
+ * Applies the provided styles to the TextNodes in the provided Selection.
477
+ * Will update partially selected TextNodes by splitting the TextNode and applying
478
+ * the styles to the appropriate one.
479
+ * @param selection - The selected node(s) to update.
480
+ * @param patch - The patch to apply, which can include multiple styles. { CSSProperty: value }
481
+ */
482
+
483
+
342
484
  function $patchStyleText(selection, patch) {
343
485
  const selectedNodes = selection.getNodes();
344
486
  const selectedNodesLength = selectedNodes.length;
345
487
  const lastIndex = selectedNodesLength - 1;
346
488
  let firstNode = selectedNodes[0];
347
489
  let lastNode = selectedNodes[lastIndex];
490
+
348
491
  if (selection.isCollapsed()) {
349
492
  $patchStyle(selection, patch);
350
493
  return;
351
494
  }
495
+
352
496
  const anchor = selection.anchor;
353
497
  const focus = selection.focus;
354
498
  const firstNodeText = firstNode.getTextContent();
@@ -360,32 +504,31 @@ function $patchStyleText(selection, patch) {
360
504
  let endOffset = isBefore ? focusOffset : anchorOffset;
361
505
  const startType = isBefore ? anchor.type : focus.type;
362
506
  const endType = isBefore ? focus.type : anchor.type;
363
- const endKey = isBefore ? focus.key : anchor.key;
364
-
365
- // This is the case where the user only selected the very end of the
507
+ const endKey = isBefore ? focus.key : anchor.key; // This is the case where the user only selected the very end of the
366
508
  // first node so we don't want to include it in the formatting change.
509
+
367
510
  if (lexical.$isTextNode(firstNode) && startOffset === firstNodeTextLength) {
368
511
  const nextSibling = firstNode.getNextSibling();
512
+
369
513
  if (lexical.$isTextNode(nextSibling)) {
370
514
  // we basically make the second node the firstNode, changing offsets accordingly
371
515
  anchorOffset = 0;
372
516
  startOffset = 0;
373
517
  firstNode = nextSibling;
374
518
  }
375
- }
519
+ } // This is the case where we only selected a single node
520
+
376
521
 
377
- // This is the case where we only selected a single node
378
- if (firstNode.is(lastNode)) {
522
+ if (selectedNodes.length === 1) {
379
523
  if (lexical.$isTextNode(firstNode)) {
380
524
  startOffset = startType === 'element' ? 0 : anchorOffset > focusOffset ? focusOffset : anchorOffset;
381
- endOffset = endType === 'element' ? firstNodeTextLength : anchorOffset > focusOffset ? anchorOffset : focusOffset;
525
+ endOffset = endType === 'element' ? firstNodeTextLength : anchorOffset > focusOffset ? anchorOffset : focusOffset; // No actual text is selected, so do nothing.
382
526
 
383
- // No actual text is selected, so do nothing.
384
527
  if (startOffset === endOffset) {
385
528
  return;
386
- }
529
+ } // The entire node is selected, so just format it
530
+
387
531
 
388
- // The entire node is selected, so just format it
389
532
  if (startOffset === 0 && endOffset === firstNodeTextLength) {
390
533
  $patchStyle(firstNode, patch);
391
534
  firstNode.select(startOffset, endOffset);
@@ -398,6 +541,7 @@ function $patchStyleText(selection, patch) {
398
541
  replacement.select(0, endOffset - startOffset);
399
542
  }
400
543
  } // multiple nodes selected.
544
+
401
545
  } else {
402
546
  if (lexical.$isTextNode(firstNode) && startOffset < firstNode.getTextContentSize()) {
403
547
  if (startOffset !== 0) {
@@ -405,33 +549,36 @@ function $patchStyleText(selection, patch) {
405
549
  firstNode = firstNode.splitText(startOffset)[1];
406
550
  startOffset = 0;
407
551
  }
552
+
408
553
  $patchStyle(firstNode, patch);
409
554
  }
555
+
410
556
  if (lexical.$isTextNode(lastNode)) {
411
557
  const lastNodeText = lastNode.getTextContent();
412
- const lastNodeTextLength = lastNodeText.length;
413
-
414
- // The last node might not actually be the end node
558
+ const lastNodeTextLength = lastNodeText.length; // The last node might not actually be the end node
415
559
  //
416
560
  // If not, assume the last node is fully-selected unless the end offset is
417
561
  // zero.
562
+
418
563
  if (lastNode.__key !== endKey && endOffset !== 0) {
419
564
  endOffset = lastNodeTextLength;
420
- }
565
+ } // if the entire last node isn't selected, split it
566
+
421
567
 
422
- // if the entire last node isn't selected, split it
423
568
  if (endOffset !== lastNodeTextLength) {
424
569
  [lastNode] = lastNode.splitText(endOffset);
425
570
  }
571
+
426
572
  if (endOffset !== 0) {
427
573
  $patchStyle(lastNode, patch);
428
574
  }
429
- }
575
+ } // style all the text nodes in between
576
+
430
577
 
431
- // style all the text nodes in between
432
578
  for (let i = 1; i < lastIndex; i++) {
433
579
  const selectedNode = selectedNodes[i];
434
580
  const selectedNodeKey = selectedNode.getKey();
581
+
435
582
  if (lexical.$isTextNode(selectedNode) && selectedNodeKey !== firstNode.getKey() && selectedNodeKey !== lastNode.getKey() && !selectedNode.isToken()) {
436
583
  $patchStyle(selectedNode, patch);
437
584
  }
@@ -446,58 +593,98 @@ function $patchStyleText(selection, patch) {
446
593
  * LICENSE file in the root directory of this source tree.
447
594
  *
448
595
  */
449
-
450
596
  /**
451
- * Converts all nodes in the selection that are of one block type to another specified by parameter
452
- *
453
- * @param selection
454
- * @param createElement
455
- * @returns
597
+ * Converts all nodes in the selection that are of one block type to another.
598
+ * @param selection - The selected blocks to be converted.
599
+ * @param createElement - The function that creates the node. eg. $createParagraphNode.
456
600
  */
457
- function $setBlocksType_experimental(selection, createElement) {
601
+
602
+ function $setBlocksType(selection, createElement) {
458
603
  if (selection.anchor.key === 'root') {
459
604
  const element = createElement();
460
605
  const root = lexical.$getRoot();
461
606
  const firstChild = root.getFirstChild();
462
- if (firstChild) firstChild.replace(element, true);else root.append(element);
607
+
608
+ if (firstChild) {
609
+ firstChild.replace(element, true);
610
+ } else {
611
+ root.append(element);
612
+ }
613
+
463
614
  return;
464
615
  }
616
+
465
617
  const nodes = selection.getNodes();
466
- if (selection.anchor.type === 'text') {
467
- let firstBlock = selection.anchor.getNode().getParent();
468
- firstBlock = firstBlock.isInline() ? firstBlock.getParent() : firstBlock;
469
- if (nodes.indexOf(firstBlock) === -1) nodes.push(firstBlock);
618
+ let maybeBlock = selection.anchor.getNode().getParentOrThrow();
619
+
620
+ if (nodes.indexOf(maybeBlock) === -1) {
621
+ nodes.push(maybeBlock);
470
622
  }
623
+
624
+ if (maybeBlock.isInline()) {
625
+ maybeBlock = maybeBlock.getParentOrThrow();
626
+
627
+ if (nodes.indexOf(maybeBlock) === -1) {
628
+ nodes.push(maybeBlock);
629
+ }
630
+ }
631
+
471
632
  for (let i = 0; i < nodes.length; i++) {
472
633
  const node = nodes[i];
473
- if (!isBlock(node)) continue;
634
+
635
+ if (!isBlock(node)) {
636
+ continue;
637
+ }
638
+
474
639
  const targetElement = createElement();
475
640
  targetElement.setFormat(node.getFormatType());
476
641
  targetElement.setIndent(node.getIndent());
477
642
  node.replace(targetElement, true);
478
643
  }
479
644
  }
645
+
480
646
  function isBlock(node) {
481
- return lexical.$isElementNode(node) && !lexical.$isRootOrShadowRoot(node) && !node.isInline();
647
+ if (!lexical.$isElementNode(node) || lexical.$isRootOrShadowRoot(node)) {
648
+ return false;
649
+ }
650
+
651
+ const firstChild = node.getFirstChild();
652
+ const isLeafElement = firstChild === null || lexical.$isTextNode(firstChild) || firstChild.isInline();
653
+ return !node.isInline() && node.canBeEmpty() !== false && isLeafElement;
482
654
  }
655
+
483
656
  function isPointAttached(point) {
484
657
  return point.getNode().isAttached();
485
658
  }
659
+
486
660
  function $removeParentEmptyElements(startingNode) {
487
661
  let node = startingNode;
662
+
488
663
  while (node !== null && !lexical.$isRootOrShadowRoot(node)) {
489
664
  const latest = node.getLatest();
490
665
  const parentNode = node.getParent();
666
+
491
667
  if (latest.getChildrenSize() === 0) {
492
668
  node.remove(true);
493
669
  }
670
+
494
671
  node = parentNode;
495
672
  }
496
673
  }
674
+ /**
675
+ * @deprecated
676
+ * Wraps all nodes in the selection into another node of the type returned by createElement.
677
+ * @param selection - The selection of nodes to be wrapped.
678
+ * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
679
+ * @param wrappingElement - An element to append the wrapped selection and its children to.
680
+ */
681
+
682
+
497
683
  function $wrapNodes(selection, createElement, wrappingElement = null) {
498
684
  const nodes = selection.getNodes();
499
685
  const nodesLength = nodes.length;
500
686
  const anchor = selection.anchor;
687
+
501
688
  if (nodesLength === 0 || nodesLength === 1 && anchor.type === 'element' && anchor.getNode().getChildrenSize() === 0) {
502
689
  const target = anchor.type === 'text' ? anchor.getNode().getParentOrThrow() : anchor.getNode();
503
690
  const children = target.getChildren();
@@ -505,20 +692,24 @@ function $wrapNodes(selection, createElement, wrappingElement = null) {
505
692
  element.setFormat(target.getFormatType());
506
693
  element.setIndent(target.getIndent());
507
694
  children.forEach(child => element.append(child));
695
+
508
696
  if (wrappingElement) {
509
697
  element = wrappingElement.append(element);
510
698
  }
699
+
511
700
  target.replace(element);
512
701
  return;
513
702
  }
703
+
514
704
  let topLevelNode = null;
515
705
  let descendants = [];
706
+
516
707
  for (let i = 0; i < nodesLength; i++) {
517
- const node = nodes[i];
518
- // Determine whether wrapping has to be broken down into multiple chunks. This can happen if the
708
+ const node = nodes[i]; // Determine whether wrapping has to be broken down into multiple chunks. This can happen if the
519
709
  // user selected multiple Root-like nodes that have to be treated separately as if they are
520
710
  // their own branch. I.e. you don't want to wrap a whole table, but rather the contents of each
521
711
  // of each of the cell nodes.
712
+
522
713
  if (lexical.$isRootOrShadowRoot(node)) {
523
714
  $wrapNodesImpl(selection, descendants, descendants.length, createElement, wrappingElement);
524
715
  descendants = [];
@@ -530,69 +721,92 @@ function $wrapNodes(selection, createElement, wrappingElement = null) {
530
721
  descendants = [node];
531
722
  }
532
723
  }
724
+
533
725
  $wrapNodesImpl(selection, descendants, descendants.length, createElement, wrappingElement);
534
726
  }
727
+ /**
728
+ * Wraps each node into a new ElementNode.
729
+ * @param selection - The selection of nodes to wrap.
730
+ * @param nodes - An array of nodes, generally the descendants of the selection.
731
+ * @param nodesLength - The length of nodes.
732
+ * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
733
+ * @param wrappingElement - An element to wrap all the nodes into.
734
+ * @returns
735
+ */
736
+
535
737
  function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingElement = null) {
536
738
  if (nodes.length === 0) {
537
739
  return;
538
740
  }
741
+
539
742
  const firstNode = nodes[0];
540
743
  const elementMapping = new Map();
541
- const elements = [];
542
- // The below logic is to find the right target for us to
744
+ const elements = []; // The below logic is to find the right target for us to
543
745
  // either insertAfter/insertBefore/append the corresponding
544
746
  // elements to. This is made more complicated due to nested
545
747
  // structures.
748
+
546
749
  let target = lexical.$isElementNode(firstNode) ? firstNode : firstNode.getParentOrThrow();
750
+
547
751
  if (target.isInline()) {
548
752
  target = target.getParentOrThrow();
549
753
  }
754
+
550
755
  let targetIsPrevSibling = false;
756
+
551
757
  while (target !== null) {
552
758
  const prevSibling = target.getPreviousSibling();
759
+
553
760
  if (prevSibling !== null) {
554
761
  target = prevSibling;
555
762
  targetIsPrevSibling = true;
556
763
  break;
557
764
  }
765
+
558
766
  target = target.getParentOrThrow();
767
+
559
768
  if (lexical.$isRootOrShadowRoot(target)) {
560
769
  break;
561
770
  }
562
771
  }
563
- const emptyElements = new Set();
564
772
 
565
- // Find any top level empty elements
773
+ const emptyElements = new Set(); // Find any top level empty elements
774
+
566
775
  for (let i = 0; i < nodesLength; i++) {
567
776
  const node = nodes[i];
777
+
568
778
  if (lexical.$isElementNode(node) && node.getChildrenSize() === 0) {
569
779
  emptyElements.add(node.getKey());
570
780
  }
571
781
  }
572
- const movedNodes = new Set();
573
782
 
574
- // Move out all leaf nodes into our elements array.
783
+ const movedNodes = new Set(); // Move out all leaf nodes into our elements array.
575
784
  // If we find a top level empty element, also move make
576
785
  // an element for that.
786
+
577
787
  for (let i = 0; i < nodesLength; i++) {
578
788
  const node = nodes[i];
579
789
  let parent = node.getParent();
790
+
580
791
  if (parent !== null && parent.isInline()) {
581
792
  parent = parent.getParent();
582
793
  }
794
+
583
795
  if (parent !== null && lexical.$isLeafNode(node) && !movedNodes.has(node.getKey())) {
584
796
  const parentKey = parent.getKey();
797
+
585
798
  if (elementMapping.get(parentKey) === undefined) {
586
799
  const targetElement = createElement();
587
800
  targetElement.setFormat(parent.getFormatType());
588
801
  targetElement.setIndent(parent.getIndent());
589
802
  elements.push(targetElement);
590
- elementMapping.set(parentKey, targetElement);
591
- // Move node and its siblings to the new
803
+ elementMapping.set(parentKey, targetElement); // Move node and its siblings to the new
592
804
  // element.
805
+
593
806
  parent.getChildren().forEach(child => {
594
807
  targetElement.append(child);
595
808
  movedNodes.add(child.getKey());
809
+
596
810
  if (lexical.$isElementNode(child)) {
597
811
  // Skip nested leaf nodes if the parent has already been moved
598
812
  child.getChildrenKeys().forEach(key => movedNodes.add(key));
@@ -608,16 +822,17 @@ function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingEl
608
822
  node.remove(true);
609
823
  }
610
824
  }
825
+
611
826
  if (wrappingElement !== null) {
612
827
  for (let i = 0; i < elements.length; i++) {
613
828
  const element = elements[i];
614
829
  wrappingElement.append(element);
615
830
  }
616
831
  }
617
- let lastElement = null;
618
832
 
619
- // If our target is Root-like, let's see if we can re-adjust
833
+ let lastElement = null; // If our target is Root-like, let's see if we can re-adjust
620
834
  // so that the target is the first child instead.
835
+
621
836
  if (lexical.$isRootOrShadowRoot(target)) {
622
837
  if (targetIsPrevSibling) {
623
838
  if (wrappingElement !== null) {
@@ -630,9 +845,11 @@ function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingEl
630
845
  }
631
846
  } else {
632
847
  const firstChild = target.getFirstChild();
848
+
633
849
  if (lexical.$isElementNode(firstChild)) {
634
850
  target = firstChild;
635
851
  }
852
+
636
853
  if (firstChild === null) {
637
854
  if (wrappingElement) {
638
855
  target.append(wrappingElement);
@@ -666,7 +883,9 @@ function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingEl
666
883
  }
667
884
  }
668
885
  }
886
+
669
887
  const prevSelection = lexical.$getPreviousSelection();
888
+
670
889
  if (lexical.$isRangeSelection(prevSelection) && isPointAttached(prevSelection.anchor) && isPointAttached(prevSelection.focus)) {
671
890
  lexical.$setSelection(prevSelection.clone());
672
891
  } else if (lastElement !== null) {
@@ -675,22 +894,55 @@ function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingEl
675
894
  selection.dirty = true;
676
895
  }
677
896
  }
897
+ /**
898
+ * Determines if the default character selection should be overridden. Used with DecoratorNodes
899
+ * @param selection - The selection whose default character selection may need to be overridden.
900
+ * @param isBackward - Is the selection backwards (the focus comes before the anchor)?
901
+ * @returns true if it should be overridden, false if not.
902
+ */
903
+
678
904
  function $shouldOverrideDefaultCharacterSelection(selection, isBackward) {
679
905
  const possibleNode = lexical.$getAdjacentNode(selection.focus, isBackward);
680
906
  return lexical.$isDecoratorNode(possibleNode) && !possibleNode.isIsolated() || lexical.$isElementNode(possibleNode) && !possibleNode.isInline() && !possibleNode.canBeEmpty();
681
907
  }
908
+ /**
909
+ * Moves the selection according to the arguments.
910
+ * @param selection - The selected text or nodes.
911
+ * @param isHoldingShift - Is the shift key being held down during the operation.
912
+ * @param isBackward - Is the selection selected backwards (the focus comes before the anchor)?
913
+ * @param granularity - The distance to adjust the current selection.
914
+ */
915
+
682
916
  function $moveCaretSelection(selection, isHoldingShift, isBackward, granularity) {
683
917
  selection.modify(isHoldingShift ? 'extend' : 'move', isBackward, granularity);
684
918
  }
919
+ /**
920
+ * Tests a parent element for right to left direction.
921
+ * @param selection - The selection whose parent is to be tested.
922
+ * @returns true if the selections' parent element has a direction of 'rtl' (right to left), false otherwise.
923
+ */
924
+
685
925
  function $isParentElementRTL(selection) {
686
926
  const anchorNode = selection.anchor.getNode();
687
927
  const parent = lexical.$isRootNode(anchorNode) ? anchorNode : anchorNode.getParentOrThrow();
688
928
  return parent.getDirection() === 'rtl';
689
929
  }
930
+ /**
931
+ * Moves selection by character according to arguments.
932
+ * @param selection - The selection of the characters to move.
933
+ * @param isHoldingShift - Is the shift key being held down during the operation.
934
+ * @param isBackward - Is the selection backward (the focus comes before the anchor)?
935
+ */
936
+
690
937
  function $moveCharacter(selection, isHoldingShift, isBackward) {
691
938
  const isRTL = $isParentElementRTL(selection);
692
939
  $moveCaretSelection(selection, isHoldingShift, isBackward ? !isRTL : isRTL, 'character');
693
940
  }
941
+ /**
942
+ * Expands the current Selection to cover all of the content in the editor.
943
+ * @param selection - The current selection.
944
+ */
945
+
694
946
  function $selectAll(selection) {
695
947
  const anchor = selection.anchor;
696
948
  const focus = selection.focus;
@@ -702,30 +954,53 @@ function $selectAll(selection) {
702
954
  let firstType = 'element';
703
955
  let lastType = 'element';
704
956
  let lastOffset = 0;
957
+
705
958
  if (lexical.$isTextNode(firstNode)) {
706
959
  firstType = 'text';
707
960
  } else if (!lexical.$isElementNode(firstNode) && firstNode !== null) {
708
961
  firstNode = firstNode.getParentOrThrow();
709
962
  }
963
+
710
964
  if (lexical.$isTextNode(lastNode)) {
711
965
  lastType = 'text';
712
966
  lastOffset = lastNode.getTextContentSize();
713
967
  } else if (!lexical.$isElementNode(lastNode) && lastNode !== null) {
714
968
  lastNode = lastNode.getParentOrThrow();
715
969
  }
970
+
716
971
  if (firstNode && lastNode) {
717
972
  anchor.set(firstNode.getKey(), 0, firstType);
718
973
  focus.set(lastNode.getKey(), lastOffset, lastType);
719
974
  }
720
975
  }
976
+ /**
977
+ * Returns the current value of a CSS property for Nodes, if set. If not set, it returns the defaultValue.
978
+ * @param node - The node whose style value to get.
979
+ * @param styleProperty - The CSS style property.
980
+ * @param defaultValue - The default value for the property.
981
+ * @returns The value of the property for node.
982
+ */
983
+
721
984
  function $getNodeStyleValueForProperty(node, styleProperty, defaultValue) {
722
985
  const css = node.getStyle();
723
986
  const styleObject = getStyleObjectFromCSS(css);
987
+
724
988
  if (styleObject !== null) {
725
989
  return styleObject[styleProperty] || defaultValue;
726
990
  }
991
+
727
992
  return defaultValue;
728
993
  }
994
+ /**
995
+ * Returns the current value of a CSS property for TextNodes in the Selection, if set. If not set, it returns the defaultValue.
996
+ * If all TextNodes do not have the same value, it returns an empty string.
997
+ * @param selection - The selection of TextNodes whose value to find.
998
+ * @param styleProperty - The CSS style property.
999
+ * @param defaultValue - The default value for the property, defaults to an empty string.
1000
+ * @returns The value of the property for the selected TextNodes.
1001
+ */
1002
+
1003
+
729
1004
  function $getSelectionStyleValueForProperty(selection, styleProperty, defaultValue = '') {
730
1005
  let styleValue = null;
731
1006
  const nodes = selection.getNodes();
@@ -734,24 +1009,28 @@ function $getSelectionStyleValueForProperty(selection, styleProperty, defaultVal
734
1009
  const isBackward = selection.isBackward();
735
1010
  const endOffset = isBackward ? focus.offset : anchor.offset;
736
1011
  const endNode = isBackward ? focus.getNode() : anchor.getNode();
1012
+
737
1013
  if (selection.style !== '') {
738
1014
  const css = selection.style;
739
1015
  const styleObject = getStyleObjectFromCSS(css);
1016
+
740
1017
  if (styleObject !== null && styleProperty in styleObject) {
741
1018
  return styleObject[styleProperty];
742
1019
  }
743
1020
  }
744
- for (let i = 0; i < nodes.length; i++) {
745
- const node = nodes[i];
746
1021
 
747
- // if no actual characters in the end node are selected, we don't
1022
+ for (let i = 0; i < nodes.length; i++) {
1023
+ const node = nodes[i]; // if no actual characters in the end node are selected, we don't
748
1024
  // include it in the selection for purposes of determining style
749
1025
  // value
1026
+
750
1027
  if (i !== 0 && endOffset === 0 && node.is(endNode)) {
751
1028
  continue;
752
1029
  }
1030
+
753
1031
  if (lexical.$isTextNode(node)) {
754
1032
  const nodeStyleValue = $getNodeStyleValueForProperty(node, styleProperty, defaultValue);
1033
+
755
1034
  if (styleValue === null) {
756
1035
  styleValue = nodeStyleValue;
757
1036
  } else if (styleValue !== nodeStyleValue) {
@@ -762,6 +1041,7 @@ function $getSelectionStyleValueForProperty(selection, styleProperty, defaultVal
762
1041
  }
763
1042
  }
764
1043
  }
1044
+
765
1045
  return styleValue === null ? defaultValue : styleValue;
766
1046
  }
767
1047
 
@@ -774,7 +1054,7 @@ exports.$moveCaretSelection = $moveCaretSelection;
774
1054
  exports.$moveCharacter = $moveCharacter;
775
1055
  exports.$patchStyleText = $patchStyleText;
776
1056
  exports.$selectAll = $selectAll;
777
- exports.$setBlocksType_experimental = $setBlocksType_experimental;
1057
+ exports.$setBlocksType = $setBlocksType;
778
1058
  exports.$shouldOverrideDefaultCharacterSelection = $shouldOverrideDefaultCharacterSelection;
779
1059
  exports.$sliceSelectedTextNodeContent = $sliceSelectedTextNodeContent;
780
1060
  exports.$wrapNodes = $wrapNodes;
@@ -49,7 +49,7 @@ declare export function $wrapNodes(
49
49
  createElement: () => ElementNode,
50
50
  wrappingElement?: ElementNode,
51
51
  ): void;
52
- declare export function $setBlocksType_experimental(
52
+ declare export function $setBlocksType(
53
53
  selection: RangeSelection,
54
54
  createElement: () => ElementNode,
55
55
  ): void;
@@ -4,25 +4,25 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- 'use strict';var l=require("lexical");let u=new Map;function v(a){for(;null!=a;){if(a.nodeType===Node.TEXT_NODE)return a;a=a.firstChild}return null}function w(a){let b=a.parentNode;if(null==b)throw Error("Should never happen");return[b,Array.from(b.childNodes).indexOf(a)]}function y(a){let b={};a=a.split(";");for(let c of a)if(""!==c){let [f,d]=c.split(/:([^]+)/);b[f.trim()]=d.trim()}return b}function z(a){let b=u.get(a);void 0===b&&(b=y(a),u.set(a,b));return b}
8
- function A(a){let b="";for(let c in a)c&&(b+=`${c}: ${a[c]};`);return b}function B(a,b){var c=z("getStyle"in a?a.getStyle():a.style);b=Object.entries(b).reduce((f,[d,e])=>{null===e?delete f[d]:f[d]=e;return f},{...c});c=A(b);a.setStyle(c);u.set(c,b)}function C(a){for(;null!==a&&!l.$isRootOrShadowRoot(a);){let b=a.getLatest(),c=a.getParent();0===b.getChildrenSize()&&a.remove(!0);a=c}}
9
- function D(a,b,c,f,d=null){if(0!==b.length){var e=b[0],h=new Map,g=[];e=l.$isElementNode(e)?e:e.getParentOrThrow();e.isInline()&&(e=e.getParentOrThrow());for(var k=!1;null!==e;){var m=e.getPreviousSibling();if(null!==m){e=m;k=!0;break}e=e.getParentOrThrow();if(l.$isRootOrShadowRoot(e))break}m=new Set;for(var q=0;q<c;q++){var n=b[q];l.$isElementNode(n)&&0===n.getChildrenSize()&&m.add(n.getKey())}var p=new Set;for(q=0;q<c;q++){n=b[q];var r=n.getParent();null!==r&&r.isInline()&&(r=r.getParent());if(null!==
10
- r&&l.$isLeafNode(n)&&!p.has(n.getKey())){if(n=r.getKey(),void 0===h.get(n)){let t=f();t.setFormat(r.getFormatType());t.setIndent(r.getIndent());g.push(t);h.set(n,t);r.getChildren().forEach(x=>{t.append(x);p.add(x.getKey());l.$isElementNode(x)&&x.getChildrenKeys().forEach(G=>p.add(G))});C(r)}}else m.has(n.getKey())&&(r=f(),r.setFormat(n.getFormatType()),r.setIndent(n.getIndent()),g.push(r),n.remove(!0))}if(null!==d)for(b=0;b<g.length;b++)d.append(g[b]);b=null;if(l.$isRootOrShadowRoot(e))if(k)if(null!==
11
- d)e.insertAfter(d);else for(d=g.length-1;0<=d;d--)e.insertAfter(g[d]);else if(k=e.getFirstChild(),l.$isElementNode(k)&&(e=k),null===k)if(d)e.append(d);else for(d=0;d<g.length;d++)k=g[d],e.append(k),b=k;else if(null!==d)k.insertBefore(d);else for(e=0;e<g.length;e++)d=g[e],k.insertBefore(d),b=d;else if(d)e.insertAfter(d);else for(d=g.length-1;0<=d;d--)k=g[d],e.insertAfter(k),b=k;g=l.$getPreviousSelection();l.$isRangeSelection(g)&&g.anchor.getNode().isAttached()&&g.focus.getNode().isAttached()?l.$setSelection(g.clone()):
12
- null!==b?b.selectEnd():a.dirty=!0}}function E(a,b,c,f){a.modify(b?"extend":"move",c,f)}function F(a){a=a.anchor.getNode();return"rtl"===(l.$isRootNode(a)?a:a.getParentOrThrow()).getDirection()}exports.$addNodeStyle=function(a){a=a.getStyle();let b=y(a);u.set(a,b)};
13
- exports.$cloneWithProperties=function(a){a=a.getLatest();let b=a.constructor.clone(a);b.__parent=a.__parent;b.__next=a.__next;b.__prev=a.__prev;if(l.$isElementNode(a)&&l.$isElementNode(b))return b.__first=a.__first,b.__last=a.__last,b.__size=a.__size,b.__format=a.__format,b.__indent=a.__indent,b.__dir=a.__dir,b;l.$isTextNode(a)&&l.$isTextNode(b)&&(b.__format=a.__format,b.__style=a.__style,b.__mode=a.__mode,b.__detail=a.__detail);return b};
14
- exports.$getSelectionStyleValueForProperty=function(a,b,c=""){let f=null,d=a.getNodes();var e=a.anchor,h=a.focus,g=a.isBackward();let k=g?h.offset:e.offset;e=g?h.getNode():e.getNode();if(""!==a.style&&(a=z(a.style),null!==a&&b in a))return a[b];for(a=0;a<d.length;a++){var m=d[a];if((0===a||0!==k||!m.is(e))&&l.$isTextNode(m))if(h=b,g=c,m=m.getStyle(),m=z(m),h=null!==m?m[h]||g:g,null===f)f=h;else if(f!==h){f="";break}}return null===f?c:f};
15
- exports.$isAtNodeEnd=function(a){return"text"===a.type?a.offset===a.getNode().getTextContentSize():a.offset===a.getNode().getChildrenSize()};exports.$isParentElementRTL=F;exports.$moveCaretSelection=E;exports.$moveCharacter=function(a,b,c){let f=F(a);E(a,b,c?!f:f,"character")};
16
- exports.$patchStyleText=function(a,b){var c=a.getNodes();let f=c.length-1,d=c[0],e=c[f];if(a.isCollapsed())B(a,b);else{var h=a.anchor,g=a.focus,k=d.getTextContent().length,m=g.offset,q=h.offset,n=h.isBefore(g),p=n?q:m;a=n?m:q;var r=n?h.type:g.type,t=n?g.type:h.type;h=n?g.key:h.key;l.$isTextNode(d)&&p===k&&(g=d.getNextSibling(),l.$isTextNode(g)&&(p=q=0,d=g));if(d.is(e))l.$isTextNode(d)&&(p="element"===r?0:q>m?m:q,a="element"===t?k:q>m?q:m,p!==a&&(0===p&&a===k?(B(d,b),d.select(p,a)):(c=d.splitText(p,
17
- a),c=0===p?c[0]:c[1],B(c,b),c.select(0,a-p))));else for(l.$isTextNode(d)&&p<d.getTextContentSize()&&(0!==p&&(d=d.splitText(p)[1]),B(d,b)),l.$isTextNode(e)&&(p=e.getTextContent().length,e.__key!==h&&0!==a&&(a=p),a!==p&&([e]=e.splitText(a)),0!==a&&B(e,b)),a=1;a<f;a++)p=c[a],k=p.getKey(),l.$isTextNode(p)&&k!==d.getKey()&&k!==e.getKey()&&!p.isToken()&&B(p,b)}};
18
- exports.$selectAll=function(a){let b=a.anchor;a=a.focus;var c=b.getNode().getTopLevelElementOrThrow().getParentOrThrow();let f=c.getFirstDescendant();c=c.getLastDescendant();let d="element",e="element",h=0;l.$isTextNode(f)?d="text":l.$isElementNode(f)||null===f||(f=f.getParentOrThrow());l.$isTextNode(c)?(e="text",h=c.getTextContentSize()):l.$isElementNode(c)||null===c||(c=c.getParentOrThrow());f&&c&&(b.set(f.getKey(),0,d),a.set(c.getKey(),h,e))};
19
- exports.$setBlocksType_experimental=function(a,b){if("root"===a.anchor.key){b=b();var c=l.$getRoot();(a=c.getFirstChild())?a.replace(b,!0):c.append(b)}else for(c=a.getNodes(),"text"===a.anchor.type&&(a=a.anchor.getNode().getParent(),a=a.isInline()?a.getParent():a,-1===c.indexOf(a)&&c.push(a)),a=0;a<c.length;a++){let d=c[a];var f=d;!l.$isElementNode(f)||l.$isRootOrShadowRoot(f)||f.isInline()||(f=b(),f.setFormat(d.getFormatType()),f.setIndent(d.getIndent()),d.replace(f,!0))}};
20
- exports.$shouldOverrideDefaultCharacterSelection=function(a,b){a=l.$getAdjacentNode(a.focus,b);return l.$isDecoratorNode(a)&&!a.isIsolated()||l.$isElementNode(a)&&!a.isInline()&&!a.canBeEmpty()};
21
- exports.$sliceSelectedTextNodeContent=function(a,b){if(b.isSelected()&&!b.isSegmented()&&!b.isToken()&&(l.$isRangeSelection(a)||l.DEPRECATED_$isGridSelection(a))){var c=a.anchor.getNode(),f=a.focus.getNode(),d=b.is(c),e=b.is(f);if(d||e){d=a.isBackward();let [h,g]=a.getCharacterOffsets();a=c.is(f);e=b.is(d?f:c);f=b.is(d?c:f);c=0;let k=void 0;a?(c=h>g?g:h,k=h>g?h:g):e?(c=d?g:h,k=void 0):f&&(d=d?h:g,c=0,k=d);b.__text=b.__text.slice(c,k)}}return b};
22
- exports.$wrapNodes=function(a,b,c=null){var f=a.getNodes();let d=f.length;var e=a.anchor;if(0===d||1===d&&"element"===e.type&&0===e.getNode().getChildrenSize()){a="text"===e.type?e.getNode().getParentOrThrow():e.getNode();f=a.getChildren();let g=b();g.setFormat(a.getFormatType());g.setIndent(a.getIndent());f.forEach(k=>g.append(k));c&&(g=c.append(g));a.replace(g)}else{e=null;var h=[];for(let g=0;g<d;g++){let k=f[g];l.$isRootOrShadowRoot(k)?(D(a,h,h.length,b,c),h=[],e=k):null===e||null!==e&&l.$hasAncestor(k,
23
- e)?h.push(k):(D(a,h,h.length,b,c),h=[k])}D(a,h,h.length,b,c)}};
24
- exports.createDOMRange=function(a,b,c,f,d){let e=b.getKey(),h=f.getKey(),g=document.createRange(),k=a.getElementByKey(e);a=a.getElementByKey(h);l.$isTextNode(b)&&(k=v(k));l.$isTextNode(f)&&(a=v(a));if(void 0===b||void 0===f||null===k||null===a)return null;"BR"===k.nodeName&&([k,c]=w(k));"BR"===a.nodeName&&([a,d]=w(a));b=k.firstChild;k===a&&null!=b&&"BR"===b.nodeName&&0===c&&0===d&&(d=1);try{g.setStart(k,c),g.setEnd(a,d)}catch(m){return null}!g.collapsed||c===d&&e===h||(g.setStart(a,d),g.setEnd(k,
25
- c));return g};exports.createRectsFromDOMRange=function(a,b){var c=a.getRootElement();if(null===c)return[];a=c.getBoundingClientRect();c=getComputedStyle(c);c=parseFloat(c.paddingLeft)+parseFloat(c.paddingRight);b=Array.from(b.getClientRects());let f=b.length,d;for(let e=0;e<f;e++){let h=b[e],g=h.width+c===a.width;d&&d.top===h.top&&d.left===h.left&&d.width===h.width&&d.height===h.height||g?(b.splice(e--,1),f--):d=h}return b};exports.getStyleObjectFromCSS=z;
26
- exports.trimTextContentFromAnchor=function(a,b,c){let f=b.getNode();if(l.$isElementNode(f)){var d=f.getDescendantByIndex(b.offset);null!==d&&(f=d)}for(;0<c&&null!==f;){var e=f.getPreviousSibling(),h=0;if(null===e){d=f.getParentOrThrow();for(var g=d.getPreviousSibling();null===g;){d=d.getParent();if(null===d){e=null;break}g=d.getPreviousSibling()}null!==d&&(h=d.isInline()?0:2,e=l.$isElementNode(g)?g.getLastDescendant():g)}let k=f.getTextContent();""===k&&l.$isElementNode(f)&&!f.isInline()&&(k="\n\n");
27
- d=k.length;g=d-c;let m=k.slice(0,g);if(!l.$isTextNode(f)||c>=d)g=f.getParent(),f.remove(),null!=g&&0===g.getChildrenSize()&&g.remove(),c-=d+h,f=e;else{let q=f.getKey();e=a.getEditorState().read(()=>{const n=l.$getNodeByKey(q);return l.$isTextNode(n)&&n.isSimpleText()?n.getTextContent():null});null!==e&&e!==k?(c=l.$getPreviousSelection(),d=f,f.isSimpleText()?f.setTextContent(e):(d=l.$createTextNode(e),f.replace(d)),l.$isRangeSelection(c)&&c.isCollapsed()&&(c=c.anchor.offset,d.select(c,c))):f.isSimpleText()?
28
- (e=b.key===q,h=b.offset,h<c&&(h=d),c=e?h-c:0,d=e?h:g,e&&0===c?([c]=f.splitText(c,d),c.remove()):([,c]=f.splitText(c,d),c.remove())):(c=l.$createTextNode(m),f.replace(c));c=0}}}
7
+ 'use strict';var k=require("lexical");let u=new Map;function v(a){for(;null!=a;){if(a.nodeType===Node.TEXT_NODE)return a;a=a.firstChild}return null}function w(a){let b=a.parentNode;if(null==b)throw Error("Should never happen");return[b,Array.from(b.childNodes).indexOf(a)]}function y(a){let b={};a=a.split(";");for(let c of a)if(""!==c){let [e,d]=c.split(/:([^]+)/);b[e.trim()]=d.trim()}return b}function z(a){let b=u.get(a);void 0===b&&(b=y(a),u.set(a,b));return b}
8
+ function A(a){let b="";for(let c in a)c&&(b+=`${c}: ${a[c]};`);return b}function B(a,b){var c=z("getStyle"in a?a.getStyle():a.style);b=Object.entries(b).reduce((e,[d,f])=>{null===f?delete e[d]:e[d]=f;return e},{...c});c=A(b);a.setStyle(c);u.set(c,b)}function C(a){for(;null!==a&&!k.$isRootOrShadowRoot(a);){let b=a.getLatest(),c=a.getParent();0===b.getChildrenSize()&&a.remove(!0);a=c}}
9
+ function D(a,b,c,e,d=null){if(0!==b.length){var f=b[0],h=new Map,g=[];f=k.$isElementNode(f)?f:f.getParentOrThrow();f.isInline()&&(f=f.getParentOrThrow());for(var l=!1;null!==f;){var m=f.getPreviousSibling();if(null!==m){f=m;l=!0;break}f=f.getParentOrThrow();if(k.$isRootOrShadowRoot(f))break}m=new Set;for(var q=0;q<c;q++){var p=b[q];k.$isElementNode(p)&&0===p.getChildrenSize()&&m.add(p.getKey())}var n=new Set;for(q=0;q<c;q++){p=b[q];var r=p.getParent();null!==r&&r.isInline()&&(r=r.getParent());if(null!==
10
+ r&&k.$isLeafNode(p)&&!n.has(p.getKey())){if(p=r.getKey(),void 0===h.get(p)){let t=e();t.setFormat(r.getFormatType());t.setIndent(r.getIndent());g.push(t);h.set(p,t);r.getChildren().forEach(x=>{t.append(x);n.add(x.getKey());k.$isElementNode(x)&&x.getChildrenKeys().forEach(G=>n.add(G))});C(r)}}else m.has(p.getKey())&&(r=e(),r.setFormat(p.getFormatType()),r.setIndent(p.getIndent()),g.push(r),p.remove(!0))}if(null!==d)for(b=0;b<g.length;b++)d.append(g[b]);b=null;if(k.$isRootOrShadowRoot(f))if(l)if(null!==
11
+ d)f.insertAfter(d);else for(d=g.length-1;0<=d;d--)f.insertAfter(g[d]);else if(l=f.getFirstChild(),k.$isElementNode(l)&&(f=l),null===l)if(d)f.append(d);else for(d=0;d<g.length;d++)l=g[d],f.append(l),b=l;else if(null!==d)l.insertBefore(d);else for(f=0;f<g.length;f++)d=g[f],l.insertBefore(d),b=d;else if(d)f.insertAfter(d);else for(d=g.length-1;0<=d;d--)l=g[d],f.insertAfter(l),b=l;g=k.$getPreviousSelection();k.$isRangeSelection(g)&&g.anchor.getNode().isAttached()&&g.focus.getNode().isAttached()?k.$setSelection(g.clone()):
12
+ null!==b?b.selectEnd():a.dirty=!0}}function E(a,b,c,e){a.modify(b?"extend":"move",c,e)}function F(a){a=a.anchor.getNode();return"rtl"===(k.$isRootNode(a)?a:a.getParentOrThrow()).getDirection()}exports.$addNodeStyle=function(a){a=a.getStyle();let b=y(a);u.set(a,b)};
13
+ exports.$cloneWithProperties=function(a){a=a.getLatest();let b=a.constructor.clone(a);b.__parent=a.__parent;b.__next=a.__next;b.__prev=a.__prev;if(k.$isElementNode(a)&&k.$isElementNode(b))return b.__first=a.__first,b.__last=a.__last,b.__size=a.__size,b.__format=a.__format,b.__indent=a.__indent,b.__dir=a.__dir,b;k.$isTextNode(a)&&k.$isTextNode(b)&&(b.__format=a.__format,b.__style=a.__style,b.__mode=a.__mode,b.__detail=a.__detail);return b};
14
+ exports.$getSelectionStyleValueForProperty=function(a,b,c=""){let e=null,d=a.getNodes();var f=a.anchor,h=a.focus,g=a.isBackward();let l=g?h.offset:f.offset;f=g?h.getNode():f.getNode();if(""!==a.style&&(a=z(a.style),null!==a&&b in a))return a[b];for(a=0;a<d.length;a++){var m=d[a];if((0===a||0!==l||!m.is(f))&&k.$isTextNode(m))if(h=b,g=c,m=m.getStyle(),m=z(m),h=null!==m?m[h]||g:g,null===e)e=h;else if(e!==h){e="";break}}return null===e?c:e};
15
+ exports.$isAtNodeEnd=function(a){return"text"===a.type?a.offset===a.getNode().getTextContentSize():a.offset===a.getNode().getChildrenSize()};exports.$isParentElementRTL=F;exports.$moveCaretSelection=E;exports.$moveCharacter=function(a,b,c){let e=F(a);E(a,b,c?!e:e,"character")};
16
+ exports.$patchStyleText=function(a,b){var c=a.getNodes();let e=c.length-1,d=c[0],f=c[e];if(a.isCollapsed())B(a,b);else{var h=a.anchor,g=a.focus,l=d.getTextContent().length,m=g.offset,q=h.offset,p=h.isBefore(g),n=p?q:m;a=p?m:q;var r=p?h.type:g.type,t=p?g.type:h.type;h=p?g.key:h.key;k.$isTextNode(d)&&n===l&&(g=d.getNextSibling(),k.$isTextNode(g)&&(n=q=0,d=g));if(1===c.length)k.$isTextNode(d)&&(n="element"===r?0:q>m?m:q,a="element"===t?l:q>m?q:m,n!==a&&(0===n&&a===l?(B(d,b),d.select(n,a)):(c=d.splitText(n,
17
+ a),c=0===n?c[0]:c[1],B(c,b),c.select(0,a-n))));else for(k.$isTextNode(d)&&n<d.getTextContentSize()&&(0!==n&&(d=d.splitText(n)[1]),B(d,b)),k.$isTextNode(f)&&(n=f.getTextContent().length,f.__key!==h&&0!==a&&(a=n),a!==n&&([f]=f.splitText(a)),0!==a&&B(f,b)),a=1;a<e;a++)n=c[a],l=n.getKey(),k.$isTextNode(n)&&l!==d.getKey()&&l!==f.getKey()&&!n.isToken()&&B(n,b)}};
18
+ exports.$selectAll=function(a){let b=a.anchor;a=a.focus;var c=b.getNode().getTopLevelElementOrThrow().getParentOrThrow();let e=c.getFirstDescendant();c=c.getLastDescendant();let d="element",f="element",h=0;k.$isTextNode(e)?d="text":k.$isElementNode(e)||null===e||(e=e.getParentOrThrow());k.$isTextNode(c)?(f="text",h=c.getTextContentSize()):k.$isElementNode(c)||null===c||(c=c.getParentOrThrow());e&&c&&(b.set(e.getKey(),0,d),a.set(c.getKey(),h,f))};
19
+ exports.$setBlocksType=function(a,b){if("root"===a.anchor.key){b=b();var c=k.$getRoot();(a=c.getFirstChild())?a.replace(b,!0):c.append(b)}else for(c=a.getNodes(),a=a.anchor.getNode().getParentOrThrow(),-1===c.indexOf(a)&&c.push(a),a.isInline()&&(a=a.getParentOrThrow(),-1===c.indexOf(a)&&c.push(a)),a=0;a<c.length;a++){let f=c[a];var e=f;if(!k.$isElementNode(e)||k.$isRootOrShadowRoot(e))e=!1;else{var d=e.getFirstChild();d=null===d||k.$isTextNode(d)||d.isInline();e=!e.isInline()&&!1!==e.canBeEmpty()&&
20
+ d}e&&(e=b(),e.setFormat(f.getFormatType()),e.setIndent(f.getIndent()),f.replace(e,!0))}};exports.$shouldOverrideDefaultCharacterSelection=function(a,b){a=k.$getAdjacentNode(a.focus,b);return k.$isDecoratorNode(a)&&!a.isIsolated()||k.$isElementNode(a)&&!a.isInline()&&!a.canBeEmpty()};
21
+ exports.$sliceSelectedTextNodeContent=function(a,b){if(b.isSelected()&&!b.isSegmented()&&!b.isToken()&&(k.$isRangeSelection(a)||k.DEPRECATED_$isGridSelection(a))){var c=a.anchor.getNode(),e=a.focus.getNode(),d=b.is(c),f=b.is(e);if(d||f){d=a.isBackward();let [h,g]=a.getCharacterOffsets();a=c.is(e);f=b.is(d?e:c);e=b.is(d?c:e);c=0;let l=void 0;a?(c=h>g?g:h,l=h>g?h:g):f?(c=d?g:h,l=void 0):e&&(d=d?h:g,c=0,l=d);b.__text=b.__text.slice(c,l)}}return b};
22
+ exports.$wrapNodes=function(a,b,c=null){var e=a.getNodes();let d=e.length;var f=a.anchor;if(0===d||1===d&&"element"===f.type&&0===f.getNode().getChildrenSize()){a="text"===f.type?f.getNode().getParentOrThrow():f.getNode();e=a.getChildren();let g=b();g.setFormat(a.getFormatType());g.setIndent(a.getIndent());e.forEach(l=>g.append(l));c&&(g=c.append(g));a.replace(g)}else{f=null;var h=[];for(let g=0;g<d;g++){let l=e[g];k.$isRootOrShadowRoot(l)?(D(a,h,h.length,b,c),h=[],f=l):null===f||null!==f&&k.$hasAncestor(l,
23
+ f)?h.push(l):(D(a,h,h.length,b,c),h=[l])}D(a,h,h.length,b,c)}};
24
+ exports.createDOMRange=function(a,b,c,e,d){let f=b.getKey(),h=e.getKey(),g=document.createRange(),l=a.getElementByKey(f);a=a.getElementByKey(h);k.$isTextNode(b)&&(l=v(l));k.$isTextNode(e)&&(a=v(a));if(void 0===b||void 0===e||null===l||null===a)return null;"BR"===l.nodeName&&([l,c]=w(l));"BR"===a.nodeName&&([a,d]=w(a));b=l.firstChild;l===a&&null!=b&&"BR"===b.nodeName&&0===c&&0===d&&(d=1);try{g.setStart(l,c),g.setEnd(a,d)}catch(m){return null}!g.collapsed||c===d&&f===h||(g.setStart(a,d),g.setEnd(l,
25
+ c));return g};exports.createRectsFromDOMRange=function(a,b){var c=a.getRootElement();if(null===c)return[];a=c.getBoundingClientRect();c=getComputedStyle(c);c=parseFloat(c.paddingLeft)+parseFloat(c.paddingRight);b=Array.from(b.getClientRects());let e=b.length,d;for(let f=0;f<e;f++){let h=b[f],g=h.width+c===a.width;d&&d.top===h.top&&d.left===h.left&&d.width===h.width&&d.height===h.height||g?(b.splice(f--,1),e--):d=h}return b};exports.getStyleObjectFromCSS=z;
26
+ exports.trimTextContentFromAnchor=function(a,b,c){let e=b.getNode();if(k.$isElementNode(e)){var d=e.getDescendantByIndex(b.offset);null!==d&&(e=d)}for(;0<c&&null!==e;){var f=e.getPreviousSibling(),h=0;if(null===f){d=e.getParentOrThrow();for(var g=d.getPreviousSibling();null===g;){d=d.getParent();if(null===d){f=null;break}g=d.getPreviousSibling()}null!==d&&(h=d.isInline()?0:2,f=k.$isElementNode(g)?g.getLastDescendant():g)}g=e.getTextContent();""===g&&k.$isElementNode(e)&&!e.isInline()&&(g="\n\n");
27
+ d=e.getTextContentSize();if(!k.$isTextNode(e)||c>=d)g=e.getParent(),e.remove(),null==g||0!==g.getChildrenSize()||k.$isRootNode(g)||g.remove(),c-=d+h,e=f;else{let l=e.getKey();h=a.getEditorState().read(()=>{const q=k.$getNodeByKey(l);return k.$isTextNode(q)&&q.isSimpleText()?q.getTextContent():null});f=d-c;let m=g.slice(0,f);null!==h&&h!==g?(c=k.$getPreviousSelection(),d=e,e.isSimpleText()?e.setTextContent(h):(d=k.$createTextNode(h),e.replace(d)),k.$isRangeSelection(c)&&c.isCollapsed()&&(c=c.anchor.offset,
28
+ d.select(c,c))):e.isSimpleText()?(h=b.key===l,g=b.offset,g<c&&(g=d),c=h?g-c:0,d=h?g:f,h&&0===c?([c]=e.splitText(c,d),c.remove()):([,c]=e.splitText(c,d),c.remove())):(c=k.$createTextNode(m),e.replace(c));c=0}}}
package/README.md CHANGED
@@ -81,12 +81,12 @@ Expands the current Selection to cover all of the content in the editor.
81
81
  export function $selectAll(selection: RangeSelection): void;
82
82
  ```
83
83
 
84
- #### `$setBlocksType_experimental`
84
+ #### `$setBlocksType`
85
85
 
86
86
  Converts all nodes in the selection that are of one block type to another specified by parameter
87
87
 
88
88
  ```ts
89
- export function $setBlocksType_experimental(
89
+ export function $setBlocksType(
90
90
  selection: RangeSelection,
91
91
  createElement: () => ElementNode,
92
92
  ): void;
package/index.d.ts CHANGED
@@ -7,8 +7,8 @@
7
7
  *
8
8
  */
9
9
  import { $addNodeStyle, $cloneWithProperties, $isAtNodeEnd, $patchStyleText, $sliceSelectedTextNodeContent, trimTextContentFromAnchor } from './lexical-node';
10
- import { $getSelectionStyleValueForProperty, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $selectAll, $setBlocksType_experimental, $shouldOverrideDefaultCharacterSelection, $wrapNodes } from './range-selection';
10
+ import { $getSelectionStyleValueForProperty, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $selectAll, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $wrapNodes } from './range-selection';
11
11
  import { createDOMRange, createRectsFromDOMRange, getStyleObjectFromCSS } from './utils';
12
12
  export { $addNodeStyle, $cloneWithProperties, $isAtNodeEnd, $patchStyleText, $sliceSelectedTextNodeContent, trimTextContentFromAnchor, };
13
- export { $getSelectionStyleValueForProperty, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $selectAll, $setBlocksType_experimental, $shouldOverrideDefaultCharacterSelection, $wrapNodes, };
13
+ export { $getSelectionStyleValueForProperty, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $selectAll, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $wrapNodes, };
14
14
  export { createDOMRange, createRectsFromDOMRange, getStyleObjectFromCSS };
package/lexical-node.d.ts CHANGED
@@ -6,9 +6,45 @@
6
6
  *
7
7
  */
8
8
  import type { GridSelection, LexicalEditor, LexicalNode, NodeSelection, Point, RangeSelection, TextNode } from 'lexical';
9
+ /**
10
+ * Returns a copy of a node, but generates a new key for the copy.
11
+ * @param node - The node to be cloned.
12
+ * @returns The clone of the node.
13
+ */
9
14
  export declare function $cloneWithProperties<T extends LexicalNode>(node: T): T;
15
+ /**
16
+ * Generally used to append text content to HTML and JSON. Grabs the text content and "slices"
17
+ * it to be generated into the new TextNode.
18
+ * @param selection - The selection containing the node whose TextNode is to be edited.
19
+ * @param textNode - The TextNode to be edited.
20
+ * @returns The updated TextNode.
21
+ */
10
22
  export declare function $sliceSelectedTextNodeContent(selection: RangeSelection | GridSelection | NodeSelection, textNode: TextNode): LexicalNode;
23
+ /**
24
+ * Determines if the current selection is at the end of the node.
25
+ * @param point - The point of the selection to test.
26
+ * @returns true if the provided point offset is in the last possible position, false otherwise.
27
+ */
11
28
  export declare function $isAtNodeEnd(point: Point): boolean;
29
+ /**
30
+ * Trims text from a node in order to shorten it, eg. to enforce a text's max length. If it deletes text
31
+ * that is an ancestor of the anchor then it will leave 2 indents, otherwise, if no text content exists, it deletes
32
+ * the TextNode. It will move the focus to either the end of any left over text or beginning of a new TextNode.
33
+ * @param editor - The lexical editor.
34
+ * @param anchor - The anchor of the current selection, where the selection should be pointing.
35
+ * @param delCount - The amount of characters to delete. Useful as a dynamic variable eg. textContentSize - maxLength;
36
+ */
12
37
  export declare function trimTextContentFromAnchor(editor: LexicalEditor, anchor: Point, delCount: number): void;
38
+ /**
39
+ * Gets the TextNode's style object and adds the styles to the CSS.
40
+ * @param node - The TextNode to add styles to.
41
+ */
13
42
  export declare function $addNodeStyle(node: TextNode): void;
43
+ /**
44
+ * Applies the provided styles to the TextNodes in the provided Selection.
45
+ * Will update partially selected TextNodes by splitting the TextNode and applying
46
+ * the styles to the appropriate one.
47
+ * @param selection - The selected node(s) to update.
48
+ * @param patch - The patch to apply, which can include multiple styles. { CSSProperty: value }
49
+ */
14
50
  export declare function $patchStyleText(selection: RangeSelection, patch: Record<string, string | null>): void;
package/package.json CHANGED
@@ -9,10 +9,10 @@
9
9
  "selection"
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "0.8.1",
12
+ "version": "0.9.1",
13
13
  "main": "LexicalSelection.js",
14
14
  "peerDependencies": {
15
- "lexical": "0.8.1"
15
+ "lexical": "0.9.1"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
@@ -7,18 +7,68 @@
7
7
  */
8
8
  import type { ElementNode, GridSelection, LexicalNode, RangeSelection } from 'lexical';
9
9
  /**
10
- * Converts all nodes in the selection that are of one block type to another specified by parameter
11
- *
12
- * @param selection
13
- * @param createElement
14
- * @returns
10
+ * Converts all nodes in the selection that are of one block type to another.
11
+ * @param selection - The selected blocks to be converted.
12
+ * @param createElement - The function that creates the node. eg. $createParagraphNode.
13
+ */
14
+ export declare function $setBlocksType(selection: RangeSelection | GridSelection, createElement: () => ElementNode): void;
15
+ /**
16
+ * @deprecated
17
+ * Wraps all nodes in the selection into another node of the type returned by createElement.
18
+ * @param selection - The selection of nodes to be wrapped.
19
+ * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
20
+ * @param wrappingElement - An element to append the wrapped selection and its children to.
15
21
  */
16
- export declare function $setBlocksType_experimental(selection: RangeSelection | GridSelection, createElement: () => ElementNode): void;
17
22
  export declare function $wrapNodes(selection: RangeSelection | GridSelection, createElement: () => ElementNode, wrappingElement?: null | ElementNode): void;
23
+ /**
24
+ * Wraps each node into a new ElementNode.
25
+ * @param selection - The selection of nodes to wrap.
26
+ * @param nodes - An array of nodes, generally the descendants of the selection.
27
+ * @param nodesLength - The length of nodes.
28
+ * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
29
+ * @param wrappingElement - An element to wrap all the nodes into.
30
+ * @returns
31
+ */
18
32
  export declare function $wrapNodesImpl(selection: RangeSelection | GridSelection, nodes: LexicalNode[], nodesLength: number, createElement: () => ElementNode, wrappingElement?: null | ElementNode): void;
33
+ /**
34
+ * Determines if the default character selection should be overridden. Used with DecoratorNodes
35
+ * @param selection - The selection whose default character selection may need to be overridden.
36
+ * @param isBackward - Is the selection backwards (the focus comes before the anchor)?
37
+ * @returns true if it should be overridden, false if not.
38
+ */
19
39
  export declare function $shouldOverrideDefaultCharacterSelection(selection: RangeSelection, isBackward: boolean): boolean;
40
+ /**
41
+ * Moves the selection according to the arguments.
42
+ * @param selection - The selected text or nodes.
43
+ * @param isHoldingShift - Is the shift key being held down during the operation.
44
+ * @param isBackward - Is the selection selected backwards (the focus comes before the anchor)?
45
+ * @param granularity - The distance to adjust the current selection.
46
+ */
20
47
  export declare function $moveCaretSelection(selection: RangeSelection, isHoldingShift: boolean, isBackward: boolean, granularity: 'character' | 'word' | 'lineboundary'): void;
48
+ /**
49
+ * Tests a parent element for right to left direction.
50
+ * @param selection - The selection whose parent is to be tested.
51
+ * @returns true if the selections' parent element has a direction of 'rtl' (right to left), false otherwise.
52
+ */
21
53
  export declare function $isParentElementRTL(selection: RangeSelection): boolean;
54
+ /**
55
+ * Moves selection by character according to arguments.
56
+ * @param selection - The selection of the characters to move.
57
+ * @param isHoldingShift - Is the shift key being held down during the operation.
58
+ * @param isBackward - Is the selection backward (the focus comes before the anchor)?
59
+ */
22
60
  export declare function $moveCharacter(selection: RangeSelection, isHoldingShift: boolean, isBackward: boolean): void;
61
+ /**
62
+ * Expands the current Selection to cover all of the content in the editor.
63
+ * @param selection - The current selection.
64
+ */
23
65
  export declare function $selectAll(selection: RangeSelection): void;
66
+ /**
67
+ * Returns the current value of a CSS property for TextNodes in the Selection, if set. If not set, it returns the defaultValue.
68
+ * If all TextNodes do not have the same value, it returns an empty string.
69
+ * @param selection - The selection of TextNodes whose value to find.
70
+ * @param styleProperty - The CSS style property.
71
+ * @param defaultValue - The default value for the property, defaults to an empty string.
72
+ * @returns The value of the property for the selected TextNodes.
73
+ */
24
74
  export declare function $getSelectionStyleValueForProperty(selection: RangeSelection, styleProperty: string, defaultValue?: string): string;
package/utils.d.ts CHANGED
@@ -6,8 +6,38 @@
6
6
  *
7
7
  */
8
8
  import type { LexicalEditor, LexicalNode } from 'lexical';
9
+ /**
10
+ * Creates a selection range for the DOM.
11
+ * @param editor - The lexical editor.
12
+ * @param anchorNode - The anchor node of a selection.
13
+ * @param _anchorOffset - The amount of space offset from the anchor to the focus.
14
+ * @param focusNode - The current focus.
15
+ * @param _focusOffset - The amount of space offset from the focus to the anchor.
16
+ * @returns The range of selection for the DOM that was created.
17
+ */
9
18
  export declare function createDOMRange(editor: LexicalEditor, anchorNode: LexicalNode, _anchorOffset: number, focusNode: LexicalNode, _focusOffset: number): Range | null;
19
+ /**
20
+ * Creates DOMRects, generally used to help the editor find a specific location on the screen.
21
+ * @param editor - The lexical editor
22
+ * @param range - A fragment of a document that can contain nodes and parts of text nodes.
23
+ * @returns The selectionRects as an array.
24
+ */
10
25
  export declare function createRectsFromDOMRange(editor: LexicalEditor, range: Range): Array<ClientRect>;
26
+ /**
27
+ * Creates an object containing all the styles and their values provided in the CSS string.
28
+ * @param css - The CSS string of styles and their values.
29
+ * @returns The styleObject containing all the styles and their values.
30
+ */
11
31
  export declare function getStyleObjectFromRawCSS(css: string): Record<string, string>;
32
+ /**
33
+ * Given a CSS string, returns an object from the style cache.
34
+ * @param css - The CSS property as a string.
35
+ * @returns The value of the given CSS property.
36
+ */
12
37
  export declare function getStyleObjectFromCSS(css: string): Record<string, string>;
38
+ /**
39
+ * Gets the CSS styles from the style object.
40
+ * @param styles - The style object containing the styles to get.
41
+ * @returns A string containing the CSS styles and their values.
42
+ */
13
43
  export declare function getCSSFromStyleObject(styles: Record<string, string>): string;