@lexical/list 0.7.7 → 0.7.9

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.
Files changed (2) hide show
  1. package/LexicalList.dev.js +49 -262
  2. package/package.json +3 -3
@@ -19,94 +19,77 @@ var utils = require('@lexical/utils');
19
19
  function $getListDepth(listNode) {
20
20
  let depth = 1;
21
21
  let parent = listNode.getParent();
22
-
23
22
  while (parent != null) {
24
23
  if ($isListItemNode(parent)) {
25
24
  const parentList = parent.getParent();
26
-
27
25
  if ($isListNode(parentList)) {
28
26
  depth++;
29
27
  parent = parentList.getParent();
30
28
  continue;
31
29
  }
32
-
33
30
  {
34
31
  throw Error(`A ListItemNode must have a ListNode for a parent.`);
35
32
  }
36
33
  }
37
-
38
34
  return depth;
39
35
  }
40
-
41
36
  return depth;
42
37
  }
43
38
  function $getTopListNode(listItem) {
44
39
  let list = listItem.getParent();
45
-
46
40
  if (!$isListNode(list)) {
47
41
  {
48
42
  throw Error(`A ListItemNode must have a ListNode for a parent.`);
49
43
  }
50
44
  }
51
-
52
45
  let parent = list;
53
-
54
46
  while (parent !== null) {
55
47
  parent = parent.getParent();
56
-
57
48
  if ($isListNode(parent)) {
58
49
  list = parent;
59
50
  }
60
51
  }
61
-
62
52
  return list;
63
53
  }
64
54
 
55
+ // This should probably be $getAllChildrenOfType
65
56
  function $getAllListItems(node) {
66
57
  let listItemNodes = [];
67
58
  const listChildren = node.getChildren().filter($isListItemNode);
68
-
69
59
  for (let i = 0; i < listChildren.length; i++) {
70
60
  const listItemNode = listChildren[i];
71
61
  const firstChild = listItemNode.getFirstChild();
72
-
73
62
  if ($isListNode(firstChild)) {
74
63
  listItemNodes = listItemNodes.concat($getAllListItems(firstChild));
75
64
  } else {
76
65
  listItemNodes.push(listItemNode);
77
66
  }
78
67
  }
79
-
80
68
  return listItemNodes;
81
69
  }
82
70
  function isNestedListNode(node) {
83
71
  return $isListItemNode(node) && $isListNode(node.getFirstChild());
84
- } // TODO: rewrite with $findMatchingParent or *nodeOfType
72
+ }
85
73
 
74
+ // TODO: rewrite with $findMatchingParent or *nodeOfType
86
75
  function findNearestListItemNode(node) {
87
76
  let currentNode = node;
88
-
89
77
  while (currentNode !== null) {
90
78
  if ($isListItemNode(currentNode)) {
91
79
  return currentNode;
92
80
  }
93
-
94
81
  currentNode = currentNode.getParent();
95
82
  }
96
-
97
83
  return null;
98
84
  }
99
85
  function getUniqueListItemNodes(nodeList) {
100
86
  const keys = new Set();
101
-
102
87
  for (let i = 0; i < nodeList.length; i++) {
103
88
  const node = nodeList[i];
104
-
105
89
  if ($isListItemNode(node)) {
106
90
  keys.add(node);
107
91
  }
108
92
  }
109
-
110
93
  return Array.from(keys);
111
94
  }
