@lexical/list 0.9.0 → 0.9.2

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.
@@ -16,6 +16,12 @@ var utils = require('@lexical/utils');
16
16
  * LICENSE file in the root directory of this source tree.
17
17
  *
18
18
  */
19
+ /**
20
+ * Checks the depth of listNode from the root node.
21
+ * @param listNode - The ListNode to be checked.
22
+ * @returns The depth of the ListNode.
23
+ */
24
+
19
25
  function $getListDepth(listNode) {
20
26
  let depth = 1;
21
27
  let parent = listNode.getParent();
@@ -40,6 +46,12 @@ function $getListDepth(listNode) {
40
46
 
41
47
  return depth;
42
48
  }
49
+ /**
50
+ * Finds the nearest ancestral ListNode and returns it, throws an invariant if listItem is not a ListItemNode.
51
+ * @param listItem - The node to be checked.
52
+ * @returns The ListNode found.
53
+ */
54
+
43
55
  function $getTopListNode(listItem) {
44
56
  let list = listItem.getParent();
45
57
 
@@ -61,6 +73,13 @@ function $getTopListNode(listItem) {
61
73
 
62
74
  return list;
63
75
  }
76
+ /**
77
+ * A recursive Depth-First Search (Postorder Traversal) that finds all of a node's children
78
+ * that are of type ListItemNode and returns them in an array.
79
+ * @param node - The ListNode to start the search.
80
+ * @returns An array containing all nodes of type ListItemNode found.
81
+ */
82
+ // This should probably be $getAllChildrenOfType
64
83
 
65
84
  function $getAllListItems(node) {
66
85
  let listItemNodes = [];
@@ -79,9 +98,23 @@ function $getAllListItems(node) {
79
98
 
80
99
  return listItemNodes;
81
100
  }
101
+ /**
102
+ * Checks to see if the passed node is a ListItemNode and has a ListNode as a child.
103
+ * @param node - The node to be checked.
104
+ * @returns true if the node is a ListItemNode and has a ListNode child, false otherwise.
105
+ */
106
+
82
107
  function isNestedListNode(node) {
83
108
  return $isListItemNode(node) && $isListNode(node.getFirstChild());
84
- } // TODO: rewrite with $findMatchingParent or *nodeOfType
109
+ }
110
+ /**
111
+ * Takes a deeply nested ListNode or ListItemNode and traverses up the branch to delete the first
112
+ * ancestral ListNode (which could be the root ListNode) or ListItemNode with siblings, essentially
113
+ * bringing the deeply nested node up the branch once. Would remove sublist if it has siblings.
114
+ * Should not break ListItem -> List -> ListItem chain as empty List/ItemNodes should be removed on .remove().
115
+ * @param sublist - The nested ListNode or ListItemNode to be brought up the branch.
116
+ */
117
+
85
118
  function $removeHighestEmptyListParent(sublist) {
86
119
  // Nodes may be repeatedly indented, to create deeply nested lists that each
87
120
  // contain just one bullet.
@@ -103,6 +136,12 @@ function $removeHighestEmptyListParent(sublist) {
103
136
 
104
137
  emptyListPtr.remove();
105
138
  }
139
+ /**
140
+ * Wraps a node into a ListItemNode.
141
+ * @param node - The node to be wrapped into a ListItemNode
142
+ * @returns The ListItemNode which the passed node is wrapped in.
143
+ */
144
+
106
145
  function wrapInListItem(node) {
107
146
  const listItemWrapper = $createListItemNode();
108
147
  return listItemWrapper.append(node);
@@ -146,6 +185,17 @@ function $getListItemValue(listItem) {
146
185
 
147
186
  return value;
148
187
  }
188
+ /**
189
+ * Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
190
+ * the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
191
+ * Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
192
+ * If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
193
+ * unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
194
+ * a new ListNode, or create a new ListNode at the nearest root/shadow root.
195
+ * @param editor - The lexical editor.
196
+ * @param listType - The type of list, "number" | "bullet" | "check".
197
+ */
198
+
149
199
 
150
200
  function insertList(editor, listType) {
151
201
  editor.update(() => {
@@ -261,6 +311,13 @@ function createListOrMerge(node, listType) {
261
311
  return list;
262
312
  }
263
313
  }
314
+ /**
315
+ * A recursive function that goes through each list and their children, including nested lists,
316
+ * appending list2 children after list1 children and updating ListItemNode values.
317
+ * @param list1 - The first list to be merged.
318
+ * @param list2 - The second list to be merged.
319
+ */
320
+
264
321
 
265
322
  function mergeLists(list1, list2) {
266
323
  const listItem1 = list1.getLastChild();
@@ -280,6 +337,14 @@ function mergeLists(list1, list2) {
280
337
 
281
338
  list2.remove();
282
339
  }
340
+ /**
341
+ * Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
342
+ * it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
343
+ * removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
344
+ * inside a ListItemNode will be appended to the new ParagraphNodes.
345
+ * @param editor - The lexical editor.
346
+ */
347
+
283
348
  function removeList(editor) {
284
349
  editor.update(() => {
285
350
  const selection = lexical.$getSelection();
@@ -336,6 +401,14 @@ function removeList(editor) {
336
401
  }
337
402
  });
338
403
  }
404
+ /**
405
+ * Takes the value of a child ListItemNode and makes it the value the ListItemNode
406
+ * should be if it isn't already. If only certain children should be updated, they
407
+ * can be passed optionally in an array.
408
+ * @param list - The list whose children are updated.
409
+ * @param children - An array of the children to be updated.
410
+ */
411
+
339
412
  function updateChildrenListItemValue(list, children) {
340
413
  const childrenOrExisting = children || list.getChildren();
341
414
 
@@ -354,6 +427,13 @@ function updateChildrenListItemValue(list, children) {
354
427
  }
355
428
  }
356
429
  }
430
+ /**
431
+ * Adds an empty ListNode/ListItemNode chain at listItemNode, so as to
432
+ * create an indent effect. Won't indent ListItemNodes that have a ListNode as
433
+ * a child, but does merge sibling ListItemNodes if one has a nested ListNode.
434
+ * @param listItemNode - The ListItemNode to be indented.
435
+ */
436
+
357
437
  function $handleIndent(listItemNode) {
358
438
  // go through each node and decide where to move it.
359
439
  const removed = new Set();
@@ -427,6 +507,13 @@ function $handleIndent(listItemNode) {
427
507
  updateChildrenListItemValue(parent);
428
508
  }
429
509
  }
510
+ /**
511
+ * Removes an indent by removing an empty ListNode/ListItemNode chain. An indented ListItemNode
512
+ * has a great grandparent node of type ListNode, which is where the ListItemNode will reside
513
+ * within as a child.
514
+ * @param listItemNode - The ListItemNode to remove the indent (outdent).
515
+ */
516
+
430
517
  function $handleOutdent(listItemNode) {
431
518
  // go through each node and decide where to move it.
432
519
  if (isNestedListNode(listItemNode)) {
@@ -479,6 +566,16 @@ function $handleOutdent(listItemNode) {
479
566
  updateChildrenListItemValue(greatGrandparentList);
480
567
  }
481
568
  }
569
+ /**
570
+ * Attempts to insert a ParagraphNode at selection and selects the new node. The selection must contain a ListItemNode
571
+ * or a node that does not already contain text. If its grandparent is the root/shadow root, it will get the ListNode
572
+ * (which should be the parent node) and insert the ParagraphNode as a sibling to the ListNode. If the ListNode is
573
+ * nested in a ListItemNode instead, it will add the ParagraphNode after the grandparent ListItemNode.
574
+ * Throws an invariant if the selection is not a child of a ListNode.
575
+ * @returns true if a ParagraphNode was inserted succesfully, false if there is no selection
576
+ * or the selection does not contain a ListItemNode or the node already holds text.
577
+ */
578
+
482
579
  function $handleListInsertParagraph() {
483
580
  const selection = lexical.$getSelection();
484
581
 
@@ -617,7 +714,6 @@ class ListItemNode extends lexical.ElementNode {
617
714
  static importJSON(serializedNode) {
618
715
  const node = new ListItemNode(serializedNode.value, serializedNode.checked);
619
716
  node.setFormat(serializedNode.format);
620
- node.setIndent(serializedNode.indent);
621
717
  node.setDirection(serializedNode.direction);
622
718
  return node;
623
719
  }
@@ -845,6 +941,10 @@ class ListItemNode extends lexical.ElementNode {
845
941
  }
846
942
 
847
943
  setIndent(indent) {
944
+ if (!(typeof indent === 'number' && indent > -1)) {
945
+ throw Error(`Invalid indent value.`);
946
+ }
947
+
848
948
  let currentIndent = this.getIndent();
849
949
 
850
950
  while (currentIndent !== indent) {
@@ -980,10 +1080,22 @@ function convertListItemElement(domNode) {
980
1080
  node: $createListItemNode(checked)
981
1081
  };
982
1082
  }
1083
+ /**
1084
+ * Creates a new List Item node, passing true/false will convert it to a checkbox input.
1085
+ * @param checked - Is the List Item a checkbox and, if so, is it checked? undefined/null: not a checkbox, true/false is a checkbox and checked/unchecked, respectively.
1086
+ * @returns The new List Item.
1087
+ */
1088
+
983
1089
 
984
1090
  function $createListItemNode(checked) {
985
1091
  return lexical.$applyNodeReplacement(new ListItemNode(undefined, checked));
986
1092
  }
1093
+ /**
1094
+ * Checks to see if the node is a ListItemNode.
1095
+ * @param node - The node to be checked.
1096
+ * @returns true if the node is a ListItemNode, false otherwise.
1097
+ */
1098
+
987
1099
  function $isListItemNode(node) {
988
1100
  return node instanceof ListItemNode;
989
1101
  }
@@ -1238,7 +1350,9 @@ function convertListNode(domNode) {
1238
1350
  let node = null;
1239
1351
 
1240
1352
  if (nodeName === 'ol') {
1241
- node = $createListNode('number');
1353
+ // @ts-ignore
1354
+ const start = domNode.start;
1355
+ node = $createListNode('number', start);
1242
1356
  } else if (nodeName === 'ul') {
1243
1357
  if (utils.isHTMLElement(domNode) && domNode.getAttribute('__lexicallisttype') === 'check') {
1244
1358
  node = $createListNode('check');
@@ -1257,9 +1371,22 @@ const TAG_TO_LIST_TYPE = {
1257
1371
  ol: 'number',
1258
1372
  ul: 'bullet'
1259
1373
  };
1374
+ /**
1375
+ * Creates a ListNode of listType.
1376
+ * @param listType - The type of list to be created. Can be 'number', 'bullet', or 'check'.
1377
+ * @param start - Where an ordered list starts its count, start = 1 if left undefined.
1378
+ * @returns The new ListNode
1379
+ */
1380
+
1260
1381
  function $createListNode(listType, start = 1) {
1261
1382
  return lexical.$applyNodeReplacement(new ListNode(listType, start));
1262
1383
  }
1384
+ /**
1385
+ * Checks to see if the node is a ListNode.
1386
+ * @param node - The node to be checked.
1387
+ * @returns true if the node is a ListNode, false otherwise.
1388
+ */
1389
+
1263
1390
  function $isListNode(node) {
1264
1391
  return node instanceof ListNode;
1265
1392
  }
@@ -74,21 +74,13 @@ declare export var INSERT_ORDERED_LIST_COMMAND: LexicalCommand<void>;
74
74
  declare export var INSERT_CHECK_LIST_COMMAND: LexicalCommand<void>;
75
75
  declare export var REMOVE_LIST_COMMAND: LexicalCommand<void>;
76
76
 
77
- export type SerializedListItemNode = {
78
- ...SerializedElementNode,
77
+ export type SerializedListItemNode = SerializedElementNode & {
79
78
  checked: boolean | void,
80
79
  value: number,
81
- type: 'listitem',
82
- version: 1,
83
- ...
84
80
  };
85
81
 
86
- export type SerializedListNode = {
87
- ...SerializedElementNode,
82
+ export type SerializedListNode = SerializedElementNode & {
88
83
  listType: ListType,
89
84
  start: number,
90
85
  tag: ListNodeTagType,
91
- type: 'list',
92
- version: 1,
93
- ...
94
86
  };
@@ -11,22 +11,22 @@ function C(a,b){if(q(a))return a;let c=a.getPreviousSibling(),d=a.getNextSibling
11
11
  function F(a,b){var c=a.getLastChild();let d=b.getFirstChild();c&&d&&u(c)&&u(d)&&(F(c.getFirstChild(),d.getFirstChild()),d.remove());c=b.getChildren();0<c.length&&(a.append(...c),E(a));b.remove()}function E(a,b){a=b||a.getChildren();if(void 0!==a)for(b=0;b<a.length;b++){let f=a[b];if(p(f)){let g=f.getValue();var c=f,d=c.getParent(),e=1;null!=d&&(q(d)?e=d.getStart():m(44));c=c.getPreviousSiblings();for(d=0;d<c.length;d++){let l=c[d];p(l)&&!q(l.getFirstChild())&&e++}g!==e&&f.setValue(e)}}}
12
12
  function G(a){if(!u(a)){var b=a.getParent(),c=b?b.getParent():void 0,d=c?c.getParent():void 0;if(q(d)&&p(c)&&q(b)){var e=b?b.getFirstChild():void 0,f=b?b.getLastChild():void 0;if(a.is(e))c.insertBefore(a),b.isEmpty()&&c.remove();else if(a.is(f))c.insertAfter(a),b.isEmpty()&&c.remove();else{var g=b.getListType();e=y();let l=D(g);e.append(l);a.getPreviousSiblings().forEach(x=>l.append(x));f=y();g=D(g);f.append(g);B(g,a.getNextSiblings());c.insertBefore(e);c.insertAfter(f);c.replace(a)}E(b);E(d)}}}
13
13
  class H extends h.ElementNode{static getType(){return"listitem"}static clone(a){return new H(a.__value,a.__checked,a.__key)}constructor(a,b,c){super(c);this.__value=void 0===a?1:a;this.__checked=b}createDOM(a){let b=document.createElement("li"),c=this.getParent();q(c)&&"check"===c.getListType()&&I(b,this,null);b.value=this.__value;J(b,a.theme,this);return b}updateDOM(a,b,c){let d=this.getParent();q(d)&&"check"===d.getListType()&&I(b,this,a);b.value=this.__value;J(b,c.theme,this);return!1}static transform(){return a=>
14
- {let b=a.getParent();q(b)&&(E(b),"check"!==b.getListType()&&null!=a.getChecked()&&a.setChecked(void 0))}}static importDOM(){return{li:()=>({conversion:K,priority:0})}}static importJSON(a){let b=new H(a.value,a.checked);b.setFormat(a.format);b.setIndent(a.indent);b.setDirection(a.direction);return b}exportJSON(){return{...super.exportJSON(),checked:this.getChecked(),type:"listitem",value:this.getValue(),version:1}}append(...a){for(let b=0;b<a.length;b++){let c=a[b];if(h.$isElementNode(c)&&this.canMergeWith(c)){let d=
15
- c.getChildren();this.append(...d);c.remove()}else super.append(c)}return this}replace(a,b){if(p(a))return super.replace(a);this.setIndent(0);let c=this.getParentOrThrow();if(!q(c))return a;if(c.__first===this.getKey())c.insertBefore(a);else if(c.__last===this.getKey())c.insertAfter(a);else{let d=D(c.getListType()),e=this.getNextSibling();for(;e;){let f=e;e=e.getNextSibling();d.append(f)}c.insertAfter(a);a.insertAfter(d)}b&&this.getChildren().forEach(d=>{a.append(d)});this.remove();0===c.getChildrenSize()&&
16
- c.remove();return a}insertAfter(a,b=!0){var c=this.getParentOrThrow();q(c)||m(39);var d=this.getNextSiblings();if(p(a))return b=super.insertAfter(a,b),a=a.getParentOrThrow(),q(a)&&E(a),b;if(q(a)&&a.getListType()===c.getListType()){c=a;a=a.getChildren();for(d=a.length-1;0<=d;d--)c=a[d],this.insertAfter(c,b);return c}c.insertAfter(a,b);if(0!==d.length){let e=D(c.getListType());d.forEach(f=>e.append(f));a.insertAfter(e,b)}return a}remove(a){let b=this.getPreviousSibling(),c=this.getNextSibling();super.remove(a);
14
+ {let b=a.getParent();q(b)&&(E(b),"check"!==b.getListType()&&null!=a.getChecked()&&a.setChecked(void 0))}}static importDOM(){return{li:()=>({conversion:K,priority:0})}}static importJSON(a){let b=new H(a.value,a.checked);b.setFormat(a.format);b.setDirection(a.direction);return b}exportJSON(){return{...super.exportJSON(),checked:this.getChecked(),type:"listitem",value:this.getValue(),version:1}}append(...a){for(let b=0;b<a.length;b++){let c=a[b];if(h.$isElementNode(c)&&this.canMergeWith(c)){let d=c.getChildren();
15
+ this.append(...d);c.remove()}else super.append(c)}return this}replace(a,b){if(p(a))return super.replace(a);this.setIndent(0);let c=this.getParentOrThrow();if(!q(c))return a;if(c.__first===this.getKey())c.insertBefore(a);else if(c.__last===this.getKey())c.insertAfter(a);else{let d=D(c.getListType()),e=this.getNextSibling();for(;e;){let f=e;e=e.getNextSibling();d.append(f)}c.insertAfter(a);a.insertAfter(d)}b&&this.getChildren().forEach(d=>{a.append(d)});this.remove();0===c.getChildrenSize()&&c.remove();
16
+ return a}insertAfter(a,b=!0){var c=this.getParentOrThrow();q(c)||m(39);var d=this.getNextSiblings();if(p(a))return b=super.insertAfter(a,b),a=a.getParentOrThrow(),q(a)&&E(a),b;if(q(a)&&a.getListType()===c.getListType()){c=a;a=a.getChildren();for(d=a.length-1;0<=d;d--)c=a[d],this.insertAfter(c,b);return c}c.insertAfter(a,b);if(0!==d.length){let e=D(c.getListType());d.forEach(f=>e.append(f));a.insertAfter(e,b)}return a}remove(a){let b=this.getPreviousSibling(),c=this.getNextSibling();super.remove(a);
17
17
  b&&c&&u(b)&&u(c)?(F(b.getFirstChild(),c.getFirstChild()),c.remove()):c&&(a=c.getParent(),q(a)&&E(a))}insertNewAfter(a,b=!0){a=y(null==this.__checked?void 0:!1);this.insertAfter(a,b);return a}collapseAtStart(a){let b=h.$createParagraphNode();this.getChildren().forEach(f=>b.append(f));var c=this.getParentOrThrow(),d=c.getParentOrThrow();let e=p(d);1===c.getChildrenSize()?e?(c.remove(),d.select()):(c.insertBefore(b),c.remove(),c=a.anchor,a=a.focus,d=b.getKey(),"element"===c.type&&c.getNode().is(this)&&
18
18
  c.set(d,c.offset,"element"),"element"===a.type&&a.getNode().is(this)&&a.set(d,a.offset,"element")):(c.insertBefore(b),this.remove());return!0}getValue(){return this.getLatest().__value}setValue(a){this.getWritable().__value=a}getChecked(){return this.getLatest().__checked}setChecked(a){this.getWritable().__checked=a}toggleChecked(){this.setChecked(!this.__checked)}getIndent(){var a=this.getParent();if(null===a)return this.getLatest().__indent;a=a.getParentOrThrow();let b=0;for(;p(a);)a=a.getParentOrThrow().getParentOrThrow(),
19
- b++;return b}setIndent(a){let b=this.getIndent();for(;b!==a;)if(b<a){a:{var c=new Set;if(u(this)||c.has(this.getKey()))break a;let g=this.getParent();var d=this.getNextSibling(),e=this.getPreviousSibling();if(u(d)&&u(e)){if(e=e.getFirstChild(),q(e)){e.append(this);var f=d.getFirstChild();q(f)&&(f=f.getChildren(),B(e,f),d.remove(),c.add(d.getKey()));E(e)}}else u(d)?(d=d.getFirstChild(),q(d)&&(c=d.getFirstChild(),null!==c&&c.insertBefore(this),E(d))):u(e)?(d=e.getFirstChild(),q(d)&&(d.append(this),
20
- E(d))):q(g)&&(c=y(),f=D(g.getListType()),c.append(f),f.append(this),e?e.insertAfter(c):d?d.insertBefore(c):g.append(c),E(f));q(g)&&E(g)}b++}else G(this),b--;return this}insertBefore(a){if(p(a)){let b=this.getParentOrThrow();if(q(b)){let c=this.getNextSiblings();E(b,c)}}return super.insertBefore(a)}canInsertAfter(a){return p(a)}canReplaceWith(a){return p(a)}canMergeWith(a){return h.$isParagraphNode(a)||p(a)}extractWithChild(a,b){if(!h.$isRangeSelection(b))return!1;a=b.anchor.getNode();let c=b.focus.getNode();
21
- return this.isParentOf(a)&&this.isParentOf(c)&&this.getTextContent().length===b.getTextContent().length}isParentRequired(){return!0}createParentElementNode(){return D("bullet")}}
19
+ b++;return b}setIndent(a){"number"===typeof a&&-1<a||m(117);let b=this.getIndent();for(;b!==a;)if(b<a){a:{var c=new Set;if(u(this)||c.has(this.getKey()))break a;let g=this.getParent();var d=this.getNextSibling(),e=this.getPreviousSibling();if(u(d)&&u(e)){if(e=e.getFirstChild(),q(e)){e.append(this);var f=d.getFirstChild();q(f)&&(f=f.getChildren(),B(e,f),d.remove(),c.add(d.getKey()));E(e)}}else u(d)?(d=d.getFirstChild(),q(d)&&(c=d.getFirstChild(),null!==c&&c.insertBefore(this),E(d))):u(e)?(d=e.getFirstChild(),
20
+ q(d)&&(d.append(this),E(d))):q(g)&&(c=y(),f=D(g.getListType()),c.append(f),f.append(this),e?e.insertAfter(c):d?d.insertBefore(c):g.append(c),E(f));q(g)&&E(g)}b++}else G(this),b--;return this}insertBefore(a){if(p(a)){let b=this.getParentOrThrow();if(q(b)){let c=this.getNextSiblings();E(b,c)}}return super.insertBefore(a)}canInsertAfter(a){return p(a)}canReplaceWith(a){return p(a)}canMergeWith(a){return h.$isParagraphNode(a)||p(a)}extractWithChild(a,b){if(!h.$isRangeSelection(b))return!1;a=b.anchor.getNode();
21
+ let c=b.focus.getNode();return this.isParentOf(a)&&this.isParentOf(c)&&this.getTextContent().length===b.getTextContent().length}isParentRequired(){return!0}createParentElementNode(){return D("bullet")}}
22
22
  function J(a,b,c){let d=[],e=[];var f=(b=b.list)?b.listitem:void 0;if(b&&b.nested)var g=b.nested.listitem;void 0!==f&&(f=f.split(" "),d.push(...f));if(b){f=c.getParent();f=q(f)&&"check"===f.getListType();let l=c.getChecked();f&&!l||e.push(b.listitemUnchecked);f&&l||e.push(b.listitemChecked);f&&d.push(l?b.listitemChecked:b.listitemUnchecked)}void 0!==g&&(g=g.split(" "),c.getChildren().some(l=>q(l))?d.push(...g):e.push(...g));0<e.length&&k.removeClassNamesFromElement(a,...e);0<d.length&&k.addClassNamesToElement(a,
23
23
  ...d)}function I(a,b,c){q(b.getFirstChild())?(a.removeAttribute("role"),a.removeAttribute("tabIndex"),a.removeAttribute("aria-checked")):(a.setAttribute("role","checkbox"),a.setAttribute("tabIndex","-1"),c&&b.__checked===c.__checked||a.setAttribute("aria-checked",b.getChecked()?"true":"false"))}function K(a){a=k.isHTMLElement(a)&&"true"===a.getAttribute("aria-checked");return{node:y(a)}}function y(a){return h.$applyNodeReplacement(new H(void 0,a))}function p(a){return a instanceof H}
24
24
  class L extends h.ElementNode{static getType(){return"list"}static clone(a){return new L(a.__listType||N[a.__tag],a.__start,a.__key)}constructor(a,b,c){super(c);this.__listType=a=N[a]||a;this.__tag="number"===a?"ol":"ul";this.__start=b}getTag(){return this.__tag}getListType(){return this.__listType}getStart(){return this.__start}createDOM(a){let b=document.createElement(this.__tag);1!==this.__start&&b.setAttribute("start",String(this.__start));b.__lexicalListType=this.__listType;O(b,a.theme,this);
25
25
  return b}updateDOM(a,b,c){if(a.__tag!==this.__tag)return!0;O(b,c.theme,this);return!1}static importDOM(){return{ol:()=>({conversion:P,priority:0}),ul:()=>({conversion:P,priority:0})}}static importJSON(a){let b=D(a.listType,a.start);b.setFormat(a.format);b.setIndent(a.indent);b.setDirection(a.direction);return b}exportDOM(a){({element:a}=super.exportDOM(a));a&&(1!==this.__start&&a.setAttribute("start",String(this.__start)),"check"===this.__listType&&a.setAttribute("__lexicalListType","check"));return{element:a}}exportJSON(){return{...super.exportJSON(),
26
26
  listType:this.getListType(),start:this.getStart(),tag:this.getTag(),type:"list",version:1}}canBeEmpty(){return!1}canIndent(){return!1}append(...a){for(let c=0;c<a.length;c++){var b=a[c];if(p(b))super.append(b);else{let d=y();q(b)?d.append(b):h.$isElementNode(b)?(b=h.$createTextNode(b.getTextContent()),d.append(b)):d.append(b);super.append(d)}}E(this);return this}extractWithChild(a){return p(a)}}
27
27
  function O(a,b,c){let d=[],e=[];var f=b.list;if(void 0!==f){let l=f[`${c.__tag}Depth`]||[];b=n(c)-1;let x=b%l.length;var g=l[x];let M=f[c.__tag],A;f=f.nested;void 0!==f&&f.list&&(A=f.list);void 0!==M&&d.push(M);if(void 0!==g)for(g=g.split(" "),d.push(...g),g=0;g<l.length;g++)g!==x&&e.push(c.__tag+g);void 0!==A&&(c=A.split(" "),1<b?d.push(...c):e.push(...c))}0<e.length&&k.removeClassNamesFromElement(a,...e);0<d.length&&k.addClassNamesToElement(a,...d)}
28
- function Q(a){let b=[];for(let d=0;d<a.length;d++){var c=a[d];p(c)?(b.push(c),c=c.getChildren(),1<c.length&&c.forEach(e=>{q(e)&&b.push(w(e))})):b.push(w(c))}return b}function P(a){let b=a.nodeName.toLowerCase(),c=null;"ol"===b?c=D("number"):"ul"===b&&(c=k.isHTMLElement(a)&&"check"===a.getAttribute("__lexicallisttype")?D("check"):D("bullet"));return{after:Q,node:c}}let N={ol:"number",ul:"bullet"};function D(a,b=1){return h.$applyNodeReplacement(new L(a,b))}function q(a){return a instanceof L}
29
- let R=h.createCommand("INSERT_UNORDERED_LIST_COMMAND"),S=h.createCommand("INSERT_ORDERED_LIST_COMMAND"),T=h.createCommand("INSERT_CHECK_LIST_COMMAND"),U=h.createCommand("REMOVE_LIST_COMMAND");exports.$createListItemNode=y;exports.$createListNode=D;exports.$getListDepth=n;
28
+ function Q(a){let b=[];for(let d=0;d<a.length;d++){var c=a[d];p(c)?(b.push(c),c=c.getChildren(),1<c.length&&c.forEach(e=>{q(e)&&b.push(w(e))})):b.push(w(c))}return b}function P(a){let b=a.nodeName.toLowerCase(),c=null;"ol"===b?c=D("number",a.start):"ul"===b&&(c=k.isHTMLElement(a)&&"check"===a.getAttribute("__lexicallisttype")?D("check"):D("bullet"));return{after:Q,node:c}}let N={ol:"number",ul:"bullet"};function D(a,b=1){return h.$applyNodeReplacement(new L(a,b))}
29
+ function q(a){return a instanceof L}let R=h.createCommand("INSERT_UNORDERED_LIST_COMMAND"),S=h.createCommand("INSERT_ORDERED_LIST_COMMAND"),T=h.createCommand("INSERT_CHECK_LIST_COMMAND"),U=h.createCommand("REMOVE_LIST_COMMAND");exports.$createListItemNode=y;exports.$createListNode=D;exports.$getListDepth=n;
30
30
  exports.$handleListInsertParagraph=function(){var a=h.$getSelection();if(!h.$isRangeSelection(a)||!a.isCollapsed())return!1;a=a.anchor.getNode();if(!p(a)||""!==a.getTextContent())return!1;var b=r(a),c=a.getParent();q(c)||m(40);let d=c.getParent(),e;if(h.$isRootOrShadowRoot(d))e=h.$createParagraphNode(),b.insertAfter(e);else if(p(d))e=y(),d.insertAfter(e);else return!1;e.select();b=a.getNextSiblings();if(0<b.length){let f=D(c.getListType());h.$isParagraphNode(e)?e.insertAfter(f):(c=y(),c.append(f),
31
31
  e.insertAfter(c));b.forEach(g=>{g.remove();f.append(g)})}v(a);return!0};exports.$isListItemNode=p;exports.$isListNode=q;exports.INSERT_CHECK_LIST_COMMAND=T;exports.INSERT_ORDERED_LIST_COMMAND=S;exports.INSERT_UNORDERED_LIST_COMMAND=R;exports.ListItemNode=H;exports.ListNode=L;exports.REMOVE_LIST_COMMAND=U;
32
32
  exports.insertList=function(a,b){a.update(()=>{var c=h.$getSelection();if(h.$isRangeSelection(c)||h.DEPRECATED_$isGridSelection(c)){var d=c.getNodes();c=c.anchor.getNode();var e=c.getParent();if(z(c,d))d=D(b),h.$isRootOrShadowRoot(e)?(c.replace(d),e=y(),h.$isElementNode(c)&&(e.setFormat(c.getFormatType()),e.setIndent(c.getIndent())),d.append(e)):p(c)&&(c=c.getParentOrThrow(),B(d,c.getChildren()),c.replace(d));else for(c=new Set,e=0;e<d.length;e++){var f=d[e];if(h.$isElementNode(f)&&f.isEmpty()&&!c.has(f.getKey()))C(f,
@@ -9,9 +9,7 @@ import type { DOMConversionMap, EditorConfig, GridSelection, LexicalNode, NodeKe
9
9
  import { ElementNode } from 'lexical';
10
10
  export declare type SerializedListItemNode = Spread<{
11
11
  checked: boolean | undefined;
12
- type: 'listitem';
13
12
  value: number;
14
- version: 1;
15
13
  }, SerializedElementNode>;
16
14
  /** @noInheritDoc */
17
15
  export declare class ListItemNode extends ElementNode {
@@ -49,5 +47,15 @@ export declare class ListItemNode extends ElementNode {
49
47
  isParentRequired(): true;
50
48
  createParentElementNode(): ElementNode;
51
49
  }
50
+ /**
51
+ * Creates a new List Item node, passing true/false will convert it to a checkbox input.
52
+ * @param checked - Is the List Item a checkbox and, if so, is it checked? undefined/null: not a checkbox, true/false is a checkbox and checked/unchecked, respectively.
53
+ * @returns The new List Item.
54
+ */
52
55
  export declare function $createListItemNode(checked?: boolean): ListItemNode;
56
+ /**
57
+ * Checks to see if the node is a ListItemNode.
58
+ * @param node - The node to be checked.
59
+ * @returns true if the node is a ListItemNode, false otherwise.
60
+ */
53
61
  export declare function $isListItemNode(node: LexicalNode | null | undefined): node is ListItemNode;
@@ -10,8 +10,6 @@ export declare type SerializedListNode = Spread<{
10
10
  listType: ListType;
11
11
  start: number;
12
12
  tag: ListNodeTagType;
13
- type: 'list';
14
- version: 1;
15
13
  }, SerializedElementNode>;
16
14
  export declare type ListType = 'number' | 'bullet' | 'check';
17
15
  export declare type ListNodeTagType = 'ul' | 'ol';
@@ -40,5 +38,16 @@ export declare class ListNode extends ElementNode {
40
38
  append(...nodesToAppend: LexicalNode[]): this;
41
39
  extractWithChild(child: LexicalNode): boolean;
42
40
  }
41
+ /**
42
+ * Creates a ListNode of listType.
43
+ * @param listType - The type of list to be created. Can be 'number', 'bullet', or 'check'.
44
+ * @param start - Where an ordered list starts its count, start = 1 if left undefined.
45
+ * @returns The new ListNode
46
+ */
43
47
  export declare function $createListNode(listType: ListType, start?: number): ListNode;
48
+ /**
49
+ * Checks to see if the node is a ListNode.
50
+ * @param node - The node to be checked.
51
+ * @returns true if the node is a ListNode, false otherwise.
52
+ */
44
53
  export declare function $isListNode(node: LexicalNode | null | undefined): node is ListNode;
package/formatList.d.ts CHANGED
@@ -8,10 +8,61 @@
8
8
  import { LexicalEditor, LexicalNode } from 'lexical';
9
9
  import { ListItemNode, ListNode } from './';
10
10
  import { ListType } from './LexicalListNode';
11
+ /**
12
+ * Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
13
+ * the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
14
+ * Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
15
+ * If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
16
+ * unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
17
+ * a new ListNode, or create a new ListNode at the nearest root/shadow root.
18
+ * @param editor - The lexical editor.
19
+ * @param listType - The type of list, "number" | "bullet" | "check".
20
+ */
11
21
  export declare function insertList(editor: LexicalEditor, listType: ListType): void;
22
+ /**
23
+ * A recursive function that goes through each list and their children, including nested lists,
24
+ * appending list2 children after list1 children and updating ListItemNode values.
25
+ * @param list1 - The first list to be merged.
26
+ * @param list2 - The second list to be merged.
27
+ */
12
28
  export declare function mergeLists(list1: ListNode, list2: ListNode): void;
29
+ /**
30
+ * Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
31
+ * it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
32
+ * removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
33
+ * inside a ListItemNode will be appended to the new ParagraphNodes.
34
+ * @param editor - The lexical editor.
35
+ */
13
36
  export declare function removeList(editor: LexicalEditor): void;
37
+ /**
38
+ * Takes the value of a child ListItemNode and makes it the value the ListItemNode
39
+ * should be if it isn't already. If only certain children should be updated, they
40
+ * can be passed optionally in an array.
41
+ * @param list - The list whose children are updated.
42
+ * @param children - An array of the children to be updated.
43
+ */
14
44
  export declare function updateChildrenListItemValue(list: ListNode, children?: Array<LexicalNode>): void;
45
+ /**
46
+ * Adds an empty ListNode/ListItemNode chain at listItemNode, so as to
47
+ * create an indent effect. Won't indent ListItemNodes that have a ListNode as
48
+ * a child, but does merge sibling ListItemNodes if one has a nested ListNode.
49
+ * @param listItemNode - The ListItemNode to be indented.
50
+ */
15
51
  export declare function $handleIndent(listItemNode: ListItemNode): void;
52
+ /**
53
+ * Removes an indent by removing an empty ListNode/ListItemNode chain. An indented ListItemNode
54
+ * has a great grandparent node of type ListNode, which is where the ListItemNode will reside
55
+ * within as a child.
56
+ * @param listItemNode - The ListItemNode to remove the indent (outdent).
57
+ */
16
58
  export declare function $handleOutdent(listItemNode: ListItemNode): void;
59
+ /**
60
+ * Attempts to insert a ParagraphNode at selection and selects the new node. The selection must contain a ListItemNode
61
+ * or a node that does not already contain text. If its grandparent is the root/shadow root, it will get the ListNode
62
+ * (which should be the parent node) and insert the ParagraphNode as a sibling to the ListNode. If the ListNode is
63
+ * nested in a ListItemNode instead, it will add the ParagraphNode after the grandparent ListItemNode.
64
+ * Throws an invariant if the selection is not a child of a ListNode.
65
+ * @returns true if a ParagraphNode was inserted succesfully, false if there is no selection
66
+ * or the selection does not contain a ListItemNode or the node already holds text.
67
+ */
17
68
  export declare function $handleListInsertParagraph(): boolean;
package/package.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "list"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.9.0",
11
+ "version": "0.9.2",
12
12
  "main": "LexicalList.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.9.0"
14
+ "lexical": "0.9.2"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/utils": "0.9.0"
17
+ "@lexical/utils": "0.9.2"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",
package/utils.d.ts CHANGED
@@ -7,11 +7,54 @@
7
7
  */
8
8
  import type { LexicalNode } from 'lexical';
9
9
  import { ListItemNode, ListNode } from './';
10
+ /**
11
+ * Checks the depth of listNode from the root node.
12
+ * @param listNode - The ListNode to be checked.
13
+ * @returns The depth of the ListNode.
14
+ */
10
15
  export declare function $getListDepth(listNode: ListNode): number;
16
+ /**
17
+ * Finds the nearest ancestral ListNode and returns it, throws an invariant if listItem is not a ListItemNode.
18
+ * @param listItem - The node to be checked.
19
+ * @returns The ListNode found.
20
+ */
11
21
  export declare function $getTopListNode(listItem: LexicalNode): ListNode;
22
+ /**
23
+ * Checks if listItem has no child ListNodes and has no ListItemNode ancestors with siblings.
24
+ * @param listItem - the ListItemNode to be checked.
25
+ * @returns true if listItem has no child ListNode and no ListItemNode ancestors with siblings, false otherwise.
26
+ */
12
27
  export declare function $isLastItemInList(listItem: ListItemNode): boolean;
28
+ /**
29
+ * A recursive Depth-First Search (Postorder Traversal) that finds all of a node's children
30
+ * that are of type ListItemNode and returns them in an array.
31
+ * @param node - The ListNode to start the search.
32
+ * @returns An array containing all nodes of type ListItemNode found.
33
+ */
13
34
  export declare function $getAllListItems(node: ListNode): Array<ListItemNode>;
35
+ /**
36
+ * Checks to see if the passed node is a ListItemNode and has a ListNode as a child.
37
+ * @param node - The node to be checked.
38
+ * @returns true if the node is a ListItemNode and has a ListNode child, false otherwise.
39
+ */
14
40
  export declare function isNestedListNode(node: LexicalNode | null | undefined): boolean;
41
+ /**
42
+ * Traverses up the tree and returns the first ListItemNode found.
43
+ * @param node - Node to start the search.
44
+ * @returns The first ListItemNode found, or null if none exist.
45
+ */
15
46
  export declare function findNearestListItemNode(node: LexicalNode): ListItemNode | null;
47
+ /**
48
+ * Takes a deeply nested ListNode or ListItemNode and traverses up the branch to delete the first
49
+ * ancestral ListNode (which could be the root ListNode) or ListItemNode with siblings, essentially
50
+ * bringing the deeply nested node up the branch once. Would remove sublist if it has siblings.
51
+ * Should not break ListItem -> List -> ListItem chain as empty List/ItemNodes should be removed on .remove().
52
+ * @param sublist - The nested ListNode or ListItemNode to be brought up the branch.
53
+ */
16
54
  export declare function $removeHighestEmptyListParent(sublist: ListItemNode | ListNode): void;
55
+ /**
56
+ * Wraps a node into a ListItemNode.
57
+ * @param node - The node to be wrapped into a ListItemNode
58
+ * @returns The ListItemNode which the passed node is wrapped in.
59
+ */
17
60
  export declare function wrapInListItem(node: LexicalNode): ListItemNode;