@lexical/utils 0.12.1 → 0.12.3

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.
@@ -9,8 +9,299 @@
9
9
  var selection = require('@lexical/selection');
10
10
  var lexical = require('lexical');
11
11
 
12
- /** @module @lexical/utils */
12
+ /**
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ *
18
+ */
19
+
20
+ /**
21
+ * Returns a function that will execute all functions passed when called. It is generally used
22
+ * to register multiple lexical listeners and then tear them down with a single function call, such
23
+ * as React's useEffect hook.
24
+ * @example
25
+ * ```ts
26
+ * useEffect(() => {
27
+ * return mergeRegister(
28
+ * editor.registerCommand(...registerCommand1 logic),
29
+ * editor.registerCommand(...registerCommand2 logic),
30
+ * editor.registerCommand(...registerCommand3 logic)
31
+ * )
32
+ * }, [editor])
33
+ * ```
34
+ * In this case, useEffect is returning the function returned by mergeRegister as a cleanup
35
+ * function to be executed after either the useEffect runs again (due to one of its dependencies
36
+ * updating) or the component it resides in unmounts.
37
+ * Note the functions don't neccesarily need to be in an array as all arguements
38
+ * are considered to be the func argument and spread from there.
39
+ * @param func - An array of functions meant to be executed by the returned function.
40
+ * @returns the function which executes all the passed register command functions.
41
+ */
42
+ function mergeRegister(...func) {
43
+ return () => {
44
+ func.forEach(f => f());
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
50
+ *
51
+ * This source code is licensed under the MIT license found in the
52
+ * LICENSE file in the root directory of this source tree.
53
+ *
54
+ */
55
+
56
+ function px(value) {
57
+ return `${value}px`;
58
+ }
13
59
 
60
+ /**
61
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
62
+ *
63
+ * This source code is licensed under the MIT license found in the
64
+ * LICENSE file in the root directory of this source tree.
65
+ *
66
+ */
67
+ const mutationObserverConfig = {
68
+ attributes: true,
69
+ characterData: true,
70
+ childList: true,
71
+ subtree: true
72
+ };
73
+ function positionNodeOnRange(editor, range, onReposition) {
74
+ let rootDOMNode = null;
75
+ let parentDOMNode = null;
76
+ let observer = null;
77
+ let lastNodes = [];
78
+ const wrapperNode = document.createElement('div');
79
+ function position() {
80
+ if (!(rootDOMNode !== null)) {
81
+ throw Error(`Unexpected null rootDOMNode`);
82
+ }
83
+ if (!(parentDOMNode !== null)) {
84
+ throw Error(`Unexpected null parentDOMNode`);
85
+ }
86
+ const {
87
+ left: rootLeft,
88
+ top: rootTop
89
+ } = rootDOMNode.getBoundingClientRect();
90
+ const parentDOMNode_ = parentDOMNode;
91
+ const rects = selection.createRectsFromDOMRange(editor, range);
92
+ if (!wrapperNode.isConnected) {
93
+ parentDOMNode_.append(wrapperNode);
94
+ }
95
+ let hasRepositioned = false;
96
+ for (let i = 0; i < rects.length; i++) {
97
+ const rect = rects[i];
98
+ // Try to reuse the previously created Node when possible, no need to
99
+ // remove/create on the most common case reposition case
100
+ const rectNode = lastNodes[i] || document.createElement('div');
101
+ const rectNodeStyle = rectNode.style;
102
+ if (rectNodeStyle.position !== 'absolute') {
103
+ rectNodeStyle.position = 'absolute';
104
+ hasRepositioned = true;
105
+ }
106
+ const left = px(rect.left - rootLeft);
107
+ if (rectNodeStyle.left !== left) {
108
+ rectNodeStyle.left = left;
109
+ hasRepositioned = true;
110
+ }
111
+ const top = px(rect.top - rootTop);
112
+ if (rectNodeStyle.top !== top) {
113
+ rectNode.style.top = top;
114
+ hasRepositioned = true;
115
+ }
116
+ const width = px(rect.width);
117
+ if (rectNodeStyle.width !== width) {
118
+ rectNode.style.width = width;
119
+ hasRepositioned = true;
120
+ }
121
+ const height = px(rect.height);
122
+ if (rectNodeStyle.height !== height) {
123
+ rectNode.style.height = height;
124
+ hasRepositioned = true;
125
+ }
126
+ if (rectNode.parentNode !== wrapperNode) {
127
+ wrapperNode.append(rectNode);
128
+ hasRepositioned = true;
129
+ }
130
+ lastNodes[i] = rectNode;
131
+ }
132
+ while (lastNodes.length > rects.length) {
133
+ lastNodes.pop();
134
+ }
135
+ if (hasRepositioned) {
136
+ onReposition(lastNodes);
137
+ }
138
+ }
139
+ function stop() {
140
+ parentDOMNode = null;
141
+ rootDOMNode = null;
142
+ if (observer !== null) {
143
+ observer.disconnect();
144
+ }
145
+ observer = null;
146
+ wrapperNode.remove();
147
+ for (const node of lastNodes) {
148
+ node.remove();
149
+ }
150
+ lastNodes = [];
151
+ }
152
+ function restart() {
153
+ const currentRootDOMNode = editor.getRootElement();
154
+ if (currentRootDOMNode === null) {
155
+ return stop();
156
+ }
157
+ const currentParentDOMNode = currentRootDOMNode.parentElement;
158
+ if (!(currentParentDOMNode instanceof HTMLElement)) {
159
+ return stop();
160
+ }
161
+ stop();
162
+ rootDOMNode = currentRootDOMNode;
163
+ parentDOMNode = currentParentDOMNode;
164
+ observer = new MutationObserver(mutations => {
165
+ const nextRootDOMNode = editor.getRootElement();
166
+ const nextParentDOMNode = nextRootDOMNode && nextRootDOMNode.parentElement;
167
+ if (nextRootDOMNode !== rootDOMNode || nextParentDOMNode !== parentDOMNode) {
168
+ return restart();
169
+ }
170
+ for (const mutation of mutations) {
171
+ if (!wrapperNode.contains(mutation.target)) {
172
+ // TODO throttle
173
+ return position();
174
+ }
175
+ }
176
+ });
177
+ observer.observe(currentParentDOMNode, mutationObserverConfig);
178
+ position();
179
+ }
180
+ const removeRootListener = editor.registerRootListener(restart);
181
+ return () => {
182
+ removeRootListener();
183
+ stop();
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
189
+ *
190
+ * This source code is licensed under the MIT license found in the
191
+ * LICENSE file in the root directory of this source tree.
192
+ *
193
+ */
194
+ function markSelection(editor, onReposition) {
195
+ let previousAnchorNode = null;
196
+ let previousAnchorOffset = null;
197
+ let previousFocusNode = null;
198
+ let previousFocusOffset = null;
199
+ let removeRangeListener = () => {};
200
+ function compute(editorState) {
201
+ editorState.read(() => {
202
+ const selection = lexical.$getSelection();
203
+ if (!lexical.$isRangeSelection(selection)) {
204
+ // TODO
205
+ previousAnchorNode = null;
206
+ previousAnchorOffset = null;
207
+ previousFocusNode = null;
208
+ previousFocusOffset = null;
209
+ removeRangeListener();
210
+ removeRangeListener = () => {};
211
+ return;
212
+ }
213
+ const {
214
+ anchor,
215
+ focus
216
+ } = selection;
217
+ const currentAnchorNode = anchor.getNode();
218
+ const currentAnchorNodeKey = currentAnchorNode.getKey();
219
+ const currentAnchorOffset = anchor.offset;
220
+ const currentFocusNode = focus.getNode();
221
+ const currentFocusNodeKey = currentFocusNode.getKey();
222
+ const currentFocusOffset = focus.offset;
223
+ const currentAnchorNodeDOM = editor.getElementByKey(currentAnchorNodeKey);
224
+ const currentFocusNodeDOM = editor.getElementByKey(currentFocusNodeKey);
225
+ const differentAnchorDOM = previousAnchorNode === null || currentAnchorNodeDOM === null || currentAnchorOffset !== previousAnchorOffset || currentAnchorNodeKey !== previousAnchorNode.getKey() || currentAnchorNode !== previousAnchorNode && (!(previousAnchorNode instanceof lexical.TextNode) || currentAnchorNode.updateDOM(previousAnchorNode, currentAnchorNodeDOM, editor._config));
226
+ const differentFocusDOM = previousFocusNode === null || currentFocusNodeDOM === null || currentFocusOffset !== previousFocusOffset || currentFocusNodeKey !== previousFocusNode.getKey() || currentFocusNode !== previousFocusNode && (!(previousFocusNode instanceof lexical.TextNode) || currentFocusNode.updateDOM(previousFocusNode, currentFocusNodeDOM, editor._config));
227
+ if (differentAnchorDOM || differentFocusDOM) {
228
+ const anchorHTMLElement = editor.getElementByKey(anchor.getNode().getKey());
229
+ const focusHTMLElement = editor.getElementByKey(focus.getNode().getKey());
230
+ // TODO handle selection beyond the common TextNode
231
+ if (anchorHTMLElement !== null && focusHTMLElement !== null && anchorHTMLElement.tagName === 'SPAN' && focusHTMLElement.tagName === 'SPAN') {
232
+ const range = document.createRange();
233
+ let firstHTMLElement;
234
+ let firstOffset;
235
+ let lastHTMLElement;
236
+ let lastOffset;
237
+ if (focus.isBefore(anchor)) {
238
+ firstHTMLElement = focusHTMLElement;
239
+ firstOffset = focus.offset;
240
+ lastHTMLElement = anchorHTMLElement;
241
+ lastOffset = anchor.offset;
242
+ } else {
243
+ firstHTMLElement = anchorHTMLElement;
244
+ firstOffset = anchor.offset;
245
+ lastHTMLElement = focusHTMLElement;
246
+ lastOffset = focus.offset;
247
+ }
248
+ const firstTextNode = firstHTMLElement.firstChild;
249
+ if (!(firstTextNode !== null)) {
250
+ throw Error(`Expected text node to be first child of span`);
251
+ }
252
+ const lastTextNode = lastHTMLElement.firstChild;
253
+ if (!(lastTextNode !== null)) {
254
+ throw Error(`Expected text node to be first child of span`);
255
+ }
256
+ range.setStart(firstTextNode, firstOffset);
257
+ range.setEnd(lastTextNode, lastOffset);
258
+ removeRangeListener();
259
+ removeRangeListener = positionNodeOnRange(editor, range, domNodes => {
260
+ for (const domNode of domNodes) {
261
+ const domNodeStyle = domNode.style;
262
+ if (domNodeStyle.background !== 'Highlight') {
263
+ domNodeStyle.background = 'Highlight';
264
+ }
265
+ if (domNodeStyle.color !== 'HighlightText') {
266
+ domNodeStyle.color = 'HighlightText';
267
+ }
268
+ if (domNodeStyle.zIndex !== '-1') {
269
+ domNodeStyle.zIndex = '-1';
270
+ }
271
+ if (domNodeStyle.pointerEvents !== 'none') {
272
+ domNodeStyle.pointerEvents = 'none';
273
+ }
274
+ if (domNodeStyle.marginTop !== px(-1.5)) {
275
+ domNodeStyle.marginTop = px(-1.5);
276
+ }
277
+ if (domNodeStyle.paddingTop !== px(4)) {
278
+ domNodeStyle.paddingTop = px(4);
279
+ }
280
+ if (domNodeStyle.paddingBottom !== px(0)) {
281
+ domNodeStyle.paddingBottom = px(0);
282
+ }
283
+ }
284
+ if (onReposition !== undefined) {
285
+ onReposition(domNodes);
286
+ }
287
+ });
288
+ }
289
+ }
290
+ previousAnchorNode = currentAnchorNode;
291
+ previousAnchorOffset = currentAnchorOffset;
292
+ previousFocusNode = currentFocusNode;
293
+ previousFocusOffset = currentFocusOffset;
294
+ });
295
+ }
296
+ compute(editor.getEditorState());
297
+ return mergeRegister(editor.registerUpdateListener(({
298
+ editorState
299
+ }) => compute(editorState)), removeRangeListener, () => {
300
+ removeRangeListener();
301
+ });
302
+ }
303
+
304
+ /** @module @lexical/utils */
14
305
  /**
15
306
  * Takes an HTML element and adds the classNames passed within an array,
16
307
  * ignoring any non-string types. A space can be used to add multiple classes
@@ -27,6 +318,7 @@ function addClassNamesToElement(element, ...classNames) {
27
318
  }
28
319
  });
29
320
  }
321
+
30
322
  /**
31
323
  * Takes an HTML element and removes the classNames passed within an array,
32
324
  * ignoring any non-string types. A space can be used to remove multiple classes
@@ -35,7 +327,6 @@ function addClassNamesToElement(element, ...classNames) {
35
327
  * @param element - The element in which the classes are removed
36
328
  * @param classNames - An array defining the class names to remove from the element
37
329
  */
38
-
39
330
  function removeClassNamesFromElement(element, ...classNames) {
40
331
  classNames.forEach(className => {
41
332
  if (typeof className === 'string') {
@@ -43,6 +334,7 @@ function removeClassNamesFromElement(element, ...classNames) {
43
334
  }
44
335
  });
45
336
  }
337
+
46
338
  /**
47
339
  * Returns true if the file type matches the types passed within the acceptableMimeTypes array, false otherwise.
48
340
  * The types passed must be strings and are CASE-SENSITIVE.
@@ -51,16 +343,15 @@ function removeClassNamesFromElement(element, ...classNames) {
51
343
  * @param acceptableMimeTypes - An array of strings of types which the file is checked against.
52
344
  * @returns true if the file is an acceptable mime type, false otherwise.
53
345
  */
54
-
55
346
  function isMimeType(file, acceptableMimeTypes) {
56
347
  for (const acceptableType of acceptableMimeTypes) {
57
348
  if (file.type.startsWith(acceptableType)) {
58
349
  return true;
59
350
  }
60
351
  }
61
-
62
352
  return false;
63
353
  }
354
+
64
355
  /**
65
356
  * Lexical File Reader with:
66
357
  * 1. MIME type support
@@ -72,47 +363,40 @@ function isMimeType(file, acceptableMimeTypes) {
72
363
  * src: file.result,
73
364
  * }));
74
365
  */
75
-
76
366
  function mediaFileReader(files, acceptableMimeTypes) {
77
367
  const filesIterator = files[Symbol.iterator]();
78
368
  return new Promise((resolve, reject) => {
79
369
  const processed = [];
80
-
81
370
  const handleNextFile = () => {
82
371
  const {
83
372
  done,
84
373
  value: file
85
374
  } = filesIterator.next();
86
-
87
375
  if (done) {
88
376
  return resolve(processed);
89
377
  }
90
-
91
378
  const fileReader = new FileReader();
92
379
  fileReader.addEventListener('error', reject);
93
380
  fileReader.addEventListener('load', () => {
94
381
  const result = fileReader.result;
95
-
96
382
  if (typeof result === 'string') {
97
383
  processed.push({
98
384
  file,
99
385
  result
100
386
  });
101
387
  }
102
-
103
388
  handleNextFile();
104
389
  });
105
-
106
390
  if (isMimeType(file, acceptableMimeTypes)) {
107
391
  fileReader.readAsDataURL(file);
108
392
  } else {
109
393
  handleNextFile();
110
394
  }
111
395
  };
112
-
113
396
  handleNextFile();
114
397
  });
115
398
  }
399
+
116
400
  /**
117
401
  * "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end
118
402
  * before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a
@@ -123,30 +407,25 @@ function mediaFileReader(files, acceptableMimeTypes) {
123
407
  * @returns An array of objects of all the nodes found by the search, including their depth into the tree.
124
408
  * {depth: number, node: LexicalNode} It will always return at least 1 node (the ending node) so long as it exists
125
409
  */
126
-
127
410
  function $dfs(startingNode, endingNode) {
128
411
  const nodes = [];
129
412
  const start = (startingNode || lexical.$getRoot()).getLatest();
130
413
  const end = endingNode || (lexical.$isElementNode(start) ? start.getLastDescendant() : start);
131
414
  let node = start;
132
415
  let depth = $getDepth(node);
133
-
134
416
  while (node !== null && !node.is(end)) {
135
417
  nodes.push({
136
418
  depth,
137
419
  node
138
420
  });
139
-
140
421
  if (lexical.$isElementNode(node) && node.getChildrenSize() > 0) {
141
422
  node = node.getFirstChild();
142
423
  depth++;
143
424
  } else {
144
425
  // Find immediate sibling or nearest parent sibling
145
426
  let sibling = null;
146
-
147
427
  while (sibling === null && node !== null) {
148
428
  sibling = node.getNextSibling();
149
-
150
429
  if (sibling === null) {
151
430
  node = node.getParent();
152
431
  depth--;
@@ -156,27 +435,23 @@ function $dfs(startingNode, endingNode) {
156
435
  }
157
436
  }
158
437
  }
159
-
160
438
  if (node !== null && node.is(end)) {
161
439
  nodes.push({
162
440
  depth,
163
441
  node
164
442
  });
165
443
  }
166
-
167
444
  return nodes;
168
445
  }
169
-
170
446
  function $getDepth(node) {
171
447
  let innerNode = node;
172
448
  let depth = 0;
173
-
174
449
  while ((innerNode = innerNode.getParent()) !== null) {
175
450
  depth++;
176
451
  }
177
-
178
452
  return depth;
179
453
  }
454
+
180
455
  /**
181
456
  * Takes a node and traverses up its ancestors (toward the root node)
182
457
  * in order to find a specific type of node.
@@ -184,39 +459,31 @@ function $getDepth(node) {
184
459
  * @param klass - an instance of the type of node to look for.
185
460
  * @returns the node of type klass that was passed, or null if none exist.
186
461
  */
187
-
188
-
189
462
  function $getNearestNodeOfType(node, klass) {
190
463
  let parent = node;
191
-
192
464
  while (parent != null) {
193
465
  if (parent instanceof klass) {
194
466
  return parent;
195
467
  }
196
-
197
468
  parent = parent.getParent();
198
469
  }
199
-
200
470
  return null;
201
471
  }
472
+
202
473
  /**
203
474
  * Returns the element node of the nearest ancestor, otherwise throws an error.
204
475
  * @param startNode - The starting node of the search
205
476
  * @returns The ancestor node found
206
477
  */
207
-
208
478
  function $getNearestBlockElementAncestorOrThrow(startNode) {
209
479
  const blockNode = $findMatchingParent(startNode, node => lexical.$isElementNode(node) && !node.isInline());
210
-
211
480
  if (!lexical.$isElementNode(blockNode)) {
212
481
  {
213
482
  throw Error(`Expected node ${startNode.__key} to have closest block element node.`);
214
483
  }
215
484
  }
216
-
217
485
  return blockNode;
218
486
  }
219
-
220
487
  /**
221
488
  * Starts with a node and moves up the tree (toward the root node) to find a matching node based on
222
489
  * the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
@@ -227,45 +494,15 @@ function $getNearestBlockElementAncestorOrThrow(startNode) {
227
494
  */
228
495
  function $findMatchingParent(startingNode, findFn) {
229
496
  let curr = startingNode;
230
-
231
497
  while (curr !== lexical.$getRoot() && curr != null) {
232
498
  if (findFn(curr)) {
233
499
  return curr;
234
500
  }
235
-
236
501
  curr = curr.getParent();
237
502
  }
238
-
239
503
  return null;
240
504
  }
241
505
 
242
- /**
243
- * Returns a function that will execute all functions passed when called. It is generally used
244
- * to register multiple lexical listeners and then tear them down with a single function call, such
245
- * as React's useEffect hook.
246
- * @example
247
- * ```ts
248
- * useEffect(() => {
249
- * return mergeRegister(
250
- * editor.registerCommand(...registerCommand1 logic),
251
- * editor.registerCommand(...registerCommand2 logic),
252
- * editor.registerCommand(...registerCommand3 logic)
253
- * )
254
- * }, [editor])
255
- * ```
256
- * In this case, useEffect is returning the function returned by mergeRegister as a cleanup
257
- * function to be executed after either the useEffect runs again (due to one of its dependencies
258
- * updating) or the compenent it resides in unmounts.
259
- * Note the functions don't neccesarily need to be in an array as all arguements
260
- * are considered to be the func argument and spread from there.
261
- * @param func - An array of functions meant to be executed by the returned function.
262
- * @returns the function which executes all the passed register command functions.
263
- */
264
- function mergeRegister(...func) {
265
- return () => {
266
- func.forEach(f => f());
267
- };
268
- }
269
506
  /**
270
507
  * Attempts to resolve nested element nodes of the same type into a single node of that type.
271
508
  * It is generally used for marks/commenting
@@ -275,32 +512,25 @@ function mergeRegister(...func) {
275
512
  * @param handleOverlap - Handles any overlap between the node to extract and the targetNode
276
513
  * @returns The lexical editor
277
514
  */
278
-
279
515
  function registerNestedElementResolver(editor, targetNode, cloneNode, handleOverlap) {
280
516
  const $isTargetNode = node => {
281
517
  return node instanceof targetNode;
282
518
  };
283
-
284
519
  const $findMatch = node => {
285
520
  // First validate we don't have any children that are of the target,
286
521
  // as we need to handle them first.
287
522
  const children = node.getChildren();
288
-
289
523
  for (let i = 0; i < children.length; i++) {
290
524
  const child = children[i];
291
-
292
525
  if ($isTargetNode(child)) {
293
526
  return null;
294
527
  }
295
528
  }
296
-
297
529
  let parentNode = node;
298
530
  let childNode = node;
299
-
300
531
  while (parentNode !== null) {
301
532
  childNode = parentNode;
302
533
  parentNode = parentNode.getParent();
303
-
304
534
  if ($isTargetNode(parentNode)) {
305
535
  return {
306
536
  child: childNode,
@@ -308,73 +538,64 @@ function registerNestedElementResolver(editor, targetNode, cloneNode, handleOver
308
538
  };
309
539
  }
310
540
  }
311
-
312
541
  return null;
313
542
  };
314
-
315
543
  const elementNodeTransform = node => {
316
544
  const match = $findMatch(node);
317
-
318
545
  if (match !== null) {
319
546
  const {
320
547
  child,
321
548
  parent
322
- } = match; // Simple path, we can move child out and siblings into a new parent.
549
+ } = match;
550
+
551
+ // Simple path, we can move child out and siblings into a new parent.
323
552
 
324
553
  if (child.is(node)) {
325
554
  handleOverlap(parent, node);
326
555
  const nextSiblings = child.getNextSiblings();
327
556
  const nextSiblingsLength = nextSiblings.length;
328
557
  parent.insertAfter(child);
329
-
330
558
  if (nextSiblingsLength !== 0) {
331
559
  const newParent = cloneNode(parent);
332
560
  child.insertAfter(newParent);
333
-
334
561
  for (let i = 0; i < nextSiblingsLength; i++) {
335
562
  newParent.append(nextSiblings[i]);
336
563
  }
337
564
  }
338
-
339
565
  if (!parent.canBeEmpty() && parent.getChildrenSize() === 0) {
340
566
  parent.remove();
341
567
  }
342
568
  }
343
569
  }
344
570
  };
345
-
346
571
  return editor.registerNodeTransform(targetNode, elementNodeTransform);
347
572
  }
573
+
348
574
  /**
349
575
  * Clones the editor and marks it as dirty to be reconciled. If there was a selection,
350
576
  * it would be set back to its previous state, or null otherwise.
351
577
  * @param editor - The lexical editor
352
578
  * @param editorState - The editor's state
353
579
  */
354
-
355
580
  function $restoreEditorState(editor, editorState) {
356
581
  const FULL_RECONCILE = 2;
357
582
  const nodeMap = new Map();
358
583
  const activeEditorState = editor._pendingEditorState;
359
-
360
584
  for (const [key, node] of editorState._nodeMap) {
361
585
  const clone = selection.$cloneWithProperties(node);
362
-
363
586
  if (lexical.$isTextNode(clone)) {
364
587
  clone.__text = node.__text;
365
588
  }
366
-
367
589
  nodeMap.set(key, clone);
368
590
  }
369
-
370
591
  if (activeEditorState) {
371
592
  activeEditorState._nodeMap = nodeMap;
372
593
  }
373
-
374
594
  editor._dirtyType = FULL_RECONCILE;
375
595
  const selection$1 = editorState._selection;
376
596
  lexical.$setSelection(selection$1 === null ? null : selection$1.clone());
377
597
  }
598
+
378
599
  /**
379
600
  * If the selected insertion area is the root/shadow root node (see {@link lexical!$isRootOrShadowRoot}),
380
601
  * the node will be appended there, otherwise, it will be inserted before the insertion area.
@@ -383,35 +604,28 @@ function $restoreEditorState(editor, editorState) {
383
604
  * @param node - The node to be inserted
384
605
  * @returns The node after its insertion
385
606
  */
386
-
387
607
  function $insertNodeToNearestRoot(node) {
388
608
  const selection = lexical.$getSelection() || lexical.$getPreviousSelection();
389
-
390
609
  if (lexical.$isRangeSelection(selection)) {
391
610
  const {
392
611
  focus
393
612
  } = selection;
394
613
  const focusNode = focus.getNode();
395
614
  const focusOffset = focus.offset;
396
-
397
615
  if (lexical.$isRootOrShadowRoot(focusNode)) {
398
616
  const focusChild = focusNode.getChildAtIndex(focusOffset);
399
-
400
617
  if (focusChild == null) {
401
618
  focusNode.append(node);
402
619
  } else {
403
620
  focusChild.insertBefore(node);
404
621
  }
405
-
406
622
  node.selectNext();
407
623
  } else {
408
624
  let splitNode;
409
625
  let splitOffset;
410
-
411
626
  if (lexical.$isTextNode(focusNode)) {
412
627
  splitNode = focusNode.getParentOrThrow();
413
628
  splitOffset = focusNode.getIndexWithinParent();
414
-
415
629
  if (focusOffset > 0) {
416
630
  splitOffset += 1;
417
631
  focusNode.splitText(focusOffset);
@@ -420,7 +634,6 @@ function $insertNodeToNearestRoot(node) {
420
634
  splitNode = focusNode;
421
635
  splitOffset = focusOffset;
422
636
  }
423
-
424
637
  const [, rightTree] = lexical.$splitNode(splitNode, splitOffset);
425
638
  rightTree.insertBefore(node);
426
639
  rightTree.selectStart();
@@ -433,44 +646,27 @@ function $insertNodeToNearestRoot(node) {
433
646
  const root = lexical.$getRoot();
434
647
  root.append(node);
435
648
  }
436
-
437
649
  const paragraphNode = lexical.$createParagraphNode();
438
650
  node.insertAfter(paragraphNode);
439
651
  paragraphNode.select();
440
652
  }
441
-
442
653
  return node.getLatest();
443
654
  }
655
+
444
656
  /**
445
657
  * Wraps the node into another node created from a createElementNode function, eg. $createParagraphNode
446
658
  * @param node - Node to be wrapped.
447
- * @param createElementNode - Creates a new lexcial element to wrap the to-be-wrapped node and returns it.
448
- * @returns A new lexcial element with the previous node appended within (as a child, including its children).
659
+ * @param createElementNode - Creates a new lexical element to wrap the to-be-wrapped node and returns it.
660
+ * @returns A new lexical element with the previous node appended within (as a child, including its children).
449
661
  */
450
-
451
662
  function $wrapNodeInElement(node, createElementNode) {
452
663
  const elementNode = createElementNode();
453
664
  node.replace(elementNode);
454
665
  elementNode.append(node);
455
666
  return elementNode;
456
667
  }
457
- /**
458
- * @param x - The element being tested
459
- * @returns Returns true if x is an HTML anchor tag, false otherwise
460
- */
461
-
462
- function isHTMLAnchorElement(x) {
463
- return isHTMLElement(x) && x.tagName === 'A';
464
- }
465
- /**
466
- * @param x - The element being testing
467
- * @returns Returns true if x is an HTML element, false otherwise.
468
- */
469
668
 
470
- function isHTMLElement(x) {
471
- // @ts-ignore-next-line - strict check on nodeType here should filter out non-Element EventTarget implementors
472
- return x.nodeType === 1;
473
- } // eslint-disable-next-line @typescript-eslint/no-explicit-any
669
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
474
670
 
475
671
  /**
476
672
  * @param object = The instance of the type
@@ -480,6 +676,7 @@ function isHTMLElement(x) {
480
676
  function objectKlassEquals(object, objectClass) {
481
677
  return object !== null ? Object.getPrototypeOf(object).constructor.name === objectClass.name : false;
482
678
  }
679
+
483
680
  /**
484
681
  * Filter the nodes
485
682
  * @param nodes Array of nodes that needs to be filtered
@@ -489,15 +686,12 @@ function objectKlassEquals(object, objectClass) {
489
686
 
490
687
  function $filter(nodes, filterFn) {
491
688
  const result = [];
492
-
493
689
  for (let i = 0; i < nodes.length; i++) {
494
690
  const node = filterFn(nodes[i]);
495
-
496
691
  if (node !== null) {
497
692
  result.push(node);
498
693
  }
499
694
  }
500
-
501
695
  return result;
502
696
  }
503
697
  /**
@@ -505,10 +699,8 @@ function $filter(nodes, filterFn) {
505
699
  * @param parent A parent node
506
700
  * @param node Node that needs to be appended
507
701
  */
508
-
509
702
  function $insertFirst(parent, node) {
510
703
  const firstChild = parent.getFirstChild();
511
-
512
704
  if (firstChild !== null) {
513
705
  firstChild.insertBefore(node);
514
706
  } else {
@@ -516,22 +708,49 @@ function $insertFirst(parent, node) {
516
708
  }
517
709
  }
518
710
 
711
+ /**
712
+ * This function is for internal use of the library.
713
+ * Please do not use it as it may change in the future.
714
+ */
715
+ function INTERNAL_$isBlock(node) {
716
+ if (lexical.$isDecoratorNode(node) && !node.isInline()) {
717
+ return true;
718
+ }
719
+ if (!lexical.$isElementNode(node) || lexical.$isRootOrShadowRoot(node)) {
720
+ return false;
721
+ }
722
+ const firstChild = node.getFirstChild();
723
+ const isLeafElement = firstChild === null || lexical.$isLineBreakNode(firstChild) || lexical.$isTextNode(firstChild) || firstChild.isInline();
724
+ return !node.isInline() && node.canBeEmpty() !== false && isLeafElement;
725
+ }
726
+ function $getAncestor(node, predicate) {
727
+ let parent = node;
728
+ while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
729
+ parent = parent.getParentOrThrow();
730
+ }
731
+ return predicate(parent) ? parent : null;
732
+ }
733
+
519
734
  exports.$splitNode = lexical.$splitNode;
735
+ exports.isHTMLAnchorElement = lexical.isHTMLAnchorElement;
736
+ exports.isHTMLElement = lexical.isHTMLElement;
520
737
  exports.$dfs = $dfs;
521
738
  exports.$filter = $filter;
522
739
  exports.$findMatchingParent = $findMatchingParent;
740
+ exports.$getAncestor = $getAncestor;
523
741
  exports.$getNearestBlockElementAncestorOrThrow = $getNearestBlockElementAncestorOrThrow;
524
742
  exports.$getNearestNodeOfType = $getNearestNodeOfType;
525
743
  exports.$insertFirst = $insertFirst;
526
744
  exports.$insertNodeToNearestRoot = $insertNodeToNearestRoot;
527
745
  exports.$restoreEditorState = $restoreEditorState;
528
746
  exports.$wrapNodeInElement = $wrapNodeInElement;
747
+ exports.INTERNAL_$isBlock = INTERNAL_$isBlock;
529
748
  exports.addClassNamesToElement = addClassNamesToElement;
530
- exports.isHTMLAnchorElement = isHTMLAnchorElement;
531
- exports.isHTMLElement = isHTMLElement;
532
749
  exports.isMimeType = isMimeType;
750
+ exports.markSelection = markSelection;
533
751
  exports.mediaFileReader = mediaFileReader;
534
752
  exports.mergeRegister = mergeRegister;
535
753
  exports.objectKlassEquals = objectKlassEquals;
754
+ exports.positionNodeOnRange = positionNodeOnRange;
536
755
  exports.registerNestedElementResolver = registerNestedElementResolver;
537
756
  exports.removeClassNamesFromElement = removeClassNamesFromElement;
@@ -4,14 +4,21 @@
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 g=require("@lexical/selection"),n=require("lexical");function p(a){let b=new URLSearchParams;b.append("code",a);for(let c=1;c<arguments.length;c++)b.append("v",arguments[c]);throw Error(`Minified Lexical error #${a}; visit https://lexical.dev/docs/error?${b} for the full message or `+"use the non-minified dev environment for full errors and additional helpful warnings.");}function q(a,b){for(let c of b)if(a.type.startsWith(c))return!0;return!1}
8
- function t(a,b){for(;a!==n.$getRoot()&&null!=a;){if(b(a))return a;a=a.getParent()}return null}function u(a){return 1===a.nodeType}exports.$splitNode=n.$splitNode;
9
- exports.$dfs=function(a,b){let c=[];a=(a||n.$getRoot()).getLatest();b=b||(n.$isElementNode(a)?a.getLastDescendant():a);for(var e=a,d=0;null!==(e=e.getParent());)d++;for(e=d;null!==a&&!a.is(b);)if(c.push({depth:e,node:a}),n.$isElementNode(a)&&0<a.getChildrenSize())a=a.getFirstChild(),e++;else for(d=null;null===d&&null!==a;)d=a.getNextSibling(),null===d?(a=a.getParent(),e--):a=d;null!==a&&a.is(b)&&c.push({depth:e,node:a});return c};
10
- exports.$filter=function(a,b){let c=[];for(let e=0;e<a.length;e++){let d=b(a[e]);null!==d&&c.push(d)}return c};exports.$findMatchingParent=t;exports.$getNearestBlockElementAncestorOrThrow=function(a){let b=t(a,c=>n.$isElementNode(c)&&!c.isInline());n.$isElementNode(b)||p(4,a.__key);return b};exports.$getNearestNodeOfType=function(a,b){for(;null!=a;){if(a instanceof b)return a;a=a.getParent()}return null};exports.$insertFirst=function(a,b){let c=a.getFirstChild();null!==c?c.insertBefore(b):a.append(b)};
11
- exports.$insertNodeToNearestRoot=function(a){var b=n.$getSelection()||n.$getPreviousSelection();if(n.$isRangeSelection(b)){var {focus:c}=b;b=c.getNode();c=c.offset;if(n.$isRootOrShadowRoot(b))c=b.getChildAtIndex(c),null==c?b.append(a):c.insertBefore(a),a.selectNext();else{let e,d;n.$isTextNode(b)?(e=b.getParentOrThrow(),d=b.getIndexWithinParent(),0<c&&(d+=1,b.splitText(c))):(e=b,d=c);[,b]=n.$splitNode(e,d);b.insertBefore(a);b.selectStart()}}else n.$isNodeSelection(b)||n.DEPRECATED_$isGridSelection(b)?
12
- (b=b.getNodes(),b[b.length-1].getTopLevelElementOrThrow().insertAfter(a)):n.$getRoot().append(a),b=n.$createParagraphNode(),a.insertAfter(b),b.select();return a.getLatest()};exports.$restoreEditorState=function(a,b){let c=new Map,e=a._pendingEditorState;for(let [d,f]of b._nodeMap){let h=g.$cloneWithProperties(f);n.$isTextNode(h)&&(h.__text=f.__text);c.set(d,h)}e&&(e._nodeMap=c);a._dirtyType=2;a=b._selection;n.$setSelection(null===a?null:a.clone())};
13
- exports.$wrapNodeInElement=function(a,b){b=b();a.replace(b);b.append(a);return b};exports.addClassNamesToElement=function(a,...b){b.forEach(c=>{"string"===typeof c&&(c=c.split(" ").filter(e=>""!==e),a.classList.add(...c))})};exports.isHTMLAnchorElement=function(a){return u(a)&&"A"===a.tagName};exports.isHTMLElement=u;exports.isMimeType=q;
14
- exports.mediaFileReader=function(a,b){let c=a[Symbol.iterator]();return new Promise((e,d)=>{let f=[],h=()=>{const {done:m,value:k}=c.next();if(m)return e(f);const l=new FileReader;l.addEventListener("error",d);l.addEventListener("load",()=>{const r=l.result;"string"===typeof r&&f.push({file:k,result:r});h()});q(k,b)?l.readAsDataURL(k):h()};h()})};exports.mergeRegister=function(...a){return()=>{a.forEach(b=>b())}};
15
- exports.objectKlassEquals=function(a,b){return null!==a?Object.getPrototypeOf(a).constructor.name===b.name:!1};
16
- exports.registerNestedElementResolver=function(a,b,c,e){return a.registerNodeTransform(b,d=>{a:{var f=d.getChildren();for(var h=0;h<f.length;h++)if(f[h]instanceof b){f=null;break a}for(f=d;null!==f;)if(h=f,f=f.getParent(),f instanceof b){f={child:h,parent:f};break a}f=null}if(null!==f){const {child:m,parent:k}=f;if(m.is(d)){e(k,d);d=m.getNextSiblings();f=d.length;k.insertAfter(m);if(0!==f){h=c(k);m.insertAfter(h);for(let l=0;l<f;l++)h.append(d[l])}k.canBeEmpty()||0!==k.getChildrenSize()||k.remove()}}})};
7
+ 'use strict';var g=require("@lexical/selection"),B=require("lexical");function C(a){let b=new URLSearchParams;b.append("code",a);for(let c=1;c<arguments.length;c++)b.append("v",arguments[c]);throw Error(`Minified Lexical error #${a}; visit https://lexical.dev/docs/error?${b} for the full message or `+"use the non-minified dev environment for full errors and additional helpful warnings.");}function D(...a){return()=>{a.forEach(b=>b())}}let E={attributes:!0,characterData:!0,childList:!0,subtree:!0};
8
+ function F(a,b,c){function e(){if(null===h)throw Error("Unexpected null rootDOMNode");if(null===n)throw Error("Unexpected null parentDOMNode");let {left:p,top:z}=h.getBoundingClientRect();var q=n;let r=g.createRectsFromDOMRange(a,b);t.isConnected||q.append(t);q=!1;for(let x=0;x<r.length;x++){var w=r[x];let u=k[x]||document.createElement("div"),y=u.style;"absolute"!==y.position&&(y.position="absolute",q=!0);var l=`${w.left-p}px`;y.left!==l&&(y.left=l,q=!0);l=`${w.top-z}px`;y.top!==l&&(u.style.top=
9
+ l,q=!0);l=`${w.width}px`;y.width!==l&&(u.style.width=l,q=!0);w=`${w.height}px`;y.height!==w&&(u.style.height=w,q=!0);u.parentNode!==t&&(t.append(u),q=!0);k[x]=u}for(;k.length>r.length;)k.pop();q&&c(k)}function d(){h=n=null;null!==m&&m.disconnect();m=null;t.remove();for(let p of k)p.remove();k=[]}function f(){let p=a.getRootElement();if(null===p)return d();let z=p.parentElement;if(!(z instanceof HTMLElement))return d();d();h=p;n=z;m=new MutationObserver(q=>{let r=a.getRootElement(),w=r&&r.parentElement;
10
+ if(r!==h||w!==n)return f();for(let l of q)if(!t.contains(l.target))return e()});m.observe(z,E);e()}let h=null,n=null,m=null,k=[],t=document.createElement("div"),A=a.registerRootListener(f);return()=>{A();d()}}function G(a,b){for(let c of b)if(a.type.startsWith(c))return!0;return!1}function H(a,b){for(;a!==B.$getRoot()&&null!=a;){if(b(a))return a;a=a.getParent()}return null}exports.$splitNode=B.$splitNode;exports.isHTMLAnchorElement=B.isHTMLAnchorElement;exports.isHTMLElement=B.isHTMLElement;
11
+ exports.$dfs=function(a,b){let c=[];a=(a||B.$getRoot()).getLatest();b=b||(B.$isElementNode(a)?a.getLastDescendant():a);for(var e=a,d=0;null!==(e=e.getParent());)d++;for(e=d;null!==a&&!a.is(b);)if(c.push({depth:e,node:a}),B.$isElementNode(a)&&0<a.getChildrenSize())a=a.getFirstChild(),e++;else for(d=null;null===d&&null!==a;)d=a.getNextSibling(),null===d?(a=a.getParent(),e--):a=d;null!==a&&a.is(b)&&c.push({depth:e,node:a});return c};
12
+ exports.$filter=function(a,b){let c=[];for(let e=0;e<a.length;e++){let d=b(a[e]);null!==d&&c.push(d)}return c};exports.$findMatchingParent=H;exports.$getAncestor=function(a,b){for(;null!==a&&null!==a.getParent()&&!b(a);)a=a.getParentOrThrow();return b(a)?a:null};exports.$getNearestBlockElementAncestorOrThrow=function(a){let b=H(a,c=>B.$isElementNode(c)&&!c.isInline());B.$isElementNode(b)||C(4,a.__key);return b};
13
+ exports.$getNearestNodeOfType=function(a,b){for(;null!=a;){if(a instanceof b)return a;a=a.getParent()}return null};exports.$insertFirst=function(a,b){let c=a.getFirstChild();null!==c?c.insertBefore(b):a.append(b)};
14
+ exports.$insertNodeToNearestRoot=function(a){var b=B.$getSelection()||B.$getPreviousSelection();if(B.$isRangeSelection(b)){var {focus:c}=b;b=c.getNode();c=c.offset;if(B.$isRootOrShadowRoot(b))c=b.getChildAtIndex(c),null==c?b.append(a):c.insertBefore(a),a.selectNext();else{let e,d;B.$isTextNode(b)?(e=b.getParentOrThrow(),d=b.getIndexWithinParent(),0<c&&(d+=1,b.splitText(c))):(e=b,d=c);[,b]=B.$splitNode(e,d);b.insertBefore(a);b.selectStart()}}else B.$isNodeSelection(b)||B.DEPRECATED_$isGridSelection(b)?
15
+ (b=b.getNodes(),b[b.length-1].getTopLevelElementOrThrow().insertAfter(a)):B.$getRoot().append(a),b=B.$createParagraphNode(),a.insertAfter(b),b.select();return a.getLatest()};exports.$restoreEditorState=function(a,b){let c=new Map,e=a._pendingEditorState;for(let [d,f]of b._nodeMap){let h=g.$cloneWithProperties(f);B.$isTextNode(h)&&(h.__text=f.__text);c.set(d,h)}e&&(e._nodeMap=c);a._dirtyType=2;a=b._selection;B.$setSelection(null===a?null:a.clone())};
16
+ exports.$wrapNodeInElement=function(a,b){b=b();a.replace(b);b.append(a);return b};exports.INTERNAL_$isBlock=function(a){if(B.$isDecoratorNode(a)&&!a.isInline())return!0;if(!B.$isElementNode(a)||B.$isRootOrShadowRoot(a))return!1;var b=a.getFirstChild();b=null===b||B.$isLineBreakNode(b)||B.$isTextNode(b)||b.isInline();return!a.isInline()&&!1!==a.canBeEmpty()&&b};exports.addClassNamesToElement=function(a,...b){b.forEach(c=>{"string"===typeof c&&(c=c.split(" ").filter(e=>""!==e),a.classList.add(...c))})};
17
+ exports.isMimeType=G;
18
+ exports.markSelection=function(a,b){function c(m){m.read(()=>{var k=B.$getSelection();if(B.$isRangeSelection(k)){var {anchor:t,focus:A}=k;k=t.getNode();var p=k.getKey(),z=t.offset,q=A.getNode(),r=q.getKey(),w=A.offset,l=a.getElementByKey(p),x=a.getElementByKey(r);p=null===e||null===l||z!==d||p!==e.getKey()||k!==e&&(!(e instanceof B.TextNode)||k.updateDOM(e,l,a._config));r=null===f||null===x||w!==h||r!==f.getKey()||q!==f&&(!(f instanceof B.TextNode)||q.updateDOM(f,x,a._config));if(p||r){l=a.getElementByKey(t.getNode().getKey());
19
+ var u=a.getElementByKey(A.getNode().getKey());if(null!==l&&null!==u&&"SPAN"===l.tagName&&"SPAN"===u.tagName){r=document.createRange();A.isBefore(t)?(p=u,x=A.offset,u=l,l=t.offset):(p=l,x=t.offset,l=A.offset);p=p.firstChild;if(null===p)throw Error("Expected text node to be first child of span");u=u.firstChild;if(null===u)throw Error("Expected text node to be first child of span");r.setStart(p,x);r.setEnd(u,l);n();n=F(a,r,y=>{for(let I of y){let v=I.style;"Highlight"!==v.background&&(v.background="Highlight");
20
+ "HighlightText"!==v.color&&(v.color="HighlightText");"-1"!==v.zIndex&&(v.zIndex="-1");"none"!==v.pointerEvents&&(v.pointerEvents="none");"-1.5px"!==v.marginTop&&(v.marginTop="-1.5px");"4px"!==v.paddingTop&&(v.paddingTop="4px");"0px"!==v.paddingBottom&&(v.paddingBottom="0px")}void 0!==b&&b(y)})}}e=k;d=z;f=q;h=w}else h=f=d=e=null,n(),n=()=>{}})}let e=null,d=null,f=null,h=null,n=()=>{};c(a.getEditorState());return D(a.registerUpdateListener(({editorState:m})=>c(m)),n,()=>{n()})};
21
+ exports.mediaFileReader=function(a,b){let c=a[Symbol.iterator]();return new Promise((e,d)=>{let f=[],h=()=>{const {done:n,value:m}=c.next();if(n)return e(f);const k=new FileReader;k.addEventListener("error",d);k.addEventListener("load",()=>{const t=k.result;"string"===typeof t&&f.push({file:m,result:t});h()});G(m,b)?k.readAsDataURL(m):h()};h()})};exports.mergeRegister=D;exports.objectKlassEquals=function(a,b){return null!==a?Object.getPrototypeOf(a).constructor.name===b.name:!1};
22
+ exports.positionNodeOnRange=F;
23
+ exports.registerNestedElementResolver=function(a,b,c,e){return a.registerNodeTransform(b,d=>{a:{var f=d.getChildren();for(var h=0;h<f.length;h++)if(f[h]instanceof b){f=null;break a}for(f=d;null!==f;)if(h=f,f=f.getParent(),f instanceof b){f={child:h,parent:f};break a}f=null}if(null!==f){const {child:n,parent:m}=f;if(n.is(d)){e(m,d);d=n.getNextSiblings();f=d.length;m.insertAfter(n);if(0!==f){h=c(m);n.insertAfter(h);for(let k=0;k<f;k++)h.append(d[k])}m.canBeEmpty()||0!==m.getChildrenSize()||m.remove()}}})};
17
24
  exports.removeClassNamesFromElement=function(a,...b){b.forEach(c=>{"string"===typeof c&&a.classList.remove(...c.split(" "))})}
package/index.d.ts CHANGED
@@ -6,8 +6,11 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  */
9
- import { $splitNode, EditorState, ElementNode, Klass, LexicalEditor, LexicalNode } from 'lexical';
10
- export { $splitNode };
9
+ import { DecoratorNode, EditorState, ElementNode, Klass, LexicalEditor, LexicalNode } from 'lexical';
10
+ export { default as markSelection } from './markSelection';
11
+ export { default as mergeRegister } from './mergeRegister';
12
+ export { default as positionNodeOnRange } from './positionNodeOnRange';
13
+ export { $splitNode, isHTMLAnchorElement, isHTMLElement } from 'lexical';
11
14
  export type DFSNode = Readonly<{
12
15
  depth: number;
13
16
  node: LexicalNode;
@@ -90,30 +93,6 @@ export type DOMNodeToLexicalConversionMap = Record<string, DOMNodeToLexicalConve
90
93
  * @returns A parent node that matches the findFn parameters, or null if one wasn't found.
91
94
  */
92
95
  export declare function $findMatchingParent(startingNode: LexicalNode, findFn: (node: LexicalNode) => boolean): LexicalNode | null;
93
- type Func = () => void;
94
- /**
95
- * Returns a function that will execute all functions passed when called. It is generally used
96
- * to register multiple lexical listeners and then tear them down with a single function call, such
97
- * as React's useEffect hook.
98
- * @example
99
- * ```ts
100
- * useEffect(() => {
101
- * return mergeRegister(
102
- * editor.registerCommand(...registerCommand1 logic),
103
- * editor.registerCommand(...registerCommand2 logic),
104
- * editor.registerCommand(...registerCommand3 logic)
105
- * )
106
- * }, [editor])
107
- * ```
108
- * In this case, useEffect is returning the function returned by mergeRegister as a cleanup
109
- * function to be executed after either the useEffect runs again (due to one of its dependencies
110
- * updating) or the compenent it resides in unmounts.
111
- * Note the functions don't neccesarily need to be in an array as all arguements
112
- * are considered to be the func argument and spread from there.
113
- * @param func - An array of functions meant to be executed by the returned function.
114
- * @returns the function which executes all the passed register command functions.
115
- */
116
- export declare function mergeRegister(...func: Array<Func>): () => void;
117
96
  /**
118
97
  * Attempts to resolve nested element nodes of the same type into a single node of that type.
119
98
  * It is generally used for marks/commenting
@@ -143,20 +122,10 @@ export declare function $insertNodeToNearestRoot<T extends LexicalNode>(node: T)
143
122
  /**
144
123
  * Wraps the node into another node created from a createElementNode function, eg. $createParagraphNode
145
124
  * @param node - Node to be wrapped.
146
- * @param createElementNode - Creates a new lexcial element to wrap the to-be-wrapped node and returns it.
147
- * @returns A new lexcial element with the previous node appended within (as a child, including its children).
125
+ * @param createElementNode - Creates a new lexical element to wrap the to-be-wrapped node and returns it.
126
+ * @returns A new lexical element with the previous node appended within (as a child, including its children).
148
127
  */
149
128
  export declare function $wrapNodeInElement(node: LexicalNode, createElementNode: () => ElementNode): ElementNode;
150
- /**
151
- * @param x - The element being tested
152
- * @returns Returns true if x is an HTML anchor tag, false otherwise
153
- */
154
- export declare function isHTMLAnchorElement(x: Node): x is HTMLAnchorElement;
155
- /**
156
- * @param x - The element being testing
157
- * @returns Returns true if x is an HTML element, false otherwise.
158
- */
159
- export declare function isHTMLElement(x: Node | EventTarget): x is HTMLElement;
160
129
  type ObjectKlass<T> = new (...args: any[]) => T;
161
130
  /**
162
131
  * @param object = The instance of the type
@@ -177,3 +146,9 @@ export declare function $filter<T>(nodes: Array<LexicalNode>, filterFn: (node: L
177
146
  * @param node Node that needs to be appended
178
147
  */
179
148
  export declare function $insertFirst(parent: ElementNode, node: LexicalNode): void;
149
+ /**
150
+ * This function is for internal use of the library.
151
+ * Please do not use it as it may change in the future.
152
+ */
153
+ export declare function INTERNAL_$isBlock(node: LexicalNode): node is ElementNode | DecoratorNode<unknown>;
154
+ export declare function $getAncestor<NodeType extends LexicalNode = LexicalNode>(node: LexicalNode, predicate: (ancestor: LexicalNode) => ancestor is NodeType): NodeType | null;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import { type LexicalEditor } from 'lexical';
9
+ export default function markSelection(editor: LexicalEditor, onReposition?: (node: Array<HTMLElement>) => void): () => void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ type Func = () => void;
9
+ /**
10
+ * Returns a function that will execute all functions passed when called. It is generally used
11
+ * to register multiple lexical listeners and then tear them down with a single function call, such
12
+ * as React's useEffect hook.
13
+ * @example
14
+ * ```ts
15
+ * useEffect(() => {
16
+ * return mergeRegister(
17
+ * editor.registerCommand(...registerCommand1 logic),
18
+ * editor.registerCommand(...registerCommand2 logic),
19
+ * editor.registerCommand(...registerCommand3 logic)
20
+ * )
21
+ * }, [editor])
22
+ * ```
23
+ * In this case, useEffect is returning the function returned by mergeRegister as a cleanup
24
+ * function to be executed after either the useEffect runs again (due to one of its dependencies
25
+ * updating) or the component it resides in unmounts.
26
+ * Note the functions don't neccesarily need to be in an array as all arguements
27
+ * are considered to be the func argument and spread from there.
28
+ * @param func - An array of functions meant to be executed by the returned function.
29
+ * @returns the function which executes all the passed register command functions.
30
+ */
31
+ export default function mergeRegister(...func: Array<Func>): () => void;
32
+ export {};
package/package.json CHANGED
@@ -8,15 +8,15 @@
8
8
  "utils"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.12.1",
11
+ "version": "0.12.3",
12
12
  "main": "LexicalUtils.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.12.1"
14
+ "lexical": "0.12.3"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/list": "0.12.1",
18
- "@lexical/table": "0.12.1",
19
- "@lexical/selection": "0.12.1"
17
+ "@lexical/list": "0.12.3",
18
+ "@lexical/table": "0.12.3",
19
+ "@lexical/selection": "0.12.3"
20
20
  },
21
21
  "repository": {
22
22
  "type": "git",
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { LexicalEditor } from 'lexical';
9
+ export default function positionNodeOnRange(editor: LexicalEditor, range: Range, onReposition: (node: Array<HTMLElement>) => void): () => void;
package/px.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ export default function px(value: number): string;