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