@ckeditor/ckeditor5-list 40.2.0 → 41.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.
Files changed (196) hide show
  1. package/CHANGELOG.md +25 -25
  2. package/LICENSE.md +1 -1
  3. package/build/list.js +2 -2
  4. package/build/translations/ug.js +1 -1
  5. package/ckeditor5-metadata.json +34 -34
  6. package/lang/translations/ar.po +1 -1
  7. package/lang/translations/ast.po +1 -1
  8. package/lang/translations/az.po +1 -1
  9. package/lang/translations/bg.po +1 -1
  10. package/lang/translations/bn.po +1 -1
  11. package/lang/translations/ca.po +1 -1
  12. package/lang/translations/cs.po +1 -1
  13. package/lang/translations/da.po +1 -1
  14. package/lang/translations/de-ch.po +1 -1
  15. package/lang/translations/de.po +1 -1
  16. package/lang/translations/el.po +1 -1
  17. package/lang/translations/en-au.po +1 -1
  18. package/lang/translations/en-gb.po +1 -1
  19. package/lang/translations/en.po +1 -1
  20. package/lang/translations/eo.po +1 -1
  21. package/lang/translations/es.po +1 -1
  22. package/lang/translations/et.po +1 -1
  23. package/lang/translations/eu.po +1 -1
  24. package/lang/translations/fa.po +1 -1
  25. package/lang/translations/fi.po +1 -1
  26. package/lang/translations/fr.po +1 -1
  27. package/lang/translations/gl.po +1 -1
  28. package/lang/translations/he.po +1 -1
  29. package/lang/translations/hi.po +1 -1
  30. package/lang/translations/hr.po +1 -1
  31. package/lang/translations/hu.po +1 -1
  32. package/lang/translations/id.po +1 -1
  33. package/lang/translations/it.po +1 -1
  34. package/lang/translations/ja.po +1 -1
  35. package/lang/translations/jv.po +1 -1
  36. package/lang/translations/km.po +1 -1
  37. package/lang/translations/kn.po +1 -1
  38. package/lang/translations/ko.po +1 -1
  39. package/lang/translations/ku.po +1 -1
  40. package/lang/translations/lt.po +1 -1
  41. package/lang/translations/lv.po +1 -1
  42. package/lang/translations/ms.po +1 -1
  43. package/lang/translations/nb.po +1 -1
  44. package/lang/translations/ne.po +1 -1
  45. package/lang/translations/nl.po +1 -1
  46. package/lang/translations/no.po +1 -1
  47. package/lang/translations/pl.po +1 -1
  48. package/lang/translations/pt-br.po +1 -1
  49. package/lang/translations/pt.po +1 -1
  50. package/lang/translations/ro.po +1 -1
  51. package/lang/translations/ru.po +1 -1
  52. package/lang/translations/si.po +1 -1
  53. package/lang/translations/sk.po +1 -1
  54. package/lang/translations/sq.po +1 -1
  55. package/lang/translations/sr-latn.po +1 -1
  56. package/lang/translations/sr.po +1 -1
  57. package/lang/translations/sv.po +1 -1
  58. package/lang/translations/th.po +1 -1
  59. package/lang/translations/tk.po +1 -1
  60. package/lang/translations/tr.po +1 -1
  61. package/lang/translations/tt.po +1 -1
  62. package/lang/translations/ug.po +20 -20
  63. package/lang/translations/uk.po +1 -1
  64. package/lang/translations/ur.po +1 -1
  65. package/lang/translations/uz.po +1 -1
  66. package/lang/translations/vi.po +1 -1
  67. package/lang/translations/zh-cn.po +1 -1
  68. package/lang/translations/zh.po +1 -1
  69. package/package.json +3 -2
  70. package/src/augmentation.d.ts +29 -28
  71. package/src/augmentation.js +1 -1
  72. package/src/documentlist.d.ts +9 -7
  73. package/src/documentlist.js +18 -7
  74. package/src/documentlistproperties.d.ts +9 -8
  75. package/src/documentlistproperties.js +18 -8
  76. package/src/index.d.ts +39 -37
  77. package/src/index.js +32 -23
  78. package/src/legacylist/legacyconverters.d.ts +196 -0
  79. package/src/legacylist/legacyconverters.js +905 -0
  80. package/src/{list/indentcommand.d.ts → legacylist/legacyindentcommand.d.ts} +4 -4
  81. package/src/{list/indentcommand.js → legacylist/legacyindentcommand.js} +5 -5
  82. package/src/{documentlist/documentlistcommand.d.ts → legacylist/legacylistcommand.d.ts} +5 -30
  83. package/src/legacylist/legacylistcommand.js +274 -0
  84. package/src/legacylist/legacylistediting.d.ts +32 -0
  85. package/src/legacylist/legacylistediting.js +161 -0
  86. package/src/legacylist/legacylistutils.d.ts +41 -0
  87. package/src/legacylist/legacylistutils.js +46 -0
  88. package/src/legacylist/legacyutils.d.ts +101 -0
  89. package/src/legacylist/legacyutils.js +347 -0
  90. package/src/legacylist.d.ts +26 -0
  91. package/src/legacylist.js +30 -0
  92. package/src/legacylistproperties/legacylistpropertiesediting.d.ts +72 -0
  93. package/src/legacylistproperties/legacylistpropertiesediting.js +696 -0
  94. package/src/legacylistproperties/legacylistreversedcommand.d.ts +38 -0
  95. package/src/legacylistproperties/legacylistreversedcommand.js +52 -0
  96. package/src/{documentlistproperties/documentliststartcommand.d.ts → legacylistproperties/legacyliststartcommand.d.ts} +6 -7
  97. package/src/legacylistproperties/legacyliststartcommand.js +51 -0
  98. package/src/{documentlistproperties/documentliststylecommand.d.ts → legacylistproperties/legacyliststylecommand.d.ts} +14 -19
  99. package/src/{documentlistproperties/documentliststylecommand.js → legacylistproperties/legacyliststylecommand.js} +22 -36
  100. package/src/legacylistproperties.d.ts +27 -0
  101. package/src/legacylistproperties.js +31 -0
  102. package/src/{tododocumentlist/checktododocumentlistcommand.d.ts → legacytodolist/legacychecktodolistcommand.d.ts} +17 -14
  103. package/src/{tododocumentlist/checktododocumentlistcommand.js → legacytodolist/legacychecktodolistcommand.js} +34 -40
  104. package/src/{todolist/todolistconverters.d.ts → legacytodolist/legacytodolistconverters.d.ts} +9 -8
  105. package/src/{todolist/todolistconverters.js → legacytodolist/legacytodolistconverters.js} +6 -5
  106. package/src/{tododocumentlist/tododocumentlistediting.d.ts → legacytodolist/legacytodolistediting.d.ts} +9 -8
  107. package/src/legacytodolist/legacytodolistediting.js +161 -0
  108. package/src/legacytodolist.d.ts +27 -0
  109. package/src/legacytodolist.js +31 -0
  110. package/src/{documentlist → list}/adjacentlistssupport.d.ts +2 -2
  111. package/src/{documentlist → list}/adjacentlistssupport.js +9 -9
  112. package/src/list/converters.d.ts +41 -172
  113. package/src/list/converters.js +357 -821
  114. package/src/list/listcommand.d.ts +28 -3
  115. package/src/list/listcommand.js +81 -205
  116. package/src/list/listediting.d.ts +189 -9
  117. package/src/list/listediting.js +592 -107
  118. package/src/{documentlist/documentlistindentcommand.d.ts → list/listindentcommand.d.ts} +10 -10
  119. package/src/{documentlist/documentlistindentcommand.js → list/listindentcommand.js} +7 -7
  120. package/src/{documentlist/documentlistmergecommand.d.ts → list/listmergecommand.d.ts} +10 -10
  121. package/src/{documentlist/documentlistmergecommand.js → list/listmergecommand.js} +7 -7
  122. package/src/{documentlist/documentlistsplitcommand.d.ts → list/listsplitcommand.d.ts} +10 -10
  123. package/src/{documentlist/documentlistsplitcommand.js → list/listsplitcommand.js} +5 -5
  124. package/src/list/listui.d.ts +2 -2
  125. package/src/list/listui.js +5 -7
  126. package/src/list/listutils.d.ts +22 -17
  127. package/src/list/listutils.js +24 -20
  128. package/src/{documentlist → list}/utils/listwalker.d.ts +5 -5
  129. package/src/{documentlist → list}/utils/listwalker.js +4 -4
  130. package/src/{documentlist → list}/utils/model.d.ts +4 -4
  131. package/src/{documentlist → list}/utils/model.js +3 -3
  132. package/src/{documentlist → list}/utils/postfixers.d.ts +5 -5
  133. package/src/{documentlist → list}/utils/postfixers.js +3 -3
  134. package/src/{documentlist → list}/utils/view.d.ts +3 -3
  135. package/src/{documentlist → list}/utils/view.js +1 -1
  136. package/src/list/utils.d.ts +2 -96
  137. package/src/list/utils.js +2 -342
  138. package/src/list.d.ts +6 -6
  139. package/src/list.js +6 -6
  140. package/src/listconfig.d.ts +10 -10
  141. package/src/listconfig.js +1 -1
  142. package/src/{documentlistproperties → listproperties}/converters.d.ts +5 -5
  143. package/src/{documentlistproperties → listproperties}/converters.js +1 -1
  144. package/src/listproperties/listpropertiesediting.d.ts +56 -40
  145. package/src/listproperties/listpropertiesediting.js +145 -575
  146. package/src/listproperties/listpropertiesui.d.ts +2 -2
  147. package/src/listproperties/listpropertiesui.js +6 -8
  148. package/src/{documentlistproperties/documentlistpropertiesutils.d.ts → listproperties/listpropertiesutils.d.ts} +5 -5
  149. package/src/{documentlistproperties/documentlistpropertiesutils.js → listproperties/listpropertiesutils.js} +6 -6
  150. package/src/listproperties/listreversedcommand.d.ts +4 -6
  151. package/src/listproperties/listreversedcommand.js +17 -14
  152. package/src/listproperties/liststartcommand.d.ts +4 -3
  153. package/src/listproperties/liststartcommand.js +17 -11
  154. package/src/listproperties/liststylecommand.d.ts +16 -11
  155. package/src/listproperties/liststylecommand.js +33 -19
  156. package/src/listproperties/ui/listpropertiesview.d.ts +5 -5
  157. package/src/listproperties/ui/listpropertiesview.js +3 -3
  158. package/src/{documentlistproperties → listproperties}/utils/style.d.ts +1 -1
  159. package/src/{documentlistproperties → listproperties}/utils/style.js +2 -2
  160. package/src/listproperties.d.ts +6 -5
  161. package/src/listproperties.js +6 -5
  162. package/src/tododocumentlist.d.ts +9 -8
  163. package/src/tododocumentlist.js +18 -8
  164. package/src/todolist/checktodolistcommand.d.ts +11 -14
  165. package/src/todolist/checktodolistcommand.js +37 -31
  166. package/src/{tododocumentlist → todolist}/todocheckboxchangeobserver.d.ts +6 -6
  167. package/src/{tododocumentlist → todolist}/todocheckboxchangeobserver.js +3 -3
  168. package/src/todolist/todolistediting.d.ts +5 -6
  169. package/src/todolist/todolistediting.js +314 -76
  170. package/src/todolist/todolistui.d.ts +2 -2
  171. package/src/todolist/todolistui.js +4 -5
  172. package/src/todolist.d.ts +6 -6
  173. package/src/todolist.js +6 -6
  174. package/theme/documentlist.css +1 -1
  175. package/theme/list.css +1 -1
  176. package/theme/listproperties.css +1 -1
  177. package/theme/liststyles.css +1 -1
  178. package/theme/todolist.css +1 -1
  179. package/src/documentlist/converters.d.ts +0 -65
  180. package/src/documentlist/converters.js +0 -441
  181. package/src/documentlist/documentlistcommand.js +0 -150
  182. package/src/documentlist/documentlistediting.d.ts +0 -212
  183. package/src/documentlist/documentlistediting.js +0 -646
  184. package/src/documentlist/documentlistutils.d.ts +0 -46
  185. package/src/documentlist/documentlistutils.js +0 -50
  186. package/src/documentlistproperties/documentlistpropertiesediting.d.ts +0 -88
  187. package/src/documentlistproperties/documentlistpropertiesediting.js +0 -266
  188. package/src/documentlistproperties/documentlistreversedcommand.d.ts +0 -36
  189. package/src/documentlistproperties/documentlistreversedcommand.js +0 -55
  190. package/src/documentlistproperties/documentliststartcommand.js +0 -57
  191. package/src/liststyle.d.ts +0 -28
  192. package/src/liststyle.js +0 -36
  193. package/src/tododocumentlist/tododocumentlistediting.js +0 -399
  194. package/theme/icons/bulletedlist.svg +0 -1
  195. package/theme/icons/numberedlist.svg +0 -1
  196. package/theme/icons/todolist.svg +0 -1
