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