@ckeditor/ckeditor5-list 39.0.2 → 40.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +2 -2
- package/build/list.js +1 -1
- package/build/translations/fi.js +1 -1
- package/build/translations/pt-br.js +1 -1
- package/ckeditor5-metadata.json +44 -0
- package/lang/translations/fi.po +1 -1
- package/lang/translations/pt-br.po +10 -10
- package/package.json +3 -3
- package/src/augmentation.d.ts +5 -3
- package/src/documentlist/converters.d.ts +3 -1
- package/src/documentlist/converters.js +106 -19
- package/src/documentlist/documentlistcommand.d.ts +2 -2
- package/src/documentlist/documentlistcommand.js +12 -7
- package/src/documentlist/documentlistediting.d.ts +65 -7
- package/src/documentlist/documentlistediting.js +157 -76
- package/src/documentlist/utils/listwalker.d.ts +4 -0
- package/src/documentlist/utils/listwalker.js +21 -1
- package/src/documentlist/utils/model.d.ts +11 -2
- package/src/documentlist/utils/model.js +21 -1
- package/src/documentlist/utils/postfixers.js +8 -0
- package/src/documentlist/utils/view.d.ts +3 -3
- package/src/documentlistproperties/documentlistpropertiesediting.js +11 -34
- package/src/index.d.ts +3 -0
- package/src/index.js +2 -0
- package/src/listconfig.d.ts +10 -0
- package/src/tododocumentlist/checktododocumentlistcommand.d.ts +49 -0
- package/src/tododocumentlist/checktododocumentlistcommand.js +82 -0
- package/src/tododocumentlist/todocheckboxchangeobserver.d.ts +41 -0
- package/src/tododocumentlist/todocheckboxchangeobserver.js +37 -0
- package/src/tododocumentlist/tododocumentlistediting.d.ts +38 -0
- package/src/tododocumentlist/tododocumentlistediting.js +399 -0
- package/src/tododocumentlist.d.ts +27 -0
- package/src/tododocumentlist.js +31 -0
- package/theme/todolist.css +101 -70
package/ckeditor5-metadata.json
CHANGED
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"name": "Document list",
|
|
98
98
|
"className": "DocumentList",
|
|
99
99
|
"description": "Implements bulleted and numbered list features. Supports block content inside list elements. Incompatible with the List plugin.",
|
|
100
|
+
"docs": "features/lists/document-lists.html",
|
|
100
101
|
"path": "src/documentlist.js",
|
|
101
102
|
"uiComponents": [
|
|
102
103
|
{
|
|
@@ -122,10 +123,53 @@
|
|
|
122
123
|
}
|
|
123
124
|
]
|
|
124
125
|
},
|
|
126
|
+
{
|
|
127
|
+
"name": "To-do document list",
|
|
128
|
+
"className": "TodoDocumentList",
|
|
129
|
+
"description": "Allows for creating a list of interactive checkboxes with labels. It supports all features of document lists so you can nest a to-do list together with bulleted and numbered lists in any combination.",
|
|
130
|
+
"docs": "features/lists/document-lists.html#to-do-lists",
|
|
131
|
+
"path": "src/tododocumentlist.js",
|
|
132
|
+
"requires": [
|
|
133
|
+
"DocumentList"
|
|
134
|
+
],
|
|
135
|
+
"uiComponents": [
|
|
136
|
+
{
|
|
137
|
+
"type": "Button",
|
|
138
|
+
"name": "todoList",
|
|
139
|
+
"iconPath": "theme/icons/todolist.svg"
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"htmlOutput": [
|
|
143
|
+
{
|
|
144
|
+
"elements": "ul",
|
|
145
|
+
"classes": "todo-list"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"elements": "li"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"elements": "label",
|
|
152
|
+
"classes": [ "todo-list__label", "todo-list__label_without-description" ]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"elements": "span",
|
|
156
|
+
"classes": "todo-list__label__description"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"elements": "input",
|
|
160
|
+
"attributes": [
|
|
161
|
+
"checked",
|
|
162
|
+
"disabled",
|
|
163
|
+
"type"
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
},
|
|
125
168
|
{
|
|
126
169
|
"name": "Document list properties",
|
|
127
170
|
"className": "DocumentListProperties",
|
|
128
171
|
"description": "Enables styling the list item markers for both ordered and unordered lists created by the document list plugin. You can choose various types of numerals and letters or visual markers to use with these lists. Also enables setting start index (initial marker value) and list reversal (from ascending to descending) for numbered lists.",
|
|
172
|
+
"docs": "features/lists/document-lists.html#document-list-properties",
|
|
129
173
|
"path": "src/documentlistproperties.js",
|
|
130
174
|
"requires": [
|
|
131
175
|
"DocumentList"
|
package/lang/translations/fi.po
CHANGED
|
@@ -119,7 +119,7 @@ msgstr "Alkaa"
|
|
|
119
119
|
|
|
120
120
|
msgctxt "The error message displayed when the numbered list start index input value is invalid."
|
|
121
121
|
msgid "Start index must be greater than 0."
|
|
122
|
-
msgstr "
|
|
122
|
+
msgstr "Aloitusindeksin pitää olla suurempi kuin 0."
|
|
123
123
|
|
|
124
124
|
msgctxt "The label of the switch button that reverses the order of the numbered list."
|
|
125
125
|
msgid "Reversed order"
|
|
@@ -55,23 +55,23 @@ msgstr "Alternar o estilo de lista decimal"
|
|
|
55
55
|
|
|
56
56
|
msgctxt "The ARIA label of the button that toggles the \"decimal with leading zero\" list style."
|
|
57
57
|
msgid "Toggle the decimal with leading zero list style"
|
|
58
|
-
msgstr "Alternar o
|
|
58
|
+
msgstr "Alternar o estilo de lista decimal com zero à esquerda"
|
|
59
59
|
|
|
60
60
|
msgctxt "The ARIA label of the button that toggles the \"lower–roman\" list style."
|
|
61
61
|
msgid "Toggle the lower–roman list style"
|
|
62
|
-
msgstr "Alternar o estilo de lista romana
|
|
62
|
+
msgstr "Alternar o estilo de lista romana minúscula"
|
|
63
63
|
|
|
64
64
|
msgctxt "The ARIA label of the button that toggles the \"upper–roman\" list style."
|
|
65
65
|
msgid "Toggle the upper–roman list style"
|
|
66
|
-
msgstr "Alternar o estilo de lista romana
|
|
66
|
+
msgstr "Alternar o estilo de lista romana maiúscula"
|
|
67
67
|
|
|
68
68
|
msgctxt "The ARIA label of the button that toggles the \"lower–latin\" list style."
|
|
69
69
|
msgid "Toggle the lower–latin list style"
|
|
70
|
-
msgstr "Alternar o estilo de lista latina
|
|
70
|
+
msgstr "Alternar o estilo de lista latina minúscula"
|
|
71
71
|
|
|
72
72
|
msgctxt "The ARIA label of the button that toggles the \"upper–latin\" list style."
|
|
73
73
|
msgid "Toggle the upper–latin list style"
|
|
74
|
-
msgstr "Alternar o estilo de lista latino
|
|
74
|
+
msgstr "Alternar o estilo de lista latino maiúscula"
|
|
75
75
|
|
|
76
76
|
msgctxt "The tooltip text of the button that toggles the \"disc\" list style."
|
|
77
77
|
msgid "Disc"
|
|
@@ -95,19 +95,19 @@ msgstr "Decimal com zero à esquerda"
|
|
|
95
95
|
|
|
96
96
|
msgctxt "The tooltip text of the button that toggles the \"lower–roman\" list style."
|
|
97
97
|
msgid "Lower–roman"
|
|
98
|
-
msgstr "Romano
|
|
98
|
+
msgstr "Romano minúsculo"
|
|
99
99
|
|
|
100
100
|
msgctxt "The tooltip text of the button that toggles the \"upper–roman\" list style."
|
|
101
101
|
msgid "Upper-roman"
|
|
102
|
-
msgstr "Romano
|
|
102
|
+
msgstr "Romano maiúsculo"
|
|
103
103
|
|
|
104
104
|
msgctxt "The tooltip text of the button that toggles the \"lower–latin\" list style."
|
|
105
105
|
msgid "Lower-latin"
|
|
106
|
-
msgstr "Latim
|
|
106
|
+
msgstr "Latim minúsculo"
|
|
107
107
|
|
|
108
108
|
msgctxt "The tooltip text of the button that toggles the \"upper–latin\" list style."
|
|
109
109
|
msgid "Upper-latin"
|
|
110
|
-
msgstr "Latim
|
|
110
|
+
msgstr "Latim maiúsculo"
|
|
111
111
|
|
|
112
112
|
msgctxt "The label of the button that toggles the visibility of additional numbered list property UI fields."
|
|
113
113
|
msgid "List properties"
|
|
@@ -123,4 +123,4 @@ msgstr "O índice inicial deve ser maior que 0."
|
|
|
123
123
|
|
|
124
124
|
msgctxt "The label of the switch button that reverses the order of the numbered list."
|
|
125
125
|
msgid "Reversed order"
|
|
126
|
-
msgstr "Ordem
|
|
126
|
+
msgstr "Ordem inversa"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-list",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "40.1.0",
|
|
4
4
|
"description": "Ordered and unordered lists feature to CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
],
|
|
13
13
|
"main": "src/index.js",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"ckeditor5": "
|
|
16
|
-
"@ckeditor/ckeditor5-ui": "
|
|
15
|
+
"ckeditor5": "40.1.0",
|
|
16
|
+
"@ckeditor/ckeditor5-ui": "40.1.0"
|
|
17
17
|
},
|
|
18
18
|
"author": "CKSource (http://cksource.com/)",
|
|
19
19
|
"license": "GPL-2.0-or-later",
|
package/src/augmentation.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
import type { DocumentList, DocumentListEditing, DocumentListProperties, DocumentListPropertiesEditing, DocumentListPropertiesUtils, AdjacentListsSupport, DocumentListUtils, ListConfig, List, ListEditing, ListProperties, ListPropertiesEditing, ListPropertiesUI, ListStyle, ListUI, ListUtils, TodoList, TodoListEditing, TodoListUI, ListCommand, DocumentListCommand, IndentCommand, DocumentListIndentCommand, DocumentListMergeCommand, DocumentListSplitCommand, ListStyleCommand, DocumentListStyleCommand, ListStartCommand, DocumentListStartCommand, ListReversedCommand, DocumentListReversedCommand, CheckTodoListCommand } from '.';
|
|
5
|
+
import type { DocumentList, DocumentListEditing, DocumentListProperties, DocumentListPropertiesEditing, DocumentListPropertiesUtils, AdjacentListsSupport, DocumentListUtils, ListConfig, List, ListEditing, ListProperties, ListPropertiesEditing, ListPropertiesUI, ListStyle, ListUI, ListUtils, TodoList, TodoListEditing, TodoListUI, TodoDocumentList, TodoDocumentListEditing, ListCommand, DocumentListCommand, IndentCommand, DocumentListIndentCommand, DocumentListMergeCommand, DocumentListSplitCommand, ListStyleCommand, DocumentListStyleCommand, ListStartCommand, DocumentListStartCommand, ListReversedCommand, DocumentListReversedCommand, CheckTodoListCommand, CheckTodoDocumentListCommand } from '.';
|
|
6
6
|
declare module '@ckeditor/ckeditor5-core' {
|
|
7
7
|
interface EditorConfig {
|
|
8
8
|
/**
|
|
@@ -31,6 +31,8 @@ declare module '@ckeditor/ckeditor5-core' {
|
|
|
31
31
|
[TodoList.pluginName]: TodoList;
|
|
32
32
|
[TodoListEditing.pluginName]: TodoListEditing;
|
|
33
33
|
[TodoListUI.pluginName]: TodoListUI;
|
|
34
|
+
[TodoDocumentList.pluginName]: TodoDocumentList;
|
|
35
|
+
[TodoDocumentListEditing.pluginName]: TodoDocumentListEditing;
|
|
34
36
|
}
|
|
35
37
|
interface CommandsMap {
|
|
36
38
|
numberedList: ListCommand | DocumentListCommand;
|
|
@@ -44,7 +46,7 @@ declare module '@ckeditor/ckeditor5-core' {
|
|
|
44
46
|
listStyle: ListStyleCommand | DocumentListStyleCommand;
|
|
45
47
|
listStart: ListStartCommand | DocumentListStartCommand;
|
|
46
48
|
listReversed: ListReversedCommand | DocumentListReversedCommand;
|
|
47
|
-
todoList: ListCommand;
|
|
48
|
-
checkTodoList: CheckTodoListCommand;
|
|
49
|
+
todoList: ListCommand | DocumentListCommand;
|
|
50
|
+
checkTodoList: CheckTodoListCommand | CheckTodoDocumentListCommand;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -41,7 +41,9 @@ export declare function reconvertItemsOnDataChange(model: Model, editing: Editin
|
|
|
41
41
|
* @param strategies The strategies.
|
|
42
42
|
* @param model The model.
|
|
43
43
|
*/
|
|
44
|
-
export declare function listItemDowncastConverter(attributeNames: Array<string>, strategies: Array<DowncastStrategy>, model: Model
|
|
44
|
+
export declare function listItemDowncastConverter(attributeNames: Array<string>, strategies: Array<DowncastStrategy>, model: Model, { dataPipeline }?: {
|
|
45
|
+
dataPipeline?: boolean;
|
|
46
|
+
}): GetCallback<DowncastAttributeEvent<ListElement>>;
|
|
45
47
|
/**
|
|
46
48
|
* Returns the bogus paragraph view element creator. A bogus paragraph is used if a list item contains only a single block or nested list.
|
|
47
49
|
*
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module list/documentlist/converters
|
|
7
7
|
*/
|
|
8
8
|
import { UpcastWriter } from 'ckeditor5/src/engine';
|
|
9
|
-
import { getAllListItemBlocks, getListItemBlocks, isListItemBlock, ListItemUid } from './utils/model';
|
|
9
|
+
import { getAllListItemBlocks, getListItemBlocks, isListItemBlock, isFirstBlockOfListItem, ListItemUid } from './utils/model';
|
|
10
10
|
import { createListElement, createListItemElement, getIndent, isListView, isListItemView } from './utils/view';
|
|
11
11
|
import ListWalker, { iterateSiblingListBlocks } from './utils/listwalker';
|
|
12
12
|
import { findAndAddListHeadToMap } from './utils/postfixers';
|
|
@@ -26,14 +26,22 @@ export function listItemUpcastConverter() {
|
|
|
26
26
|
if (!items.length) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
+
const listItemId = ListItemUid.next();
|
|
30
|
+
const listIndent = getIndent(data.viewItem);
|
|
31
|
+
let listType = data.viewItem.parent && data.viewItem.parent.is('element', 'ol') ? 'numbered' : 'bulleted';
|
|
32
|
+
// Preserve list type if was already set (for example by to-do list feature).
|
|
33
|
+
const firstItemListType = items[0].getAttribute('listType');
|
|
34
|
+
if (firstItemListType) {
|
|
35
|
+
listType = firstItemListType;
|
|
36
|
+
}
|
|
29
37
|
const attributes = {
|
|
30
|
-
listItemId
|
|
31
|
-
listIndent
|
|
32
|
-
listType
|
|
38
|
+
listItemId,
|
|
39
|
+
listIndent,
|
|
40
|
+
listType
|
|
33
41
|
};
|
|
34
42
|
for (const item of items) {
|
|
35
43
|
// Set list attributes only on same level items, those nested deeper are already handled by the recursive conversion.
|
|
36
|
-
if (!
|
|
44
|
+
if (!item.hasAttribute('listItemId')) {
|
|
37
45
|
writer.setAttributes(attributes, item);
|
|
38
46
|
}
|
|
39
47
|
}
|
|
@@ -111,7 +119,7 @@ export function reconvertItemsOnDataChange(model, editing, attributeNames, docum
|
|
|
111
119
|
if (entry.attributeNewValue === null) {
|
|
112
120
|
findAndAddListHeadToMap(entry.range.start.getShiftedBy(1), itemToListHead);
|
|
113
121
|
// Check if paragraph should be converted from bogus to plain paragraph.
|
|
114
|
-
if (
|
|
122
|
+
if (doesItemBlockRequiresRefresh(item)) {
|
|
115
123
|
itemsToRefresh.push(item);
|
|
116
124
|
}
|
|
117
125
|
}
|
|
@@ -122,7 +130,7 @@ export function reconvertItemsOnDataChange(model, editing, attributeNames, docum
|
|
|
122
130
|
else if (isListItemBlock(item)) {
|
|
123
131
|
// Some other attribute was changed on the list item,
|
|
124
132
|
// check if paragraph does not need to be converted to bogus or back.
|
|
125
|
-
if (
|
|
133
|
+
if (doesItemBlockRequiresRefresh(item)) {
|
|
126
134
|
itemsToRefresh.push(item);
|
|
127
135
|
}
|
|
128
136
|
}
|
|
@@ -156,7 +164,7 @@ export function reconvertItemsOnDataChange(model, editing, attributeNames, docum
|
|
|
156
164
|
for (const block of blocks) {
|
|
157
165
|
visited.add(block);
|
|
158
166
|
// Check if bogus vs plain paragraph needs refresh.
|
|
159
|
-
if (
|
|
167
|
+
if (doesItemBlockRequiresRefresh(block, blocks)) {
|
|
160
168
|
itemsToRefresh.push(block);
|
|
161
169
|
}
|
|
162
170
|
// Check if wrapping with UL, OL, LIs needs refresh.
|
|
@@ -167,14 +175,21 @@ export function reconvertItemsOnDataChange(model, editing, attributeNames, docum
|
|
|
167
175
|
}
|
|
168
176
|
return itemsToRefresh;
|
|
169
177
|
}
|
|
170
|
-
function
|
|
171
|
-
if (!item.is('element', 'paragraph')) {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
178
|
+
function doesItemBlockRequiresRefresh(item, blocks) {
|
|
174
179
|
const viewElement = editing.mapper.toViewElement(item);
|
|
175
180
|
if (!viewElement) {
|
|
176
181
|
return false;
|
|
177
182
|
}
|
|
183
|
+
const needsRefresh = documentListEditing.fire('checkElement', {
|
|
184
|
+
modelElement: item,
|
|
185
|
+
viewElement
|
|
186
|
+
});
|
|
187
|
+
if (needsRefresh) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
if (!item.is('element', 'paragraph') && !item.is('element', 'listItem')) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
178
193
|
const useBogus = shouldUseBogusParagraph(item, attributeNames, blocks);
|
|
179
194
|
if (useBogus && viewElement.is('element', 'p')) {
|
|
180
195
|
return true;
|
|
@@ -225,7 +240,7 @@ export function reconvertItemsOnDataChange(model, editing, attributeNames, docum
|
|
|
225
240
|
* @param strategies The strategies.
|
|
226
241
|
* @param model The model.
|
|
227
242
|
*/
|
|
228
|
-
export function listItemDowncastConverter(attributeNames, strategies, model) {
|
|
243
|
+
export function listItemDowncastConverter(attributeNames, strategies, model, { dataPipeline } = {}) {
|
|
229
244
|
const consumer = createAttributesConsumer(attributeNames);
|
|
230
245
|
return (evt, data, conversionApi) => {
|
|
231
246
|
const { writer, mapper, consumable } = conversionApi;
|
|
@@ -240,10 +255,14 @@ export function listItemDowncastConverter(attributeNames, strategies, model) {
|
|
|
240
255
|
// Use positions mapping instead of mapper.toViewElement( listItem ) to find outermost view element.
|
|
241
256
|
// This is for cases when mapping is using inner view element like in the code blocks (pre > code).
|
|
242
257
|
const viewElement = findMappedViewElement(listItem, mapper, model);
|
|
258
|
+
// Remove custom item marker.
|
|
259
|
+
removeCustomMarkerElements(viewElement, writer, mapper);
|
|
243
260
|
// Unwrap element from current list wrappers.
|
|
244
261
|
unwrapListItemBlock(viewElement, writer);
|
|
245
|
-
//
|
|
246
|
-
|
|
262
|
+
// Insert custom item marker.
|
|
263
|
+
const viewRange = insertCustomMarkerElements(listItem, viewElement, strategies, writer, { dataPipeline });
|
|
264
|
+
// Then wrap them with the new list wrappers (UL, OL, LI).
|
|
265
|
+
wrapListItemBlock(listItem, viewRange, strategies, writer);
|
|
247
266
|
};
|
|
248
267
|
}
|
|
249
268
|
/**
|
|
@@ -279,9 +298,74 @@ export function bogusParagraphCreator(attributeNames, { dataPipeline } = {}) {
|
|
|
279
298
|
export function findMappedViewElement(element, mapper, model) {
|
|
280
299
|
const modelRange = model.createRangeOn(element);
|
|
281
300
|
const viewRange = mapper.toViewRange(modelRange).getTrimmed();
|
|
282
|
-
return viewRange.
|
|
301
|
+
return viewRange.end.nodeBefore;
|
|
283
302
|
}
|
|
284
|
-
|
|
303
|
+
/**
|
|
304
|
+
* Removes a custom marker elements and item wrappers related to that marker.
|
|
305
|
+
*/
|
|
306
|
+
function removeCustomMarkerElements(viewElement, viewWriter, mapper) {
|
|
307
|
+
// Remove item wrapper.
|
|
308
|
+
while (viewElement.parent.is('attributeElement') && viewElement.parent.getCustomProperty('listItemWrapper')) {
|
|
309
|
+
viewWriter.unwrap(viewWriter.createRangeIn(viewElement.parent), viewElement.parent);
|
|
310
|
+
}
|
|
311
|
+
// Remove custom item markers.
|
|
312
|
+
const viewWalker = viewWriter.createPositionBefore(viewElement).getWalker({ direction: 'backward' });
|
|
313
|
+
const markersToRemove = [];
|
|
314
|
+
for (const { item } of viewWalker) {
|
|
315
|
+
// Walk only over the non-mapped elements between list item blocks.
|
|
316
|
+
if (item.is('element') && mapper.toModelElement(item)) {
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
if (item.is('element') && item.getCustomProperty('listItemMarker')) {
|
|
320
|
+
markersToRemove.push(item);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const marker of markersToRemove) {
|
|
324
|
+
viewWriter.remove(marker);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Inserts a custom marker elements and wraps first block of a list item if marker requires it.
|
|
329
|
+
*/
|
|
330
|
+
function insertCustomMarkerElements(listItem, viewElement, strategies, writer, { dataPipeline }) {
|
|
331
|
+
let viewRange = writer.createRangeOn(viewElement);
|
|
332
|
+
// Marker can be inserted only before the first block of a list item.
|
|
333
|
+
if (!isFirstBlockOfListItem(listItem)) {
|
|
334
|
+
return viewRange;
|
|
335
|
+
}
|
|
336
|
+
for (const strategy of strategies) {
|
|
337
|
+
if (strategy.scope != 'itemMarker') {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// Create the custom marker element and inject it before the first block of the list item.
|
|
341
|
+
const markerElement = strategy.createElement(writer, listItem, { dataPipeline });
|
|
342
|
+
if (!markerElement) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
writer.setCustomProperty('listItemMarker', true, markerElement);
|
|
346
|
+
writer.insert(viewRange.start, markerElement);
|
|
347
|
+
viewRange = writer.createRange(writer.createPositionBefore(markerElement), writer.createPositionAfter(viewElement));
|
|
348
|
+
// Wrap the marker and optionally the first block with an attribute element (label for to-do lists).
|
|
349
|
+
if (!strategy.createWrapperElement || !strategy.canWrapElement) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const wrapper = strategy.createWrapperElement(writer, listItem, { dataPipeline });
|
|
353
|
+
writer.setCustomProperty('listItemWrapper', true, wrapper);
|
|
354
|
+
// The whole block can be wrapped...
|
|
355
|
+
if (strategy.canWrapElement(listItem)) {
|
|
356
|
+
viewRange = writer.wrap(viewRange, wrapper);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// ... or only the marker element (if the block is downcasted to heading or block widget).
|
|
360
|
+
viewRange = writer.wrap(writer.createRangeOn(markerElement), wrapper);
|
|
361
|
+
viewRange = writer.createRange(viewRange.start, writer.createPositionAfter(viewElement));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return viewRange;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Unwraps all ol, ul, and li attribute elements that are wrapping the provided view element.
|
|
368
|
+
*/
|
|
285
369
|
function unwrapListItemBlock(viewElement, viewWriter) {
|
|
286
370
|
let attributeElement = viewElement.parent;
|
|
287
371
|
while (attributeElement.is('attributeElement') && ['ul', 'ol', 'li'].includes(attributeElement.name)) {
|
|
@@ -290,7 +374,9 @@ function unwrapListItemBlock(viewElement, viewWriter) {
|
|
|
290
374
|
attributeElement = parentElement;
|
|
291
375
|
}
|
|
292
376
|
}
|
|
293
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Wraps the given list item with appropriate attribute elements for ul, ol, and li.
|
|
379
|
+
*/
|
|
294
380
|
function wrapListItemBlock(listItem, viewRange, strategies, writer) {
|
|
295
381
|
if (!listItem.hasAttribute('listIndent')) {
|
|
296
382
|
return;
|
|
@@ -301,7 +387,8 @@ function wrapListItemBlock(listItem, viewRange, strategies, writer) {
|
|
|
301
387
|
const listItemViewElement = createListItemElement(writer, indent, currentListItem.getAttribute('listItemId'));
|
|
302
388
|
const listViewElement = createListElement(writer, indent, currentListItem.getAttribute('listType'));
|
|
303
389
|
for (const strategy of strategies) {
|
|
304
|
-
if (
|
|
390
|
+
if ((strategy.scope == 'list' || strategy.scope == 'item') &&
|
|
391
|
+
currentListItem.hasAttribute(strategy.attributeName)) {
|
|
305
392
|
strategy.setAttributeOnDowncast(writer, currentListItem.getAttribute(strategy.attributeName), strategy.scope == 'list' ? listViewElement : listItemViewElement);
|
|
306
393
|
}
|
|
307
394
|
}
|
|
@@ -14,7 +14,7 @@ export default class DocumentListCommand extends Command {
|
|
|
14
14
|
/**
|
|
15
15
|
* The type of the list created by the command.
|
|
16
16
|
*/
|
|
17
|
-
readonly type: 'numbered' | 'bulleted';
|
|
17
|
+
readonly type: 'numbered' | 'bulleted' | 'todo';
|
|
18
18
|
/**
|
|
19
19
|
* A flag indicating whether the command is active, which means that the selection starts in a list of the same type.
|
|
20
20
|
*
|
|
@@ -28,7 +28,7 @@ export default class DocumentListCommand extends Command {
|
|
|
28
28
|
* @param editor The editor instance.
|
|
29
29
|
* @param type List type that will be handled by this command.
|
|
30
30
|
*/
|
|
31
|
-
constructor(editor: Editor, type: 'numbered' | 'bulleted');
|
|
31
|
+
constructor(editor: Editor, type: 'numbered' | 'bulleted' | 'todo');
|
|
32
32
|
/**
|
|
33
33
|
* @inheritDoc
|
|
34
34
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
import { Command } from 'ckeditor5/src/core';
|
|
6
|
-
import { splitListItemBefore, expandListBlocksToCompleteItems, getListItemBlocks, getListItems, removeListAttributes, outdentFollowingItems, ListItemUid, sortBlocks, getSelectedBlockObject, isListItemBlock } from './utils/model';
|
|
6
|
+
import { splitListItemBefore, expandListBlocksToCompleteItems, getListItemBlocks, getListItems, removeListAttributes, outdentFollowingItems, ListItemUid, sortBlocks, getSelectedBlockObject, isListItemBlock, canBecomeSimpleListItem } from './utils/model';
|
|
7
7
|
/**
|
|
8
8
|
* The list command. It is used by the {@link module:list/documentlist~DocumentList document list feature}.
|
|
9
9
|
*/
|
|
@@ -40,7 +40,7 @@ export default class DocumentListCommand extends Command {
|
|
|
40
40
|
const document = model.document;
|
|
41
41
|
const selectedBlockObject = getSelectedBlockObject(model);
|
|
42
42
|
const blocks = Array.from(document.selection.getSelectedBlocks())
|
|
43
|
-
.filter(block => model.schema.checkAttribute(block, 'listType'));
|
|
43
|
+
.filter(block => model.schema.checkAttribute(block, 'listType') || canBecomeSimpleListItem(block, model.schema));
|
|
44
44
|
// Whether we are turning off some items.
|
|
45
45
|
const turnOff = options.forceValue !== undefined ? !options.forceValue : this.value;
|
|
46
46
|
model.change(writer => {
|
|
@@ -52,13 +52,13 @@ export default class DocumentListCommand extends Command {
|
|
|
52
52
|
if (itemBlocks.length > 1) {
|
|
53
53
|
changedBlocks.push(...splitListItemBefore(itemBlocks[1], writer));
|
|
54
54
|
}
|
|
55
|
-
//
|
|
55
|
+
// Strip list attributes.
|
|
56
56
|
changedBlocks.push(...removeListAttributes(blocks, writer));
|
|
57
57
|
// Outdent items following the selected list item.
|
|
58
58
|
changedBlocks.push(...outdentFollowingItems(lastBlock, writer));
|
|
59
59
|
this._fireAfterExecute(changedBlocks);
|
|
60
60
|
}
|
|
61
|
-
//
|
|
61
|
+
// Changing type of list items for a collapsed selection inside a list item.
|
|
62
62
|
else if ((selectedBlockObject || document.selection.isCollapsed) && isListItemBlock(blocks[0])) {
|
|
63
63
|
const changedBlocks = getListItems(selectedBlockObject || blocks[0]);
|
|
64
64
|
for (const block of changedBlocks) {
|
|
@@ -72,6 +72,10 @@ export default class DocumentListCommand extends Command {
|
|
|
72
72
|
for (const block of blocks) {
|
|
73
73
|
// Promote the given block to the list item.
|
|
74
74
|
if (!block.hasAttribute('listType')) {
|
|
75
|
+
// Rename block to a simple list item if this option is enabled.
|
|
76
|
+
if (!block.is('element', 'listItem') && canBecomeSimpleListItem(block, model.schema)) {
|
|
77
|
+
writer.rename(block, 'listItem');
|
|
78
|
+
}
|
|
75
79
|
writer.setAttributes({
|
|
76
80
|
listIndent: 0,
|
|
77
81
|
listItemId: ListItemUid.next(),
|
|
@@ -125,8 +129,9 @@ export default class DocumentListCommand extends Command {
|
|
|
125
129
|
* @returns Whether the command should be enabled.
|
|
126
130
|
*/
|
|
127
131
|
_checkEnabled() {
|
|
128
|
-
const
|
|
129
|
-
const schema =
|
|
132
|
+
const model = this.editor.model;
|
|
133
|
+
const schema = model.schema;
|
|
134
|
+
const selection = model.document.selection;
|
|
130
135
|
const blocks = Array.from(selection.getSelectedBlocks());
|
|
131
136
|
if (!blocks.length) {
|
|
132
137
|
return false;
|
|
@@ -136,7 +141,7 @@ export default class DocumentListCommand extends Command {
|
|
|
136
141
|
return true;
|
|
137
142
|
}
|
|
138
143
|
for (const block of blocks) {
|
|
139
|
-
if (schema.checkAttribute(block, 'listType')) {
|
|
144
|
+
if (schema.checkAttribute(block, 'listType') || canBecomeSimpleListItem(block, schema)) {
|
|
140
145
|
return true;
|
|
141
146
|
}
|
|
142
147
|
}
|
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @module list/documentlist/documentlistediting
|
|
7
7
|
*/
|
|
8
|
-
import { Plugin } from 'ckeditor5/src/core';
|
|
9
|
-
import type { DowncastWriter, Element, ViewElement, Writer } from 'ckeditor5/src/engine';
|
|
8
|
+
import { Plugin, type Editor } from 'ckeditor5/src/core';
|
|
9
|
+
import type { DowncastWriter, Element, ViewElement, ViewAttributeElement, Writer } from 'ckeditor5/src/engine';
|
|
10
10
|
import { Delete } from 'ckeditor5/src/typing';
|
|
11
11
|
import { Enter } from 'ckeditor5/src/enter';
|
|
12
12
|
import DocumentListUtils from './documentlistutils';
|
|
13
13
|
import { ListBlocksIterable } from './utils/listwalker';
|
|
14
|
+
import { ClipboardPipeline } from 'ckeditor5/src/clipboard';
|
|
14
15
|
import '../../theme/documentlist.css';
|
|
15
16
|
import '../../theme/list.css';
|
|
16
17
|
/**
|
|
17
18
|
* Map of model attributes applicable to list blocks.
|
|
18
19
|
*/
|
|
19
20
|
export interface ListItemAttributesMap {
|
|
20
|
-
listType?: 'numbered' | 'bulleted';
|
|
21
|
+
listType?: 'numbered' | 'bulleted' | 'todo';
|
|
21
22
|
listIndent?: number;
|
|
22
23
|
listItemId?: string;
|
|
23
24
|
}
|
|
@@ -36,7 +37,11 @@ export default class DocumentListEditing extends Plugin {
|
|
|
36
37
|
/**
|
|
37
38
|
* @inheritDoc
|
|
38
39
|
*/
|
|
39
|
-
static get requires(): readonly [typeof Enter, typeof Delete, typeof DocumentListUtils];
|
|
40
|
+
static get requires(): readonly [typeof Enter, typeof Delete, typeof DocumentListUtils, typeof ClipboardPipeline];
|
|
41
|
+
/**
|
|
42
|
+
* @inheritDoc
|
|
43
|
+
*/
|
|
44
|
+
constructor(editor: Editor);
|
|
40
45
|
/**
|
|
41
46
|
* @inheritDoc
|
|
42
47
|
*/
|
|
@@ -57,7 +62,7 @@ export default class DocumentListEditing extends Plugin {
|
|
|
57
62
|
/**
|
|
58
63
|
* Returns list of model attribute names that should affect downcast conversion.
|
|
59
64
|
*/
|
|
60
|
-
|
|
65
|
+
getListAttributeNames(): Array<string>;
|
|
61
66
|
/**
|
|
62
67
|
* Attaches the listener to the {@link module:engine/view/document~Document#event:delete} event and handles backspace/delete
|
|
63
68
|
* keys in and around document lists.
|
|
@@ -88,9 +93,9 @@ export default class DocumentListEditing extends Plugin {
|
|
|
88
93
|
private _setupClipboardIntegration;
|
|
89
94
|
}
|
|
90
95
|
/**
|
|
91
|
-
* The downcast strategy.
|
|
96
|
+
* The attribute to attribute downcast strategy for UL, OL, LI elements.
|
|
92
97
|
*/
|
|
93
|
-
export interface
|
|
98
|
+
export interface AttributeDowncastStrategy {
|
|
94
99
|
/**
|
|
95
100
|
* The scope of the downcast (whether it applies to LI or OL/UL).
|
|
96
101
|
*/
|
|
@@ -104,6 +109,40 @@ export interface DowncastStrategy {
|
|
|
104
109
|
*/
|
|
105
110
|
setAttributeOnDowncast(writer: DowncastWriter, value: unknown, element: ViewElement): void;
|
|
106
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The custom marker downcast strategy.
|
|
114
|
+
*/
|
|
115
|
+
export interface ItemMarkerDowncastStrategy {
|
|
116
|
+
/**
|
|
117
|
+
* The scope of the downcast.
|
|
118
|
+
*/
|
|
119
|
+
scope: 'itemMarker';
|
|
120
|
+
/**
|
|
121
|
+
* The model attribute name.
|
|
122
|
+
*/
|
|
123
|
+
attributeName: string;
|
|
124
|
+
/**
|
|
125
|
+
* Creates a view element for a custom item marker.
|
|
126
|
+
*/
|
|
127
|
+
createElement(writer: DowncastWriter, modelElement: Element, { dataPipeline }: {
|
|
128
|
+
dataPipeline?: boolean;
|
|
129
|
+
}): ViewElement | null;
|
|
130
|
+
/**
|
|
131
|
+
* Creates an AttributeElement to be used for wrapping a first block of a list item.
|
|
132
|
+
*/
|
|
133
|
+
createWrapperElement?(writer: DowncastWriter, modelElement: Element, { dataPipeline }: {
|
|
134
|
+
dataPipeline?: boolean;
|
|
135
|
+
}): ViewAttributeElement;
|
|
136
|
+
/**
|
|
137
|
+
* Should return true if the given list block can be wrapped with the wrapper created by `createWrapperElement()`
|
|
138
|
+
* or only the marker element should be wrapped.
|
|
139
|
+
*/
|
|
140
|
+
canWrapElement?(modelElement: Element): boolean;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The downcast strategy.
|
|
144
|
+
*/
|
|
145
|
+
export type DowncastStrategy = AttributeDowncastStrategy | ItemMarkerDowncastStrategy;
|
|
107
146
|
/**
|
|
108
147
|
* Event fired on changes detected on the model list element to verify if the view representation of a list element
|
|
109
148
|
* is representing those attributes.
|
|
@@ -152,3 +191,22 @@ export type DocumentListEditingCheckAttributesEvent = {
|
|
|
152
191
|
];
|
|
153
192
|
return: boolean;
|
|
154
193
|
};
|
|
194
|
+
/**
|
|
195
|
+
* Event fired on changes detected on the model list element to verify if the view representation of a list block element
|
|
196
|
+
* is representing those attributes.
|
|
197
|
+
*
|
|
198
|
+
* It allows triggering a reconversion of a list item block.
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
201
|
+
* @eventName ~DocumentListEditing#checkElement
|
|
202
|
+
*/
|
|
203
|
+
export type DocumentListEditingCheckElementEvent = {
|
|
204
|
+
name: 'checkElement';
|
|
205
|
+
args: [
|
|
206
|
+
{
|
|
207
|
+
viewElement: ViewElement;
|
|
208
|
+
modelElement: Element;
|
|
209
|
+
}
|
|
210
|
+
];
|
|
211
|
+
return: boolean;
|
|
212
|
+
};
|