@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.
- package/LICENSE +24 -0
- package/README.md +11 -0
- package/dist/BaseListPlugin-n9uuTtLe.js +1390 -0
- package/dist/BaseListPlugin-n9uuTtLe.js.map +1 -0
- package/dist/index-CYSu3DSy.d.ts +328 -0
- package/dist/index-CYSu3DSy.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +49 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +156 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +59 -0
|
@@ -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
|