@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.
- package/LexicalList.dev.js +130 -3
- package/LexicalList.js.flow +2 -10
- package/LexicalList.prod.js +8 -8
- package/LexicalListItemNode.d.ts +10 -2
- package/LexicalListNode.d.ts +11 -2
- package/formatList.d.ts +51 -0
- package/package.json +3 -3
- package/utils.d.ts +43 -0
package/LexicalList.dev.js
CHANGED
|
@@ -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
|
-
}
|
|
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
|
-
|
|
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
|
}
|
package/LexicalList.js.flow
CHANGED
|
@@ -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
|
};
|
package/LexicalList.prod.js
CHANGED
|
@@ -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.
|
|
15
|
-
|
|
16
|
-
|
|
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(),
|
|
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();
|
|
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))}
|
|
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,
|
package/LexicalListItemNode.d.ts
CHANGED
|
@@ -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;
|
package/LexicalListNode.d.ts
CHANGED
|
@@ -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.
|
|
11
|
+
"version": "0.9.2",
|
|
12
12
|
"main": "LexicalList.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.9.
|
|
14
|
+
"lexical": "0.9.2"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/utils": "0.9.
|
|
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;
|