@ckeditor/ckeditor5-list 29.2.0 → 32.0.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 +3 -3
- package/build/translations/ar.js +1 -1
- package/build/translations/ast.js +1 -1
- package/build/translations/az.js +1 -1
- package/build/translations/bg.js +1 -1
- package/build/translations/cs.js +1 -1
- package/build/translations/da.js +1 -1
- package/build/translations/de-ch.js +1 -1
- package/build/translations/de.js +1 -1
- package/build/translations/el.js +1 -1
- package/build/translations/en-au.js +1 -1
- package/build/translations/en-gb.js +1 -1
- package/build/translations/eo.js +1 -1
- package/build/translations/es.js +1 -1
- package/build/translations/et.js +1 -1
- package/build/translations/eu.js +1 -1
- package/build/translations/fa.js +1 -1
- package/build/translations/fi.js +1 -1
- package/build/translations/fr.js +1 -1
- package/build/translations/gl.js +1 -1
- package/build/translations/he.js +1 -1
- package/build/translations/hi.js +1 -1
- package/build/translations/hr.js +1 -1
- package/build/translations/hu.js +1 -1
- package/build/translations/id.js +1 -1
- package/build/translations/it.js +1 -1
- package/build/translations/ja.js +1 -1
- package/build/translations/km.js +1 -1
- package/build/translations/kn.js +1 -1
- package/build/translations/ko.js +1 -1
- package/build/translations/ku.js +1 -1
- package/build/translations/lt.js +1 -1
- package/build/translations/lv.js +1 -1
- package/build/translations/nb.js +1 -1
- package/build/translations/ne.js +1 -1
- package/build/translations/nl.js +1 -1
- package/build/translations/no.js +1 -1
- package/build/translations/pl.js +1 -1
- package/build/translations/pt-br.js +1 -1
- package/build/translations/pt.js +1 -1
- package/build/translations/ro.js +1 -1
- package/build/translations/ru.js +1 -1
- package/build/translations/si.js +1 -1
- package/build/translations/sk.js +1 -1
- package/build/translations/sq.js +1 -1
- package/build/translations/sr-latn.js +1 -1
- package/build/translations/sr.js +1 -1
- package/build/translations/sv.js +1 -1
- package/build/translations/tk.js +1 -1
- package/build/translations/tr.js +1 -1
- package/build/translations/ug.js +1 -1
- package/build/translations/uk.js +1 -1
- package/build/translations/uz.js +1 -0
- package/build/translations/vi.js +1 -1
- package/build/translations/zh-cn.js +1 -1
- package/build/translations/zh.js +1 -1
- package/ckeditor5-metadata.json +8 -4
- package/lang/contexts.json +5 -1
- package/lang/translations/ar.po +17 -1
- package/lang/translations/ast.po +17 -1
- package/lang/translations/az.po +17 -1
- package/lang/translations/bg.po +17 -1
- package/lang/translations/cs.po +17 -1
- package/lang/translations/da.po +17 -1
- package/lang/translations/de-ch.po +17 -1
- package/lang/translations/de.po +17 -1
- package/lang/translations/el.po +17 -1
- package/lang/translations/en-au.po +17 -1
- package/lang/translations/en-gb.po +17 -1
- package/lang/translations/en.po +17 -1
- package/lang/translations/eo.po +17 -1
- package/lang/translations/es.po +20 -4
- package/lang/translations/et.po +17 -1
- package/lang/translations/eu.po +17 -1
- package/lang/translations/fa.po +17 -1
- package/lang/translations/fi.po +17 -1
- package/lang/translations/fr.po +17 -1
- package/lang/translations/gl.po +17 -1
- package/lang/translations/he.po +17 -1
- package/lang/translations/hi.po +17 -1
- package/lang/translations/hr.po +17 -1
- package/lang/translations/hu.po +17 -1
- package/lang/translations/id.po +17 -1
- package/lang/translations/it.po +17 -1
- package/lang/translations/ja.po +17 -1
- package/lang/translations/km.po +17 -1
- package/lang/translations/kn.po +17 -1
- package/lang/translations/ko.po +17 -1
- package/lang/translations/ku.po +17 -1
- package/lang/translations/lt.po +17 -1
- package/lang/translations/lv.po +17 -1
- package/lang/translations/nb.po +17 -1
- package/lang/translations/ne.po +17 -1
- package/lang/translations/nl.po +21 -5
- package/lang/translations/no.po +17 -1
- package/lang/translations/pl.po +17 -1
- package/lang/translations/pt-br.po +17 -1
- package/lang/translations/pt.po +17 -1
- package/lang/translations/ro.po +17 -1
- package/lang/translations/ru.po +17 -1
- package/lang/translations/si.po +17 -1
- package/lang/translations/sk.po +17 -1
- package/lang/translations/sq.po +17 -1
- package/lang/translations/sr-latn.po +17 -1
- package/lang/translations/sr.po +17 -1
- package/lang/translations/sv.po +17 -1
- package/lang/translations/tk.po +17 -1
- package/lang/translations/tr.po +17 -1
- package/lang/translations/ug.po +17 -1
- package/lang/translations/uk.po +17 -1
- package/lang/translations/uz.po +125 -0
- package/lang/translations/vi.po +17 -1
- package/lang/translations/zh-cn.po +17 -1
- package/lang/translations/zh.po +36 -20
- package/package.json +27 -27
- package/src/checktodolistcommand.js +1 -1
- package/src/converters.js +14 -15
- package/src/indentcommand.js +1 -1
- package/src/index.js +4 -4
- package/src/list.js +24 -1
- package/src/listcommand.js +2 -2
- package/src/listediting.js +1 -1
- package/src/listproperties.js +96 -0
- package/src/{liststyleediting.js → listpropertiesediting.js} +299 -122
- package/src/listpropertiesui.js +314 -0
- package/src/listreversedcommand.js +64 -0
- package/src/liststartcommand.js +63 -0
- package/src/liststyle.js +12 -6
- package/src/liststylecommand.js +5 -22
- package/src/listui.js +1 -1
- package/src/todolist.js +1 -1
- package/src/todolistconverters.js +1 -1
- package/src/todolistediting.js +1 -1
- package/src/todolistui.js +1 -1
- package/src/ui/collapsibleview.js +152 -0
- package/src/ui/listpropertiesview.js +405 -0
- package/src/utils.js +49 -4
- package/theme/collapsible.css +10 -0
- package/theme/listproperties.css +10 -0
- package/theme/liststyles.css +2 -6
- package/theme/todolist.css +1 -1
- package/CHANGELOG.md +0 -272
- package/src/liststyleui.js +0 -225
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @module list/
|
|
7
|
+
* @module list/listpropertiesediting
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Plugin } from 'ckeditor5/src/core';
|
|
11
11
|
import ListEditing from './listediting';
|
|
12
12
|
import ListStyleCommand from './liststylecommand';
|
|
13
|
+
import ListReversedCommand from './listreversedcommand';
|
|
14
|
+
import ListStartCommand from './liststartcommand';
|
|
13
15
|
import { getSiblingListItem, getSiblingNodes } from './utils';
|
|
14
16
|
|
|
15
17
|
const DEFAULT_LIST_TYPE = 'default';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
* The list
|
|
20
|
+
* The engine of the list properties feature.
|
|
19
21
|
*
|
|
20
22
|
* It sets the value for the `listItem` attribute of the {@link module:list/list~List `<listItem>`} element that
|
|
21
23
|
* allows modifying the list style type.
|
|
22
24
|
*
|
|
23
|
-
* It registers the `'listStyle'`
|
|
25
|
+
* It registers the `'listStyle'`, `'listReversed'` and `'listStart'` commands if they are enabled in the configuration.
|
|
26
|
+
* Read more in {@link module:list/listproperties~ListPropertiesConfig}.
|
|
24
27
|
*
|
|
25
28
|
* @extends module:core/plugin~Plugin
|
|
26
29
|
*/
|
|
27
|
-
export default class
|
|
30
|
+
export default class ListPropertiesEditing extends Plugin {
|
|
28
31
|
/**
|
|
29
32
|
* @inheritDoc
|
|
30
33
|
*/
|
|
@@ -36,7 +39,22 @@ export default class ListStyleEditing extends Plugin {
|
|
|
36
39
|
* @inheritDoc
|
|
37
40
|
*/
|
|
38
41
|
static get pluginName() {
|
|
39
|
-
return '
|
|
42
|
+
return 'ListPropertiesEditing';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @inheritDoc
|
|
47
|
+
*/
|
|
48
|
+
constructor( editor ) {
|
|
49
|
+
super( editor );
|
|
50
|
+
|
|
51
|
+
editor.config.define( 'list', {
|
|
52
|
+
properties: {
|
|
53
|
+
styles: true,
|
|
54
|
+
startIndex: false,
|
|
55
|
+
reversed: false
|
|
56
|
+
}
|
|
57
|
+
} );
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
/**
|
|
@@ -46,29 +64,34 @@ export default class ListStyleEditing extends Plugin {
|
|
|
46
64
|
const editor = this.editor;
|
|
47
65
|
const model = editor.model;
|
|
48
66
|
|
|
67
|
+
const enabledProperties = editor.config.get( 'list.properties' );
|
|
68
|
+
const strategies = createAttributeStrategies( enabledProperties );
|
|
69
|
+
|
|
49
70
|
// Extend schema.
|
|
50
71
|
model.schema.extend( 'listItem', {
|
|
51
|
-
allowAttributes:
|
|
72
|
+
allowAttributes: strategies.map( s => s.attributeName )
|
|
52
73
|
} );
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
for ( const strategy of strategies ) {
|
|
76
|
+
strategy.addCommand( editor );
|
|
77
|
+
}
|
|
55
78
|
|
|
56
79
|
// Fix list attributes when modifying their nesting levels (the `listIndent` attribute).
|
|
57
|
-
this.listenTo( editor.commands.get( 'indentList' ), '_executeCleanup', fixListAfterIndentListCommand( editor ) );
|
|
58
|
-
this.listenTo( editor.commands.get( 'outdentList' ), '_executeCleanup', fixListAfterOutdentListCommand( editor ) );
|
|
80
|
+
this.listenTo( editor.commands.get( 'indentList' ), '_executeCleanup', fixListAfterIndentListCommand( editor, strategies ) );
|
|
81
|
+
this.listenTo( editor.commands.get( 'outdentList' ), '_executeCleanup', fixListAfterOutdentListCommand( editor, strategies ) );
|
|
59
82
|
|
|
60
83
|
this.listenTo( editor.commands.get( 'bulletedList' ), '_executeCleanup', restoreDefaultListStyle( editor ) );
|
|
61
84
|
this.listenTo( editor.commands.get( 'numberedList' ), '_executeCleanup', restoreDefaultListStyle( editor ) );
|
|
62
85
|
|
|
63
|
-
// Register a post-fixer that ensures that the
|
|
64
|
-
model.document.registerPostFixer(
|
|
86
|
+
// Register a post-fixer that ensures that the attributes is specified in each `listItem` element.
|
|
87
|
+
model.document.registerPostFixer( fixListAttributesOnListItemElements( editor, strategies ) );
|
|
65
88
|
|
|
66
89
|
// Set up conversion.
|
|
67
|
-
editor.conversion.for( 'upcast' ).add(
|
|
68
|
-
editor.conversion.for( 'downcast' ).add(
|
|
90
|
+
editor.conversion.for( 'upcast' ).add( upcastListItemAttributes( strategies ) );
|
|
91
|
+
editor.conversion.for( 'downcast' ).add( downcastListItemAttributes( strategies ) );
|
|
69
92
|
|
|
70
93
|
// Handle merging two separated lists into the single one.
|
|
71
|
-
this.
|
|
94
|
+
this._mergeListAttributesWhileMergingLists( strategies );
|
|
72
95
|
}
|
|
73
96
|
|
|
74
97
|
/**
|
|
@@ -77,18 +100,19 @@ export default class ListStyleEditing extends Plugin {
|
|
|
77
100
|
afterInit() {
|
|
78
101
|
const editor = this.editor;
|
|
79
102
|
|
|
80
|
-
// Enable post-fixer that removes the
|
|
81
|
-
// We need to registry the hook here since the `TodoList` plugin can be added after the `
|
|
103
|
+
// Enable post-fixer that removes the attributes from to-do list items only if the "TodoList" plugin is on.
|
|
104
|
+
// We need to registry the hook here since the `TodoList` plugin can be added after the `ListPropertiesEditing`.
|
|
82
105
|
if ( editor.commands.get( 'todoList' ) ) {
|
|
83
|
-
editor.model.document.registerPostFixer(
|
|
106
|
+
editor.model.document.registerPostFixer( removeListItemAttributesFromTodoList( editor ) );
|
|
84
107
|
}
|
|
85
108
|
}
|
|
86
109
|
|
|
87
110
|
/**
|
|
88
|
-
* Starts listening to {@link module:engine/model/model~Model#deleteContent} checks whether two lists will be merged into a single
|
|
89
|
-
* after deleting the content.
|
|
111
|
+
* Starts listening to {@link module:engine/model/model~Model#deleteContent} and checks whether two lists will be merged into a single
|
|
112
|
+
* one after deleting the content.
|
|
90
113
|
*
|
|
91
|
-
* The purpose of this action is to adjust the `listStyle`
|
|
114
|
+
* The purpose of this action is to adjust the `listStyle`, `listReversed` and `listStart` values
|
|
115
|
+
* for the list that was merged.
|
|
92
116
|
*
|
|
93
117
|
* Consider the following model's content:
|
|
94
118
|
*
|
|
@@ -109,13 +133,14 @@ export default class ListStyleEditing extends Plugin {
|
|
|
109
133
|
* See https://github.com/ckeditor/ckeditor5/issues/7879.
|
|
110
134
|
*
|
|
111
135
|
* @private
|
|
136
|
+
* @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies Strategies for the enabled attributes.
|
|
112
137
|
*/
|
|
113
|
-
|
|
138
|
+
_mergeListAttributesWhileMergingLists( attributeStrategies ) {
|
|
114
139
|
const editor = this.editor;
|
|
115
140
|
const model = editor.model;
|
|
116
141
|
|
|
117
142
|
// First the outer-most`listItem` in the first list reference.
|
|
118
|
-
// If found, the lists should be merged and this `listItem` provides the
|
|
143
|
+
// If found, the lists should be merged and this `listItem` provides the attributes
|
|
119
144
|
// and it is also a starting point when searching for items in the second list.
|
|
120
145
|
let firstMostOuterItem;
|
|
121
146
|
|
|
@@ -189,13 +214,27 @@ export default class ListStyleEditing extends Plugin {
|
|
|
189
214
|
direction: 'forward'
|
|
190
215
|
} );
|
|
191
216
|
|
|
217
|
+
// If the selection ends in a non-list element, there are no <listItem>s that would require adjustments.
|
|
218
|
+
// See: #8642.
|
|
219
|
+
if ( !secondListMostOuterItem ) {
|
|
220
|
+
firstMostOuterItem = null;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
192
224
|
const items = [
|
|
193
225
|
secondListMostOuterItem,
|
|
194
226
|
...getSiblingNodes( writer.createPositionAt( secondListMostOuterItem, 0 ), 'forward' )
|
|
195
227
|
];
|
|
196
228
|
|
|
197
229
|
for ( const listItem of items ) {
|
|
198
|
-
|
|
230
|
+
for ( const strategy of attributeStrategies ) {
|
|
231
|
+
if ( strategy.appliesToListItem( listItem ) ) {
|
|
232
|
+
const attributeName = strategy.attributeName;
|
|
233
|
+
const value = firstMostOuterItem.getAttribute( attributeName );
|
|
234
|
+
|
|
235
|
+
writer.setAttribute( attributeName, value, listItem );
|
|
236
|
+
}
|
|
237
|
+
}
|
|
199
238
|
}
|
|
200
239
|
} );
|
|
201
240
|
|
|
@@ -204,11 +243,120 @@ export default class ListStyleEditing extends Plugin {
|
|
|
204
243
|
}
|
|
205
244
|
}
|
|
206
245
|
|
|
207
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Strategy for dealing with `listItem` attributes supported by this plugin.
|
|
248
|
+
*
|
|
249
|
+
* @typedef {Object} AttributeStrategy
|
|
250
|
+
* @private
|
|
251
|
+
* @property {String} #attributeName
|
|
252
|
+
* @property {*} #defaultValue
|
|
253
|
+
* @property {Function} #addCommand
|
|
254
|
+
* @property {Function} #appliesToListItem
|
|
255
|
+
* @property {Function} #setAttributeOnDowncast
|
|
256
|
+
* @property {Function} #getAttributeOnUpcast
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
// Creates an array of strategies for dealing with enabled listItem attributes.
|
|
260
|
+
//
|
|
261
|
+
// @param {Object} enabledProperties
|
|
262
|
+
// @param {Boolean} enabledProperties.styles
|
|
263
|
+
// @param {Boolean} enabledProperties.reversed
|
|
264
|
+
// @param {Boolean} enabledProperties.startIndex
|
|
265
|
+
// @returns {Array.<module:list/listpropertiesediting~AttributeStrategy>}
|
|
266
|
+
function createAttributeStrategies( enabledProperties ) {
|
|
267
|
+
const strategies = [];
|
|
268
|
+
|
|
269
|
+
if ( enabledProperties.styles ) {
|
|
270
|
+
strategies.push( {
|
|
271
|
+
attributeName: 'listStyle',
|
|
272
|
+
defaultValue: DEFAULT_LIST_TYPE,
|
|
273
|
+
|
|
274
|
+
addCommand( editor ) {
|
|
275
|
+
editor.commands.add( 'listStyle', new ListStyleCommand( editor, DEFAULT_LIST_TYPE ) );
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
appliesToListItem() {
|
|
279
|
+
return true;
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
setAttributeOnDowncast( writer, listStyle, element ) {
|
|
283
|
+
if ( listStyle && listStyle !== DEFAULT_LIST_TYPE ) {
|
|
284
|
+
writer.setStyle( 'list-style-type', listStyle, element );
|
|
285
|
+
} else {
|
|
286
|
+
writer.removeStyle( 'list-style-type', element );
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
getAttributeOnUpcast( listParent ) {
|
|
291
|
+
return listParent.getStyle( 'list-style-type' ) || DEFAULT_LIST_TYPE;
|
|
292
|
+
}
|
|
293
|
+
} );
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if ( enabledProperties.reversed ) {
|
|
297
|
+
strategies.push( {
|
|
298
|
+
attributeName: 'listReversed',
|
|
299
|
+
defaultValue: false,
|
|
300
|
+
|
|
301
|
+
addCommand( editor ) {
|
|
302
|
+
editor.commands.add( 'listReversed', new ListReversedCommand( editor ) );
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
appliesToListItem( item ) {
|
|
306
|
+
return item.getAttribute( 'listType' ) == 'numbered';
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
setAttributeOnDowncast( writer, listReversed, element ) {
|
|
310
|
+
if ( listReversed ) {
|
|
311
|
+
writer.setAttribute( 'reversed', 'reversed', element );
|
|
312
|
+
} else {
|
|
313
|
+
writer.removeAttribute( 'reversed', element );
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
getAttributeOnUpcast( listParent ) {
|
|
318
|
+
return listParent.hasAttribute( 'reversed' );
|
|
319
|
+
}
|
|
320
|
+
} );
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if ( enabledProperties.startIndex ) {
|
|
324
|
+
strategies.push( {
|
|
325
|
+
attributeName: 'listStart',
|
|
326
|
+
defaultValue: 1,
|
|
327
|
+
|
|
328
|
+
addCommand( editor ) {
|
|
329
|
+
editor.commands.add( 'listStart', new ListStartCommand( editor ) );
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
appliesToListItem( item ) {
|
|
333
|
+
return item.getAttribute( 'listType' ) == 'numbered';
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
setAttributeOnDowncast( writer, listStart, element ) {
|
|
337
|
+
if ( listStart != 1 ) {
|
|
338
|
+
writer.setAttribute( 'start', listStart, element );
|
|
339
|
+
} else {
|
|
340
|
+
writer.removeAttribute( 'start', element );
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
getAttributeOnUpcast( listParent ) {
|
|
345
|
+
return listParent.getAttribute( 'start' ) || 1;
|
|
346
|
+
}
|
|
347
|
+
} );
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return strategies;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Returns a converter consumes the `style`, `reversed` and `start` attribute.
|
|
354
|
+
// In `style` it searches for the `list-style-type` definition.
|
|
208
355
|
// If not found, the `"default"` value will be used.
|
|
209
356
|
//
|
|
357
|
+
// @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies
|
|
210
358
|
// @returns {Function}
|
|
211
|
-
function
|
|
359
|
+
function upcastListItemAttributes( attributeStrategies ) {
|
|
212
360
|
return dispatcher => {
|
|
213
361
|
dispatcher.on( 'element:li', ( evt, data, conversionApi ) => {
|
|
214
362
|
const listParent = data.viewItem.parent;
|
|
@@ -219,39 +367,45 @@ function upcastListItemStyle() {
|
|
|
219
367
|
return;
|
|
220
368
|
}
|
|
221
369
|
|
|
222
|
-
const listStyle = listParent.getStyle( 'list-style-type' ) || DEFAULT_LIST_TYPE;
|
|
223
370
|
const listItem = data.modelRange.start.nodeAfter || data.modelRange.end.nodeBefore;
|
|
224
371
|
|
|
225
|
-
|
|
372
|
+
for ( const strategy of attributeStrategies ) {
|
|
373
|
+
if ( strategy.appliesToListItem( listItem ) ) {
|
|
374
|
+
const listStyle = strategy.getAttributeOnUpcast( listParent );
|
|
375
|
+
conversionApi.writer.setAttribute( strategy.attributeName, listStyle, listItem );
|
|
376
|
+
}
|
|
377
|
+
}
|
|
226
378
|
}, { priority: 'low' } );
|
|
227
379
|
};
|
|
228
380
|
}
|
|
229
381
|
|
|
230
|
-
// Returns a converter that adds
|
|
231
|
-
// The `"default"`
|
|
382
|
+
// Returns a converter that adds `reversed`, `start` attributes and adds `list-style-type` definition as a value for the `style` attribute.
|
|
383
|
+
// The `"default"` values are removed and not present in the view/data.
|
|
232
384
|
//
|
|
385
|
+
// @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies
|
|
233
386
|
// @returns {Function}
|
|
234
|
-
function
|
|
387
|
+
function downcastListItemAttributes( attributeStrategies ) {
|
|
235
388
|
return dispatcher => {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
389
|
+
for ( const strategy of attributeStrategies ) {
|
|
390
|
+
dispatcher.on( `attribute:${ strategy.attributeName }:listItem`, ( evt, data, conversionApi ) => {
|
|
391
|
+
const viewWriter = conversionApi.writer;
|
|
392
|
+
const currentElement = data.item;
|
|
239
393
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const viewItem = conversionApi.mapper.toViewElement( currentElement );
|
|
394
|
+
const previousElement = getSiblingListItem( currentElement.previousSibling, {
|
|
395
|
+
sameIndent: true,
|
|
396
|
+
listIndent: currentElement.getAttribute( 'listIndent' ),
|
|
397
|
+
direction: 'backward'
|
|
398
|
+
} );
|
|
247
399
|
|
|
248
|
-
|
|
249
|
-
if ( !areRepresentingSameList( currentElement, previousElement ) ) {
|
|
250
|
-
viewWriter.breakContainer( viewWriter.createPositionBefore( viewItem ) );
|
|
251
|
-
}
|
|
400
|
+
const viewItem = conversionApi.mapper.toViewElement( currentElement );
|
|
252
401
|
|
|
253
|
-
|
|
254
|
-
|
|
402
|
+
// A case when elements represent different lists. We need to separate their container.
|
|
403
|
+
if ( !areRepresentingSameList( currentElement, previousElement ) ) {
|
|
404
|
+
viewWriter.breakContainer( viewWriter.createPositionBefore( viewItem ) );
|
|
405
|
+
}
|
|
406
|
+
strategy.setAttributeOnDowncast( viewWriter, data.attributeNewValue, viewItem.parent );
|
|
407
|
+
}, { priority: 'low' } );
|
|
408
|
+
}
|
|
255
409
|
};
|
|
256
410
|
|
|
257
411
|
// Checks whether specified list items belong to the same list.
|
|
@@ -263,24 +417,13 @@ function downcastListStyleAttribute() {
|
|
|
263
417
|
return listItem2 &&
|
|
264
418
|
listItem1.getAttribute( 'listType' ) === listItem2.getAttribute( 'listType' ) &&
|
|
265
419
|
listItem1.getAttribute( 'listIndent' ) === listItem2.getAttribute( 'listIndent' ) &&
|
|
266
|
-
listItem1.getAttribute( 'listStyle' ) === listItem2.getAttribute( 'listStyle' )
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
// Updates or removes the `list-style-type` from the `element`.
|
|
270
|
-
//
|
|
271
|
-
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
|
|
272
|
-
// @param {String} listStyle
|
|
273
|
-
// @param {module:engine/view/element~Element} element
|
|
274
|
-
function setListStyle( writer, listStyle, element ) {
|
|
275
|
-
if ( listStyle && listStyle !== DEFAULT_LIST_TYPE ) {
|
|
276
|
-
writer.setStyle( 'list-style-type', listStyle, element );
|
|
277
|
-
} else {
|
|
278
|
-
writer.removeStyle( 'list-style-type', element );
|
|
279
|
-
}
|
|
420
|
+
listItem1.getAttribute( 'listStyle' ) === listItem2.getAttribute( 'listStyle' ) &&
|
|
421
|
+
listItem1.getAttribute( 'listReversed' ) === listItem2.getAttribute( 'listReversed' ) &&
|
|
422
|
+
listItem1.getAttribute( 'listStart' ) === listItem2.getAttribute( 'listStart' );
|
|
280
423
|
}
|
|
281
424
|
}
|
|
282
425
|
|
|
283
|
-
// When indenting list, nested list should clear its value for the
|
|
426
|
+
// When indenting list, nested list should clear its value for the attributes or inherit from nested lists.
|
|
284
427
|
//
|
|
285
428
|
// ■ List item 1.
|
|
286
429
|
// ■ List item 2.[]
|
|
@@ -292,11 +435,10 @@ function downcastListStyleAttribute() {
|
|
|
292
435
|
// ■ List item 3.
|
|
293
436
|
//
|
|
294
437
|
// @param {module:core/editor/editor~Editor} editor
|
|
438
|
+
// @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies
|
|
295
439
|
// @returns {Function}
|
|
296
|
-
function fixListAfterIndentListCommand( editor ) {
|
|
440
|
+
function fixListAfterIndentListCommand( editor, attributeStrategies ) {
|
|
297
441
|
return ( evt, changedItems ) => {
|
|
298
|
-
let valueToSet;
|
|
299
|
-
|
|
300
442
|
const root = changedItems[ 0 ];
|
|
301
443
|
const rootIndent = root.getAttribute( 'listIndent' );
|
|
302
444
|
|
|
@@ -310,26 +452,31 @@ function fixListAfterIndentListCommand( editor ) {
|
|
|
310
452
|
// ■ List item 4.
|
|
311
453
|
//
|
|
312
454
|
// List items: `2` and `3` should be adjusted.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const previousSibling = getSiblingListItem( root.previousSibling, {
|
|
455
|
+
let previousSibling = null;
|
|
456
|
+
|
|
457
|
+
if ( root.previousSibling.getAttribute( 'listIndent' ) + 1 !== rootIndent ) {
|
|
458
|
+
previousSibling = getSiblingListItem( root.previousSibling, {
|
|
318
459
|
sameIndent: true, direction: 'backward', listIndent: rootIndent
|
|
319
460
|
} );
|
|
320
|
-
|
|
321
|
-
valueToSet = previousSibling.getAttribute( 'listStyle' );
|
|
322
461
|
}
|
|
323
462
|
|
|
324
463
|
editor.model.change( writer => {
|
|
325
464
|
for ( const item of itemsToUpdate ) {
|
|
326
|
-
|
|
465
|
+
for ( const strategy of attributeStrategies ) {
|
|
466
|
+
if ( strategy.appliesToListItem( item ) ) {
|
|
467
|
+
const valueToSet = previousSibling == null ?
|
|
468
|
+
strategy.defaultValue :
|
|
469
|
+
previousSibling.getAttribute( strategy.attributeName );
|
|
470
|
+
|
|
471
|
+
writer.setAttribute( strategy.attributeName, valueToSet, item );
|
|
472
|
+
}
|
|
473
|
+
}
|
|
327
474
|
}
|
|
328
475
|
} );
|
|
329
476
|
};
|
|
330
477
|
}
|
|
331
478
|
|
|
332
|
-
// When outdenting a list, a nested list should copy
|
|
479
|
+
// When outdenting a list, a nested list should copy attribute values
|
|
333
480
|
// from the previous sibling list item including the same value for the `listIndent` value.
|
|
334
481
|
//
|
|
335
482
|
// ■ List item 1.
|
|
@@ -343,8 +490,9 @@ function fixListAfterIndentListCommand( editor ) {
|
|
|
343
490
|
// ■ List item 3.
|
|
344
491
|
//
|
|
345
492
|
// @param {module:core/editor/editor~Editor} editor
|
|
493
|
+
// @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies
|
|
346
494
|
// @returns {Function}
|
|
347
|
-
function fixListAfterOutdentListCommand( editor ) {
|
|
495
|
+
function fixListAfterOutdentListCommand( editor, attributeStrategies ) {
|
|
348
496
|
return ( evt, changedItems ) => {
|
|
349
497
|
changedItems = changedItems.reverse().filter( item => item.is( 'element', 'listItem' ) );
|
|
350
498
|
|
|
@@ -403,15 +551,23 @@ function fixListAfterOutdentListCommand( editor ) {
|
|
|
403
551
|
const itemsToUpdate = changedItems.filter( item => item.getAttribute( 'listIndent' ) === indent );
|
|
404
552
|
|
|
405
553
|
for ( const item of itemsToUpdate ) {
|
|
406
|
-
|
|
554
|
+
for ( const strategy of attributeStrategies ) {
|
|
555
|
+
if ( strategy.appliesToListItem( item ) ) {
|
|
556
|
+
const attributeName = strategy.attributeName;
|
|
557
|
+
const valueToSet = listItem.getAttribute( attributeName );
|
|
558
|
+
|
|
559
|
+
writer.setAttribute( attributeName, valueToSet, item );
|
|
560
|
+
}
|
|
561
|
+
}
|
|
407
562
|
}
|
|
408
563
|
} );
|
|
409
564
|
};
|
|
410
565
|
}
|
|
411
566
|
|
|
412
|
-
// Each `listItem` element must have specified the `listStyle`
|
|
413
|
-
//
|
|
414
|
-
//
|
|
567
|
+
// Each `listItem` element must have specified the `listStyle`, `listReversed` and `listStart` attributes
|
|
568
|
+
// if they are enabled and supported by its `listType`.
|
|
569
|
+
// This post-fixer checks whether inserted elements `listItem` elements should inherit the attribute values from
|
|
570
|
+
// their sibling nodes or should use the default values.
|
|
415
571
|
//
|
|
416
572
|
// Paragraph[]
|
|
417
573
|
// ■ List item 1. // [listStyle="square", listType="bulleted"]
|
|
@@ -442,8 +598,9 @@ function fixListAfterOutdentListCommand( editor ) {
|
|
|
442
598
|
// ■ List item 3. // ...
|
|
443
599
|
//
|
|
444
600
|
// @param {module:core/editor/editor~Editor} editor
|
|
601
|
+
// @param {Array.<module:list/listpropertiesediting~AttributeStrategy>} attributeStrategies
|
|
445
602
|
// @returns {Function}
|
|
446
|
-
function
|
|
603
|
+
function fixListAttributesOnListItemElements( editor, attributeStrategies ) {
|
|
447
604
|
return writer => {
|
|
448
605
|
let wasFixed = false;
|
|
449
606
|
|
|
@@ -490,39 +647,50 @@ function fixListStyleAttributeOnListItemElements( editor ) {
|
|
|
490
647
|
}
|
|
491
648
|
}
|
|
492
649
|
|
|
493
|
-
for ( const
|
|
494
|
-
|
|
495
|
-
if ( shouldInheritListType( existingListItem, item ) ) {
|
|
496
|
-
writer.setAttribute( 'listStyle', existingListItem.getAttribute( 'listStyle' ), item );
|
|
497
|
-
} else {
|
|
498
|
-
writer.setAttribute( 'listStyle', DEFAULT_LIST_TYPE, item );
|
|
499
|
-
}
|
|
500
|
-
wasFixed = true;
|
|
501
|
-
} else {
|
|
502
|
-
// Adjust the `listStyle` attribute for inserted (pasted) items. See #8160.
|
|
503
|
-
//
|
|
504
|
-
// ■ List item 1. // [listStyle="square", listType="bulleted"]
|
|
505
|
-
// ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
|
|
506
|
-
// ○ [] (selection is here)
|
|
507
|
-
//
|
|
508
|
-
// Then, pasting a list with different attributes (listStyle, listType):
|
|
509
|
-
//
|
|
510
|
-
// 1. First. // [listStyle="decimal", listType="numbered"]
|
|
511
|
-
// 2. Second // [listStyle="decimal", listType="numbered"]
|
|
512
|
-
//
|
|
513
|
-
// The `listType` attribute will be corrected by the `ListEditing` converters.
|
|
514
|
-
// We need to adjust the `listStyle` attribute. Expected structure:
|
|
515
|
-
//
|
|
516
|
-
// ■ List item 1. // [listStyle="square", listType="bulleted"]
|
|
517
|
-
// ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
|
|
518
|
-
// ○ First. // [listStyle="circle", listType="bulleted"]
|
|
519
|
-
// ○ Second // [listStyle="circle", listType="bulleted"]
|
|
520
|
-
const previousSibling = item.previousSibling;
|
|
650
|
+
for ( const strategy of attributeStrategies ) {
|
|
651
|
+
const attributeName = strategy.attributeName;
|
|
521
652
|
|
|
522
|
-
|
|
523
|
-
|
|
653
|
+
for ( const item of insertedListItems ) {
|
|
654
|
+
if ( !strategy.appliesToListItem( item ) ) {
|
|
655
|
+
writer.removeAttribute( attributeName, item );
|
|
524
656
|
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if ( !item.hasAttribute( attributeName ) ) {
|
|
661
|
+
if ( shouldInheritListType( existingListItem, item, strategy ) ) {
|
|
662
|
+
writer.setAttribute( attributeName, existingListItem.getAttribute( attributeName ), item );
|
|
663
|
+
} else {
|
|
664
|
+
writer.setAttribute( attributeName, strategy.defaultValue, item );
|
|
665
|
+
}
|
|
525
666
|
wasFixed = true;
|
|
667
|
+
} else {
|
|
668
|
+
// Adjust the `listStyle`, `listReversed` and `listStart`
|
|
669
|
+
// attributes for inserted (pasted) items. See #8160.
|
|
670
|
+
//
|
|
671
|
+
// ■ List item 1. // [listStyle="square", listType="bulleted"]
|
|
672
|
+
// ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
|
|
673
|
+
// ○ [] (selection is here)
|
|
674
|
+
//
|
|
675
|
+
// Then, pasting a list with different attributes (listStyle, listType):
|
|
676
|
+
//
|
|
677
|
+
// 1. First. // [listStyle="decimal", listType="numbered"]
|
|
678
|
+
// 2. Second // [listStyle="decimal", listType="numbered"]
|
|
679
|
+
//
|
|
680
|
+
// The `listType` attribute will be corrected by the `ListEditing` converters.
|
|
681
|
+
// We need to adjust the `listStyle` attribute. Expected structure:
|
|
682
|
+
//
|
|
683
|
+
// ■ List item 1. // [listStyle="square", listType="bulleted"]
|
|
684
|
+
// ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
|
|
685
|
+
// ○ First. // [listStyle="circle", listType="bulleted"]
|
|
686
|
+
// ○ Second // [listStyle="circle", listType="bulleted"]
|
|
687
|
+
const previousSibling = item.previousSibling;
|
|
688
|
+
|
|
689
|
+
if ( shouldInheritListTypeFromPreviousItem( previousSibling, item, strategy.attributeName ) ) {
|
|
690
|
+
writer.setAttribute( attributeName, previousSibling.getAttribute( attributeName ), item );
|
|
691
|
+
|
|
692
|
+
wasFixed = true;
|
|
693
|
+
}
|
|
526
694
|
}
|
|
527
695
|
}
|
|
528
696
|
}
|
|
@@ -531,26 +699,28 @@ function fixListStyleAttributeOnListItemElements( editor ) {
|
|
|
531
699
|
};
|
|
532
700
|
}
|
|
533
701
|
|
|
534
|
-
// Checks whether the `listStyle`
|
|
702
|
+
// Checks whether the `listStyle`, `listReversed` and `listStart` attributes
|
|
703
|
+
// should be copied from the `baseItem` element.
|
|
535
704
|
//
|
|
536
705
|
// The attribute should be copied if the inserted element does not have defined it and
|
|
537
706
|
// the value for the element is other than default in the base element.
|
|
538
707
|
//
|
|
539
708
|
// @param {module:engine/model/element~Element|null} baseItem
|
|
540
709
|
// @param {module:engine/model/element~Element} itemToChange
|
|
710
|
+
// @param {module:list/listpropertiesediting~AttributeStrategy} attributeStrategy
|
|
541
711
|
// @returns {Boolean}
|
|
542
|
-
function shouldInheritListType( baseItem, itemToChange ) {
|
|
712
|
+
function shouldInheritListType( baseItem, itemToChange, attributeStrategy ) {
|
|
543
713
|
if ( !baseItem ) {
|
|
544
714
|
return false;
|
|
545
715
|
}
|
|
546
716
|
|
|
547
|
-
const
|
|
717
|
+
const baseListAttribute = baseItem.getAttribute( attributeStrategy.attributeName );
|
|
548
718
|
|
|
549
|
-
if ( !
|
|
719
|
+
if ( !baseListAttribute ) {
|
|
550
720
|
return false;
|
|
551
721
|
}
|
|
552
722
|
|
|
553
|
-
if (
|
|
723
|
+
if ( baseListAttribute == attributeStrategy.defaultValue ) {
|
|
554
724
|
return false;
|
|
555
725
|
}
|
|
556
726
|
|
|
@@ -561,7 +731,8 @@ function shouldInheritListType( baseItem, itemToChange ) {
|
|
|
561
731
|
return true;
|
|
562
732
|
}
|
|
563
733
|
|
|
564
|
-
// Checks whether the `listStyle`
|
|
734
|
+
// Checks whether the `listStyle`, `listReversed` and `listStart` attributes
|
|
735
|
+
// should be copied from previous list item.
|
|
565
736
|
//
|
|
566
737
|
// The attribute should be copied if there's a mismatch of styles of the pasted list into a nested list.
|
|
567
738
|
// Top-level lists are not normalized as we allow side-by-side list of different types.
|
|
@@ -569,7 +740,7 @@ function shouldInheritListType( baseItem, itemToChange ) {
|
|
|
569
740
|
// @param {module:engine/model/element~Element|null} previousItem
|
|
570
741
|
// @param {module:engine/model/element~Element} itemToChange
|
|
571
742
|
// @returns {Boolean}
|
|
572
|
-
function shouldInheritListTypeFromPreviousItem( previousItem, itemToChange ) {
|
|
743
|
+
function shouldInheritListTypeFromPreviousItem( previousItem, itemToChange, attributeName ) {
|
|
573
744
|
if ( !previousItem || !previousItem.is( 'element', 'listItem' ) ) {
|
|
574
745
|
return false;
|
|
575
746
|
}
|
|
@@ -584,25 +755,29 @@ function shouldInheritListTypeFromPreviousItem( previousItem, itemToChange ) {
|
|
|
584
755
|
return false;
|
|
585
756
|
}
|
|
586
757
|
|
|
587
|
-
const
|
|
758
|
+
const previousItemListAttribute = previousItem.getAttribute( attributeName );
|
|
588
759
|
|
|
589
|
-
if ( !
|
|
760
|
+
if ( !previousItemListAttribute || previousItemListAttribute === itemToChange.getAttribute( attributeName ) ) {
|
|
590
761
|
return false;
|
|
591
762
|
}
|
|
592
763
|
|
|
593
764
|
return true;
|
|
594
765
|
}
|
|
595
766
|
|
|
596
|
-
// Removes the `listStyle`
|
|
767
|
+
// Removes the `listStyle`, `listReversed` and `listStart` attributes from "todo" list items.
|
|
597
768
|
//
|
|
598
769
|
// @param {module:core/editor/editor~Editor} editor
|
|
599
770
|
// @returns {Function}
|
|
600
|
-
function
|
|
771
|
+
function removeListItemAttributesFromTodoList( editor ) {
|
|
601
772
|
return writer => {
|
|
602
773
|
const todoListItems = getChangedListItems( editor.model.document.differ.getChanges() )
|
|
603
774
|
.filter( item => {
|
|
604
775
|
// Handle the todo lists only. The rest is handled in another post-fixer.
|
|
605
|
-
return item.getAttribute( 'listType' ) === 'todo' &&
|
|
776
|
+
return item.getAttribute( 'listType' ) === 'todo' && (
|
|
777
|
+
item.hasAttribute( 'listStyle' ) ||
|
|
778
|
+
item.hasAttribute( 'listReversed' ) ||
|
|
779
|
+
item.hasAttribute( 'listStart' )
|
|
780
|
+
);
|
|
606
781
|
} );
|
|
607
782
|
|
|
608
783
|
if ( !todoListItems.length ) {
|
|
@@ -611,6 +786,8 @@ function removeListStyleAttributeFromTodoList( editor ) {
|
|
|
611
786
|
|
|
612
787
|
for ( const item of todoListItems ) {
|
|
613
788
|
writer.removeAttribute( 'listStyle', item );
|
|
789
|
+
writer.removeAttribute( 'listReversed', item );
|
|
790
|
+
writer.removeAttribute( 'listStart', item );
|
|
614
791
|
}
|
|
615
792
|
|
|
616
793
|
return true;
|