@ckeditor/ckeditor5-list 32.0.0 → 34.0.1
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/README.md +2 -1
- package/build/list.js +2 -2
- package/build/list.js.map +1 -0
- package/build/translations/cs.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/es.js +1 -1
- package/build/translations/gl.js +1 -1
- package/build/translations/it.js +1 -1
- package/build/translations/jv.js +1 -0
- package/build/translations/pt-br.js +1 -1
- package/build/translations/sk.js +1 -1
- package/build/translations/sr-latn.js +1 -1
- package/build/translations/sr.js +1 -1
- package/ckeditor5-metadata.json +1 -1
- package/lang/translations/cs.po +4 -4
- package/lang/translations/de.po +4 -4
- package/lang/translations/el.po +27 -27
- package/lang/translations/en-au.po +4 -4
- package/lang/translations/es.po +22 -22
- package/lang/translations/gl.po +4 -4
- package/lang/translations/it.po +4 -4
- package/lang/translations/jv.po +125 -0
- package/lang/translations/pt-br.po +4 -4
- package/lang/translations/sk.po +4 -4
- package/lang/translations/sr-latn.po +4 -4
- package/lang/translations/sr.po +4 -4
- package/package.json +36 -24
- package/src/documentlist/converters.js +470 -0
- package/src/documentlist/documentlistcommand.js +216 -0
- package/src/documentlist/documentlistediting.js +676 -0
- package/src/documentlist/documentlistindentcommand.js +182 -0
- package/src/documentlist/documentlistmergecommand.js +235 -0
- package/src/documentlist/documentlistsplitcommand.js +114 -0
- package/src/documentlist/utils/listwalker.js +260 -0
- package/src/documentlist/utils/model.js +534 -0
- package/src/documentlist/utils/postfixers.js +138 -0
- package/src/documentlist/utils/view.js +148 -0
- package/src/documentlist.js +36 -0
- package/src/documentlistproperties/converters.js +57 -0
- package/src/documentlistproperties/documentlistpropertiesediting.js +338 -0
- package/src/documentlistproperties/documentlistreversedcommand.js +76 -0
- package/src/documentlistproperties/documentliststartcommand.js +76 -0
- package/src/documentlistproperties/documentliststylecommand.js +140 -0
- package/src/documentlistproperties/utils/style.js +41 -0
- package/src/documentlistproperties.js +37 -0
- package/src/index.js +10 -6
- package/src/{converters.js → list/converters.js} +11 -10
- package/src/{indentcommand.js → list/indentcommand.js} +1 -1
- package/src/{listcommand.js → list/listcommand.js} +1 -1
- package/src/{listediting.js → list/listediting.js} +11 -12
- package/src/{listui.js → list/listui.js} +3 -3
- package/src/{utils.js → list/utils.js} +35 -4
- package/src/list.js +7 -6
- package/src/{listpropertiesediting.js → listproperties/listpropertiesediting.js} +6 -5
- package/src/{listpropertiesui.js → listproperties/listpropertiesui.js} +15 -16
- package/src/{listreversedcommand.js → listproperties/listreversedcommand.js} +3 -3
- package/src/{liststartcommand.js → listproperties/liststartcommand.js} +3 -3
- package/src/{liststylecommand.js → listproperties/liststylecommand.js} +36 -4
- package/src/{ui → listproperties/ui}/collapsibleview.js +1 -1
- package/src/{ui → listproperties/ui}/listpropertiesview.js +1 -1
- package/src/listproperties.js +14 -9
- package/src/{checktodolistcommand.js → todolist/checktodolistcommand.js} +2 -2
- package/src/{todolistconverters.js → todolist/todolistconverters.js} +8 -4
- package/src/{todolistediting.js → todolist/todolistediting.js} +4 -4
- package/src/{todolistui.js → todolist/todolistui.js} +3 -3
- package/src/todolist.js +4 -4
- package/theme/documentlist.css +8 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module list/documentlist/utils/view
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if view element is a list type (ul or ol).
|
|
12
|
+
*
|
|
13
|
+
* @protected
|
|
14
|
+
* @param {module:engine/view/element~Element} viewElement
|
|
15
|
+
* @returns {Boolean}
|
|
16
|
+
*/
|
|
17
|
+
export function isListView( viewElement ) {
|
|
18
|
+
return viewElement.is( 'element', 'ol' ) || viewElement.is( 'element', 'ul' );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Checks if view element is a list item (li).
|
|
23
|
+
*
|
|
24
|
+
* @protected
|
|
25
|
+
* @param {module:engine/view/element~Element} viewElement
|
|
26
|
+
* @returns {Boolean}
|
|
27
|
+
*/
|
|
28
|
+
export function isListItemView( viewElement ) {
|
|
29
|
+
return viewElement.is( 'element', 'li' );
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Calculates the indent value for a list item. Handles HTML compliant and non-compliant lists.
|
|
34
|
+
*
|
|
35
|
+
* Also, fixes non HTML compliant lists indents:
|
|
36
|
+
*
|
|
37
|
+
* before: fixed list:
|
|
38
|
+
* OL OL
|
|
39
|
+
* |-> LI (parent LIs: 0) |-> LI (indent: 0)
|
|
40
|
+
* |-> OL |-> OL
|
|
41
|
+
* |-> OL |
|
|
42
|
+
* | |-> OL |
|
|
43
|
+
* | |-> OL |
|
|
44
|
+
* | |-> LI (parent LIs: 1) |-> LI (indent: 1)
|
|
45
|
+
* |-> LI (parent LIs: 1) |-> LI (indent: 1)
|
|
46
|
+
*
|
|
47
|
+
* before: fixed list:
|
|
48
|
+
* OL OL
|
|
49
|
+
* |-> OL |
|
|
50
|
+
* |-> OL |
|
|
51
|
+
* |-> OL |
|
|
52
|
+
* |-> LI (parent LIs: 0) |-> LI (indent: 0)
|
|
53
|
+
*
|
|
54
|
+
* before: fixed list:
|
|
55
|
+
* OL OL
|
|
56
|
+
* |-> LI (parent LIs: 0) |-> LI (indent: 0)
|
|
57
|
+
* |-> OL |-> OL
|
|
58
|
+
* |-> LI (parent LIs: 0) |-> LI (indent: 1)
|
|
59
|
+
*
|
|
60
|
+
* @protected
|
|
61
|
+
* @param {module:engine/view/element~Element} listItem
|
|
62
|
+
* @returns {Number}
|
|
63
|
+
*/
|
|
64
|
+
export function getIndent( listItem ) {
|
|
65
|
+
let indent = 0;
|
|
66
|
+
let parent = listItem.parent;
|
|
67
|
+
|
|
68
|
+
while ( parent ) {
|
|
69
|
+
// Each LI in the tree will result in an increased indent for HTML compliant lists.
|
|
70
|
+
if ( isListItemView( parent ) ) {
|
|
71
|
+
indent++;
|
|
72
|
+
} else {
|
|
73
|
+
// If however the list is nested in other list we should check previous sibling of any of the list elements...
|
|
74
|
+
const previousSibling = parent.previousSibling;
|
|
75
|
+
|
|
76
|
+
// ...because the we might need increase its indent:
|
|
77
|
+
// before: fixed list:
|
|
78
|
+
// OL OL
|
|
79
|
+
// |-> LI (parent LIs: 0) |-> LI (indent: 0)
|
|
80
|
+
// |-> OL |-> OL
|
|
81
|
+
// |-> LI (parent LIs: 0) |-> LI (indent: 1)
|
|
82
|
+
if ( previousSibling && isListItemView( previousSibling ) ) {
|
|
83
|
+
indent++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
parent = parent.parent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return indent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates a list attribute element (ol or ul).
|
|
95
|
+
*
|
|
96
|
+
* @protected
|
|
97
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The downcast writer.
|
|
98
|
+
* @param {Number} indent The list item indent.
|
|
99
|
+
* @param {'bulleted'|'numbered'} type The list type.
|
|
100
|
+
* @returns {module:engine/view/attributeelement~AttributeElement}
|
|
101
|
+
*/
|
|
102
|
+
export function createListElement( writer, indent, type, id = getViewElementIdForListType( type, indent ) ) {
|
|
103
|
+
// Negative priorities so that restricted editing attribute won't wrap lists.
|
|
104
|
+
return writer.createAttributeElement( getViewElementNameForListType( type ), null, {
|
|
105
|
+
priority: 2 * indent / 100 - 100,
|
|
106
|
+
id
|
|
107
|
+
} );
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates a list item attribute element (li).
|
|
112
|
+
*
|
|
113
|
+
* @protected
|
|
114
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The downcast writer.
|
|
115
|
+
* @param {Number} indent The list item indent.
|
|
116
|
+
* @param {String} id The list item ID.
|
|
117
|
+
* @returns {module:engine/view/attributeelement~AttributeElement}
|
|
118
|
+
*/
|
|
119
|
+
export function createListItemElement( writer, indent, id ) {
|
|
120
|
+
// Negative priorities so that restricted editing attribute won't wrap list items.
|
|
121
|
+
return writer.createAttributeElement( 'li', null, {
|
|
122
|
+
priority: ( 2 * indent + 1 ) / 100 - 100,
|
|
123
|
+
id
|
|
124
|
+
} );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns a view element name for the given list type.
|
|
129
|
+
*
|
|
130
|
+
* @protected
|
|
131
|
+
* @param {'bulleted'|'numbered'} type The list type.
|
|
132
|
+
* @returns {String}
|
|
133
|
+
*/
|
|
134
|
+
export function getViewElementNameForListType( type ) {
|
|
135
|
+
return type == 'numbered' ? 'ol' : 'ul';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Returns a view element ID for the given list type and indent.
|
|
140
|
+
*
|
|
141
|
+
* @protected
|
|
142
|
+
* @param {'bulleted'|'numbered'} type The list type.
|
|
143
|
+
* @param {Number} indent The list indent level.
|
|
144
|
+
* @returns {String}
|
|
145
|
+
*/
|
|
146
|
+
export function getViewElementIdForListType( type, indent ) {
|
|
147
|
+
return `list-${ type }-${ indent }`;
|
|
148
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module list/documentlist
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
11
|
+
import DocumentListEditing from './documentlist/documentlistediting';
|
|
12
|
+
import ListUI from './list/listui';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The document list feature.
|
|
16
|
+
*
|
|
17
|
+
* This is a "glue" plugin that loads the {@link module:list/documentlist/documentlistediting~DocumentListEditing document list
|
|
18
|
+
* editing feature} and {@link module:list/list/listui~ListUI list UI feature}.
|
|
19
|
+
*
|
|
20
|
+
* @extends module:core/plugin~Plugin
|
|
21
|
+
*/
|
|
22
|
+
export default class DocumentList extends Plugin {
|
|
23
|
+
/**
|
|
24
|
+
* @inheritDoc
|
|
25
|
+
*/
|
|
26
|
+
static get requires() {
|
|
27
|
+
return [ DocumentListEditing, ListUI ];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
static get pluginName() {
|
|
34
|
+
return 'DocumentList';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module list/documentlistproperties/converters
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns a converter that consumes the `style`, `reversed`, and `start` attributes.
|
|
12
|
+
* In `style`, it searches for the `list-style-type` definition.
|
|
13
|
+
* If not found, the `"default"` value will be used.
|
|
14
|
+
*
|
|
15
|
+
* @protected
|
|
16
|
+
* @param {module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy} strategy
|
|
17
|
+
* @returns {Function}
|
|
18
|
+
*/
|
|
19
|
+
export function listPropertiesUpcastConverter( strategy ) {
|
|
20
|
+
return ( evt, data, conversionApi ) => {
|
|
21
|
+
const { writer, schema, consumable } = conversionApi;
|
|
22
|
+
|
|
23
|
+
// If there is no view consumable to consume, set the default attribute value to be able to reconvert nested lists on parent change.
|
|
24
|
+
// So abort converting if attribute was directly consumed.
|
|
25
|
+
if ( consumable.test( data.viewItem, strategy.viewConsumables ) === false ) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ( !data.modelRange ) {
|
|
30
|
+
Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let applied = false;
|
|
34
|
+
|
|
35
|
+
for ( const item of data.modelRange.getItems( { shallow: true } ) ) {
|
|
36
|
+
if ( !schema.checkAttribute( item, strategy.attributeName ) ) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ( !strategy.appliesToListItem( item ) ) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Set list attributes only on same level items, those nested deeper are already handled by the recursive conversion.
|
|
45
|
+
if ( item.hasAttribute( strategy.attributeName ) ) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
writer.setAttribute( strategy.attributeName, strategy.getAttributeOnUpcast( data.viewItem ), item );
|
|
50
|
+
applied = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ( applied ) {
|
|
54
|
+
consumable.consume( data.viewItem, strategy.viewConsumables );
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module list/documentlistproperties/documentlistpropertiesediting
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
11
|
+
|
|
12
|
+
import DocumentListEditing from '../documentlist/documentlistediting';
|
|
13
|
+
import DocumentListStartCommand from './documentliststartcommand';
|
|
14
|
+
import DocumentListStyleCommand from './documentliststylecommand';
|
|
15
|
+
import DocumentListReversedCommand from './documentlistreversedcommand';
|
|
16
|
+
import { listPropertiesUpcastConverter } from './converters';
|
|
17
|
+
import { getListTypeFromListStyleType } from './utils/style';
|
|
18
|
+
|
|
19
|
+
const DEFAULT_LIST_TYPE = 'default';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The document list properties engine feature.
|
|
23
|
+
*
|
|
24
|
+
* It registers the `'listStyle'`, `'listReversed'` and `'listStart'` commands if they are enabled in the configuration.
|
|
25
|
+
* Read more in {@link module:list/listproperties~ListPropertiesConfig}.
|
|
26
|
+
*
|
|
27
|
+
* @extends module:core/plugin~Plugin
|
|
28
|
+
*/
|
|
29
|
+
export default class DocumentListPropertiesEditing extends Plugin {
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
static get requires() {
|
|
34
|
+
return [ DocumentListEditing ];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @inheritDoc
|
|
39
|
+
*/
|
|
40
|
+
static get pluginName() {
|
|
41
|
+
return 'DocumentListPropertiesEditing';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @inheritDoc
|
|
46
|
+
*/
|
|
47
|
+
constructor( editor ) {
|
|
48
|
+
super( editor );
|
|
49
|
+
|
|
50
|
+
editor.config.define( 'list', {
|
|
51
|
+
properties: {
|
|
52
|
+
styles: true,
|
|
53
|
+
startIndex: false,
|
|
54
|
+
reversed: false
|
|
55
|
+
}
|
|
56
|
+
} );
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @inheritDoc
|
|
61
|
+
*/
|
|
62
|
+
init() {
|
|
63
|
+
const editor = this.editor;
|
|
64
|
+
const model = editor.model;
|
|
65
|
+
const documentListEditing = editor.plugins.get( DocumentListEditing );
|
|
66
|
+
|
|
67
|
+
const enabledProperties = editor.config.get( 'list.properties' );
|
|
68
|
+
const strategies = createAttributeStrategies( enabledProperties );
|
|
69
|
+
|
|
70
|
+
for ( const strategy of strategies ) {
|
|
71
|
+
strategy.addCommand( editor );
|
|
72
|
+
|
|
73
|
+
model.schema.extend( '$container', { allowAttributes: strategy.attributeName } );
|
|
74
|
+
model.schema.extend( '$block', { allowAttributes: strategy.attributeName } );
|
|
75
|
+
model.schema.extend( '$blockObject', { allowAttributes: strategy.attributeName } );
|
|
76
|
+
|
|
77
|
+
// Register downcast strategy.
|
|
78
|
+
documentListEditing.registerDowncastStrategy( {
|
|
79
|
+
scope: 'list',
|
|
80
|
+
attributeName: strategy.attributeName,
|
|
81
|
+
|
|
82
|
+
setAttributeOnDowncast( writer, attributeValue, viewElement ) {
|
|
83
|
+
strategy.setAttributeOnDowncast( writer, attributeValue, viewElement );
|
|
84
|
+
}
|
|
85
|
+
} );
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Set up conversion.
|
|
89
|
+
editor.conversion.for( 'upcast' ).add( dispatcher => {
|
|
90
|
+
for ( const strategy of strategies ) {
|
|
91
|
+
dispatcher.on( 'element:ol', listPropertiesUpcastConverter( strategy ) );
|
|
92
|
+
dispatcher.on( 'element:ul', listPropertiesUpcastConverter( strategy ) );
|
|
93
|
+
}
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
// Verify if the list view element (ul or ol) requires refreshing.
|
|
97
|
+
documentListEditing.on( 'checkAttributes:list', ( evt, { viewElement, modelAttributes } ) => {
|
|
98
|
+
for ( const strategy of strategies ) {
|
|
99
|
+
if ( strategy.getAttributeOnUpcast( viewElement ) != modelAttributes[ strategy.attributeName ] ) {
|
|
100
|
+
evt.return = true;
|
|
101
|
+
evt.stop();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} );
|
|
105
|
+
|
|
106
|
+
// Reset list properties after indenting list items.
|
|
107
|
+
this.listenTo( editor.commands.get( 'indentList' ), 'afterExecute', ( evt, changedBlocks ) => {
|
|
108
|
+
model.change( writer => {
|
|
109
|
+
for ( const node of changedBlocks ) {
|
|
110
|
+
for ( const strategy of strategies ) {
|
|
111
|
+
if ( strategy.appliesToListItem( node ) ) {
|
|
112
|
+
// Just reset the attribute.
|
|
113
|
+
// If there is a previous indented list that this node should be merged into,
|
|
114
|
+
// the postfixer will unify all the attributes of both sub-lists.
|
|
115
|
+
writer.setAttribute( strategy.attributeName, strategy.defaultValue, node );
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} );
|
|
120
|
+
} );
|
|
121
|
+
|
|
122
|
+
// Add or remove list properties attributes depending on the list type.
|
|
123
|
+
documentListEditing.on( 'postFixer', ( evt, { listNodes, writer } ) => {
|
|
124
|
+
for ( const { node } of listNodes ) {
|
|
125
|
+
for ( const strategy of strategies ) {
|
|
126
|
+
// Check if attribute is valid.
|
|
127
|
+
if ( strategy.hasValidAttribute( node ) ) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add missing default property attributes...
|
|
132
|
+
if ( strategy.appliesToListItem( node ) ) {
|
|
133
|
+
writer.setAttribute( strategy.attributeName, strategy.defaultValue, node );
|
|
134
|
+
}
|
|
135
|
+
// ...or remove invalid property attributes.
|
|
136
|
+
else {
|
|
137
|
+
writer.removeAttribute( strategy.attributeName, node );
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
evt.return = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} );
|
|
144
|
+
|
|
145
|
+
// Make sure that all items in a single list (items at the same level & listType) have the same properties.
|
|
146
|
+
documentListEditing.on( 'postFixer', ( evt, { listNodes, writer } ) => {
|
|
147
|
+
const previousNodesByIndent = []; // Last seen nodes of lower indented lists.
|
|
148
|
+
|
|
149
|
+
for ( const { node, previous } of listNodes ) {
|
|
150
|
+
// For the first list block there is nothing to compare with.
|
|
151
|
+
if ( !previous ) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const nodeIndent = node.getAttribute( 'listIndent' );
|
|
156
|
+
const previousNodeIndent = previous.getAttribute( 'listIndent' );
|
|
157
|
+
|
|
158
|
+
let previousNodeInList = null; // It's like `previous` but has the same indent as current node.
|
|
159
|
+
|
|
160
|
+
// Let's find previous node for the same indent.
|
|
161
|
+
// We're going to need that when we get back to previous indent.
|
|
162
|
+
if ( nodeIndent > previousNodeIndent ) {
|
|
163
|
+
previousNodesByIndent[ previousNodeIndent ] = previous;
|
|
164
|
+
}
|
|
165
|
+
// Restore the one for given indent.
|
|
166
|
+
else if ( nodeIndent < previousNodeIndent ) {
|
|
167
|
+
previousNodeInList = previousNodesByIndent[ nodeIndent ];
|
|
168
|
+
previousNodesByIndent.length = nodeIndent;
|
|
169
|
+
}
|
|
170
|
+
// Same indent.
|
|
171
|
+
else {
|
|
172
|
+
previousNodeInList = previous;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// This is a first item of a nested list.
|
|
176
|
+
if ( !previousNodeInList ) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// This is a first block of a list of a different type.
|
|
181
|
+
if ( previousNodeInList.getAttribute( 'listType' ) != node.getAttribute( 'listType' ) ) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Copy properties from the previous one.
|
|
186
|
+
for ( const strategy of strategies ) {
|
|
187
|
+
const { attributeName } = strategy;
|
|
188
|
+
|
|
189
|
+
if ( !strategy.appliesToListItem( node ) ) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const value = previousNodeInList.getAttribute( attributeName );
|
|
194
|
+
|
|
195
|
+
if ( node.getAttribute( attributeName ) != value ) {
|
|
196
|
+
writer.setAttribute( attributeName, value, node );
|
|
197
|
+
evt.return = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} );
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Strategy for dealing with `listItem` attributes supported by this plugin.
|
|
207
|
+
*
|
|
208
|
+
* @typedef {Object} module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy
|
|
209
|
+
* @protected
|
|
210
|
+
* @property {String} attributeName The model attribute name.
|
|
211
|
+
* @property {*} defaultValue The model attribute default value.
|
|
212
|
+
* @property {Object} viewConsumables The view consumable as expected by
|
|
213
|
+
* {@link module:engine/conversion/viewconsumable~ViewConsumable#consume `ViewConsumable`}.
|
|
214
|
+
* @property {Function} addCommand Registers an editor command.
|
|
215
|
+
* @property {Function} appliesToListItem Verifies whether the strategy is applicable for the specified model element.
|
|
216
|
+
* @property {Function} hasValidAttribute Verifies whether the model attribute value is valid.
|
|
217
|
+
* @property {Function} setAttributeOnDowncast Sets the property on the view element.
|
|
218
|
+
* @property {Function} getAttributeOnUpcast Retrieves the property value from the view element.
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
// Creates an array of strategies for dealing with enabled listItem attributes.
|
|
222
|
+
//
|
|
223
|
+
// @param {Object} enabledProperties
|
|
224
|
+
// @param {Boolean} enabledProperties.styles
|
|
225
|
+
// @param {Boolean} enabledProperties.reversed
|
|
226
|
+
// @param {Boolean} enabledProperties.startIndex
|
|
227
|
+
// @returns {Array.<module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy>}
|
|
228
|
+
function createAttributeStrategies( enabledProperties ) {
|
|
229
|
+
const strategies = [];
|
|
230
|
+
|
|
231
|
+
if ( enabledProperties.styles ) {
|
|
232
|
+
strategies.push( {
|
|
233
|
+
attributeName: 'listStyle',
|
|
234
|
+
defaultValue: DEFAULT_LIST_TYPE,
|
|
235
|
+
viewConsumables: { styles: 'list-style-type' },
|
|
236
|
+
|
|
237
|
+
addCommand( editor ) {
|
|
238
|
+
editor.commands.add( 'listStyle', new DocumentListStyleCommand( editor, DEFAULT_LIST_TYPE ) );
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
appliesToListItem() {
|
|
242
|
+
return true;
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
hasValidAttribute( item ) {
|
|
246
|
+
if ( !item.hasAttribute( 'listStyle' ) ) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const value = item.getAttribute( 'listStyle' );
|
|
251
|
+
|
|
252
|
+
if ( value == DEFAULT_LIST_TYPE ) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return getListTypeFromListStyleType( value ) == item.getAttribute( 'listType' );
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
setAttributeOnDowncast( writer, listStyle, element ) {
|
|
260
|
+
if ( listStyle && listStyle !== DEFAULT_LIST_TYPE ) {
|
|
261
|
+
writer.setStyle( 'list-style-type', listStyle, element );
|
|
262
|
+
} else {
|
|
263
|
+
writer.removeStyle( 'list-style-type', element );
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
getAttributeOnUpcast( listParent ) {
|
|
268
|
+
return listParent.getStyle( 'list-style-type' ) || DEFAULT_LIST_TYPE;
|
|
269
|
+
}
|
|
270
|
+
} );
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if ( enabledProperties.reversed ) {
|
|
274
|
+
strategies.push( {
|
|
275
|
+
attributeName: 'listReversed',
|
|
276
|
+
defaultValue: false,
|
|
277
|
+
viewConsumables: { attributes: 'reversed' },
|
|
278
|
+
|
|
279
|
+
addCommand( editor ) {
|
|
280
|
+
editor.commands.add( 'listReversed', new DocumentListReversedCommand( editor ) );
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
appliesToListItem( item ) {
|
|
284
|
+
return item.getAttribute( 'listType' ) == 'numbered';
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
hasValidAttribute( item ) {
|
|
288
|
+
return this.appliesToListItem( item ) == item.hasAttribute( 'listReversed' );
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
setAttributeOnDowncast( writer, listReversed, element ) {
|
|
292
|
+
if ( listReversed ) {
|
|
293
|
+
writer.setAttribute( 'reversed', 'reversed', element );
|
|
294
|
+
} else {
|
|
295
|
+
writer.removeAttribute( 'reversed', element );
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
getAttributeOnUpcast( listParent ) {
|
|
300
|
+
return listParent.hasAttribute( 'reversed' );
|
|
301
|
+
}
|
|
302
|
+
} );
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if ( enabledProperties.startIndex ) {
|
|
306
|
+
strategies.push( {
|
|
307
|
+
attributeName: 'listStart',
|
|
308
|
+
defaultValue: 1,
|
|
309
|
+
viewConsumables: { attributes: 'start' },
|
|
310
|
+
|
|
311
|
+
addCommand( editor ) {
|
|
312
|
+
editor.commands.add( 'listStart', new DocumentListStartCommand( editor ) );
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
appliesToListItem( item ) {
|
|
316
|
+
return item.getAttribute( 'listType' ) == 'numbered';
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
hasValidAttribute( item ) {
|
|
320
|
+
return this.appliesToListItem( item ) == item.hasAttribute( 'listStart' );
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
setAttributeOnDowncast( writer, listStart, element ) {
|
|
324
|
+
if ( listStart && listStart > 1 ) {
|
|
325
|
+
writer.setAttribute( 'start', listStart, element );
|
|
326
|
+
} else {
|
|
327
|
+
writer.removeAttribute( 'start', element );
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
getAttributeOnUpcast( listParent ) {
|
|
332
|
+
return listParent.getAttribute( 'start' ) || 1;
|
|
333
|
+
}
|
|
334
|
+
} );
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return strategies;
|
|
338
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module list/documentlistproperties/documentlistreversedcommand
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Command } from 'ckeditor5/src/core';
|
|
11
|
+
import { first } from 'ckeditor5/src/utils';
|
|
12
|
+
import {
|
|
13
|
+
expandListBlocksToCompleteList,
|
|
14
|
+
isListItemBlock
|
|
15
|
+
} from '../documentlist/utils/model';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The list reversed command. It changes the `listReversed` attribute of the selected list items,
|
|
19
|
+
* letting the user to choose the order of an ordered list.
|
|
20
|
+
* It is used by the {@link module:list/documentlistproperties~DocumentListProperties list properties feature}.
|
|
21
|
+
*
|
|
22
|
+
* @extends module:core/command~Command
|
|
23
|
+
*/
|
|
24
|
+
export default class DocumentListReversedCommand extends Command {
|
|
25
|
+
/**
|
|
26
|
+
* @inheritDoc
|
|
27
|
+
*/
|
|
28
|
+
refresh() {
|
|
29
|
+
const value = this._getValue();
|
|
30
|
+
|
|
31
|
+
this.value = value;
|
|
32
|
+
this.isEnabled = value != null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Executes the command.
|
|
37
|
+
*
|
|
38
|
+
* @fires execute
|
|
39
|
+
* @param {Object} [options]
|
|
40
|
+
* @param {Boolean} [options.reversed=false] Whether the list should be reversed.
|
|
41
|
+
*/
|
|
42
|
+
execute( options = {} ) {
|
|
43
|
+
const model = this.editor.model;
|
|
44
|
+
const document = model.document;
|
|
45
|
+
|
|
46
|
+
let blocks = Array.from( document.selection.getSelectedBlocks() )
|
|
47
|
+
.filter( block => isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' );
|
|
48
|
+
|
|
49
|
+
blocks = expandListBlocksToCompleteList( blocks );
|
|
50
|
+
|
|
51
|
+
model.change( writer => {
|
|
52
|
+
for ( const block of blocks ) {
|
|
53
|
+
writer.setAttribute( 'listReversed', !!options.reversed, block );
|
|
54
|
+
}
|
|
55
|
+
} );
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks the command's {@link #value}.
|
|
60
|
+
*
|
|
61
|
+
* @private
|
|
62
|
+
* @returns {Boolean|null} The current value.
|
|
63
|
+
*/
|
|
64
|
+
_getValue() {
|
|
65
|
+
const model = this.editor.model;
|
|
66
|
+
const document = model.document;
|
|
67
|
+
|
|
68
|
+
const block = first( document.selection.getSelectedBlocks() );
|
|
69
|
+
|
|
70
|
+
if ( isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' ) {
|
|
71
|
+
return block.getAttribute( 'listReversed' );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|