112
95
  function $removeHighestEmptyListParent(sublist) {
@@ -117,17 +100,13 @@ function $removeHighestEmptyListParent(sublist) {
117
100
  // (e.g. is actually part of the list contents) and delete that, or delete
118
101
  // the root of the list (if no list nodes have siblings.)
119
102
  let emptyListPtr = sublist;
120
-
121
103
  while (emptyListPtr.getNextSibling() == null && emptyListPtr.getPreviousSibling() == null) {
122
104
  const parent = emptyListPtr.getParent();
123
-
124
105
  if (parent == null || !($isListItemNode(emptyListPtr) || $isListNode(emptyListPtr))) {
125
106
  break;
126
107
  }
127
-
128
108
  emptyListPtr = parent;
129
109
  }
130
-
131
110
  emptyListPtr.remove();
132
111
  }
133
112
  function wrapInListItem(node) {
@@ -142,15 +121,12 @@ function wrapInListItem(node) {
142
121
  * LICENSE file in the root directory of this source tree.
143
122
  *
144
123
  */
145
-
146
124
  function $isSelectingEmptyListItem(anchorNode, nodes) {
147
125
  return $isListItemNode(anchorNode) && (nodes.length === 0 || nodes.length === 1 && anchorNode.is(nodes[0]) && anchorNode.getChildrenSize() === 0);
148
126
  }
149
-
150
127
  function $getListItemValue(listItem) {
151
128
  const list = listItem.getParent();
152
129
  let value = 1;
153
-
154
130
  if (list != null) {
155
131
  if (!$isListNode(list)) {
156
132
  {
@@ -160,67 +136,51 @@ function $getListItemValue(listItem) {
160
136
  value = list.getStart();
161
137
  }
162
138
  }
163
-
164
139
  const siblings = listItem.getPreviousSiblings();
165
-
166
140
  for (let i = 0; i < siblings.length; i++) {
167
141
  const sibling = siblings[i];
168
-
169
142
  if ($isListItemNode(sibling) && !$isListNode(sibling.getFirstChild())) {
170
143
  value++;
171
144
  }
172
145
  }
173
-
174
146
  return value;
175
147
  }
176
-
177
148
  function insertList(editor, listType) {
178
149
  editor.update(() => {
179
150
  const selection = lexical.$getSelection();
180
-
181
151
  if (lexical.$isRangeSelection(selection) || lexical.DEPRECATED_$isGridSelection(selection)) {
182
152
  const nodes = selection.getNodes();
183
153
  const anchor = selection.anchor;
184
154
  const anchorNode = anchor.getNode();
185
155
  const anchorNodeParent = anchorNode.getParent();
186
-
187
156
  if ($isSelectingEmptyListItem(anchorNode, nodes)) {
188
157
  const list = $createListNode(listType);
189
-
190
158
  if (lexical.$isRootOrShadowRoot(anchorNodeParent)) {
191
159
  anchorNode.replace(list);
192
160
  const listItem = $createListItemNode();
193
-
194
161
  if (lexical.$isElementNode(anchorNode)) {
195
162
  listItem.setFormat(anchorNode.getFormatType());
196
163
  listItem.setIndent(anchorNode.getIndent());
197
164
  }
198
-
199
165
  list.append(listItem);
200
166
  } else if ($isListItemNode(anchorNode)) {
201
167
  const parent = anchorNode.getParentOrThrow();
202
168
  append(list, parent.getChildren());
203
169
  parent.replace(list);
204
170
  }
205
-
206
171
  return;
207
172
  } else {
208
173
  const handled = new Set();
209
-
210
174
  for (let i = 0; i < nodes.length; i++) {
211
175
  const node = nodes[i];
212
-
213
176
  if (lexical.$isElementNode(node) && node.isEmpty() && !handled.has(node.getKey())) {
214
177
  createListOrMerge(node, listType);
215
178
  continue;
216
179
  }
217
-
218
180
  if (lexical.$isLeafNode(node)) {
219
181
  let parent = node.getParent();
220
-
221
182
  while (parent != null) {
222
183
  const parentKey = parent.getKey();
223
-
224
184
  if ($isListNode(parent)) {
225
185
  if (!handled.has(parentKey)) {
226
186
  const newListNode = $createListNode(listType);
@@ -229,17 +189,14 @@ function insertList(editor, listType) {
229
189
  updateChildrenListItemValue(newListNode);
230
190
  handled.add(parentKey);
231
191
  }
232
-
233
192
  break;
234
193
  } else {
235
194
  const nextParent = parent.getParent();
236
-
237
195
  if (lexical.$isRootOrShadowRoot(nextParent) && !handled.has(parentKey)) {
238
196
  handled.add(parentKey);
239
197
  createListOrMerge(parent, listType);
240
198
  break;
241
199
  }
242
-
243
200
  parent = nextParent;
244
201
  }
245
202
  }
@@ -249,32 +206,28 @@ function insertList(editor, listType) {
249
206
  }
250
207
  });
251
208
  }
252
-
253
209
  function append(node, nodesToAppend) {
254
210
  node.splice(node.getChildrenSize(), 0, nodesToAppend);
255
211
  }
256
-
257
212
  function createListOrMerge(node, listType) {
258
213
  if ($isListNode(node)) {
259
214
  return node;
260
215
  }
261
-
262
216
  const previousSibling = node.getPreviousSibling();
263
217
  const nextSibling = node.getNextSibling();
264
218
  const listItem = $createListItemNode();
265
219
  listItem.setFormat(node.getFormatType());
266
220
  listItem.setIndent(node.getIndent());
267
221
  append(listItem, node.getChildren());
268
-
269
222
  if ($isListNode(previousSibling) && listType === previousSibling.getListType()) {
270
223
  previousSibling.append(listItem);
271
- node.remove(); // if the same type of list is on both sides, merge them.
224
+ node.remove();
225
+ // if the same type of list is on both sides, merge them.
272
226
 
273
227
  if ($isListNode(nextSibling) && listType === nextSibling.getListType()) {
274
228
  append(previousSibling, nextSibling.getChildren());
275
229
  nextSibling.remove();
276
230
  }
277
-
278
231
  return previousSibling;
279
232
  } else if ($isListNode(nextSibling) && listType === nextSibling.getListType()) {
280
233
  nextSibling.getFirstChildOrThrow().insertBefore(listItem);
@@ -288,58 +241,49 @@ function createListOrMerge(node, listType) {
288
241
  return list;
289
242
  }
290
243
  }
291
-
292
244
  function removeList(editor) {
293
245
  editor.update(() => {
294
246
  const selection = lexical.$getSelection();
295
-
296
247
  if (lexical.$isRangeSelection(selection)) {
297
248
  const listNodes = new Set();
298
249
  const nodes = selection.getNodes();
299
250
  const anchorNode = selection.anchor.getNode();
300
-
301
251
  if ($isSelectingEmptyListItem(anchorNode, nodes)) {
302
252
  listNodes.add($getTopListNode(anchorNode));
303
253
  } else {
304
254
  for (let i = 0; i < nodes.length; i++) {
305
255
  const node = nodes[i];
306
-
307
256
  if (lexical.$isLeafNode(node)) {
308
257
  const listItemNode = utils.$getNearestNodeOfType(node, ListItemNode);
309
-
310
258
  if (listItemNode != null) {
311
259
  listNodes.add($getTopListNode(listItemNode));
312
260
  }
313
261
  }
314
262
  }
315
263
  }
316
-
317
264
  for (const listNode of listNodes) {
318
265
  let insertionPoint = listNode;
319
266
  const listItems = $getAllListItems(listNode);
320
-
321
267
  for (const listItemNode of listItems) {
322
268
  const paragraph = lexical.$createParagraphNode();
323
269
  append(paragraph, listItemNode.getChildren());
324
270
  insertionPoint.insertAfter(paragraph);
325
- insertionPoint = paragraph; // When the anchor and focus fall on the textNode
271
+ insertionPoint = paragraph;
272
+
273
+ // When the anchor and focus fall on the textNode
326
274
  // we don't have to change the selection because the textNode will be appended to
327
275
  // the newly generated paragraph.
328
276
  // When selection is in empty nested list item, selection is actually on the listItemNode.
329
277
  // When the corresponding listItemNode is deleted and replaced by the newly generated paragraph
330
278
  // we should manually set the selection's focus and anchor to the newly generated paragraph.
331
-
332
279
  if (listItemNode.__key === selection.anchor.key) {
333
280
  selection.anchor.set(paragraph.getKey(), 0, 'element');
334
281
  }
335
-
336
282
  if (listItemNode.__key === selection.focus.key) {
337
283
  selection.focus.set(paragraph.getKey(), 0, 'element');
338
284
  }
339
-
340
285
  listItemNode.remove();
341
286
  }
342
-
343
287
  listNode.remove();
344
288
  }
345
289
  }
@@ -347,15 +291,12 @@ function removeList(editor) {
347
291
  }
348
292
  function updateChildrenListItemValue(list, children) {
349
293
  const childrenOrExisting = children || list.getChildren();
350
-
351
294
  if (childrenOrExisting !== undefined) {
352
295
  for (let i = 0; i < childrenOrExisting.length; i++) {
353
296
  const child = childrenOrExisting[i];
354
-
355
297
  if ($isListItemNode(child)) {
356
298
  const prevValue = child.getValue();
357
299
  const nextValue = $getListItemValue(child);
358
-
359
300
  if (prevValue !== nextValue) {
360
301
  child.setValue(nextValue);
361
302
  }
@@ -370,56 +311,50 @@ function $handleIndent(listItemNodes) {
370
311
  if (isNestedListNode(listItemNode) || removed.has(listItemNode.getKey())) {
371
312
  return;
372
313
  }
314
+ const parent = listItemNode.getParent();
373
315
 
374
- const parent = listItemNode.getParent(); // We can cast both of the below `isNestedListNode` only returns a boolean type instead of a user-defined type guards
375
-
316
+ // We can cast both of the below `isNestedListNode` only returns a boolean type instead of a user-defined type guards
376
317
  const nextSibling = listItemNode.getNextSibling();
377
- const previousSibling = listItemNode.getPreviousSibling(); // if there are nested lists on either side, merge them all together.
318
+ const previousSibling = listItemNode.getPreviousSibling();
319
+ // if there are nested lists on either side, merge them all together.
378
320
 
379
321
  if (isNestedListNode(nextSibling) && isNestedListNode(previousSibling)) {
380
322
  const innerList = previousSibling.getFirstChild();
381
-
382
323
  if ($isListNode(innerList)) {
383
324
  innerList.append(listItemNode);
384
325
  const nextInnerList = nextSibling.getFirstChild();
385
-
386
326
  if ($isListNode(nextInnerList)) {
387
327
  const children = nextInnerList.getChildren();
388
328
  append(innerList, children);
389
329
  nextSibling.remove();
390
330
  removed.add(nextSibling.getKey());
391
331
  }
392
-
393
332
  updateChildrenListItemValue(innerList);
394
333
  }
395
334
  } else if (isNestedListNode(nextSibling)) {
396
335
  // if the ListItemNode is next to a nested ListNode, merge them
397
336
  const innerList = nextSibling.getFirstChild();
398
-
399
337
  if ($isListNode(innerList)) {
400
338
  const firstChild = innerList.getFirstChild();
401
-
402
339
  if (firstChild !== null) {
403
340
  firstChild.insertBefore(listItemNode);
404
341
  }
405
-
406
342
  updateChildrenListItemValue(innerList);
407
343
  }
408
344
  } else if (isNestedListNode(previousSibling)) {
409
345
  const innerList = previousSibling.getFirstChild();
410
-
411
346
  if ($isListNode(innerList)) {
412
347
  innerList.append(listItemNode);
413
348
  updateChildrenListItemValue(innerList);
414
349
  }
415
350
  } else {
416
351
  // otherwise, we need to create a new nested ListNode
352
+
417
353
  if ($isListNode(parent)) {
418
354
  const newListItem = $createListItemNode();
419
355
  const newList = $createListNode(parent.getListType());
420
356
  newListItem.append(newList);
421
357
  newList.append(listItemNode);
422
-
423
358
  if (previousSibling) {
424
359
  previousSibling.insertAfter(newListItem);
425
360
  } else if (nextSibling) {
@@ -429,7 +364,6 @@ function $handleIndent(listItemNodes) {
429
364
  }
430
365
  }
431
366
  }
432
-
433
367
  if ($isListNode(parent)) {
434
368
  updateChildrenListItemValue(parent);
435
369
  }
@@ -437,32 +371,30 @@ function $handleIndent(listItemNodes) {
437
371
  }
438
372
  function $handleOutdent(listItemNodes) {
439
373
  // go through each node and decide where to move it.
374
+
440
375
  listItemNodes.forEach(listItemNode => {
441
376
  if (isNestedListNode(listItemNode)) {
442
377
  return;
443
378
  }
444
-
445
379
  const parentList = listItemNode.getParent();
446
380
  const grandparentListItem = parentList ? parentList.getParent() : undefined;
447
- const greatGrandparentList = grandparentListItem ? grandparentListItem.getParent() : undefined; // If it doesn't have these ancestors, it's not indented.
381
+ const greatGrandparentList = grandparentListItem ? grandparentListItem.getParent() : undefined;
382
+ // If it doesn't have these ancestors, it's not indented.
448
383
 
449
384
  if ($isListNode(greatGrandparentList) && $isListItemNode(grandparentListItem) && $isListNode(parentList)) {
450
385
  // if it's the first child in it's parent list, insert it into the
451
386
  // great grandparent list before the grandparent
452
387
  const firstChild = parentList ? parentList.getFirstChild() : undefined;
453
388
  const lastChild = parentList ? parentList.getLastChild() : undefined;
454
-
455
389
  if (listItemNode.is(firstChild)) {
456
390
  grandparentListItem.insertBefore(listItemNode);
457
-
458
391
  if (parentList.isEmpty()) {
459
392
  grandparentListItem.remove();
460
- } // if it's the last child in it's parent list, insert it into the
393
+ }
394
+ // if it's the last child in it's parent list, insert it into the
461
395
  // great grandparent list after the grandparent.
462
-
463
396
  } else if (listItemNode.is(lastChild)) {
464
397
  grandparentListItem.insertAfter(listItemNode);
465
-
466
398
  if (parentList.isEmpty()) {
467
399
  grandparentListItem.remove();
468
400
  }
@@ -476,46 +408,38 @@ function $handleOutdent(listItemNodes) {
476
408
  const nextSiblingsListItem = $createListItemNode();
477
409
  const nextSiblingsList = $createListNode(listType);
478
410
  nextSiblingsListItem.append(nextSiblingsList);
479
- append(nextSiblingsList, listItemNode.getNextSiblings()); // put the sibling nested lists on either side of the grandparent list item in the great grandparent.
480
-
411
+ append(nextSiblingsList, listItemNode.getNextSiblings());
412
+ // put the sibling nested lists on either side of the grandparent list item in the great grandparent.
481
413
  grandparentListItem.insertBefore(previousSiblingsListItem);
482
- grandparentListItem.insertAfter(nextSiblingsListItem); // replace the grandparent list item (now between the siblings) with the outdented list item.
483
-
414
+ grandparentListItem.insertAfter(nextSiblingsListItem);
415
+ // replace the grandparent list item (now between the siblings) with the outdented list item.
484
416
  grandparentListItem.replace(listItemNode);
485
417
  }
486
-
487
418
  updateChildrenListItemValue(parentList);
488
419
  updateChildrenListItemValue(greatGrandparentList);
489
420
  }
490
421
  });
491
422
  }
492
-
493
423
  function maybeIndentOrOutdent(direction) {
494
424
  const selection = lexical.$getSelection();
495
-
496
425
  if (!lexical.$isRangeSelection(selection)) {
497
426
  return;
498
427
  }
499
-
500
428
  const selectedNodes = selection.getNodes();
501
429
  let listItemNodes = [];
502
-
503
430
  if (selectedNodes.length === 0) {
504
431
  selectedNodes.push(selection.anchor.getNode());
505
432
  }
506
-
507
433
  if (selectedNodes.length === 1) {
508
434
  // Only 1 node selected. Selection may not contain the ListNodeItem so we traverse the tree to
509
435
  // find whether this is part of a ListItemNode
510
436
  const nearestListItemNode = findNearestListItemNode(selectedNodes[0]);
511
-
512
437
  if (nearestListItemNode !== null) {
513
438
  listItemNodes = [nearestListItemNode];
514
439
  }
515
440
  } else {
516
441
  listItemNodes = getUniqueListItemNodes(selectedNodes);
517
442
  }
518
-
519
443
  if (listItemNodes.length > 0) {
520
444
  if (direction === 'indent') {
521
445
  $handleIndent(listItemNodes);
@@ -524,7 +448,6 @@ function maybeIndentOrOutdent(direction) {
524
448
  }
525
449
  }
526
450
  }
527
-
528
451
  function indentList() {
529
452
  maybeIndentOrOutdent('indent');
530
453
  }
@@ -533,28 +456,21 @@ function outdentList() {
533
456
  }
534
457
  function $handleListInsertParagraph() {
535
458
  const selection = lexical.$getSelection();
536
-
537
459
  if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
538
460
  return false;
539
- } // Only run this code on empty list items
540
-
541
-
461
+ }
462
+ // Only run this code on empty list items
542
463
  const anchor = selection.anchor.getNode();
543
-
544
464
  if (!$isListItemNode(anchor) || anchor.getTextContent() !== '') {
545
465
  return false;
546
466
  }
547
-
548
467
  const topListNode = $getTopListNode(anchor);
549
468
  const parent = anchor.getParent();
550
-
551
469
  if (!$isListNode(parent)) {
552
470
  throw Error(`A ListItemNode must have a ListNode for a parent.`);
553
471
  }
554
-
555
472
  const grandparent = parent.getParent();
556
473
  let replacementNode;
557
-
558
474
  if (lexical.$isRootOrShadowRoot(grandparent)) {
559
475
  replacementNode = lexical.$createParagraphNode();
560
476
  topListNode.insertAfter(replacementNode);
@@ -564,13 +480,10 @@ function $handleListInsertParagraph() {
564
480
  } else {
565
481
  return false;
566
482
  }
567
-
568
483
  replacementNode.select();
569
484
  const nextSiblings = anchor.getNextSiblings();
570
-
571
485
  if (nextSiblings.length > 0) {
572
486
  const newList = $createListNode(parent.getListType());
573
-
574
487
  if (lexical.$isParagraphNode(replacementNode)) {
575
488
  replacementNode.insertAfter(newList);
576
489
  } else {
@@ -578,14 +491,13 @@ function $handleListInsertParagraph() {
578
491
  newListItem.append(newList);
579
492
  replacementNode.insertAfter(newListItem);
580
493
  }
581
-
582
494
  nextSiblings.forEach(sibling => {
583
495
  sibling.remove();
584
496
  newList.append(sibling);
585
497
  });
586
- } // Don't leave hanging nested empty lists
587
-
498
+ }
588
499
 
500
+ // Don't leave hanging nested empty lists
589
501
  $removeHighestEmptyListParent(anchor);
590
502
  return true;
591
503
  }
@@ -597,54 +509,45 @@ function $handleListInsertParagraph() {
597
509
  * LICENSE file in the root directory of this source tree.
598
510
  *
599
511
  */
600
-
601
512
  /** @noInheritDoc */
602
513
  class ListItemNode extends lexical.ElementNode {
603
514
  /** @internal */
604
515
 
605
516
  /** @internal */
517
+
606
518
  static getType() {
607
519
  return 'listitem';
608
520
  }
609
-
610
521
  static clone(node) {
611
522
  return new ListItemNode(node.__value, node.__checked, node.__key);
612
523
  }
613
-
614
524
  constructor(value, checked, key) {
615
525
  super(key);
616
526
  this.__value = value === undefined ? 1 : value;
617
527
  this.__checked = checked;
618
528
  }
619
-
620
529
  createDOM(config) {
621
530
  const element = document.createElement('li');
622
531
  const parent = this.getParent();
623
-
624
532
  if ($isListNode(parent)) {
625
533
  updateChildrenListItemValue(parent);
626
534
  updateListItemChecked(element, this, null, parent);
627
535
  }
628
-
629
536
  element.value = this.__value;
630
537
  $setListItemThemeClassNames(element, config.theme, this);
631
538
  return element;
632
539
  }
633
-
634
540
  updateDOM(prevNode, dom, config) {
635
541
  const parent = this.getParent();
636
-
637
542
  if ($isListNode(parent)) {
638
543
  updateChildrenListItemValue(parent);
639
544
  updateListItemChecked(dom, this, prevNode, parent);
640
- } // @ts-expect-error - this is always HTMLListItemElement
641
-
642
-
545
+ }
546
+ // @ts-expect-error - this is always HTMLListItemElement
643
547
  dom.value = this.__value;
644
548
  $setListItemThemeClassNames(dom, config.theme, this);
645
549
  return false;
646
550
  }
647
-
648
551
  static importDOM() {
649
552
  return {
650
553
  li: node => ({
@@ -653,7 +556,6 @@ class ListItemNode extends lexical.ElementNode {
653
556
  })
654
557
  };
655
558
  }
656
-
657
559
  static importJSON(serializedNode) {
658
560
  const node = new ListItemNode(serializedNode.value, serializedNode.checked);
659
561
  node.setFormat(serializedNode.format);
@@ -661,20 +563,18 @@ class ListItemNode extends lexical.ElementNode {
661
563
  node.setDirection(serializedNode.direction);
662
564
  return node;
663
565
  }
664
-
665
566
  exportJSON() {
666
- return { ...super.exportJSON(),
567
+ return {
568
+ ...super.exportJSON(),
667
569
  checked: this.getChecked(),
668
570
  type: 'listitem',
669
571
  value: this.getValue(),
670
572
  version: 1
671
573
  };
672
574
  }
673
-
674
575
  append(...nodes) {
675
576
  for (let i = 0; i < nodes.length; i++) {
676
577
  const node = nodes[i];
677
-
678
578
  if (lexical.$isElementNode(node) && this.canMergeWith(node)) {
679
579
  const children = node.getChildren();
680
580
  this.append(...children);
@@ -683,22 +583,17 @@ class ListItemNode extends lexical.ElementNode {
683
583
  super.append(node);
684
584
  }
685
585
  }
686
-
687
586
  return this;
688
587
  }
689
-
690
588
  replace(replaceWithNode, includeChildren) {
691
589
  if ($isListItemNode(replaceWithNode)) {
692
590
  return super.replace(replaceWithNode);
693
591
  }
694
-
695
592
  const list = this.getParentOrThrow();
696
-
697
593
  if ($isListNode(list)) {
698
594
  const childrenKeys = list.getChildrenKeys();
699
595
  const childrenLength = childrenKeys.length;
700
596
  const index = childrenKeys.indexOf(this.__key);
701
-
702
597
  if (index === 0) {
703
598
  list.insertBefore(replaceWithNode);
704
599
  } else if (index === childrenLength - 1) {
@@ -707,99 +602,79 @@ class ListItemNode extends lexical.ElementNode {
707
602
  // Split the list
708
603
  const newList = $createListNode(list.getListType());
709
604
  const children = list.getChildren();
710
-
711
605
  for (let i = index + 1; i < childrenLength; i++) {
712
606
  const child = children[i];
713
607
  newList.append(child);
714
608
  }
715
-
716
609
  list.insertAfter(replaceWithNode);
717
610
  replaceWithNode.insertAfter(newList);
718
611
  }
719
-
720
612
  if (includeChildren) {
721
613
  this.getChildren().forEach(child => {
722
614
  replaceWithNode.append(child);
723
615
  });
724
616
  }
725
-
726
617
  this.remove();
727
-
728
618
  if (childrenLength === 1) {
729
619
  list.remove();
730
620
  }
731
621
  }
732
-
733
622
  return replaceWithNode;
734
623
  }
735
-
736
624
  insertAfter(node, restoreSelection = true) {
737
625
  const listNode = this.getParentOrThrow();
738
-
739
626
  if (!$isListNode(listNode)) {
740
627
  {
741
628
  throw Error(`insertAfter: list node is not parent of list item node`);
742
629
  }
743
630
  }
744
-
745
631
  const siblings = this.getNextSiblings();
746
-
747
632
  if ($isListItemNode(node)) {
748
633
  const after = super.insertAfter(node, restoreSelection);
749
634
  const afterListNode = node.getParentOrThrow();
750
-
751
635
  if ($isListNode(afterListNode)) {
752
636
  updateChildrenListItemValue(afterListNode);
753
637
  }
754
-
755
638
  return after;
756
- } // Attempt to merge if the list is of the same type.
639
+ }
757
640
 
641
+ // Attempt to merge if the list is of the same type.
758
642
 
759
643
  if ($isListNode(node) && node.getListType() === listNode.getListType()) {
760
644
  let child = node;
761
645
  const children = node.getChildren();
762
-
763
646
  for (let i = children.length - 1; i >= 0; i--) {
764
647
  child = children[i];
765
648
  this.insertAfter(child, restoreSelection);
766
649
  }
767
-
768
650
  return child;
769
- } // Otherwise, split the list
770
- // Split the lists and insert the node in between them
771
-
651
+ }
772
652
 
653
+ // Otherwise, split the list
654
+ // Split the lists and insert the node in between them
773
655
  listNode.insertAfter(node, restoreSelection);
774
-
775
656
  if (siblings.length !== 0) {
776
657
  const newListNode = $createListNode(listNode.getListType());
777
658
  siblings.forEach(sibling => newListNode.append(sibling));
778
659
  node.insertAfter(newListNode, restoreSelection);
779
660
  }
780
-
781
661
  return node;
782
662
  }
783
-
784
663
  remove(preserveEmptyParent) {
785
664
  const nextSibling = this.getNextSibling();
786
665
  super.remove(preserveEmptyParent);
787
-
788
666
  if (nextSibling !== null) {
789
667
  const parent = nextSibling.getParent();
790
-
791
668
  if ($isListNode(parent)) {
792
669
  updateChildrenListItemValue(parent);
793
670
  }
794
671
  }
795
672
  }
796
-
797
673
  insertNewAfter(_, restoreSelection = true) {
798
674
  const newElement = $createListItemNode(this.__checked == null ? undefined : false);
799
675
  this.insertAfter(newElement, restoreSelection);
800
676
  return newElement;
801
677
  }
802
-
803
678
  collapseAtStart(selection) {
804
679
  const paragraph = lexical.$createParagraphNode();
805
680
  const children = this.getChildren();
@@ -807,7 +682,6 @@ class ListItemNode extends lexical.ElementNode {
807
682
  const listNode = this.getParentOrThrow();
808
683
  const listNodeParent = listNode.getParentOrThrow();
809
684
  const isIndented = $isListItemNode(listNodeParent);
810
-
811
685
  if (listNode.getChildrenSize() === 1) {
812
686
  if (isIndented) {
813
687
  // if the list node is nested, we just want to remove it,
@@ -816,17 +690,15 @@ class ListItemNode extends lexical.ElementNode {
816
690
  listNodeParent.select();
817
691
  } else {
818
692
  listNode.insertBefore(paragraph);
819
- listNode.remove(); // If we have selection on the list item, we'll need to move it
693
+ listNode.remove();
694
+ // If we have selection on the list item, we'll need to move it
820
695
  // to the paragraph
821
-
822
696
  const anchor = selection.anchor;
823
697
  const focus = selection.focus;
824
698
  const key = paragraph.getKey();
825
-
826
699
  if (anchor.type === 'element' && anchor.getNode().is(this)) {
827
700
  anchor.set(key, anchor.offset, 'element');
828
701
  }
829
-
830
702
  if (focus.type === 'element' && focus.getNode().is(this)) {
831
703
  focus.set(key, focus.offset, 'element');
832
704
  }
@@ -835,57 +707,44 @@ class ListItemNode extends lexical.ElementNode {
835
707
  listNode.insertBefore(paragraph);
836
708
  this.remove();
837
709
  }
838
-
839
710
  return true;
840
711
  }
841
-
842
712
  getValue() {
843
713
  const self = this.getLatest();
844
714
  return self.__value;
845
715
  }
846
-
847
716
  setValue(value) {
848
717
  const self = this.getWritable();
849
718
  self.__value = value;
850
719
  }
851
-
852
720
  getChecked() {
853
721
  const self = this.getLatest();
854
722
  return self.__checked;
855
723
  }
856
-
857
724
  setChecked(checked) {
858
725
  const self = this.getWritable();
859
726
  self.__checked = checked;
860
727
  }
861
-
862
728
  toggleChecked() {
863
729
  this.setChecked(!this.__checked);
864
730
  }
865
-
866
731
  getIndent() {
867
732
  // If we don't have a parent, we are likely serializing
868
733
  const parent = this.getParent();
869
-
870
734
  if (parent === null) {
871
735
  return this.getLatest().__indent;
872
- } // ListItemNode should always have a ListNode for a parent.
873
-
874
-
736
+ }
737
+ // ListItemNode should always have a ListNode for a parent.
875
738
  let listNodeParent = parent.getParentOrThrow();
876
739
  let indentLevel = 0;
877
-
878
740
  while ($isListItemNode(listNodeParent)) {
879
741
  listNodeParent = listNodeParent.getParentOrThrow().getParentOrThrow();
880
742
  indentLevel++;
881
743
  }
882
-
883
744
  return indentLevel;
884
745
  }
885
-
886
746
  setIndent(indent) {
887
747
  let currentIndent = this.getIndent();
888
-
889
748
  while (currentIndent !== indent) {
890
749
  if (currentIndent < indent) {
891
750
  $handleIndent([this]);
@@ -895,116 +754,91 @@ class ListItemNode extends lexical.ElementNode {
895
754
  currentIndent--;
896
755
  }
897
756
  }
898
-
899
757
  return this;
900
758
  }
901
-
902
759
  canIndent() {
903
760
  // Indent/outdent is handled specifically in the RichText logic.
761
+
904
762
  return false;
905
763
  }
906
-
907
764
  insertBefore(nodeToInsert) {
908
765
  if ($isListItemNode(nodeToInsert)) {
909
766
  const parent = this.getParentOrThrow();
910
-
911
767
  if ($isListNode(parent)) {
912
768
  const siblings = this.getNextSiblings();
913
769
  updateChildrenListItemValue(parent, siblings);
914
770
  }
915
771
  }
916
-
917
772
  return super.insertBefore(nodeToInsert);
918
773
  }
919
-
920
774
  canInsertAfter(node) {
921
775
  return $isListItemNode(node);
922
776
  }
923
-
924
777
  canReplaceWith(replacement) {
925
778
  return $isListItemNode(replacement);
926
779
  }
927
-
928
780
  canMergeWith(node) {
929
781
  return lexical.$isParagraphNode(node) || $isListItemNode(node);
930
782
  }
931
-
932
783
  extractWithChild(child, selection) {
933
784
  if (!lexical.$isRangeSelection(selection)) {
934
785
  return false;
935
786
  }
936
-
937
787
  const anchorNode = selection.anchor.getNode();
938
788
  const focusNode = selection.focus.getNode();
939
789
  return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && this.getTextContent().length === selection.getTextContent().length;
940
790
  }
941
-
942
791
  isParentRequired() {
943
792
  return true;
944
793
  }
945
-
946
794
  createParentElementNode() {
947
795
  return $createListNode('bullet');
948
796
  }
949
-
950
797
  }
951
-
952
798
  function $setListItemThemeClassNames(dom, editorThemeClasses, node) {
953
799
  const classesToAdd = [];
954
800
  const classesToRemove = [];
955
801
  const listTheme = editorThemeClasses.list;
956
802
  const listItemClassName = listTheme ? listTheme.listitem : undefined;
957
803
  let nestedListItemClassName;
958
-
959
804
  if (listTheme && listTheme.nested) {
960
805
  nestedListItemClassName = listTheme.nested.listitem;
961
806
  }
962
-
963
807
  if (listItemClassName !== undefined) {
964
808
  const listItemClasses = listItemClassName.split(' ');
965
809
  classesToAdd.push(...listItemClasses);
966
810
  }
967
-
968
811
  if (listTheme) {
969
812
  const parentNode = node.getParent();
970
813
  const isCheckList = $isListNode(parentNode) && parentNode.getListType() === 'check';
971
814
  const checked = node.getChecked();
972
-
973
815
  if (!isCheckList || checked) {
974
816
  classesToRemove.push(listTheme.listitemUnchecked);
975
817
  }
976
-
977
818
  if (!isCheckList || !checked) {
978
819
  classesToRemove.push(listTheme.listitemChecked);
979
820
  }
980
-
981
821
  if (isCheckList) {
982
822
  classesToAdd.push(checked ? listTheme.listitemChecked : listTheme.listitemUnchecked);
983
823
  }
984
824
  }
985
-
986
825
  if (nestedListItemClassName !== undefined) {
987
826
  const nestedListItemClasses = nestedListItemClassName.split(' ');
988
-
989
827
  if (node.getChildren().some(child => $isListNode(child))) {
990
828
  classesToAdd.push(...nestedListItemClasses);
991
829
  } else {
992
830
  classesToRemove.push(...nestedListItemClasses);
993
831
  }
994
832
  }
995
-
996
833
  if (classesToRemove.length > 0) {
997
834
  utils.removeClassNamesFromElement(dom, ...classesToRemove);
998
835
  }
999
-
1000
836
  if (classesToAdd.length > 0) {
1001
837
  utils.addClassNamesToElement(dom, ...classesToAdd);
1002
838
  }
1003
839
  }
1004
-
1005
840
  function updateListItemChecked(dom, listItemNode, prevListItemNode, listNode) {
1006
841
  const isCheckList = listNode.getListType() === 'check';
1007
-
1008
842
  if (isCheckList) {
1009
843
  // Only add attributes for leaf list items
1010
844
  if ($isListNode(listItemNode.getFirstChild())) {
@@ -1014,7 +848,6 @@ function updateListItemChecked(dom, listItemNode, prevListItemNode, listNode) {
1014
848
  } else {
1015
849
  dom.setAttribute('role', 'checkbox');
1016
850
  dom.setAttribute('tabIndex', '-1');
1017
-
1018
851
  if (!prevListItemNode || listItemNode.__checked !== prevListItemNode.__checked) {
1019
852
  dom.setAttribute('aria-checked', listItemNode.getChecked() ? 'true' : 'false');
1020
853
  }
@@ -1026,14 +859,12 @@ function updateListItemChecked(dom, listItemNode, prevListItemNode, listNode) {
1026
859
  }
1027
860
  }
1028
861
  }
1029
-
1030
862
  function convertListItemElement(domNode) {
1031
863
  const checked = domNode instanceof HTMLElement && domNode.getAttribute('aria-checked') === 'true';
1032
864
  return {
1033
865
  node: $createListItemNode(checked)
1034
866
  };
1035
867
  }
1036
-
1037
868
  function $createListItemNode(checked) {
1038
869
  return lexical.$applyNodeReplacement(new ListItemNode(undefined, checked));
1039
870
  }
@@ -1048,7 +879,6 @@ function $isListItemNode(node) {
1048
879
  * LICENSE file in the root directory of this source tree.
1049
880
  *
1050
881
  */
1051
-
1052
882
  /** @noInheritDoc */
1053
883
  class ListNode extends lexical.ElementNode {
1054
884
  /** @internal */
@@ -1056,61 +886,51 @@ class ListNode extends lexical.ElementNode {
1056
886
  /** @internal */
1057
887
 
1058
888
  /** @internal */
889
+
1059
890
  static getType() {
1060
891
  return 'list';
1061
892
  }
1062
-
1063
893
  static clone(node) {
1064
894
  const listType = node.__listType || TAG_TO_LIST_TYPE[node.__tag];
1065
895
  return new ListNode(listType, node.__start, node.__key);
1066
896
  }
1067
-
1068
897
  constructor(listType, start, key) {
1069
898
  super(key);
1070
-
1071
899
  const _listType = TAG_TO_LIST_TYPE[listType] || listType;
1072
-
1073
900
  this.__listType = _listType;
1074
901
  this.__tag = _listType === 'number' ? 'ol' : 'ul';
1075
902
  this.__start = start;
1076
903
  }
1077
-
1078
904
  getTag() {
1079
905
  return this.__tag;
1080
906
  }
1081
-
1082
907
  getListType() {
1083
908
  return this.__listType;
1084
909
  }
1085
-
1086
910
  getStart() {
1087
911
  return this.__start;
1088
- } // View
912
+ }
1089
913
 
914
+ // View
1090
915
 
1091
916
  createDOM(config, _editor) {
1092
917
  const tag = this.__tag;
1093
918
  const dom = document.createElement(tag);
1094
-
1095
919
  if (this.__start !== 1) {
1096
920
  dom.setAttribute('start', String(this.__start));
1097
- } // @ts-expect-error Internal field.
1098
-
1099
-
921
+ }
922
+ // @ts-expect-error Internal field.
1100
923
  dom.__lexicalListType = this.__listType;
1101
924
  setListThemeClassNames(dom, config.theme, this);
1102
925
  return dom;
1103
926
  }
1104
-
1105
927
  updateDOM(prevNode, dom, config) {
1106
928
  if (prevNode.__tag !== this.__tag) {
1107
929
  return true;
1108
930
  }
1109
-
1110
931
  setListThemeClassNames(dom, config.theme, this);
1111
932
  return false;
1112
933
  }
1113
-
1114
934
  static importDOM() {
1115
935
  return {
1116
936
  ol: node => ({
@@ -1123,7 +943,6 @@ class ListNode extends lexical.ElementNode {
1123
943
  })
1124
944
  };
1125
945
  }
1126
-
1127
946
  static importJSON(serializedNode) {
1128
947
  const node = $createListNode(serializedNode.listType, serializedNode.start);
1129
948
  node.setFormat(serializedNode.format);
@@ -1131,29 +950,25 @@ class ListNode extends lexical.ElementNode {
1131
950
  node.setDirection(serializedNode.direction);
1132
951
  return node;
1133
952
  }
1134
-
1135
953
  exportDOM(editor) {
1136
954
  const {
1137
955
  element
1138
956
  } = super.exportDOM(editor);
1139
-
1140
957
  if (element) {
1141
958
  if (this.__start !== 1) {
1142
959
  element.setAttribute('start', String(this.__start));
1143
960
  }
1144
-
1145
961
  if (this.__listType === 'check') {
1146
962
  element.setAttribute('__lexicalListType', 'check');
1147
963
  }
1148
964
  }
1149
-
1150
965
  return {
1151
966
  element
1152
967
  };
1153
968
  }
1154
-
1155
969
  exportJSON() {
1156
- return { ...super.exportJSON(),
970
+ return {
971
+ ...super.exportJSON(),
1157
972
  listType: this.getListType(),
1158
973
  start: this.getStart(),
1159
974
  tag: this.getTag(),
@@ -1161,24 +976,19 @@ class ListNode extends lexical.ElementNode {
1161
976
  version: 1
1162
977
  };
1163
978
  }
1164
-
1165
979
  canBeEmpty() {
1166
980
  return false;
1167
981
  }
1168
-
1169
982
  canIndent() {
1170
983
  return false;
1171
984
  }
1172
-
1173
985
  append(...nodesToAppend) {
1174
986
  for (let i = 0; i < nodesToAppend.length; i++) {
1175
987
  const currentNode = nodesToAppend[i];
1176
-
1177
988
  if ($isListItemNode(currentNode)) {
1178
989
  super.append(currentNode);
1179
990
  } else {
1180
991
  const listItemNode = $createListItemNode();
1181
-
1182
992
  if ($isListNode(currentNode)) {
1183
993
  listItemNode.append(currentNode);
1184
994
  } else if (lexical.$isElementNode(currentNode)) {
@@ -1187,25 +997,19 @@ class ListNode extends lexical.ElementNode {
1187
997
  } else {
1188
998
  listItemNode.append(currentNode);
1189
999
  }
1190
-
1191
1000
  super.append(listItemNode);
1192
1001
  }
1193
1002
  }
1194
-
1195
1003
  return this;
1196
1004
  }
1197
-
1198
1005
  extractWithChild(child) {
1199
1006
  return $isListItemNode(child);
1200
1007
  }
1201
-
1202
1008
  }
1203
-
1204
1009
  function setListThemeClassNames(dom, editorThemeClasses, node) {
1205
1010
  const classesToAdd = [];
1206
1011
  const classesToRemove = [];
1207
1012
  const listTheme = editorThemeClasses.list;
1208
-
1209
1013
  if (listTheme !== undefined) {
1210
1014
  const listLevelsClassNames = listTheme[`${node.__tag}Depth`] || [];
1211
1015
  const listDepth = $getListDepth(node) - 1;
@@ -1214,29 +1018,23 @@ function setListThemeClassNames(dom, editorThemeClasses, node) {
1214
1018
  const listClassName = listTheme[node.__tag];
1215
1019
  let nestedListClassName;
1216
1020
  const nestedListTheme = listTheme.nested;
1217
-
1218
1021
  if (nestedListTheme !== undefined && nestedListTheme.list) {
1219
1022
  nestedListClassName = nestedListTheme.list;
1220
1023
  }
1221
-
1222
1024
  if (listClassName !== undefined) {
1223
1025
  classesToAdd.push(listClassName);
1224
1026
  }
1225
-
1226
1027
  if (listLevelClassName !== undefined) {
1227
1028
  const listItemClasses = listLevelClassName.split(' ');
1228
1029
  classesToAdd.push(...listItemClasses);
1229
-
1230
1030
  for (let i = 0; i < listLevelsClassNames.length; i++) {
1231
1031
  if (i !== normalizedListDepth) {
1232
1032
  classesToRemove.push(node.__tag + i);
1233
1033
  }
1234
1034
  }
1235
1035
  }
1236
-
1237
1036
  if (nestedListClassName !== undefined) {
1238
1037
  const nestedListItemClasses = nestedListClassName.split(' ');
1239
-
1240
1038
  if (listDepth > 1) {
1241
1039
  classesToAdd.push(...nestedListItemClasses);
1242
1040
  } else {
@@ -1244,32 +1042,26 @@ function setListThemeClassNames(dom, editorThemeClasses, node) {
1244
1042
  }
1245
1043
  }
1246
1044
  }
1247
-
1248
1045
  if (classesToRemove.length > 0) {
1249
1046
  utils.removeClassNamesFromElement(dom, ...classesToRemove);
1250
1047
  }
1251
-
1252
1048
  if (classesToAdd.length > 0) {
1253
1049
  utils.addClassNamesToElement(dom, ...classesToAdd);
1254
1050
  }
1255
1051
  }
1052
+
1256
1053
  /*
1257
1054
  * This function normalizes the children of a ListNode after the conversion from HTML,
1258
1055
  * ensuring that they are all ListItemNodes and contain either a single nested ListNode
1259
1056
  * or some other inline content.
1260
1057
  */
1261
-
1262
-
1263
1058
  function normalizeChildren(nodes) {
1264
1059
  const normalizedListItems = [];
1265
-
1266
1060
  for (let i = 0; i < nodes.length; i++) {
1267
1061
  const node = nodes[i];
1268
-
1269
1062
  if ($isListItemNode(node)) {
1270
1063
  normalizedListItems.push(node);
1271
1064
  const children = node.getChildren();
1272
-
1273
1065
  if (children.length > 1) {
1274
1066
  children.forEach(child => {
1275
1067
  if ($isListNode(child)) {
@@ -1281,14 +1073,11 @@ function normalizeChildren(nodes) {
1281
1073
  normalizedListItems.push(wrapInListItem(node));
1282
1074
  }
1283
1075
  }
1284
-
1285
1076
  return normalizedListItems;
1286
1077
  }
1287
-
1288
1078
  function convertListNode(domNode) {
1289
1079
  const nodeName = domNode.nodeName.toLowerCase();
1290
1080
  let node = null;
1291
-
1292
1081
  if (nodeName === 'ol') {
1293
1082
  node = $createListNode('number');
1294
1083
  } else if (nodeName === 'ul') {
@@ -1298,13 +1087,11 @@ function convertListNode(domNode) {
1298
1087
  node = $createListNode('bullet');
1299
1088
  }
1300
1089
  }
1301
-
1302
1090
  return {
1303
1091
  after: normalizeChildren,
1304
1092
  node
1305
1093
  };
1306
1094
  }
1307
-
1308
1095
  const TAG_TO_LIST_TYPE = {
1309
1096
  ol: 'number',
1310
1097
  ul: 'bullet'
package/package.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "list"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.7.7",
11
+ "version": "0.7.9",
12
12
  "main": "LexicalList.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.7.7"
14
+ "lexical": "0.7.9"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/utils": "0.7.7"
17
+ "@lexical/utils": "0.7.9"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",