@lofcz/platejs-list-classic 52.0.11

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.
@@ -0,0 +1,1390 @@
1
+ import { ElementApi, KEYS, NodeApi, PathApi, RangeApi, bindFirst, createSlatePlugin, createTSlatePlugin, deleteMerge, match } from "platejs";
2
+
3
+ //#region src/lib/queries/isListNested.ts
4
+ /** Is the list nested, i.e. its parent is a list item. */
5
+ const isListNested = (editor, listPath) => {
6
+ return (editor.api.parent(listPath)?.[0])?.type === editor.getType(KEYS.li);
7
+ };
8
+
9
+ //#endregion
10
+ //#region src/lib/queries/getListTypes.ts
11
+ const getListTypes = (editor) => [
12
+ editor.getType(KEYS.olClassic),
13
+ editor.getType(KEYS.ulClassic),
14
+ editor.getType(KEYS.taskList)
15
+ ];
16
+
17
+ //#endregion
18
+ //#region src/lib/queries/getHighestEmptyList.ts
19
+ /**
20
+ * Find the highest end list that can be deleted. Its path should be different
21
+ * to diffListPath. If the highest end list 2+ items, return liPath. Get the
22
+ * parent list until:
23
+ *
24
+ * - The list has less than 2 items.
25
+ * - Its path is not equals to diffListPath.
26
+ */
27
+ const getHighestEmptyList = (editor, { diffListPath, liPath }) => {
28
+ const list = editor.api.above({
29
+ at: liPath,
30
+ match: { type: getListTypes(editor) }
31
+ });
32
+ if (!list) return;
33
+ const [listNode, listPath] = list;
34
+ if (!diffListPath || !PathApi.equals(listPath, diffListPath)) {
35
+ if (listNode.children.length < 2) {
36
+ const liParent = editor.api.above({
37
+ at: listPath,
38
+ match: { type: editor.getType(KEYS.li) }
39
+ });
40
+ if (liParent) return getHighestEmptyList(editor, {
41
+ diffListPath,
42
+ liPath: liParent[1]
43
+ }) || listPath;
44
+ }
45
+ return liPath;
46
+ }
47
+ };
48
+
49
+ //#endregion
50
+ //#region src/lib/queries/getListItemEntry.ts
51
+ /**
52
+ * Returns the nearest li and ul / ol wrapping node entries for a given path
53
+ * (default = selection)
54
+ */
55
+ const getListItemEntry = (editor, { at = editor.selection } = {}) => {
56
+ const liType = editor.getType(KEYS.li);
57
+ let _at;
58
+ if (RangeApi.isRange(at) && !RangeApi.isCollapsed(at)) _at = at.focus.path;
59
+ else if (RangeApi.isRange(at)) _at = at.anchor.path;
60
+ else _at = at;
61
+ if (_at) {
62
+ if (NodeApi.get(editor, _at)) {
63
+ const listItem = editor.api.above({
64
+ at: _at,
65
+ match: { type: liType }
66
+ });
67
+ if (listItem) return {
68
+ list: editor.api.parent(listItem[1]),
69
+ listItem
70
+ };
71
+ }
72
+ }
73
+ };
74
+
75
+ //#endregion
76
+ //#region src/lib/queries/getListRoot.ts
77
+ /** Searches upward for the root list element */
78
+ const getListRoot = (editor, at = editor.selection) => {
79
+ if (!at) return;
80
+ const parentList = editor.api.above({
81
+ at,
82
+ match: { type: getListTypes(editor) }
83
+ });
84
+ if (parentList) {
85
+ const [, parentListPath] = parentList;
86
+ return getListRoot(editor, parentListPath) ?? parentList;
87
+ }
88
+ };
89
+
90
+ //#endregion
91
+ //#region src/lib/queries/getTaskListProps.ts
92
+ const getPropsIfTaskListLiNode = (editor, { inherit = false, liNode: node }) => editor.getType(KEYS.li) === node.type && "checked" in node ? { checked: inherit ? node.checked : false } : void 0;
93
+ const getPropsIfTaskList = (editor, type, partial = {}) => editor.getType(KEYS.taskList) === type ? {
94
+ checked: false,
95
+ ...partial
96
+ } : void 0;
97
+
98
+ //#endregion
99
+ //#region src/lib/queries/getTodoListItemEntry.ts
100
+ /**
101
+ * Returns the nearest li and ul / ol wrapping node entries for a given path
102
+ * (default = selection)
103
+ */
104
+ const getTodoListItemEntry = (editor, { at = editor.selection } = {}) => {
105
+ const todoType = editor.getType(KEYS.listTodoClassic);
106
+ let _at;
107
+ if (RangeApi.isRange(at) && !RangeApi.isCollapsed(at)) _at = at.focus.path;
108
+ else if (RangeApi.isRange(at)) _at = at.anchor.path;
109
+ else _at = at;
110
+ if (_at) {
111
+ if (NodeApi.get(editor, _at)) {
112
+ const listItem = editor.api.above({
113
+ at: _at,
114
+ match: { type: todoType }
115
+ });
116
+ if (listItem) return {
117
+ list: editor.api.parent(listItem[1]),
118
+ listItem
119
+ };
120
+ }
121
+ }
122
+ };
123
+
124
+ //#endregion
125
+ //#region src/lib/queries/hasListChild.ts
126
+ /** Is there a list child in the node. */
127
+ const hasListChild = (editor, node) => node.children.some((n) => match(n, [], { type: getListTypes(editor) }));
128
+
129
+ //#endregion
130
+ //#region src/lib/queries/isAcrossListItems.ts
131
+ /** Is selection across blocks with list items */
132
+ const isAcrossListItems = (editor, at = editor.selection) => {
133
+ if (!at || RangeApi.isCollapsed(at)) return false;
134
+ if (!editor.api.isAt({
135
+ at,
136
+ blocks: true
137
+ })) return false;
138
+ return editor.api.some({
139
+ at,
140
+ match: { type: editor.getType(KEYS.li) }
141
+ });
142
+ };
143
+
144
+ //#endregion
145
+ //#region src/lib/queries/isListRoot.ts
146
+ const isListRoot = (editor, node) => ElementApi.isElement(node) && getListTypes(editor).includes(node.type);
147
+
148
+ //#endregion
149
+ //#region src/lib/transforms/moveListItemDown.ts
150
+ const moveListItemDown = (editor, { list, listItem }) => {
151
+ let moved = false;
152
+ const [listNode] = list;
153
+ const [, listItemPath] = listItem;
154
+ const previousListItemPath = PathApi.previous(listItemPath);
155
+ if (!previousListItemPath) return;
156
+ const previousSiblingItem = editor.api.node(previousListItemPath);
157
+ if (previousSiblingItem) {
158
+ const [previousNode, previousPath] = previousSiblingItem;
159
+ const sublist = previousNode.children.find((n) => match(n, [], { type: getListTypes(editor) }));
160
+ const newPath = previousPath.concat(sublist ? [1, sublist.children.length] : [1]);
161
+ editor.tf.withoutNormalizing(() => {
162
+ if (!sublist) editor.tf.wrapNodes({
163
+ children: [],
164
+ type: listNode.type
165
+ }, { at: listItemPath });
166
+ editor.tf.moveNodes({
167
+ at: listItemPath,
168
+ to: newPath
169
+ });
170
+ moved = true;
171
+ });
172
+ }
173
+ return moved;
174
+ };
175
+
176
+ //#endregion
177
+ //#region src/lib/transforms/moveListItemsToList.ts
178
+ /**
179
+ * Move the list items of the sublist of `fromListItem` to `toList` (if
180
+ * `fromListItem` is defined). Move the list items of `fromList` to `toList` (if
181
+ * `fromList` is defined).
182
+ */
183
+ const moveListItemsToList = (editor, { deleteFromList = true, fromList, fromListItem, fromStartIndex, to: _to, toList, toListIndex = null }) => {
184
+ let fromListPath;
185
+ let moved = false;
186
+ editor.tf.withoutNormalizing(() => {
187
+ if (fromListItem) {
188
+ const fromListItemSublist = editor.api.descendant({
189
+ at: fromListItem[1],
190
+ match: { type: getListTypes(editor) }
191
+ });
192
+ if (!fromListItemSublist) return;
193
+ fromListPath = fromListItemSublist?.[1];
194
+ } else if (fromList) fromListPath = fromList[1];
195
+ else return;
196
+ let to = null;
197
+ if (_to) to = _to;
198
+ if (toList) if (toListIndex === null) {
199
+ const lastChildPath = NodeApi.lastChild(editor, toList[1])?.[1];
200
+ to = lastChildPath ? PathApi.next(lastChildPath) : toList[1].concat([0]);
201
+ } else to = toList[1].concat([toListIndex]);
202
+ if (!to) return;
203
+ moved = editor.tf.moveNodes({
204
+ at: fromListPath,
205
+ children: true,
206
+ fromIndex: fromStartIndex,
207
+ to
208
+ });
209
+ if (deleteFromList) editor.tf.delete({ at: fromListPath });
210
+ });
211
+ return moved;
212
+ };
213
+
214
+ //#endregion
215
+ //#region src/lib/transforms/unwrapList.ts
216
+ const unwrapList = (editor, { at } = {}) => {
217
+ const ancestorListTypeCheck = () => {
218
+ if (editor.api.above({
219
+ at,
220
+ match: { type: getListTypes(editor) }
221
+ })) return true;
222
+ if (!at && editor.selection) {
223
+ const commonNode = NodeApi.common(editor, editor.selection.anchor.path, editor.selection.focus.path);
224
+ if (ElementApi.isElement(commonNode[0]) && getListTypes(editor).includes(commonNode[0].type)) return true;
225
+ }
226
+ return false;
227
+ };
228
+ editor.tf.withoutNormalizing(() => {
229
+ do {
230
+ editor.tf.unwrapNodes({
231
+ at,
232
+ match: { type: editor.getType(KEYS.li) },
233
+ split: true
234
+ });
235
+ editor.tf.unwrapNodes({
236
+ at,
237
+ match: { type: getListTypes(editor) },
238
+ split: true
239
+ });
240
+ } while (ancestorListTypeCheck());
241
+ });
242
+ };
243
+
244
+ //#endregion
245
+ //#region src/lib/transforms/moveListItemUp.ts
246
+ /** Move a list item up. */
247
+ const moveListItemUp = (editor, { list, listItem }) => {
248
+ const move = () => {
249
+ const [listNode, listPath] = list;
250
+ const [liNode, liPath] = listItem;
251
+ const liParent = editor.api.above({
252
+ at: listPath,
253
+ match: { type: editor.getType(KEYS.li) }
254
+ });
255
+ if (!liParent) {
256
+ let toListPath$1;
257
+ try {
258
+ toListPath$1 = PathApi.next(listPath);
259
+ } catch (_error) {
260
+ return;
261
+ }
262
+ const condA = hasListChild(editor, liNode);
263
+ const condB = !NodeApi.isLastChild(editor, liPath);
264
+ if (condA || condB) editor.tf.insertNodes({
265
+ children: [],
266
+ type: listNode.type
267
+ }, { at: toListPath$1 });
268
+ if (condA) {
269
+ const toListNode = NodeApi.get(editor, toListPath$1);
270
+ if (!toListNode) return;
271
+ moveListItemsToList(editor, {
272
+ fromListItem: listItem,
273
+ toList: [toListNode, toListPath$1]
274
+ });
275
+ }
276
+ if (condB) {
277
+ const toListNode = NodeApi.get(editor, toListPath$1);
278
+ if (!toListNode) return;
279
+ moveListItemsToList(editor, {
280
+ deleteFromList: false,
281
+ fromList: list,
282
+ fromStartIndex: liPath.at(-1) + 1,
283
+ toList: [toListNode, toListPath$1]
284
+ });
285
+ }
286
+ unwrapList(editor, { at: liPath.concat(0) });
287
+ return true;
288
+ }
289
+ const [, liParentPath] = liParent;
290
+ const toListPath = liPath.concat([1]);
291
+ if (!NodeApi.isLastChild(editor, liPath)) {
292
+ if (!hasListChild(editor, liNode)) editor.tf.insertNodes({
293
+ children: [],
294
+ type: listNode.type
295
+ }, { at: toListPath });
296
+ const toListNode = NodeApi.get(editor, toListPath);
297
+ if (!toListNode) return;
298
+ moveListItemsToList(editor, {
299
+ deleteFromList: false,
300
+ fromListItem: liParent,
301
+ fromStartIndex: liPath.at(-1) + 1,
302
+ toList: [toListNode, toListPath]
303
+ });
304
+ }
305
+ const movedUpLiPath = PathApi.next(liParentPath);
306
+ editor.tf.moveNodes({
307
+ at: liPath,
308
+ to: movedUpLiPath
309
+ });
310
+ return true;
311
+ };
312
+ let moved = false;
313
+ editor.tf.withoutNormalizing(() => {
314
+ moved = move();
315
+ });
316
+ return moved;
317
+ };
318
+
319
+ //#endregion
320
+ //#region src/lib/transforms/removeFirstListItem.ts
321
+ /** If list is not nested and if li is not the first child, move li up. */
322
+ const removeFirstListItem = (editor, { list, listItem }) => {
323
+ const [, listPath] = list;
324
+ if (!isListNested(editor, listPath)) {
325
+ moveListItemUp(editor, {
326
+ list,
327
+ listItem
328
+ });
329
+ return true;
330
+ }
331
+ return false;
332
+ };
333
+
334
+ //#endregion
335
+ //#region src/lib/transforms/moveListItems.ts
336
+ const moveListItems = (editor, { at = editor.selection ?? void 0, enableResetOnShiftTab, increase = true } = {}) => {
337
+ const _nodes = editor.api.nodes({
338
+ at,
339
+ match: { type: editor.getType(KEYS.lic) }
340
+ });
341
+ const lics = Array.from(_nodes);
342
+ if (lics.length === 0) return;
343
+ const highestLicPaths = [];
344
+ const highestLicPathRefs = [];
345
+ lics.forEach((lic) => {
346
+ const licPath = lic[1];
347
+ const liPath = PathApi.parent(licPath);
348
+ if (!highestLicPaths.some((path) => {
349
+ const highestLiPath = PathApi.parent(path);
350
+ return PathApi.isAncestor(highestLiPath, liPath);
351
+ })) {
352
+ highestLicPaths.push(licPath);
353
+ highestLicPathRefs.push(editor.api.pathRef(licPath));
354
+ }
355
+ });
356
+ const licPathRefsToMove = increase ? highestLicPathRefs : highestLicPathRefs.reverse();
357
+ return editor.tf.withoutNormalizing(() => {
358
+ let moved = false;
359
+ licPathRefsToMove.forEach((licPathRef) => {
360
+ const licPath = licPathRef.unref();
361
+ if (!licPath) return;
362
+ const listItem = editor.api.parent(licPath);
363
+ if (!listItem) return;
364
+ const parentList = editor.api.parent(listItem[1]);
365
+ if (!parentList) return;
366
+ let _moved;
367
+ if (increase) _moved = moveListItemDown(editor, {
368
+ list: parentList,
369
+ listItem
370
+ });
371
+ else if (isListNested(editor, parentList[1])) _moved = moveListItemUp(editor, {
372
+ list: parentList,
373
+ listItem
374
+ });
375
+ else if (enableResetOnShiftTab) _moved = removeFirstListItem(editor, {
376
+ list: parentList,
377
+ listItem
378
+ });
379
+ moved = _moved || moved;
380
+ });
381
+ return moved;
382
+ });
383
+ };
384
+
385
+ //#endregion
386
+ //#region src/lib/transforms/insertListItem.ts
387
+ /** Insert list item if selection in li>p. TODO: test */
388
+ const insertListItem = (editor, options = {}) => {
389
+ const liType = editor.getType(KEYS.li);
390
+ const licType = editor.getType(KEYS.lic);
391
+ if (!editor.selection) return false;
392
+ const licEntry = editor.api.above({ match: { type: licType } });
393
+ if (!licEntry) return false;
394
+ const [, paragraphPath] = licEntry;
395
+ const listItemEntry = editor.api.parent(paragraphPath);
396
+ if (!listItemEntry) return false;
397
+ const [listItemNode, listItemPath] = listItemEntry;
398
+ if (listItemNode.type !== liType) return false;
399
+ const optionalTasklistProps = "checked" in listItemNode ? { checked: false } : void 0;
400
+ let success = false;
401
+ editor.tf.withoutNormalizing(() => {
402
+ if (!editor.api.isCollapsed()) editor.tf.delete();
403
+ const isStart = editor.api.isStart(editor.selection.focus, paragraphPath);
404
+ const isEnd = editor.api.isEmpty(editor.selection, { after: true });
405
+ const nextParagraphPath = PathApi.next(paragraphPath);
406
+ const nextListItemPath = PathApi.next(listItemPath);
407
+ /** If start, insert a list item before */
408
+ if (isStart) {
409
+ if (optionalTasklistProps && options.inheritCheckStateOnLineStartBreak) optionalTasklistProps.checked = listItemNode.checked;
410
+ editor.tf.insertNodes({
411
+ children: [{
412
+ children: [{ text: "" }],
413
+ type: licType
414
+ }],
415
+ ...optionalTasklistProps,
416
+ type: liType
417
+ }, { at: listItemPath });
418
+ success = true;
419
+ return;
420
+ }
421
+ /**
422
+ * If not end, split nodes, wrap a list item on the new paragraph and move
423
+ * it to the next list item
424
+ */
425
+ if (isEnd) {
426
+ /** If end, insert a list item after and select it */
427
+ const marks = editor.api.marks() || {};
428
+ if (optionalTasklistProps && options.inheritCheckStateOnLineEndBreak) optionalTasklistProps.checked = listItemNode.checked;
429
+ editor.tf.insertNodes({
430
+ children: [{
431
+ children: [{
432
+ text: "",
433
+ ...marks
434
+ }],
435
+ type: licType
436
+ }],
437
+ ...optionalTasklistProps,
438
+ type: liType
439
+ }, { at: nextListItemPath });
440
+ editor.tf.select(nextListItemPath);
441
+ } else editor.tf.withoutNormalizing(() => {
442
+ editor.tf.splitNodes();
443
+ editor.tf.wrapNodes({
444
+ children: [],
445
+ ...optionalTasklistProps,
446
+ type: liType
447
+ }, { at: nextParagraphPath });
448
+ editor.tf.moveNodes({
449
+ at: nextParagraphPath,
450
+ to: nextListItemPath
451
+ });
452
+ editor.tf.select(nextListItemPath);
453
+ editor.tf.collapse({ edge: "start" });
454
+ });
455
+ /** If there is a list in the list item, move it to the next list item */
456
+ if (listItemNode.children.length > 1) editor.tf.moveNodes({
457
+ at: nextParagraphPath,
458
+ to: nextListItemPath.concat(1)
459
+ });
460
+ success = true;
461
+ });
462
+ return success;
463
+ };
464
+
465
+ //#endregion
466
+ //#region src/lib/BaseTodoListPlugin.ts
467
+ const BaseTodoListPlugin = createSlatePlugin({
468
+ key: KEYS.listTodoClassic,
469
+ node: { isElement: true },
470
+ options: {
471
+ inheritCheckStateOnLineEndBreak: false,
472
+ inheritCheckStateOnLineStartBreak: false
473
+ }
474
+ }).overrideEditor(({ editor, tf: { insertBreak } }) => ({ transforms: { insertBreak() {
475
+ const insertBreakTodoList = () => {
476
+ if (!editor.selection) return;
477
+ if (getTodoListItemEntry(editor)) {
478
+ if (insertTodoListItem(editor)) return true;
479
+ }
480
+ };
481
+ if (insertBreakTodoList()) return;
482
+ insertBreak();
483
+ } } })).extendTransforms(({ editor, type }) => ({ toggle: () => {
484
+ editor.tf.toggleBlock(type);
485
+ } }));
486
+
487
+ //#endregion
488
+ //#region src/lib/transforms/insertTodoListItem.ts
489
+ /** Insert todo list item if selection in li>p. TODO: test */
490
+ const insertTodoListItem = (editor) => {
491
+ const { inheritCheckStateOnLineEndBreak, inheritCheckStateOnLineStartBreak } = editor.getOptions(BaseTodoListPlugin);
492
+ const todoType = editor.getType(KEYS.listTodoClassic);
493
+ if (!editor.selection) return false;
494
+ const todoEntry = editor.api.above({ match: { type: todoType } });
495
+ if (!todoEntry) return false;
496
+ const [todo, paragraphPath] = todoEntry;
497
+ let success = false;
498
+ editor.tf.withoutNormalizing(() => {
499
+ if (!editor.api.isCollapsed()) editor.tf.delete();
500
+ const isStart = editor.api.isStart(editor.selection.focus, paragraphPath);
501
+ const isEnd = editor.api.isEmpty(editor.selection, { after: true });
502
+ const nextParagraphPath = PathApi.next(paragraphPath);
503
+ /** If start, insert a list item before */
504
+ if (isStart) {
505
+ editor.tf.insertNodes({
506
+ checked: inheritCheckStateOnLineStartBreak ? todo.checked : false,
507
+ children: [{ text: "" }],
508
+ type: todoType
509
+ }, { at: paragraphPath });
510
+ success = true;
511
+ return;
512
+ }
513
+ /** If not end, split the nodes */
514
+ if (isEnd) {
515
+ /** If end, insert a list item after and select it */
516
+ const marks = editor.api.marks() || {};
517
+ editor.tf.insertNodes({
518
+ checked: inheritCheckStateOnLineEndBreak ? todo.checked : false,
519
+ children: [{
520
+ text: "",
521
+ ...marks
522
+ }],
523
+ type: todoType
524
+ }, { at: nextParagraphPath });
525
+ editor.tf.select(nextParagraphPath);
526
+ } else editor.tf.withoutNormalizing(() => {
527
+ editor.tf.splitNodes();
528
+ });
529
+ success = true;
530
+ });
531
+ return success;
532
+ };
533
+
534
+ //#endregion
535
+ //#region src/lib/transforms/moveListItemSublistItemsToListItemSublist.ts
536
+ /**
537
+ * Move fromListItem sublist list items to the end of `toListItem` sublist. If
538
+ * there is no `toListItem` sublist, insert one.
539
+ */
540
+ const moveListItemSublistItemsToListItemSublist = (editor, { fromListItem, start, toListItem }) => {
541
+ const [, fromListItemPath] = fromListItem;
542
+ const [, toListItemPath] = toListItem;
543
+ let moved = false;
544
+ editor.tf.withoutNormalizing(() => {
545
+ const fromListItemSublist = editor.api.descendant({
546
+ at: fromListItemPath,
547
+ match: { type: getListTypes(editor) }
548
+ });
549
+ if (!fromListItemSublist) return;
550
+ const [, fromListItemSublistPath] = fromListItemSublist;
551
+ const toListItemSublist = editor.api.descendant({
552
+ at: toListItemPath,
553
+ match: { type: getListTypes(editor) }
554
+ });
555
+ let to;
556
+ if (!toListItemSublist) {
557
+ const fromList = editor.api.parent(fromListItemPath);
558
+ if (!fromList) return;
559
+ const [fromListNode] = fromList;
560
+ const fromListType = fromListNode.type;
561
+ const toListItemSublistPath = toListItemPath.concat([1]);
562
+ editor.tf.insertNodes({
563
+ children: [],
564
+ type: fromListType
565
+ }, { at: toListItemSublistPath });
566
+ to = toListItemSublistPath.concat([0]);
567
+ } else if (start) {
568
+ const [, toListItemSublistPath] = toListItemSublist;
569
+ to = toListItemSublistPath.concat([0]);
570
+ } else to = PathApi.next(NodeApi.lastChild(editor, toListItemSublist[1])[1]);
571
+ moved = editor.tf.moveNodes({
572
+ at: fromListItemSublistPath,
573
+ children: true,
574
+ to
575
+ });
576
+ editor.tf.delete({ at: fromListItemSublistPath });
577
+ });
578
+ return moved;
579
+ };
580
+
581
+ //#endregion
582
+ //#region src/lib/transforms/removeListItem.ts
583
+ /** Remove list item and move its sublist to list if any. */
584
+ const removeListItem = (editor, { list, listItem, reverse = true }) => {
585
+ const [liNode, liPath] = listItem;
586
+ if (editor.api.isExpanded() || !hasListChild(editor, liNode)) return false;
587
+ const previousLiPath = PathApi.previous(liPath);
588
+ let success = false;
589
+ editor.tf.withoutNormalizing(() => {
590
+ /**
591
+ * If there is a previous li, we need to move sub-lis to the previous li. As
592
+ * we need to delete first, we will:
593
+ *
594
+ * 1. Insert a temporary li: tempLi
595
+ * 2. Move sub-lis to tempLi
596
+ * 3. Delete
597
+ * 4. Move sub-lis from tempLi to the previous li.
598
+ * 5. Remove tempLi
599
+ */
600
+ if (previousLiPath) {
601
+ const previousLi = editor.api.node(previousLiPath);
602
+ if (!previousLi) return;
603
+ let tempLiPath = PathApi.next(liPath);
604
+ editor.tf.insertNodes({
605
+ children: [{
606
+ children: [{ text: "" }],
607
+ type: editor.getType(KEYS.lic)
608
+ }],
609
+ ...getPropsIfTaskListLiNode(editor, {
610
+ inherit: true,
611
+ liNode: previousLi[0]
612
+ }),
613
+ type: editor.getType(KEYS.li)
614
+ }, { at: tempLiPath });
615
+ const tempLi = editor.api.node(tempLiPath);
616
+ if (!tempLi) return;
617
+ const tempLiPathRef = editor.api.pathRef(tempLi[1]);
618
+ moveListItemSublistItemsToListItemSublist(editor, {
619
+ fromListItem: listItem,
620
+ toListItem: tempLi
621
+ });
622
+ deleteMerge(editor, { reverse });
623
+ tempLiPath = tempLiPathRef.unref();
624
+ moveListItemSublistItemsToListItemSublist(editor, {
625
+ fromListItem: [tempLi[0], tempLiPath],
626
+ toListItem: previousLi
627
+ });
628
+ editor.tf.removeNodes({ at: tempLiPath });
629
+ success = true;
630
+ return;
631
+ }
632
+ moveListItemsToList(editor, {
633
+ fromListItem: listItem,
634
+ toList: list,
635
+ toListIndex: 1
636
+ });
637
+ });
638
+ return success;
639
+ };
640
+
641
+ //#endregion
642
+ //#region src/lib/transforms/toggleList.ts
643
+ const _toggleList = (editor, { checked = false, type }) => editor.tf.withoutNormalizing(() => {
644
+ if (!editor.selection) return;
645
+ const { validLiChildrenTypes } = editor.getOptions(BaseListPlugin);
646
+ if (editor.api.isCollapsed() || !editor.api.isAt({ blocks: true })) {
647
+ const res = getListItemEntry(editor);
648
+ if (res) {
649
+ const { list } = res;
650
+ if (list[0].type === type) unwrapList(editor);
651
+ else editor.tf.setNodes({ type }, {
652
+ at: editor.selection,
653
+ mode: "lowest",
654
+ match: (n) => ElementApi.isElement(n) && getListTypes(editor).includes(n.type)
655
+ });
656
+ } else {
657
+ const list = {
658
+ children: [],
659
+ type
660
+ };
661
+ editor.tf.wrapNodes(list);
662
+ const _nodes = editor.api.nodes({ match: { type: editor.getType(KEYS.p) } });
663
+ const nodes = Array.from(_nodes);
664
+ if (!editor.api.block({ match: { type: validLiChildrenTypes } })) editor.tf.setNodes({ type: editor.getType(KEYS.lic) });
665
+ const listItem = {
666
+ children: [],
667
+ ...getPropsIfTaskList(editor, type, { checked }),
668
+ type: editor.getType(KEYS.li)
669
+ };
670
+ for (const [, path] of nodes) editor.tf.wrapNodes(listItem, { at: path });
671
+ }
672
+ } else {
673
+ const [startPoint, endPoint] = RangeApi.edges(editor.selection);
674
+ const commonEntry = NodeApi.common(editor, startPoint.path, endPoint.path);
675
+ if (getListTypes(editor).includes(commonEntry[0].type) || commonEntry[0].type === editor.getType(KEYS.li)) if (commonEntry[0].type === type) unwrapList(editor);
676
+ else {
677
+ const startList = editor.api.node({
678
+ at: RangeApi.start(editor.selection),
679
+ match: { type: getListTypes(editor) },
680
+ mode: "lowest"
681
+ });
682
+ const endList = editor.api.node({
683
+ at: RangeApi.end(editor.selection),
684
+ match: { type: getListTypes(editor) },
685
+ mode: "lowest"
686
+ });
687
+ const rangeLength = Math.min(startList[1].length, endList[1].length);
688
+ editor.tf.setNodes({ type }, {
689
+ at: editor.selection,
690
+ mode: "all",
691
+ match: (n, path) => ElementApi.isElement(n) && getListTypes(editor).includes(n.type) && path.length >= rangeLength
692
+ });
693
+ }
694
+ else {
695
+ const rootPathLength = commonEntry[1].length;
696
+ const _nodes = editor.api.nodes({ mode: "all" });
697
+ Array.from(_nodes).filter(([, path]) => path.length === rootPathLength + 1).forEach((n) => {
698
+ if (getListTypes(editor).includes(n[0].type)) editor.tf.setNodes({ type }, {
699
+ at: n[1],
700
+ mode: "all",
701
+ match: (_n) => ElementApi.isElement(_n) && getListTypes(editor).includes(_n.type)
702
+ });
703
+ else {
704
+ if (!validLiChildrenTypes?.includes(n[0].type)) editor.tf.setNodes({ type: editor.getType(KEYS.lic) }, { at: n[1] });
705
+ const listItem = {
706
+ children: [],
707
+ ...getPropsIfTaskList(editor, type, { checked }),
708
+ type: editor.getType(KEYS.li)
709
+ };
710
+ editor.tf.wrapNodes(listItem, { at: n[1] });
711
+ const list = {
712
+ children: [],
713
+ type
714
+ };
715
+ editor.tf.wrapNodes(list, { at: n[1] });
716
+ }
717
+ });
718
+ }
719
+ }
720
+ });
721
+ const toggleList = (editor, { type }) => _toggleList(editor, { type });
722
+ const toggleBulletedList = (editor) => toggleList(editor, { type: editor.getType(KEYS.ulClassic) });
723
+ const toggleTaskList = (editor, defaultChecked = false) => _toggleList(editor, {
724
+ checked: defaultChecked,
725
+ type: editor.getType(KEYS.taskList)
726
+ });
727
+ const toggleNumberedList = (editor) => toggleList(editor, { type: editor.getType(KEYS.olClassic) });
728
+
729
+ //#endregion
730
+ //#region src/lib/withDeleteBackwardList.ts
731
+ const withDeleteBackwardList = ({ editor, tf: { deleteBackward } }) => ({ transforms: { deleteBackward(unit) {
732
+ const deleteBackwardList = () => {
733
+ const res = getListItemEntry(editor, {});
734
+ let moved = false;
735
+ if (res) {
736
+ const { list, listItem } = res;
737
+ if (editor.api.isAt({
738
+ start: true,
739
+ match: (node) => node.type === editor.getType(KEYS.li)
740
+ })) editor.tf.withoutNormalizing(() => {
741
+ moved = removeFirstListItem(editor, {
742
+ list,
743
+ listItem
744
+ });
745
+ if (moved) return true;
746
+ moved = removeListItem(editor, {
747
+ list,
748
+ listItem
749
+ });
750
+ if (moved) return true;
751
+ if (!PathApi.hasPrevious(listItem[1]) && !isListNested(editor, list[1])) {
752
+ editor.tf.resetBlock({ at: listItem[1] });
753
+ moved = true;
754
+ return;
755
+ }
756
+ const pointBeforeListItem = editor.api.before(editor.selection.focus);
757
+ let currentLic;
758
+ let hasMultipleChildren = false;
759
+ if (pointBeforeListItem && isAcrossListItems(editor, {
760
+ anchor: editor.selection.anchor,
761
+ focus: pointBeforeListItem
762
+ })) {
763
+ const licType = editor.getType(KEYS.lic);
764
+ currentLic = [...editor.api.nodes({
765
+ at: listItem[1],
766
+ mode: "lowest",
767
+ match: (node) => node.type === licType
768
+ })][0];
769
+ hasMultipleChildren = currentLic[0].children.length > 1;
770
+ }
771
+ deleteMerge(editor, {
772
+ reverse: true,
773
+ unit
774
+ });
775
+ moved = true;
776
+ if (!currentLic || !hasMultipleChildren) return;
777
+ const leftoverListItem = editor.api.node(PathApi.parent(currentLic[1]));
778
+ if (leftoverListItem && leftoverListItem[0].children.length === 0) editor.tf.removeNodes({ at: leftoverListItem[1] });
779
+ });
780
+ }
781
+ return moved;
782
+ };
783
+ if (deleteBackwardList()) return;
784
+ deleteBackward(unit);
785
+ } } });
786
+
787
+ //#endregion
788
+ //#region src/lib/withDeleteForwardList.ts
789
+ const selectionIsNotInAListHandler = (editor) => {
790
+ const pointAfterSelection = editor.api.after(editor.selection.focus);
791
+ if (pointAfterSelection) {
792
+ const nextSiblingListRes = getListItemEntry(editor, { at: pointAfterSelection });
793
+ if (nextSiblingListRes) {
794
+ const { listItem } = nextSiblingListRes;
795
+ const parentBlockEntity = editor.api.block({ at: editor.selection.anchor });
796
+ if (!editor.api.string(parentBlockEntity[1])) {
797
+ editor.tf.removeNodes();
798
+ return true;
799
+ }
800
+ if (hasListChild(editor, listItem[0])) moveListItemUp(editor, getListItemEntry(editor, { at: [
801
+ ...listItem[1],
802
+ 1,
803
+ 0,
804
+ 0
805
+ ] }));
806
+ }
807
+ }
808
+ return false;
809
+ };
810
+ const selectionIsInAListHandler = (editor, res, defaultDelete, unit = "character") => {
811
+ const { listItem } = res;
812
+ if (!hasListChild(editor, listItem[0])) {
813
+ const liType = editor.getType(KEYS.li);
814
+ const _nodes = editor.api.nodes({
815
+ at: listItem[1],
816
+ mode: "lowest",
817
+ match: (node, path) => {
818
+ if (path.length === 0) return false;
819
+ const isNodeLi = node.type === liType;
820
+ const isSiblingOfNodeLi = NodeApi.get(editor, PathApi.next(path))?.type === liType;
821
+ return isNodeLi && isSiblingOfNodeLi;
822
+ }
823
+ });
824
+ const liWithSiblings = Array.from(_nodes, (entry) => entry[1])[0];
825
+ if (!liWithSiblings) {
826
+ const pointAfterListItem$1 = editor.api.after(listItem[1]);
827
+ if (pointAfterListItem$1) {
828
+ const nextSiblingListRes = getListItemEntry(editor, { at: pointAfterListItem$1 });
829
+ if (nextSiblingListRes) {
830
+ const listRoot = getListRoot(editor, listItem[1]);
831
+ moveListItemsToList(editor, {
832
+ deleteFromList: true,
833
+ fromList: nextSiblingListRes.list,
834
+ toList: listRoot
835
+ });
836
+ return true;
837
+ }
838
+ }
839
+ return false;
840
+ }
841
+ const siblingListItem = editor.api.node(PathApi.next(liWithSiblings));
842
+ if (!siblingListItem) return false;
843
+ const siblingList = editor.api.parent(siblingListItem[1]);
844
+ if (siblingList && removeListItem(editor, {
845
+ list: siblingList,
846
+ listItem: siblingListItem,
847
+ reverse: false
848
+ })) return true;
849
+ const pointAfterListItem = editor.api.after(editor.selection.focus);
850
+ if (!pointAfterListItem || !isAcrossListItems(editor, {
851
+ anchor: editor.selection.anchor,
852
+ focus: pointAfterListItem
853
+ })) return false;
854
+ const licType = editor.getType(KEYS.lic);
855
+ const nextSelectableLic = [...editor.api.nodes({
856
+ at: pointAfterListItem.path,
857
+ mode: "lowest",
858
+ match: (node) => node.type === licType
859
+ })][0];
860
+ if (nextSelectableLic[0].children.length < 2) return false;
861
+ defaultDelete(unit);
862
+ const leftoverListItem = editor.api.node(PathApi.parent(nextSelectableLic[1]));
863
+ if (leftoverListItem && leftoverListItem[0].children.length === 0) editor.tf.removeNodes({ at: leftoverListItem[1] });
864
+ return true;
865
+ }
866
+ const nestedList = editor.api.node(PathApi.next([...listItem[1], 0]));
867
+ if (!nestedList) return false;
868
+ const nestedListItem = Array.from(NodeApi.children(editor, nestedList[1]))[0];
869
+ if (removeFirstListItem(editor, {
870
+ list: nestedList,
871
+ listItem: nestedListItem
872
+ })) return true;
873
+ if (removeListItem(editor, {
874
+ list: nestedList,
875
+ listItem: nestedListItem
876
+ })) return true;
877
+ return false;
878
+ };
879
+ const withDeleteForwardList = ({ editor, tf: { deleteForward } }) => ({ transforms: { deleteForward(unit) {
880
+ const deleteForwardList = () => {
881
+ let skipDefaultDelete = false;
882
+ if (!editor?.selection) return skipDefaultDelete;
883
+ if (!editor.api.isAt({ end: true })) return skipDefaultDelete;
884
+ editor.tf.withoutNormalizing(() => {
885
+ const res = getListItemEntry(editor, {});
886
+ if (!res) {
887
+ skipDefaultDelete = selectionIsNotInAListHandler(editor);
888
+ return;
889
+ }
890
+ skipDefaultDelete = selectionIsInAListHandler(editor, res, deleteForward, unit);
891
+ });
892
+ return skipDefaultDelete;
893
+ };
894
+ if (deleteForwardList()) return;
895
+ deleteForward(unit);
896
+ } } });
897
+
898
+ //#endregion
899
+ //#region src/lib/withDeleteFragmentList.ts
900
+ const getLiStart = (editor) => {
901
+ const start = editor.api.start(editor.selection);
902
+ return editor.api.above({
903
+ at: start,
904
+ match: { type: editor.getType(KEYS.li) }
905
+ });
906
+ };
907
+ const withDeleteFragmentList = ({ editor, tf: { deleteFragment } }) => ({ transforms: { deleteFragment(direction) {
908
+ const deleteFragmentList = () => {
909
+ let deleted = false;
910
+ editor.tf.withoutNormalizing(() => {
911
+ if (!isAcrossListItems(editor)) return;
912
+ /**
913
+ * Check if the end li can be deleted (if it has no sublist). Store
914
+ * the path ref to delete it after deleteMerge.
915
+ */
916
+ const end = editor.api.end(editor.selection);
917
+ const liEnd = editor.api.above({
918
+ at: end,
919
+ match: { type: editor.getType(KEYS.li) }
920
+ });
921
+ const liEndPathRef = liEnd && !hasListChild(editor, liEnd[0]) ? editor.api.pathRef(liEnd[1]) : void 0;
922
+ if (!getLiStart(editor) || !liEnd) {
923
+ deleted = false;
924
+ return;
925
+ }
926
+ /** Delete fragment and move end block children to start block */
927
+ deleteMerge(editor);
928
+ const liStart = getLiStart(editor);
929
+ if (liEndPathRef) {
930
+ const liEndPath = liEndPathRef.unref();
931
+ const deletePath = getHighestEmptyList(editor, {
932
+ diffListPath: (liStart && editor.api.parent(liStart[1]))?.[1],
933
+ liPath: liEndPath
934
+ });
935
+ if (deletePath) editor.tf.removeNodes({ at: deletePath });
936
+ deleted = true;
937
+ }
938
+ });
939
+ return deleted;
940
+ };
941
+ if (deleteFragmentList()) return;
942
+ deleteFragment(direction);
943
+ } } });
944
+
945
+ //#endregion
946
+ //#region src/lib/withInsertBreakList.ts
947
+ const withInsertBreakList = ({ editor, getOptions, tf: { insertBreak } }) => ({ transforms: { insertBreak() {
948
+ const insertBreakList = () => {
949
+ if (!editor.selection) return;
950
+ const res = getListItemEntry(editor, {});
951
+ let moved;
952
+ if (res) {
953
+ const { list, listItem } = res;
954
+ if (editor.api.isEmpty(editor.selection, { block: true })) {
955
+ moved = moveListItemUp(editor, {
956
+ list,
957
+ listItem
958
+ });
959
+ if (moved) return true;
960
+ }
961
+ }
962
+ const block = editor.api.block({ match: { type: editor.getType(KEYS.li) } });
963
+ if (block && editor.api.isEmpty(editor.selection, { block: true })) {
964
+ if (editor.tf.resetBlock({ at: block[1] })) return true;
965
+ }
966
+ /** If selection is in li > p, insert li. */
967
+ if (!moved) {
968
+ if (insertListItem(editor, getOptions())) return true;
969
+ }
970
+ };
971
+ if (insertBreakList()) return;
972
+ insertBreak();
973
+ } } });
974
+
975
+ //#endregion
976
+ //#region src/lib/withInsertFragmentList.ts
977
+ const withInsertFragmentList = ({ editor, tf: { insertFragment } }) => {
978
+ const listItemType = editor.getType(KEYS.li);
979
+ const listItemContentType = editor.getType(KEYS.lic);
980
+ const getFirstAncestorOfType = (root, entry, type) => {
981
+ let ancestor = PathApi.parent(entry[1]);
982
+ while (NodeApi.get(root, ancestor).type !== type) ancestor = PathApi.parent(ancestor);
983
+ return [NodeApi.get(root, ancestor), ancestor];
984
+ };
985
+ const findListItemsWithContent = (first) => {
986
+ let prev = null;
987
+ let node = first;
988
+ while (isListRoot(editor, node) || node.type === listItemType && node.children[0].type !== listItemContentType) {
989
+ prev = node;
990
+ [node] = node.children;
991
+ }
992
+ return prev ? prev.children : [node];
993
+ };
994
+ /**
995
+ * Removes the "empty" leading lis. Empty in this context means lis only with
996
+ * other lis as children.
997
+ *
998
+ * @returns If argument is not a list root, returns it, otherwise returns ul[]
999
+ * or li[].
1000
+ */
1001
+ const trimList = (listRoot) => {
1002
+ if (!isListRoot(editor, listRoot)) return [listRoot];
1003
+ const _texts = NodeApi.texts(listRoot);
1004
+ const textEntries = Array.from(_texts);
1005
+ const commonAncestorEntry = textEntries.reduce((commonAncestor, textEntry) => PathApi.isAncestor(commonAncestor[1], textEntry[1]) ? commonAncestor : NodeApi.common(listRoot, textEntry[1], commonAncestor[1]), getFirstAncestorOfType(listRoot, textEntries[0], listItemType));
1006
+ const [first, ...rest] = isListRoot(editor, commonAncestorEntry[0]) ? commonAncestorEntry[0].children : [commonAncestorEntry[0]];
1007
+ return [...findListItemsWithContent(first), ...rest];
1008
+ };
1009
+ const wrapNodeIntoListItem = (node, props) => node.type === listItemType ? node : {
1010
+ children: [node],
1011
+ ...props,
1012
+ type: listItemType
1013
+ };
1014
+ /**
1015
+ * Checks if the fragment only consists of a single LIC in which case it is
1016
+ * considered the user's intention was to copy a text, not a list
1017
+ */
1018
+ const isSingleLic = (fragment) => {
1019
+ return fragment.length === 1 && isListRoot(editor, fragment[0]) && [...NodeApi.nodes({ children: fragment })].filter((entry) => ElementApi.isElement(entry[0])).filter(([node]) => node.type === listItemContentType).length === 1;
1020
+ };
1021
+ const getTextAndListItemNodes = (fragment, liEntry, licEntry) => {
1022
+ const [, liPath] = liEntry;
1023
+ const [licNode, licPath] = licEntry;
1024
+ const isEmptyNode = !NodeApi.string(licNode);
1025
+ const [first, ...rest] = fragment.flatMap(trimList).map((v) => wrapNodeIntoListItem(v, getPropsIfTaskListLiNode(editor, {
1026
+ inherit: true,
1027
+ liNode: liEntry[0]
1028
+ })));
1029
+ let textNode;
1030
+ let listItemNodes;
1031
+ if (isListRoot(editor, fragment[0])) if (isSingleLic(fragment)) {
1032
+ textNode = first;
1033
+ listItemNodes = rest;
1034
+ } else if (isEmptyNode) {
1035
+ const [, ...currentSublists] = NodeApi.get(editor, liPath).children;
1036
+ const [newLic, ...newSublists] = first.children;
1037
+ editor.tf.insertNodes(newLic, {
1038
+ at: PathApi.next(licPath),
1039
+ select: true
1040
+ });
1041
+ editor.tf.removeNodes({ at: licPath });
1042
+ if (newSublists?.length) if (currentSublists?.length) {
1043
+ const path = [
1044
+ ...liPath,
1045
+ 1,
1046
+ 0
1047
+ ];
1048
+ editor.tf.insertNodes(newSublists[0].children, {
1049
+ at: path,
1050
+ select: true
1051
+ });
1052
+ } else editor.tf.insertNodes(newSublists, {
1053
+ at: PathApi.next(licPath),
1054
+ select: true
1055
+ });
1056
+ textNode = { text: "" };
1057
+ listItemNodes = rest;
1058
+ } else {
1059
+ textNode = { text: "" };
1060
+ listItemNodes = [first, ...rest];
1061
+ }
1062
+ else {
1063
+ textNode = first;
1064
+ listItemNodes = rest;
1065
+ }
1066
+ return {
1067
+ listItemNodes,
1068
+ textNode
1069
+ };
1070
+ };
1071
+ return { transforms: { insertFragment(fragment) {
1072
+ let liEntry = editor.api.node({
1073
+ match: { type: listItemType },
1074
+ mode: "lowest"
1075
+ });
1076
+ if (!liEntry) return insertFragment(isListRoot(editor, fragment[0]) ? [{ text: "" }, ...fragment] : fragment);
1077
+ insertFragment([{ text: "" }]);
1078
+ liEntry = editor.api.node({
1079
+ match: { type: listItemType },
1080
+ mode: "lowest"
1081
+ });
1082
+ if (!liEntry) return insertFragment(isListRoot(editor, fragment[0]) ? [{ text: "" }, ...fragment] : fragment);
1083
+ const licEntry = editor.api.node({
1084
+ match: { type: listItemContentType },
1085
+ mode: "lowest"
1086
+ });
1087
+ if (!licEntry) return insertFragment(isListRoot(editor, fragment[0]) ? [{ text: "" }, ...fragment] : fragment);
1088
+ const { listItemNodes, textNode } = getTextAndListItemNodes(fragment, liEntry, licEntry);
1089
+ insertFragment([textNode]);
1090
+ const [, liPath] = liEntry;
1091
+ return editor.tf.insertNodes(listItemNodes, {
1092
+ at: PathApi.next(liPath),
1093
+ select: true
1094
+ });
1095
+ } } };
1096
+ };
1097
+
1098
+ //#endregion
1099
+ //#region src/lib/normalizers/normalizeListItem.ts
1100
+ /**
1101
+ * Recursively get all the:
1102
+ *
1103
+ * - Block children
1104
+ * - Inline children except those at excludeDepth
1105
+ */
1106
+ const getDeepInlineChildren = (editor, { children }) => {
1107
+ const inlineChildren = [];
1108
+ for (const child of children) if (editor.api.isBlock(child[0])) inlineChildren.push(...getDeepInlineChildren(editor, { children: Array.from(NodeApi.children(editor, child[1])) }));
1109
+ else inlineChildren.push(child);
1110
+ return inlineChildren;
1111
+ };
1112
+ /**
1113
+ * If the list item has no child: insert an empty list item container. Else:
1114
+ * move the children that are not valid to the list item container.
1115
+ */
1116
+ const normalizeListItem = (editor, { listItem, validLiChildrenTypes = [] }) => {
1117
+ let changed = false;
1118
+ const allValidLiChildrenTypes = new Set([
1119
+ editor.getType(KEYS.lic),
1120
+ editor.getType(KEYS.olClassic),
1121
+ editor.getType(KEYS.taskList),
1122
+ editor.getType(KEYS.ulClassic),
1123
+ ...validLiChildrenTypes
1124
+ ]);
1125
+ const [, liPath] = listItem;
1126
+ const liChildren = Array.from(NodeApi.children(editor, listItem[1]));
1127
+ const invalidLiChildrenPathRefs = liChildren.filter(([child]) => !allValidLiChildrenTypes.has(child.type)).map(([, childPath]) => editor.api.pathRef(childPath));
1128
+ const firstLiChild = liChildren[0];
1129
+ const [firstLiChildNode, firstLiChildPath] = firstLiChild ?? [];
1130
+ if (!firstLiChild || !editor.api.isBlock(firstLiChildNode)) {
1131
+ editor.tf.insertNodes(editor.api.create.block({ type: editor.getType(KEYS.lic) }), { at: liPath.concat([0]) });
1132
+ return true;
1133
+ }
1134
+ if (editor.api.isBlock(firstLiChildNode) && !match(firstLiChildNode, [], { type: editor.getType(KEYS.lic) })) {
1135
+ if (match(firstLiChildNode, [], { type: getListTypes(editor) })) {
1136
+ const parent = editor.api.parent(listItem[1]);
1137
+ const sublist = firstLiChild;
1138
+ Array.from(NodeApi.children(editor, firstLiChild[1])).reverse().forEach((c) => {
1139
+ moveListItemUp(editor, {
1140
+ list: sublist,
1141
+ listItem: c
1142
+ });
1143
+ });
1144
+ editor.tf.removeNodes({ at: [...parent[1], 0] });
1145
+ return true;
1146
+ }
1147
+ if (validLiChildrenTypes.includes(firstLiChildNode.type)) return true;
1148
+ editor.tf.setNodes({ type: editor.getType(KEYS.lic) }, { at: firstLiChildPath });
1149
+ changed = true;
1150
+ }
1151
+ const licChildren = Array.from(NodeApi.children(editor, firstLiChild[1]));
1152
+ if (licChildren.length > 0) {
1153
+ const blockPathRefs = [];
1154
+ const inlineChildren = [];
1155
+ for (const licChild of licChildren) {
1156
+ if (!editor.api.isBlock(licChild[0])) break;
1157
+ blockPathRefs.push(editor.api.pathRef(licChild[1]));
1158
+ inlineChildren.push(...getDeepInlineChildren(editor, { children: Array.from(NodeApi.children(editor, licChild[1])) }));
1159
+ }
1160
+ const to = PathApi.next(licChildren.at(-1)[1]);
1161
+ inlineChildren.reverse().forEach(([, path]) => {
1162
+ editor.tf.moveNodes({
1163
+ at: path,
1164
+ to
1165
+ });
1166
+ });
1167
+ blockPathRefs.forEach((pathRef) => {
1168
+ const path = pathRef.unref();
1169
+ if (path) editor.tf.removeNodes({ at: path });
1170
+ });
1171
+ if (blockPathRefs.length > 0) changed = true;
1172
+ }
1173
+ if (changed) return true;
1174
+ invalidLiChildrenPathRefs.reverse().forEach((ref) => {
1175
+ const path = ref.unref();
1176
+ if (path) editor.tf.moveNodes({
1177
+ at: path,
1178
+ to: firstLiChildPath.concat([0])
1179
+ });
1180
+ });
1181
+ return invalidLiChildrenPathRefs.length > 0;
1182
+ };
1183
+
1184
+ //#endregion
1185
+ //#region src/lib/normalizers/normalizeNestedList.ts
1186
+ const normalizeNestedList = (editor, { nestedListItem }) => {
1187
+ const [, path] = nestedListItem;
1188
+ const parentNode = editor.api.parent(path);
1189
+ if (!(parentNode && match(parentNode[0], [], { type: getListTypes(editor) }))) return false;
1190
+ const previousListItemPath = PathApi.previous(path);
1191
+ if (!previousListItemPath) return false;
1192
+ const previousSiblingItem = editor.api.node(previousListItemPath);
1193
+ if (previousSiblingItem) {
1194
+ const [, previousPath] = previousSiblingItem;
1195
+ const newPath = previousPath.concat([1]);
1196
+ editor.tf.moveNodes({
1197
+ at: path,
1198
+ to: newPath
1199
+ });
1200
+ return true;
1201
+ }
1202
+ };
1203
+
1204
+ //#endregion
1205
+ //#region src/lib/withNormalizeList.ts
1206
+ /** Normalize list node to force the ul>li>p+ul structure. */
1207
+ const withNormalizeList = ({ editor, getOptions, tf: { normalizeNode } }) => ({ transforms: { normalizeNode([node, path]) {
1208
+ const liType = editor.getType(KEYS.li);
1209
+ const licType = editor.getType(KEYS.lic);
1210
+ const defaultType = editor.getType(KEYS.p);
1211
+ if (!ElementApi.isElement(node)) return normalizeNode([node, path]);
1212
+ if (isListRoot(editor, node)) {
1213
+ const nonLiChild = Array.from(NodeApi.children(editor, path)).find(([child]) => child.type !== liType);
1214
+ if (nonLiChild) return editor.tf.wrapNodes({
1215
+ children: [],
1216
+ type: liType
1217
+ }, { at: nonLiChild[1] });
1218
+ if (node.type === editor.getType(KEYS.taskList)) {
1219
+ const nonTaskListItems = Array.from(NodeApi.children(editor, path)).filter(([child]) => child.type === liType && !("checked" in child));
1220
+ if (nonTaskListItems.length > 0) return editor.tf.withoutNormalizing(() => nonTaskListItems.forEach(([, itemPath]) => {
1221
+ editor.tf.setNodes({ checked: false }, { at: itemPath });
1222
+ }));
1223
+ } else {
1224
+ const taskListItems = Array.from(NodeApi.children(editor, path)).filter(([child]) => child.type === liType && "checked" in child);
1225
+ if (taskListItems.length > 0) return editor.tf.withoutNormalizing(() => taskListItems.forEach(([, itemPath]) => {
1226
+ editor.tf.unsetNodes("checked", { at: itemPath });
1227
+ }));
1228
+ }
1229
+ }
1230
+ if (match(node, [], { type: getListTypes(editor) })) {
1231
+ if (node.children.length === 0 || !node.children.some((item) => item.type === liType)) return editor.tf.removeNodes({ at: path });
1232
+ const nextPath = PathApi.next(path);
1233
+ const nextNode = NodeApi.get(editor, nextPath);
1234
+ if (nextNode?.type === node.type) moveListItemsToList(editor, {
1235
+ deleteFromList: true,
1236
+ fromList: [nextNode, nextPath],
1237
+ toList: [node, path]
1238
+ });
1239
+ const prevPath = PathApi.previous(path);
1240
+ const prevNode = NodeApi.get(editor, prevPath);
1241
+ if (prevNode?.type === node.type) {
1242
+ editor.tf.normalizeNode([prevNode, prevPath]);
1243
+ return;
1244
+ }
1245
+ if (normalizeNestedList(editor, { nestedListItem: [node, path] })) return;
1246
+ }
1247
+ if (node.type === editor.getType(KEYS.li) && normalizeListItem(editor, {
1248
+ listItem: [node, path],
1249
+ validLiChildrenTypes: getOptions().validLiChildrenTypes
1250
+ })) return;
1251
+ if (node.type === licType && licType !== defaultType && editor.api.parent(path)?.[0].type !== liType) {
1252
+ editor.tf.setNodes({ type: defaultType }, { at: path });
1253
+ return;
1254
+ }
1255
+ normalizeNode([node, path]);
1256
+ } } });
1257
+
1258
+ //#endregion
1259
+ //#region src/lib/withList.ts
1260
+ const withList = (ctx) => {
1261
+ const { editor, getOptions, tf: { resetBlock, tab } } = ctx;
1262
+ return { transforms: {
1263
+ resetBlock: (options) => {
1264
+ if (editor.api.block({
1265
+ at: options?.at,
1266
+ match: { type: editor.getType(KEYS.li) }
1267
+ })) {
1268
+ unwrapList(editor);
1269
+ return;
1270
+ }
1271
+ return resetBlock(options);
1272
+ },
1273
+ tab: (options) => {
1274
+ const apply = () => {
1275
+ let workRange = editor.selection;
1276
+ if (editor.selection) {
1277
+ const { selection } = editor;
1278
+ if (!editor.api.isCollapsed()) {
1279
+ const { anchor, focus } = RangeApi.isBackward(selection) ? {
1280
+ anchor: { ...selection.focus },
1281
+ focus: { ...selection.anchor }
1282
+ } : {
1283
+ anchor: { ...selection.anchor },
1284
+ focus: { ...selection.focus }
1285
+ };
1286
+ const unhangRange = editor.api.unhangRange({
1287
+ anchor,
1288
+ focus
1289
+ });
1290
+ if (unhangRange) {
1291
+ workRange = unhangRange;
1292
+ editor.tf.select(unhangRange);
1293
+ }
1294
+ }
1295
+ const listSelected = editor.api.some({ match: { type: editor.getType(KEYS.li) } });
1296
+ if (workRange && listSelected) {
1297
+ moveListItems(editor, {
1298
+ at: workRange,
1299
+ enableResetOnShiftTab: getOptions().enableResetOnShiftTab,
1300
+ increase: !options.reverse
1301
+ });
1302
+ return true;
1303
+ }
1304
+ }
1305
+ };
1306
+ if (apply()) return true;
1307
+ return tab(options);
1308
+ },
1309
+ ...withInsertBreakList(ctx).transforms,
1310
+ ...withDeleteBackwardList(ctx).transforms,
1311
+ ...withDeleteForwardList(ctx).transforms,
1312
+ ...withDeleteFragmentList(ctx).transforms,
1313
+ ...withInsertFragmentList(ctx).transforms,
1314
+ ...withNormalizeList(ctx).transforms
1315
+ } };
1316
+ };
1317
+
1318
+ //#endregion
1319
+ //#region src/lib/BaseListPlugin.ts
1320
+ const BaseBulletedListPlugin = createSlatePlugin({
1321
+ key: KEYS.ulClassic,
1322
+ node: {
1323
+ isContainer: true,
1324
+ isElement: true
1325
+ },
1326
+ parsers: { html: { deserializer: { rules: [{ validNodeName: "UL" }] } } },
1327
+ render: { as: "ul" }
1328
+ }).extendTransforms(({ editor }) => ({ toggle: () => {
1329
+ toggleBulletedList(editor);
1330
+ } }));
1331
+ const BaseNumberedListPlugin = createSlatePlugin({
1332
+ key: KEYS.olClassic,
1333
+ node: {
1334
+ isContainer: true,
1335
+ isElement: true
1336
+ },
1337
+ parsers: { html: { deserializer: { rules: [{ validNodeName: "OL" }] } } },
1338
+ render: { as: "ol" }
1339
+ }).extendTransforms(({ editor }) => ({ toggle: () => {
1340
+ toggleNumberedList(editor);
1341
+ } }));
1342
+ const BaseTaskListPlugin = createSlatePlugin({
1343
+ key: KEYS.taskList,
1344
+ node: {
1345
+ isContainer: true,
1346
+ isElement: true
1347
+ },
1348
+ options: {
1349
+ inheritCheckStateOnLineEndBreak: false,
1350
+ inheritCheckStateOnLineStartBreak: false
1351
+ },
1352
+ render: { as: "ul" }
1353
+ }).extendTransforms(({ editor }) => ({ toggle: () => {
1354
+ toggleTaskList(editor);
1355
+ } }));
1356
+ const BaseListItemPlugin = createSlatePlugin({
1357
+ key: KEYS.li,
1358
+ inject: { plugins: { [KEYS.html]: { parser: { preInsert: ({ editor, type }) => editor.api.some({ match: { type } }) } } } },
1359
+ node: {
1360
+ isContainer: true,
1361
+ isElement: true,
1362
+ isStrictSiblings: true
1363
+ },
1364
+ parsers: { html: { deserializer: { rules: [{ validNodeName: "LI" }] } } },
1365
+ render: { as: "li" }
1366
+ });
1367
+ const BaseListItemContentPlugin = createSlatePlugin({
1368
+ key: KEYS.lic,
1369
+ node: { isElement: true }
1370
+ });
1371
+ /** Enables support for bulleted, numbered and to-do lists. */
1372
+ const BaseListPlugin = createTSlatePlugin({
1373
+ key: KEYS.listClassic,
1374
+ plugins: [
1375
+ BaseBulletedListPlugin,
1376
+ BaseNumberedListPlugin,
1377
+ BaseTaskListPlugin,
1378
+ BaseListItemPlugin,
1379
+ BaseListItemContentPlugin
1380
+ ]
1381
+ }).overrideEditor(withList).extendEditorTransforms(({ editor }) => ({ toggle: {
1382
+ bulletedList: bindFirst(toggleBulletedList, editor),
1383
+ list: bindFirst(toggleList, editor),
1384
+ numberedList: bindFirst(toggleNumberedList, editor),
1385
+ taskList: bindFirst(toggleTaskList, editor)
1386
+ } }));
1387
+
1388
+ //#endregion
1389
+ export { moveListItemsToList as A, getHighestEmptyList as B, insertTodoListItem as C, removeFirstListItem as D, moveListItems as E, getTodoListItemEntry as F, isListNested as H, getPropsIfTaskList as I, getPropsIfTaskListLiNode as L, isListRoot as M, isAcrossListItems as N, moveListItemUp as O, hasListChild as P, getListRoot as R, moveListItemSublistItemsToListItemSublist as S, insertListItem as T, getListTypes as V, toggleBulletedList as _, BaseNumberedListPlugin as a, toggleTaskList as b, withNormalizeList as c, normalizeListItem as d, withInsertFragmentList as f, withDeleteBackwardList as g, withDeleteForwardList as h, BaseListPlugin as i, moveListItemDown as j, unwrapList as k, normalizeNestedList as l, withDeleteFragmentList as m, BaseListItemContentPlugin as n, BaseTaskListPlugin as o, withInsertBreakList as p, BaseListItemPlugin as r, withList as s, BaseBulletedListPlugin as t, getDeepInlineChildren as u, toggleList as v, BaseTodoListPlugin as w, removeListItem as x, toggleNumberedList as y, getListItemEntry as z };
1390
+ //# sourceMappingURL=BaseListPlugin-n9uuTtLe.js.map