@lofcz/platejs-list 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 +13 -0
- package/dist/index-BnhNqFus.d.ts +289 -0
- package/dist/index-BnhNqFus.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/react/index.d.ts +64 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +182 -0
- package/dist/react/index.js.map +1 -0
- package/dist/src-Dvsp2AeZ.js +803 -0
- package/dist/src-Dvsp2AeZ.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { c } from "react-compiler-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { KEYS, NodeApi, PathApi, createTSlatePlugin, getInjectMatch, isDefined, isHtmlBlockElement, postCleanHtml, traverseHtmlElements } from "platejs";
|
|
4
|
+
import { setIndent } from "@platejs/indent";
|
|
5
|
+
|
|
6
|
+
//#region src/lib/types.ts
|
|
7
|
+
const ListStyleType = {
|
|
8
|
+
ArabicIndic: "arabic-indic",
|
|
9
|
+
Armenian: "armenian",
|
|
10
|
+
Bengali: "bengali",
|
|
11
|
+
Cambodian: "cambodian",
|
|
12
|
+
Circle: "circle",
|
|
13
|
+
CjkDecimal: "cjk-decimal",
|
|
14
|
+
CjkEarthlyBranch: "cjk-earthly-branch",
|
|
15
|
+
CjkHeavenlyStem: "cjk-heavenly-stem",
|
|
16
|
+
Decimal: "decimal",
|
|
17
|
+
DecimalLeadingZero: "decimal-leading-zero",
|
|
18
|
+
Devanagari: "devanagari",
|
|
19
|
+
Disc: "disc",
|
|
20
|
+
DisclosureClosed: "disclosure-closed",
|
|
21
|
+
DisclosureOpen: "disclosure-open",
|
|
22
|
+
EthiopicNumeric: "ethiopic-numeric",
|
|
23
|
+
Georgian: "georgian",
|
|
24
|
+
Gujarati: "gujarati",
|
|
25
|
+
Gurmukhi: "gurmukhi",
|
|
26
|
+
Hebrew: "hebrew",
|
|
27
|
+
Hiragana: "hiragana",
|
|
28
|
+
HiraganaIroha: "hiragana-iroha",
|
|
29
|
+
Inherit: "inherit",
|
|
30
|
+
Initial: "initial",
|
|
31
|
+
JapaneseFormal: "japanese-formal",
|
|
32
|
+
JapaneseInformal: "japanese-informal",
|
|
33
|
+
Kannada: "kannada",
|
|
34
|
+
Katakana: "katakana",
|
|
35
|
+
KatakanaIroha: "katakana-iroha",
|
|
36
|
+
Khmer: "khmer",
|
|
37
|
+
KoreanHangulFormal: "korean-hangul-formal",
|
|
38
|
+
KoreanHanjaFormal: "korean-hanja-formal",
|
|
39
|
+
KoreanHanjaInformal: "korean-hanja-informal",
|
|
40
|
+
Lao: "lao",
|
|
41
|
+
LowerAlpha: "lower-alpha",
|
|
42
|
+
LowerArmenian: "lower-armenian",
|
|
43
|
+
LowerGreek: "lower-greek",
|
|
44
|
+
LowerLatin: "lower-latin",
|
|
45
|
+
LowerRoman: "lower-roman",
|
|
46
|
+
Malayalam: "malayalam",
|
|
47
|
+
Mongolian: "mongolian",
|
|
48
|
+
Myanmar: "myanmar",
|
|
49
|
+
None: "none",
|
|
50
|
+
Oriya: "oriya",
|
|
51
|
+
Persian: "persian",
|
|
52
|
+
SimpChineseFormal: "simp-chinese-formal",
|
|
53
|
+
SimpChineseInformal: "simp-chinese-informal",
|
|
54
|
+
Square: "square",
|
|
55
|
+
Tamil: "tamil",
|
|
56
|
+
Telugu: "telugu",
|
|
57
|
+
Thai: "thai",
|
|
58
|
+
Tibetan: "tibetan",
|
|
59
|
+
TradChineseFormal: "trad-chinese-formal",
|
|
60
|
+
TradChineseInformal: "trad-chinese-informal",
|
|
61
|
+
UpperAlpha: "upper-alpha",
|
|
62
|
+
UpperArmenian: "upper-armenian",
|
|
63
|
+
UpperLatin: "upper-latin",
|
|
64
|
+
UpperRoman: "upper-roman"
|
|
65
|
+
};
|
|
66
|
+
const ULIST_STYLE_TYPES = [
|
|
67
|
+
ListStyleType.Disc,
|
|
68
|
+
ListStyleType.Circle,
|
|
69
|
+
ListStyleType.Square,
|
|
70
|
+
ListStyleType.DisclosureOpen,
|
|
71
|
+
ListStyleType.DisclosureClosed
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/lib/queries/areEqListStyleType.ts
|
|
76
|
+
const areEqListStyleType = (_editor, entries, { listStyleType = ListStyleType.Disc }) => {
|
|
77
|
+
let eqListStyleType = true;
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
const [block] = entry;
|
|
80
|
+
if (listStyleType === KEYS.listTodo) {
|
|
81
|
+
if (!Object.hasOwn(block, KEYS.listChecked)) {
|
|
82
|
+
eqListStyleType = false;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!block[KEYS.listType] || block[KEYS.listType] !== listStyleType) {
|
|
88
|
+
eqListStyleType = false;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return eqListStyleType;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/lib/queries/getListChildren.ts
|
|
97
|
+
/**
|
|
98
|
+
* Get all list items that are children of the current list item (have bigger
|
|
99
|
+
* indent). Stops when encountering an item with equal or lower indent.
|
|
100
|
+
*/
|
|
101
|
+
const getListChildren = (editor, entry) => {
|
|
102
|
+
const children = [];
|
|
103
|
+
const [node, path] = entry;
|
|
104
|
+
const parentIndent = node[KEYS.indent];
|
|
105
|
+
if (!isDefined(parentIndent) || !isDefined(node[KEYS.listType])) return children;
|
|
106
|
+
let currentPath = path;
|
|
107
|
+
while (true) {
|
|
108
|
+
const nextPath = PathApi.next(currentPath);
|
|
109
|
+
if (!nextPath) break;
|
|
110
|
+
const nextNode = NodeApi.get(editor, nextPath);
|
|
111
|
+
if (!nextNode) break;
|
|
112
|
+
const nextIndent = nextNode[KEYS.indent];
|
|
113
|
+
if (!isDefined(nextIndent) || !isDefined(nextNode[KEYS.listType])) break;
|
|
114
|
+
if (nextIndent <= parentIndent) break;
|
|
115
|
+
children.push([nextNode, nextPath]);
|
|
116
|
+
currentPath = nextPath;
|
|
117
|
+
}
|
|
118
|
+
return children;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/lib/queries/expandListItemsWithChildren.ts
|
|
123
|
+
/**
|
|
124
|
+
* Expands a list of blocks to include list item children. For each list item in
|
|
125
|
+
* the input, adds all its children (items with bigger indent). Non-list blocks
|
|
126
|
+
* are kept as-is. Requires id to be set on the blocks.
|
|
127
|
+
*
|
|
128
|
+
* @returns Array of block entries with list items expanded to include their
|
|
129
|
+
* children
|
|
130
|
+
*/
|
|
131
|
+
const expandListItemsWithChildren = (editor, entries) => {
|
|
132
|
+
const expandedEntries = [];
|
|
133
|
+
const processedIds = /* @__PURE__ */ new Set();
|
|
134
|
+
entries.forEach((entry) => {
|
|
135
|
+
const [node] = entry;
|
|
136
|
+
if (processedIds.has(node.id)) return;
|
|
137
|
+
expandedEntries.push(entry);
|
|
138
|
+
processedIds.add(node.id);
|
|
139
|
+
if (isDefined(node[KEYS.listType]) && isDefined(node[KEYS.indent])) getListChildren(editor, entry).forEach((childEntry) => {
|
|
140
|
+
if (!processedIds.has(childEntry[0].id)) {
|
|
141
|
+
expandedEntries.push(childEntry);
|
|
142
|
+
processedIds.add(childEntry[0].id);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
return expandedEntries;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/lib/queries/getListAbove.ts
|
|
151
|
+
const getListAbove = (editor, options) => editor.api.above({
|
|
152
|
+
...options,
|
|
153
|
+
match: (node) => isDefined(node[KEYS.listType])
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/lib/queries/getSiblingList.ts
|
|
158
|
+
/**
|
|
159
|
+
* Get the next sibling indent list node. Default query: the sibling node should
|
|
160
|
+
* have the same listStyleType.
|
|
161
|
+
*/
|
|
162
|
+
const getSiblingList = (_editor, [node, path], { breakOnEqIndentNeqListStyleType = true, breakOnListRestart = false, breakOnLowerIndent = true, breakQuery, eqIndent = true, getNextEntry, getPreviousEntry, query }) => {
|
|
163
|
+
if (!getPreviousEntry && !getNextEntry) return;
|
|
164
|
+
const getSiblingEntry = getNextEntry ?? getPreviousEntry;
|
|
165
|
+
let nextEntry = getSiblingEntry([node, path]);
|
|
166
|
+
while (true) {
|
|
167
|
+
if (!nextEntry) return;
|
|
168
|
+
const [nextNode, nextPath] = nextEntry;
|
|
169
|
+
const indent = node[KEYS.indent];
|
|
170
|
+
const nextIndent = nextNode[KEYS.indent];
|
|
171
|
+
if (breakQuery?.(nextNode, node)) return;
|
|
172
|
+
if (!isDefined(nextIndent)) return;
|
|
173
|
+
if (breakOnListRestart) {
|
|
174
|
+
if (getPreviousEntry && node[KEYS.listRestart]) return;
|
|
175
|
+
if (getNextEntry && nextNode[KEYS.listRestart]) return;
|
|
176
|
+
}
|
|
177
|
+
if (breakOnLowerIndent && nextIndent < indent) return;
|
|
178
|
+
if (breakOnEqIndentNeqListStyleType && nextIndent === indent && nextNode[KEYS.listType] !== node[KEYS.listType]) return;
|
|
179
|
+
let valid = !query || query(nextNode, node);
|
|
180
|
+
if (valid) {
|
|
181
|
+
valid = !eqIndent || nextIndent === indent;
|
|
182
|
+
if (valid) return [nextNode, nextPath];
|
|
183
|
+
}
|
|
184
|
+
nextEntry = getSiblingEntry(nextEntry);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/lib/queries/getNextList.ts
|
|
190
|
+
/** Get the next indent list. */
|
|
191
|
+
const getNextList = (editor, entry, options) => getSiblingList(editor, entry, {
|
|
192
|
+
getNextEntry: ([, currPath]) => {
|
|
193
|
+
const nextPath = PathApi.next(currPath);
|
|
194
|
+
const nextNode = NodeApi.get(editor, nextPath);
|
|
195
|
+
if (!nextNode) return;
|
|
196
|
+
return [nextNode, nextPath];
|
|
197
|
+
},
|
|
198
|
+
...options,
|
|
199
|
+
getPreviousEntry: void 0
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/lib/queries/getPreviousList.ts
|
|
204
|
+
/** Get the previous indent list node. */
|
|
205
|
+
const getPreviousList = (editor, entry, options) => getSiblingList(editor, entry, {
|
|
206
|
+
getPreviousEntry: ([, currPath]) => {
|
|
207
|
+
const prevPath = PathApi.previous(currPath);
|
|
208
|
+
if (!prevPath) return;
|
|
209
|
+
const prevNode = NodeApi.get(editor, prevPath);
|
|
210
|
+
if (!prevNode) return;
|
|
211
|
+
return [prevNode, prevPath];
|
|
212
|
+
},
|
|
213
|
+
...options,
|
|
214
|
+
getNextEntry: void 0
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/lib/queries/getListSiblings.ts
|
|
219
|
+
const getListSiblings = (editor, entry, { current = true, next = true, previous = true, ...options } = {}) => {
|
|
220
|
+
const siblings = [];
|
|
221
|
+
const node = entry[0];
|
|
222
|
+
if (!node[KEYS.listType] && !Object.hasOwn(node, KEYS.listChecked)) return siblings;
|
|
223
|
+
let iterEntry = entry;
|
|
224
|
+
if (previous) while (true) {
|
|
225
|
+
const prevEntry = getPreviousList(editor, iterEntry, options);
|
|
226
|
+
if (!prevEntry) break;
|
|
227
|
+
siblings.push(prevEntry);
|
|
228
|
+
iterEntry = prevEntry;
|
|
229
|
+
}
|
|
230
|
+
if (current) siblings.push(entry);
|
|
231
|
+
if (next) {
|
|
232
|
+
iterEntry = entry;
|
|
233
|
+
while (true) {
|
|
234
|
+
const nextEntry = getNextList(editor, iterEntry, options);
|
|
235
|
+
if (!nextEntry) break;
|
|
236
|
+
siblings.push(nextEntry);
|
|
237
|
+
iterEntry = nextEntry;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return siblings;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/lib/queries/getSiblingListStyleType.ts
|
|
245
|
+
/**
|
|
246
|
+
* Get the first sibling list style type at the given indent. If none, return
|
|
247
|
+
* the entry list style type.
|
|
248
|
+
*/
|
|
249
|
+
const getSiblingListStyleType = (editor, { entry, indent, ...options }) => {
|
|
250
|
+
const siblings = getListSiblings(editor, [{
|
|
251
|
+
...entry[0],
|
|
252
|
+
indent
|
|
253
|
+
}, entry[1]], {
|
|
254
|
+
breakOnEqIndentNeqListStyleType: false,
|
|
255
|
+
current: false,
|
|
256
|
+
eqIndent: true,
|
|
257
|
+
...options
|
|
258
|
+
});
|
|
259
|
+
return siblings.length > 0 ? siblings[0][0][KEYS.listType] : entry[0][KEYS.listType];
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/lib/queries/isOrderedList.ts
|
|
264
|
+
function isOrderedList(element) {
|
|
265
|
+
return !!element.listStyleType && !ULIST_STYLE_TYPES.includes(element.listStyleType);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/lib/queries/someList.ts
|
|
270
|
+
const someList = (editor, type) => !!editor.selection && editor.api.some({ match: (n) => {
|
|
271
|
+
if (Object.hasOwn(n, KEYS.listChecked)) return false;
|
|
272
|
+
const list = n[KEYS.listType];
|
|
273
|
+
return Array.isArray(type) ? type.includes(list) : list === type;
|
|
274
|
+
} });
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/lib/queries/someTodoList.ts
|
|
278
|
+
const someTodoList = (editor) => editor.api.some({
|
|
279
|
+
at: editor.selection,
|
|
280
|
+
match: (n) => {
|
|
281
|
+
const list = n[KEYS.listType];
|
|
282
|
+
const isHasProperty = Object.hasOwn(n, KEYS.listChecked);
|
|
283
|
+
return n.type === "p" && isHasProperty && list === KEYS.listTodo;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/lib/normalizers/normalizeListNotIndented.ts
|
|
289
|
+
/** Unset listStyle, listStart if KEYS.indent is not defined. */
|
|
290
|
+
const normalizeListNotIndented = (editor, [node, path]) => {
|
|
291
|
+
if (!isDefined(node[KEYS.indent]) && (node[KEYS.listType] || node[KEYS.listStart])) {
|
|
292
|
+
editor.tf.unsetNodes([KEYS.listType, KEYS.listStart], { at: path });
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/lib/normalizers/normalizeListStart.ts
|
|
299
|
+
const getListExpectedListStart = (entry, prevEntry) => {
|
|
300
|
+
const [node] = entry;
|
|
301
|
+
const [prevNode] = prevEntry ?? [null];
|
|
302
|
+
const restart = node[KEYS.listRestart] ?? null;
|
|
303
|
+
const restartPolite = node[KEYS.listRestartPolite] ?? null;
|
|
304
|
+
if (restart) return restart;
|
|
305
|
+
if (restartPolite && !prevNode) return restartPolite;
|
|
306
|
+
if (prevNode) return (prevNode[KEYS.listStart] ?? 1) + 1;
|
|
307
|
+
return 1;
|
|
308
|
+
};
|
|
309
|
+
const normalizeListStart = (editor, entry, options) => editor.tf.withoutNormalizing(() => {
|
|
310
|
+
const [node, path] = entry;
|
|
311
|
+
const listStyleType = node[KEYS.listType];
|
|
312
|
+
const listStart = node[KEYS.listStart];
|
|
313
|
+
if (!listStyleType) return;
|
|
314
|
+
const expectedListStart = getListExpectedListStart(entry, getPreviousList(editor, entry, options));
|
|
315
|
+
if (isDefined(listStart) && expectedListStart === 1) {
|
|
316
|
+
editor.tf.unsetNodes(KEYS.listStart, { at: path });
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
if (listStart !== expectedListStart && expectedListStart > 1) {
|
|
320
|
+
editor.tf.setNodes({ [KEYS.listStart]: expectedListStart }, { at: path });
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/lib/normalizers/withInsertBreakList.ts
|
|
328
|
+
const withInsertBreakList = ({ editor, tf: { insertBreak } }) => ({ transforms: { insertBreak() {
|
|
329
|
+
const nodeEntry = editor.api.above();
|
|
330
|
+
if (!nodeEntry) return insertBreak();
|
|
331
|
+
const [node, path] = nodeEntry;
|
|
332
|
+
if (!isDefined(node[KEYS.listType]) || node[KEYS.listType] !== KEYS.listTodo || editor.api.isExpanded() || !editor.api.isEnd(editor.selection?.focus, path)) return insertBreak();
|
|
333
|
+
editor.tf.withoutNormalizing(() => {
|
|
334
|
+
insertBreak();
|
|
335
|
+
const newEntry = editor.api.above();
|
|
336
|
+
if (newEntry) editor.tf.setNodes({ checked: false }, { at: newEntry[1] });
|
|
337
|
+
});
|
|
338
|
+
} } });
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/lib/transforms/indentList.ts
|
|
342
|
+
/** Increase the indentation of the selected blocks. */
|
|
343
|
+
const indentList = (editor, { listStyleType = ListStyleType.Disc, ...options } = {}) => {
|
|
344
|
+
setIndent(editor, {
|
|
345
|
+
offset: 1,
|
|
346
|
+
setNodesProps: () => ({ [KEYS.listType]: listStyleType }),
|
|
347
|
+
...options
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
const indentTodo = (editor, { listStyleType = ListStyleType.Disc, ...options } = {}) => {
|
|
351
|
+
setIndent(editor, {
|
|
352
|
+
offset: 1,
|
|
353
|
+
setNodesProps: () => ({
|
|
354
|
+
[KEYS.listChecked]: false,
|
|
355
|
+
[KEYS.listType]: listStyleType
|
|
356
|
+
}),
|
|
357
|
+
...options
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/lib/transforms/outdentList.ts
|
|
363
|
+
/** Decrease the indentation of the selected blocks. */
|
|
364
|
+
const outdentList = (editor, options = {}) => {
|
|
365
|
+
setIndent(editor, {
|
|
366
|
+
offset: -1,
|
|
367
|
+
unsetNodesProps: [KEYS.listType, KEYS.listChecked],
|
|
368
|
+
...options
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region src/lib/transforms/setListNode.ts
|
|
374
|
+
const setListNode = (editor, { at, indent = 0, listStyleType = ListStyleType.Disc }) => {
|
|
375
|
+
const newIndent = indent || indent + 1;
|
|
376
|
+
editor.tf.setNodes({
|
|
377
|
+
[KEYS.indent]: newIndent,
|
|
378
|
+
[KEYS.listType]: listStyleType
|
|
379
|
+
}, { at });
|
|
380
|
+
};
|
|
381
|
+
const setIndentTodoNode = (editor, { at, indent = 0, listStyleType = KEYS.listTodo }) => {
|
|
382
|
+
const newIndent = indent || indent + 1;
|
|
383
|
+
editor.tf.setNodes({
|
|
384
|
+
[KEYS.indent]: newIndent,
|
|
385
|
+
[KEYS.listChecked]: false,
|
|
386
|
+
[KEYS.listType]: listStyleType
|
|
387
|
+
}, { at });
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/lib/transforms/setListNodes.ts
|
|
392
|
+
/**
|
|
393
|
+
* Set indent list to the given entries. Add indent if listStyleType was not
|
|
394
|
+
* defined.
|
|
395
|
+
*/
|
|
396
|
+
const setListNodes = (editor, entries, { listStyleType = ListStyleType.Disc }) => {
|
|
397
|
+
editor.tf.withoutNormalizing(() => {
|
|
398
|
+
entries.forEach((entry) => {
|
|
399
|
+
const [node, path] = entry;
|
|
400
|
+
let indent = node[KEYS.indent] ?? 0;
|
|
401
|
+
indent = node[KEYS.listType] || Object.hasOwn(node, KEYS.listChecked) ? indent : indent + 1;
|
|
402
|
+
if (listStyleType === "todo") {
|
|
403
|
+
editor.tf.unsetNodes(KEYS.listType, { at: path });
|
|
404
|
+
setIndentTodoNode(editor, {
|
|
405
|
+
at: path,
|
|
406
|
+
indent,
|
|
407
|
+
listStyleType
|
|
408
|
+
});
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
editor.tf.unsetNodes(KEYS.listChecked, { at: path });
|
|
412
|
+
setListNode(editor, {
|
|
413
|
+
at: path,
|
|
414
|
+
indent,
|
|
415
|
+
listStyleType
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region src/lib/transforms/setListSiblingNodes.ts
|
|
423
|
+
/** Set indent list to entry + siblings. */
|
|
424
|
+
const setListSiblingNodes = (editor, entry, { getSiblingListOptions, listStyleType = ListStyleType.Disc }) => {
|
|
425
|
+
editor.tf.withoutNormalizing(() => {
|
|
426
|
+
getListSiblings(editor, entry, getSiblingListOptions).forEach(([node, path]) => {
|
|
427
|
+
if (listStyleType === KEYS.listTodo) {
|
|
428
|
+
editor.tf.unsetNodes(KEYS.listType, { at: path });
|
|
429
|
+
setIndentTodoNode(editor, {
|
|
430
|
+
at: path,
|
|
431
|
+
indent: node[KEYS.indent],
|
|
432
|
+
listStyleType
|
|
433
|
+
});
|
|
434
|
+
} else {
|
|
435
|
+
editor.tf.unsetNodes(KEYS.listChecked, { at: path });
|
|
436
|
+
setListNode(editor, {
|
|
437
|
+
at: path,
|
|
438
|
+
indent: node[KEYS.indent],
|
|
439
|
+
listStyleType
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region src/lib/transforms/toggleListSet.ts
|
|
448
|
+
/** Set indent list if not set. */
|
|
449
|
+
const toggleListSet = (editor, [node, _path], { listStyleType = ListStyleType.Disc, ...options }) => {
|
|
450
|
+
if (Object.hasOwn(node, KEYS.listChecked) || node[KEYS.listType]) return;
|
|
451
|
+
if (listStyleType === "todo") indentTodo(editor, {
|
|
452
|
+
listStyleType,
|
|
453
|
+
...options
|
|
454
|
+
});
|
|
455
|
+
else indentList(editor, {
|
|
456
|
+
listStyleType,
|
|
457
|
+
...options
|
|
458
|
+
});
|
|
459
|
+
return true;
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/lib/transforms/toggleListUnset.ts
|
|
464
|
+
/** Unset list style type if already set. */
|
|
465
|
+
const toggleListUnset = (editor, [node, path], { listStyleType = ListStyleType.Disc }) => {
|
|
466
|
+
if (listStyleType === KEYS.listTodo && Object.hasOwn(node, KEYS.listChecked)) {
|
|
467
|
+
editor.tf.unsetNodes(KEYS.listChecked, { at: path });
|
|
468
|
+
outdentList(editor, { listStyleType });
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
if (listStyleType === node[KEYS.listType]) {
|
|
472
|
+
editor.tf.unsetNodes([KEYS.listType], { at: path });
|
|
473
|
+
outdentList(editor, { listStyleType });
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/lib/transforms/toggleList.ts
|
|
480
|
+
/** Toggle indent list. */
|
|
481
|
+
const toggleList = (editor, options, getSiblingListOptions) => {
|
|
482
|
+
const { listRestart, listRestartPolite, listStyleType } = options;
|
|
483
|
+
/**
|
|
484
|
+
* True - One or more blocks were converted to lists or changed such that they
|
|
485
|
+
* remain lists.
|
|
486
|
+
*
|
|
487
|
+
* False - One or more list blocks were unset.
|
|
488
|
+
*
|
|
489
|
+
* Null - No action was taken.
|
|
490
|
+
*/
|
|
491
|
+
const setList = (() => {
|
|
492
|
+
const { getSiblingListOptions: _getSiblingListOptions } = editor.getOptions(BaseListPlugin);
|
|
493
|
+
if (editor.api.isCollapsed()) {
|
|
494
|
+
const entry = editor.api.block();
|
|
495
|
+
if (!entry) return null;
|
|
496
|
+
if (toggleListSet(editor, entry, options)) return true;
|
|
497
|
+
if (toggleListUnset(editor, entry, { listStyleType })) return false;
|
|
498
|
+
setListSiblingNodes(editor, entry, {
|
|
499
|
+
getSiblingListOptions: {
|
|
500
|
+
..._getSiblingListOptions,
|
|
501
|
+
...getSiblingListOptions
|
|
502
|
+
},
|
|
503
|
+
listStyleType
|
|
504
|
+
});
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
if (editor.api.isExpanded()) {
|
|
508
|
+
const match = getInjectMatch(editor, editor.getPlugin({ key: KEYS.list }));
|
|
509
|
+
const entries = [...editor.api.nodes({
|
|
510
|
+
block: true,
|
|
511
|
+
match
|
|
512
|
+
})];
|
|
513
|
+
if (areEqListStyleType(editor, entries, { listStyleType })) {
|
|
514
|
+
editor.tf.withoutNormalizing(() => {
|
|
515
|
+
entries.forEach((entry) => {
|
|
516
|
+
const [node, path] = entry;
|
|
517
|
+
const indent = node[KEYS.indent];
|
|
518
|
+
editor.tf.unsetNodes(KEYS.listType, { at: path });
|
|
519
|
+
if (indent > 1) editor.tf.setNodes({ [KEYS.indent]: indent - 1 }, { at: path });
|
|
520
|
+
else editor.tf.unsetNodes([KEYS.indent, KEYS.listChecked], { at: path });
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
setListNodes(editor, entries, { listStyleType });
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
return null;
|
|
529
|
+
})();
|
|
530
|
+
const restartValue = listRestart || listRestartPolite;
|
|
531
|
+
const isRestart = !!listRestart;
|
|
532
|
+
if (setList && restartValue) {
|
|
533
|
+
const entry = getListAbove(editor, { at: editor.api.start(editor.selection) });
|
|
534
|
+
if (!entry) return;
|
|
535
|
+
const isFirst = !getPreviousList(editor, entry);
|
|
536
|
+
/**
|
|
537
|
+
* Only apply listRestartPolite if this is the first item and restartValue >
|
|
538
|
+
* 1.
|
|
539
|
+
*/
|
|
540
|
+
if (!isRestart && (!isFirst || restartValue <= 0)) return;
|
|
541
|
+
if (isRestart && restartValue === 1 && isFirst) return;
|
|
542
|
+
const prop = isRestart ? KEYS.listRestart : KEYS.listRestartPolite;
|
|
543
|
+
editor.tf.setNodes({ [prop]: restartValue }, { at: entry[1] });
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/lib/transforms/toggleListByPath.ts
|
|
549
|
+
const toggleListByPath = (editor, [node, path], listStyleType) => {
|
|
550
|
+
editor.tf.setNodes({
|
|
551
|
+
[KEYS.indent]: node.indent ?? 1,
|
|
552
|
+
[KEYS.listChecked]: false,
|
|
553
|
+
[KEYS.listType]: listStyleType,
|
|
554
|
+
type: KEYS.p
|
|
555
|
+
}, { at: path });
|
|
556
|
+
};
|
|
557
|
+
const toggleListByPathUnSet = (editor, [, path]) => editor.tf.unsetNodes([
|
|
558
|
+
KEYS.listType,
|
|
559
|
+
KEYS.indent,
|
|
560
|
+
KEYS.listChecked
|
|
561
|
+
], { at: path });
|
|
562
|
+
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/lib/withNormalizeList.ts
|
|
565
|
+
const withNormalizeList = ({ editor, getOptions, tf: { normalizeNode } }) => ({ transforms: { normalizeNode([node, path]) {
|
|
566
|
+
if (editor.tf.withoutNormalizing(() => {
|
|
567
|
+
if (normalizeListNotIndented(editor, [node, path])) return true;
|
|
568
|
+
if (normalizeListStart(editor, [node, path], getOptions().getSiblingListOptions)) return true;
|
|
569
|
+
})) return;
|
|
570
|
+
return normalizeNode([node, path]);
|
|
571
|
+
} } });
|
|
572
|
+
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/lib/withList.ts
|
|
575
|
+
const withList = (ctx) => {
|
|
576
|
+
const { editor, getOptions, tf: { apply, resetBlock } } = ctx;
|
|
577
|
+
return { transforms: {
|
|
578
|
+
resetBlock(options) {
|
|
579
|
+
if (editor.api.block(options)?.[0]?.[KEYS.listType]) {
|
|
580
|
+
outdentList(editor);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
return resetBlock(options);
|
|
584
|
+
},
|
|
585
|
+
...withNormalizeList(ctx).transforms,
|
|
586
|
+
...withInsertBreakList(ctx).transforms,
|
|
587
|
+
apply(operation) {
|
|
588
|
+
const { getSiblingListOptions } = getOptions();
|
|
589
|
+
/**
|
|
590
|
+
* If there is a previous indent list, the inserted indent list style
|
|
591
|
+
* type should be the same. Only for lower-roman and upper-roman as it
|
|
592
|
+
* overlaps with lower-alpha and upper-alpha.
|
|
593
|
+
*/
|
|
594
|
+
if (operation.type === "insert_node") {
|
|
595
|
+
const listStyleType = operation.node[KEYS.listType];
|
|
596
|
+
if (listStyleType && ["lower-roman", "upper-roman"].includes(listStyleType)) {
|
|
597
|
+
const prevNodeEntry = getPreviousList(editor, [operation.node, operation.path], {
|
|
598
|
+
breakOnEqIndentNeqListStyleType: false,
|
|
599
|
+
eqIndent: false,
|
|
600
|
+
...getSiblingListOptions
|
|
601
|
+
});
|
|
602
|
+
if (prevNodeEntry) {
|
|
603
|
+
const prevListStyleType = prevNodeEntry[0][KEYS.listType];
|
|
604
|
+
if (prevListStyleType === ListStyleType.LowerAlpha && listStyleType === ListStyleType.LowerRoman) operation.node[KEYS.listType] = ListStyleType.LowerAlpha;
|
|
605
|
+
else if (prevListStyleType === ListStyleType.UpperAlpha && listStyleType === ListStyleType.UpperRoman) operation.node[KEYS.listType] = ListStyleType.UpperAlpha;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* When inserting a line break, remove listRestart and listRestartPolite
|
|
611
|
+
* from the new list item.
|
|
612
|
+
*/
|
|
613
|
+
if (operation.type === "split_node" && operation.properties[KEYS.listType]) {
|
|
614
|
+
delete operation.properties[KEYS.listRestart];
|
|
615
|
+
delete operation.properties[KEYS.listRestartPolite];
|
|
616
|
+
}
|
|
617
|
+
apply(operation);
|
|
618
|
+
const affectedPaths = [];
|
|
619
|
+
switch (operation.type) {
|
|
620
|
+
case "insert_node":
|
|
621
|
+
case "remove_node":
|
|
622
|
+
case "set_node":
|
|
623
|
+
affectedPaths.push(operation.path);
|
|
624
|
+
break;
|
|
625
|
+
case "merge_node":
|
|
626
|
+
affectedPaths.push(PathApi.previous(operation.path));
|
|
627
|
+
break;
|
|
628
|
+
case "move_node":
|
|
629
|
+
affectedPaths.push(operation.path, operation.newPath);
|
|
630
|
+
break;
|
|
631
|
+
case "split_node":
|
|
632
|
+
affectedPaths.push(operation.path, PathApi.next(operation.path));
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
const isListItem = (node) => KEYS.listType in node;
|
|
636
|
+
affectedPaths.forEach((affectedPath) => {
|
|
637
|
+
let entry = editor.api.node(affectedPath);
|
|
638
|
+
if (!entry) return;
|
|
639
|
+
/**
|
|
640
|
+
* Even if the affected node isn't a list item, the subsequent node
|
|
641
|
+
* might be, in which case we want to normalize that node instead.
|
|
642
|
+
*/
|
|
643
|
+
if (!isListItem(entry[0])) entry = editor.api.node(PathApi.next(affectedPath));
|
|
644
|
+
while (entry && isListItem(entry[0])) {
|
|
645
|
+
/**
|
|
646
|
+
* Break early since the subsequent list items will already have
|
|
647
|
+
* been normalized by the `apply` that modified the current node.
|
|
648
|
+
*/
|
|
649
|
+
if (normalizeListStart(editor, entry, getSiblingListOptions)) break;
|
|
650
|
+
entry = getNextList(editor, entry, {
|
|
651
|
+
...getSiblingListOptions,
|
|
652
|
+
breakOnEqIndentNeqListStyleType: false,
|
|
653
|
+
breakOnLowerIndent: false,
|
|
654
|
+
eqIndent: false
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
} };
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/lib/BaseListPlugin.tsx
|
|
664
|
+
const BaseListPlugin = createTSlatePlugin({
|
|
665
|
+
key: KEYS.list,
|
|
666
|
+
inject: {
|
|
667
|
+
plugins: { [KEYS.html]: { parser: { transformData: ({ data }) => {
|
|
668
|
+
const { body } = new DOMParser().parseFromString(data, "text/html");
|
|
669
|
+
const lisWithNestedLists = [];
|
|
670
|
+
traverseHtmlElements(body, (element) => {
|
|
671
|
+
if (element.tagName === "LI") {
|
|
672
|
+
const nestedLists = [];
|
|
673
|
+
Array.from(element.children).forEach((child) => {
|
|
674
|
+
if (child.tagName === "UL" || child.tagName === "OL") nestedLists.push(child);
|
|
675
|
+
});
|
|
676
|
+
if (nestedLists.length > 0) lisWithNestedLists.push({
|
|
677
|
+
li: element,
|
|
678
|
+
nestedLists
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
return true;
|
|
682
|
+
});
|
|
683
|
+
lisWithNestedLists.forEach(({ li, nestedLists }) => {
|
|
684
|
+
nestedLists.forEach((nestedList) => {
|
|
685
|
+
nestedList.remove();
|
|
686
|
+
if (li.parentNode) li.parentNode.insertBefore(nestedList, li.nextSibling);
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
traverseHtmlElements(body, (element) => {
|
|
690
|
+
if (element.tagName === "LI") {
|
|
691
|
+
const htmlElement = element;
|
|
692
|
+
const { childNodes } = element;
|
|
693
|
+
const liChildren = [];
|
|
694
|
+
childNodes.forEach((child) => {
|
|
695
|
+
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
696
|
+
const childElement = child;
|
|
697
|
+
if (isHtmlBlockElement(childElement)) {
|
|
698
|
+
liChildren.push(...childElement.childNodes);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
liChildren.push(child);
|
|
703
|
+
});
|
|
704
|
+
element.replaceChildren(...liChildren);
|
|
705
|
+
const ariaLevel = element.getAttribute("aria-level");
|
|
706
|
+
if (ariaLevel) htmlElement.dataset.indent = ariaLevel;
|
|
707
|
+
else {
|
|
708
|
+
let indent = 0;
|
|
709
|
+
let parent = element.parentElement;
|
|
710
|
+
while (parent && parent !== body) {
|
|
711
|
+
if (parent.tagName === "UL" || parent.tagName === "OL") indent++;
|
|
712
|
+
parent = parent.parentElement;
|
|
713
|
+
}
|
|
714
|
+
if (indent > 0) htmlElement.dataset.indent = String(indent);
|
|
715
|
+
}
|
|
716
|
+
const listStyleType = htmlElement.style.listStyleType;
|
|
717
|
+
if (listStyleType) htmlElement.dataset.listStyleType = listStyleType;
|
|
718
|
+
else {
|
|
719
|
+
const listParent = element.closest("ul, ol");
|
|
720
|
+
if (listParent) {
|
|
721
|
+
const parentListStyleType = listParent.style.listStyleType;
|
|
722
|
+
if (parentListStyleType) htmlElement.dataset.listStyleType = parentListStyleType;
|
|
723
|
+
else if (listParent.tagName === "UL") htmlElement.dataset.listStyleType = "disc";
|
|
724
|
+
else if (listParent.tagName === "OL") htmlElement.dataset.listStyleType = "decimal";
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
});
|
|
731
|
+
return postCleanHtml(body.innerHTML);
|
|
732
|
+
} } } },
|
|
733
|
+
targetPlugins: [KEYS.p]
|
|
734
|
+
},
|
|
735
|
+
options: { getListStyleType: (element) => element.style.listStyleType },
|
|
736
|
+
parsers: { html: { deserializer: {
|
|
737
|
+
isElement: true,
|
|
738
|
+
rules: [{ validNodeName: "LI" }],
|
|
739
|
+
parse: ({ editor, element, getOptions }) => {
|
|
740
|
+
const dataIndent = element.dataset.indent;
|
|
741
|
+
const ariaLevel = element.getAttribute("aria-level");
|
|
742
|
+
const indent = dataIndent ? Number(dataIndent) : Number(ariaLevel);
|
|
743
|
+
const listStyleType = element.dataset.listStyleType || getOptions().getListStyleType?.(element);
|
|
744
|
+
return {
|
|
745
|
+
indent: indent || void 0,
|
|
746
|
+
listStyleType: listStyleType || void 0,
|
|
747
|
+
type: editor.getType(KEYS.p)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
} } },
|
|
751
|
+
render: { belowNodes: (props) => {
|
|
752
|
+
if (!props.element.listStyleType) return;
|
|
753
|
+
return (props$1) => /* @__PURE__ */ React.createElement(List, props$1);
|
|
754
|
+
} },
|
|
755
|
+
rules: {
|
|
756
|
+
break: {
|
|
757
|
+
empty: "reset",
|
|
758
|
+
splitReset: false
|
|
759
|
+
},
|
|
760
|
+
delete: { start: "reset" },
|
|
761
|
+
merge: { removeEmpty: false },
|
|
762
|
+
match: ({ node }) => isDefined(node[KEYS.listType])
|
|
763
|
+
}
|
|
764
|
+
}).overrideEditor(withList);
|
|
765
|
+
function List(props) {
|
|
766
|
+
const $ = c(9);
|
|
767
|
+
const { listStart, listStyleType } = props.element;
|
|
768
|
+
const List$1 = isOrderedList(props.element) ? "ol" : "ul";
|
|
769
|
+
let t0;
|
|
770
|
+
if ($[0] !== listStyleType) {
|
|
771
|
+
t0 = {
|
|
772
|
+
listStyleType,
|
|
773
|
+
margin: 0,
|
|
774
|
+
padding: 0,
|
|
775
|
+
position: "relative"
|
|
776
|
+
};
|
|
777
|
+
$[0] = listStyleType;
|
|
778
|
+
$[1] = t0;
|
|
779
|
+
} else t0 = $[1];
|
|
780
|
+
let t1;
|
|
781
|
+
if ($[2] !== props.children) {
|
|
782
|
+
t1 = /* @__PURE__ */ React.createElement("li", null, props.children);
|
|
783
|
+
$[2] = props.children;
|
|
784
|
+
$[3] = t1;
|
|
785
|
+
} else t1 = $[3];
|
|
786
|
+
let t2;
|
|
787
|
+
if ($[4] !== List$1 || $[5] !== listStart || $[6] !== t0 || $[7] !== t1) {
|
|
788
|
+
t2 = /* @__PURE__ */ React.createElement(List$1, {
|
|
789
|
+
style: t0,
|
|
790
|
+
start: listStart
|
|
791
|
+
}, t1);
|
|
792
|
+
$[4] = List$1;
|
|
793
|
+
$[5] = listStart;
|
|
794
|
+
$[6] = t0;
|
|
795
|
+
$[7] = t1;
|
|
796
|
+
$[8] = t2;
|
|
797
|
+
} else t2 = $[8];
|
|
798
|
+
return t2;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
//#endregion
|
|
802
|
+
export { getListChildren as A, getSiblingListStyleType as C, getSiblingList as D, getNextList as E, ListStyleType as M, ULIST_STYLE_TYPES as N, getListAbove as O, isOrderedList as S, getPreviousList as T, getListExpectedListStart as _, toggleListByPathUnSet as a, someTodoList as b, toggleListSet as c, setIndentTodoNode as d, setListNode as f, withInsertBreakList as g, indentTodo as h, toggleListByPath as i, areEqListStyleType as j, expandListItemsWithChildren as k, setListSiblingNodes as l, indentList as m, withList as n, toggleList as o, outdentList as p, withNormalizeList as r, toggleListUnset as s, BaseListPlugin as t, setListNodes as u, normalizeListStart as v, getListSiblings as w, someList as x, normalizeListNotIndented as y };
|
|
803
|
+
//# sourceMappingURL=src-Dvsp2AeZ.js.map
|