@@ -1,65 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module list/documentlist/converters
7
- */
8
- import { type DowncastAttributeEvent, type EditingController, type Element, type ElementCreatorFunction, type Mapper, type Model, type UpcastElementEvent, type ViewElement } from 'ckeditor5/src/engine';
9
- import type { GetCallback } from 'ckeditor5/src/utils';
10
- import { type ListElement } from './utils/model';
11
- import type { default as DocumentListEditing, DowncastStrategy } from './documentlistediting';
12
- /**
13
- * Returns the upcast converter for list items. It's supposed to work after the block converters (content inside list items) are converted.
14
- *
15
- * @internal
16
- */
17
- export declare function listItemUpcastConverter(): GetCallback<UpcastElementEvent>;
18
- /**
19
- * Returns the upcast converter for the `<ul>` and `<ol>` view elements that cleans the input view of garbage.
20
- * This is mostly to clean whitespaces from between the `<li>` view elements inside the view list element. However,
21
- * incorrect data can also be cleared if the view was incorrect.
22
- *
23
- * @internal
24
- */
25
- export declare function listUpcastCleanList(): GetCallback<UpcastElementEvent>;
26
- /**
27
- * Returns a model document change:data event listener that triggers conversion of related items if needed.
28
- *
29
- * @internal
30
- * @param model The editor model.
31
- * @param editing The editing controller.
32
- * @param attributeNames The list of all model list attributes (including registered strategies).
33
- * @param documentListEditing The document list editing plugin.
34
- */
35
- export declare function reconvertItemsOnDataChange(model: Model, editing: EditingController, attributeNames: Array<string>, documentListEditing: DocumentListEditing): () => void;
36
- /**
37
- * Returns the list item downcast converter.
38
- *
39
- * @internal
40
- * @param attributeNames A list of attribute names that should be converted if they are set.
41
- * @param strategies The strategies.
42
- * @param model The model.
43
- */
44
- export declare function listItemDowncastConverter(attributeNames: Array<string>, strategies: Array<DowncastStrategy>, model: Model, { dataPipeline }?: {
45
- dataPipeline?: boolean;
46
- }): GetCallback<DowncastAttributeEvent<ListElement>>;
47
- /**
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.
49
- *
50
- * @internal
51
- * @param attributeNames The list of all model list attributes (including registered strategies).
52
- */
53
- export declare function bogusParagraphCreator(attributeNames: Array<string>, { dataPipeline }?: {
54
- dataPipeline?: boolean;
55
- }): ElementCreatorFunction;
56
- /**
57
- * Helper for mapping mode to view elements. It's using positions mapping instead of mapper.toViewElement( element )
58
- * to find outermost view element. This is for cases when mapping is using inner view element like in the code blocks (pre > code).
59
- *
60
- * @internal
61
- * @param element The model element.
62
- * @param mapper The mapper instance.
63
- * @param model The model.
64
- */
65
- export declare function findMappedViewElement(element: Element, mapper: Mapper, model: Model): ViewElement | null;
@@ -1,441 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module list/documentlist/converters
7
- */
8
- import { UpcastWriter } from 'ckeditor5/src/engine';
9
- import { getAllListItemBlocks, getListItemBlocks, isListItemBlock, isFirstBlockOfListItem, ListItemUid } from './utils/model';
10
- import { createListElement, createListItemElement, getIndent, isListView, isListItemView } from './utils/view';
11
- import ListWalker, { iterateSiblingListBlocks } from './utils/listwalker';
12
- import { findAndAddListHeadToMap } from './utils/postfixers';
13
- /**
14
- * Returns the upcast converter for list items. It's supposed to work after the block converters (content inside list items) are converted.
15
- *
16
- * @internal
17
- */
18
- export function listItemUpcastConverter() {
19
- return (evt, data, conversionApi) => {
20
- const { writer, schema } = conversionApi;
21
- if (!data.modelRange) {
22
- return;
23
- }
24
- const items = Array.from(data.modelRange.getItems({ shallow: true }))
25
- .filter((item) => schema.checkAttribute(item, 'listItemId'));
26
- if (!items.length) {
27
- return;
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
- }
37
- const attributes = {
38
- listItemId,
39
- listIndent,
40
- listType
41
- };
42
- for (const item of items) {
43
- // Set list attributes only on same level items, those nested deeper are already handled by the recursive conversion.
44
- if (!item.hasAttribute('listItemId')) {
45
- writer.setAttributes(attributes, item);
46
- }
47
- }
48
- if (items.length > 1) {
49
- // Make sure that list item that contain only nested list will preserve paragraph for itself:
50
- // <ul>
51
- // <li>
52
- // <p></p> <-- this one must be kept
53
- // <ul>
54
- // <li></li>
55
- // </ul>
56
- // </li>
57
- // </ul>
58
- if (items[1].getAttribute('listItemId') != attributes.listItemId) {
59
- conversionApi.keepEmptyElement(items[0]);
60
- }
61
- }
62
- };
63
- }
64
- /**
65
- * Returns the upcast converter for the `<ul>` and `<ol>` view elements that cleans the input view of garbage.
66
- * This is mostly to clean whitespaces from between the `<li>` view elements inside the view list element. However,
67
- * incorrect data can also be cleared if the view was incorrect.
68
- *
69
- * @internal
70
- */
71
- export function listUpcastCleanList() {
72
- return (evt, data, conversionApi) => {
73
- if (!conversionApi.consumable.test(data.viewItem, { name: true })) {
74
- return;
75
- }
76
- const viewWriter = new UpcastWriter(data.viewItem.document);
77
- for (const child of Array.from(data.viewItem.getChildren())) {
78
- if (!isListItemView(child) && !isListView(child)) {
79
- viewWriter.remove(child);
80
- }
81
- }
82
- };
83
- }
84
- /**
85
- * Returns a model document change:data event listener that triggers conversion of related items if needed.
86
- *
87
- * @internal
88
- * @param model The editor model.
89
- * @param editing The editing controller.
90
- * @param attributeNames The list of all model list attributes (including registered strategies).
91
- * @param documentListEditing The document list editing plugin.
92
- */
93
- export function reconvertItemsOnDataChange(model, editing, attributeNames, documentListEditing) {
94
- return () => {
95
- const changes = model.document.differ.getChanges();
96
- const itemsToRefresh = [];
97
- const itemToListHead = new Map();
98
- const changedItems = new Set();
99
- for (const entry of changes) {
100
- if (entry.type == 'insert' && entry.name != '$text') {
101
- findAndAddListHeadToMap(entry.position, itemToListHead);
102
- // Insert of a non-list item.
103
- if (!entry.attributes.has('listItemId')) {
104
- findAndAddListHeadToMap(entry.position.getShiftedBy(entry.length), itemToListHead);
105
- }
106
- else {
107
- changedItems.add(entry.position.nodeAfter);
108
- }
109
- }
110
- // Removed list item.
111
- else if (entry.type == 'remove' && entry.attributes.has('listItemId')) {
112
- findAndAddListHeadToMap(entry.position, itemToListHead);
113
- }
114
- // Changed list attribute.
115
- else if (entry.type == 'attribute') {
116
- const item = entry.range.start.nodeAfter;
117
- if (attributeNames.includes(entry.attributeKey)) {
118
- findAndAddListHeadToMap(entry.range.start, itemToListHead);
119
- if (entry.attributeNewValue === null) {
120
- findAndAddListHeadToMap(entry.range.start.getShiftedBy(1), itemToListHead);
121
- // Check if paragraph should be converted from bogus to plain paragraph.
122
- if (doesItemBlockRequiresRefresh(item)) {
123
- itemsToRefresh.push(item);
124
- }
125
- }
126
- else {
127
- changedItems.add(item);
128
- }
129
- }
130
- else if (isListItemBlock(item)) {
131
- // Some other attribute was changed on the list item,
132
- // check if paragraph does not need to be converted to bogus or back.
133
- if (doesItemBlockRequiresRefresh(item)) {
134
- itemsToRefresh.push(item);
135
- }
136
- }
137
- }
138
- }
139
- for (const listHead of itemToListHead.values()) {
140
- itemsToRefresh.push(...collectListItemsToRefresh(listHead, changedItems));
141
- }
142
- for (const item of new Set(itemsToRefresh)) {
143
- editing.reconvertItem(item);
144
- }
145
- };
146
- function collectListItemsToRefresh(listHead, changedItems) {
147
- const itemsToRefresh = [];
148
- const visited = new Set();
149
- const stack = [];
150
- for (const { node, previous } of iterateSiblingListBlocks(listHead, 'forward')) {
151
- if (visited.has(node)) {
152
- continue;
153
- }
154
- const itemIndent = node.getAttribute('listIndent');
155
- // Current node is at the lower indent so trim the stack.
156
- if (previous && itemIndent < previous.getAttribute('listIndent')) {
157
- stack.length = itemIndent + 1;
158
- }
159
- // Update the stack for the current indent level.
160
- stack[itemIndent] = Object.fromEntries(Array.from(node.getAttributes())
161
- .filter(([key]) => attributeNames.includes(key)));
162
- // Find all blocks of the current node.
163
- const blocks = getListItemBlocks(node, { direction: 'forward' });
164
- for (const block of blocks) {
165
- visited.add(block);
166
- // Check if bogus vs plain paragraph needs refresh.
167
- if (doesItemBlockRequiresRefresh(block, blocks)) {
168
- itemsToRefresh.push(block);
169
- }
170
- // Check if wrapping with UL, OL, LIs needs refresh.
171
- else if (doesItemWrappingRequiresRefresh(block, stack, changedItems)) {
172
- itemsToRefresh.push(block);
173
- }
174
- }
175
- }
176
- return itemsToRefresh;
177
- }
178
- function doesItemBlockRequiresRefresh(item, blocks) {
179
- const viewElement = editing.mapper.toViewElement(item);
180
- if (!viewElement) {
181
- return false;
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
- }
193
- const useBogus = shouldUseBogusParagraph(item, attributeNames, blocks);
194
- if (useBogus && viewElement.is('element', 'p')) {
195
- return true;
196
- }
197
- else if (!useBogus && viewElement.is('element', 'span')) {
198
- return true;
199
- }
200
- return false;
201
- }
202
- function doesItemWrappingRequiresRefresh(item, stack, changedItems) {
203
- // Items directly affected by some "change" don't need a refresh, they will be converted by their own changes.
204
- if (changedItems.has(item)) {
205
- return false;
206
- }
207
- const viewElement = editing.mapper.toViewElement(item);
208
- let indent = stack.length - 1;
209
- // Traverse down the stack to the root to verify if all ULs, OLs, and LIs are as expected.
210
- for (let element = viewElement.parent; !element.is('editableElement'); element = element.parent) {
211
- const isListItemElement = isListItemView(element);
212
- const isListElement = isListView(element);
213
- if (!isListElement && !isListItemElement) {
214
- continue;
215
- }
216
- const eventName = `checkAttributes:${isListItemElement ? 'item' : 'list'}`;
217
- const needsRefresh = documentListEditing.fire(eventName, {
218
- viewElement: element,
219
- modelAttributes: stack[indent]
220
- });
221
- if (needsRefresh) {
222
- break;
223
- }
224
- if (isListElement) {
225
- indent--;
226
- // Don't need to iterate further if we already know that the item is wrapped appropriately.
227
- if (indent < 0) {
228
- return false;
229
- }
230
- }
231
- }
232
- return true;
233
- }
234
- }
235
- /**
236
- * Returns the list item downcast converter.
237
- *
238
- * @internal
239
- * @param attributeNames A list of attribute names that should be converted if they are set.
240
- * @param strategies The strategies.
241
- * @param model The model.
242
- */
243
- export function listItemDowncastConverter(attributeNames, strategies, model, { dataPipeline } = {}) {
244
- const consumer = createAttributesConsumer(attributeNames);
245
- return (evt, data, conversionApi) => {
246
- const { writer, mapper, consumable } = conversionApi;
247
- const listItem = data.item;
248
- if (!attributeNames.includes(data.attributeKey)) {
249
- return;
250
- }
251
- // Test if attributes on the converted items are not consumed.
252
- if (!consumer(listItem, consumable)) {
253
- return;
254
- }
255
- // Use positions mapping instead of mapper.toViewElement( listItem ) to find outermost view element.
256
- // This is for cases when mapping is using inner view element like in the code blocks (pre > code).
257
- const viewElement = findMappedViewElement(listItem, mapper, model);
258
- // Remove custom item marker.
259
- removeCustomMarkerElements(viewElement, writer, mapper);
260
- // Unwrap element from current list wrappers.
261
- unwrapListItemBlock(viewElement, writer);
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);
266
- };
267
- }
268
- /**
269
- * Returns the bogus paragraph view element creator. A bogus paragraph is used if a list item contains only a single block or nested list.
270
- *
271
- * @internal
272
- * @param attributeNames The list of all model list attributes (including registered strategies).
273
- */
274
- export function bogusParagraphCreator(attributeNames, { dataPipeline } = {}) {
275
- return (modelElement, { writer }) => {
276
- // Convert only if a bogus paragraph should be used.
277
- if (!shouldUseBogusParagraph(modelElement, attributeNames)) {
278
- return null;
279
- }
280
- if (!dataPipeline) {
281
- return writer.createContainerElement('span', { class: 'ck-list-bogus-paragraph' });
282
- }
283
- // Using `<p>` in case there are some markers on it and transparentRendering will render it anyway.
284
- const viewElement = writer.createContainerElement('p');
285
- writer.setCustomProperty('dataPipeline:transparentRendering', true, viewElement);
286
- return viewElement;
287
- };
288
- }
289
- /**
290
- * Helper for mapping mode to view elements. It's using positions mapping instead of mapper.toViewElement( element )
291
- * to find outermost view element. This is for cases when mapping is using inner view element like in the code blocks (pre > code).
292
- *
293
- * @internal
294
- * @param element The model element.
295
- * @param mapper The mapper instance.
296
- * @param model The model.
297
- */
298
- export function findMappedViewElement(element, mapper, model) {
299
- const modelRange = model.createRangeOn(element);
300
- const viewRange = mapper.toViewRange(modelRange).getTrimmed();
301
- return viewRange.end.nodeBefore;
302
- }
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
- */
369
- function unwrapListItemBlock(viewElement, viewWriter) {
370
- let attributeElement = viewElement.parent;
371
- while (attributeElement.is('attributeElement') && ['ul', 'ol', 'li'].includes(attributeElement.name)) {
372
- const parentElement = attributeElement.parent;
373
- viewWriter.unwrap(viewWriter.createRangeOn(viewElement), attributeElement);
374
- attributeElement = parentElement;
375
- }
376
- }
377
- /**
378
- * Wraps the given list item with appropriate attribute elements for ul, ol, and li.
379
- */
380
- function wrapListItemBlock(listItem, viewRange, strategies, writer) {
381
- if (!listItem.hasAttribute('listIndent')) {
382
- return;
383
- }
384
- const listItemIndent = listItem.getAttribute('listIndent');
385
- let currentListItem = listItem;
386
- for (let indent = listItemIndent; indent >= 0; indent--) {
387
- const listItemViewElement = createListItemElement(writer, indent, currentListItem.getAttribute('listItemId'));
388
- const listViewElement = createListElement(writer, indent, currentListItem.getAttribute('listType'));
389
- for (const strategy of strategies) {
390
- if ((strategy.scope == 'list' || strategy.scope == 'item') &&
391
- currentListItem.hasAttribute(strategy.attributeName)) {
392
- strategy.setAttributeOnDowncast(writer, currentListItem.getAttribute(strategy.attributeName), strategy.scope == 'list' ? listViewElement : listItemViewElement);
393
- }
394
- }
395
- viewRange = writer.wrap(viewRange, listItemViewElement);
396
- viewRange = writer.wrap(viewRange, listViewElement);
397
- if (indent == 0) {
398
- break;
399
- }
400
- currentListItem = ListWalker.first(currentListItem, { lowerIndent: true });
401
- // There is no list item with lower indent, this means this is a document fragment containing
402
- // only a part of nested list (like copy to clipboard) so we don't need to try to wrap it further.
403
- if (!currentListItem) {
404
- break;
405
- }
406
- }
407
- }
408
- // Returns the function that is responsible for consuming attributes that are set on the model node.
409
- function createAttributesConsumer(attributeNames) {
410
- return (node, consumable) => {
411
- const events = [];
412
- // Collect all set attributes that are triggering conversion.
413
- for (const attributeName of attributeNames) {
414
- if (node.hasAttribute(attributeName)) {
415
- events.push(`attribute:${attributeName}`);
416
- }
417
- }
418
- if (!events.every(event => consumable.test(node, event) !== false)) {
419
- return false;
420
- }
421
- events.forEach(event => consumable.consume(node, event));
422
- return true;
423
- };
424
- }
425
- // Whether the given item should be rendered as a bogus paragraph.
426
- function shouldUseBogusParagraph(item, attributeNames, blocks = getAllListItemBlocks(item)) {
427
- if (!isListItemBlock(item)) {
428
- return false;
429
- }
430
- for (const attributeKey of item.getAttributeKeys()) {
431
- // Ignore selection attributes stored on block elements.
432
- if (attributeKey.startsWith('selection:')) {
433
- continue;
434
- }
435
- // Don't use bogus paragraph if there are attributes from other features.
436
- if (!attributeNames.includes(attributeKey)) {
437
- return false;
438
- }
439
- }
440
- return blocks.length < 2;
441
- }
@@ -1,150 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { Command } from 'ckeditor5/src/core';
6
- import { splitListItemBefore, expandListBlocksToCompleteItems, getListItemBlocks, getListItems, removeListAttributes, outdentFollowingItems, ListItemUid, sortBlocks, getSelectedBlockObject, isListItemBlock, canBecomeSimpleListItem } from './utils/model';
7
- /**
8
- * The list command. It is used by the {@link module:list/documentlist~DocumentList document list feature}.
9
- */
10
- export default class DocumentListCommand extends Command {
11
- /**
12
- * Creates an instance of the command.
13
- *
14
- * @param editor The editor instance.
15
- * @param type List type that will be handled by this command.
16
- */
17
- constructor(editor, type) {
18
- super(editor);
19
- this.type = type;
20
- }
21
- /**
22
- * @inheritDoc
23
- */
24
- refresh() {
25
- this.value = this._getValue();
26
- this.isEnabled = this._checkEnabled();
27
- }
28
- /**
29
- * Executes the list command.
30
- *
31
- * @fires execute
32
- * @fires afterExecute
33
- * @param options Command options.
34
- * @param options.forceValue If set, it will force the command behavior. If `true`, the command will try to convert the
35
- * selected items and potentially the neighbor elements to the proper list items. If set to `false` it will convert selected elements
36
- * to paragraphs. If not set, the command will toggle selected elements to list items or paragraphs, depending on the selection.
37
- */
38
- execute(options = {}) {
39
- const model = this.editor.model;
40
- const document = model.document;
41
- const selectedBlockObject = getSelectedBlockObject(model);
42
- const blocks = Array.from(document.selection.getSelectedBlocks())
43
- .filter(block => model.schema.checkAttribute(block, 'listType') || canBecomeSimpleListItem(block, model.schema));
44
- // Whether we are turning off some items.
45
- const turnOff = options.forceValue !== undefined ? !options.forceValue : this.value;
46
- model.change(writer => {
47
- if (turnOff) {
48
- const lastBlock = blocks[blocks.length - 1];
49
- // Split the first block from the list item.
50
- const itemBlocks = getListItemBlocks(lastBlock, { direction: 'forward' });
51
- const changedBlocks = [];
52
- if (itemBlocks.length > 1) {
53
- changedBlocks.push(...splitListItemBefore(itemBlocks[1], writer));
54
- }
55
- // Strip list attributes.
56
- changedBlocks.push(...removeListAttributes(blocks, writer));
57
- // Outdent items following the selected list item.
58
- changedBlocks.push(...outdentFollowingItems(lastBlock, writer));
59
- this._fireAfterExecute(changedBlocks);
60
- }
61
- // Changing type of list items for a collapsed selection inside a list item.
62
- else if ((selectedBlockObject || document.selection.isCollapsed) && isListItemBlock(blocks[0])) {
63
- const changedBlocks = getListItems(selectedBlockObject || blocks[0]);
64
- for (const block of changedBlocks) {
65
- writer.setAttribute('listType', this.type, block);
66
- }
67
- this._fireAfterExecute(changedBlocks);
68
- }
69
- // Turning on the list items for a non-collapsed selection.
70
- else {
71
- const changedBlocks = [];
72
- for (const block of blocks) {
73
- // Promote the given block to the list item.
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
- }
79
- writer.setAttributes({
80
- listIndent: 0,
81
- listItemId: ListItemUid.next(),
82
- listType: this.type
83
- }, block);
84
- changedBlocks.push(block);
85
- }
86
- // Change the type of list item.
87
- else {
88
- for (const node of expandListBlocksToCompleteItems(block, { withNested: false })) {
89
- if (node.getAttribute('listType') != this.type) {
90
- writer.setAttribute('listType', this.type, node);
91
- changedBlocks.push(node);
92
- }
93
- }
94
- }
95
- }
96
- this._fireAfterExecute(changedBlocks);
97
- }
98
- });
99
- }
100
- /**
101
- * Fires the `afterExecute` event.
102
- *
103
- * @param changedBlocks The changed list elements.
104
- */
105
- _fireAfterExecute(changedBlocks) {
106
- this.fire('afterExecute', sortBlocks(new Set(changedBlocks)));
107
- }
108
- /**
109
- * Checks the command's {@link #value}.
110
- *
111
- * @returns The current value.
112
- */
113
- _getValue() {
114
- const selection = this.editor.model.document.selection;
115
- const blocks = Array.from(selection.getSelectedBlocks());
116
- if (!blocks.length) {
117
- return false;
118
- }
119
- for (const block of blocks) {
120
- if (block.getAttribute('listType') != this.type) {
121
- return false;
122
- }
123
- }
124
- return true;
125
- }
126
- /**
127
- * Checks whether the command can be enabled in the current context.
128
- *
129
- * @returns Whether the command should be enabled.
130
- */
131
- _checkEnabled() {
132
- const model = this.editor.model;
133
- const schema = model.schema;
134
- const selection = model.document.selection;
135
- const blocks = Array.from(selection.getSelectedBlocks());
136
- if (!blocks.length) {
137
- return false;
138
- }
139
- // If command value is true it means that we are in list item, so the command should be enabled.
140
- if (this.value) {
141
- return true;
142
- }
143
- for (const block of blocks) {
144
- if (schema.checkAttribute(block, 'listType') || canBecomeSimpleListItem(block, schema)) {
145
- return true;
146
- }
147
- }
148
- return false;
149
- }
150
- }