@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.
Files changed (144) hide show
  1. package/LICENSE.md +2 -2
  2. package/build/list.js +3 -3
  3. package/build/translations/ar.js +1 -1
  4. package/build/translations/ast.js +1 -1
  5. package/build/translations/az.js +1 -1
  6. package/build/translations/bg.js +1 -1
  7. package/build/translations/cs.js +1 -1
  8. package/build/translations/da.js +1 -1
  9. package/build/translations/de-ch.js +1 -1
  10. package/build/translations/de.js +1 -1
  11. package/build/translations/el.js +1 -1
  12. package/build/translations/en-au.js +1 -1
  13. package/build/translations/en-gb.js +1 -1
  14. package/build/translations/eo.js +1 -1
  15. package/build/translations/es.js +1 -1
  16. package/build/translations/et.js +1 -1
  17. package/build/translations/eu.js +1 -1
  18. package/build/translations/fa.js +1 -1
  19. package/build/translations/fi.js +1 -1
  20. package/build/translations/fr.js +1 -1
  21. package/build/translations/gl.js +1 -1
  22. package/build/translations/he.js +1 -1
  23. package/build/translations/hi.js +1 -1
  24. package/build/translations/hr.js +1 -1
  25. package/build/translations/hu.js +1 -1
  26. package/build/translations/id.js +1 -1
  27. package/build/translations/it.js +1 -1
  28. package/build/translations/ja.js +1 -1
  29. package/build/translations/km.js +1 -1
  30. package/build/translations/kn.js +1 -1
  31. package/build/translations/ko.js +1 -1
  32. package/build/translations/ku.js +1 -1
  33. package/build/translations/lt.js +1 -1
  34. package/build/translations/lv.js +1 -1
  35. package/build/translations/nb.js +1 -1
  36. package/build/translations/ne.js +1 -1
  37. package/build/translations/nl.js +1 -1
  38. package/build/translations/no.js +1 -1
  39. package/build/translations/pl.js +1 -1
  40. package/build/translations/pt-br.js +1 -1
  41. package/build/translations/pt.js +1 -1
  42. package/build/translations/ro.js +1 -1
  43. package/build/translations/ru.js +1 -1
  44. package/build/translations/si.js +1 -1
  45. package/build/translations/sk.js +1 -1
  46. package/build/translations/sq.js +1 -1
  47. package/build/translations/sr-latn.js +1 -1
  48. package/build/translations/sr.js +1 -1
  49. package/build/translations/sv.js +1 -1
  50. package/build/translations/tk.js +1 -1
  51. package/build/translations/tr.js +1 -1
  52. package/build/translations/ug.js +1 -1
  53. package/build/translations/uk.js +1 -1
  54. package/build/translations/uz.js +1 -0
  55. package/build/translations/vi.js +1 -1
  56. package/build/translations/zh-cn.js +1 -1
  57. package/build/translations/zh.js +1 -1
  58. package/ckeditor5-metadata.json +8 -4
  59. package/lang/contexts.json +5 -1
  60. package/lang/translations/ar.po +17 -1
  61. package/lang/translations/ast.po +17 -1
  62. package/lang/translations/az.po +17 -1
  63. package/lang/translations/bg.po +17 -1
  64. package/lang/translations/cs.po +17 -1
  65. package/lang/translations/da.po +17 -1
  66. package/lang/translations/de-ch.po +17 -1
  67. package/lang/translations/de.po +17 -1
  68. package/lang/translations/el.po +17 -1
  69. package/lang/translations/en-au.po +17 -1
  70. package/lang/translations/en-gb.po +17 -1
  71. package/lang/translations/en.po +17 -1
  72. package/lang/translations/eo.po +17 -1
  73. package/lang/translations/es.po +20 -4
  74. package/lang/translations/et.po +17 -1
  75. package/lang/translations/eu.po +17 -1
  76. package/lang/translations/fa.po +17 -1
  77. package/lang/translations/fi.po +17 -1
  78. package/lang/translations/fr.po +17 -1
  79. package/lang/translations/gl.po +17 -1
  80. package/lang/translations/he.po +17 -1
  81. package/lang/translations/hi.po +17 -1
  82. package/lang/translations/hr.po +17 -1
  83. package/lang/translations/hu.po +17 -1
  84. package/lang/translations/id.po +17 -1
  85. package/lang/translations/it.po +17 -1
  86. package/lang/translations/ja.po +17 -1
  87. package/lang/translations/km.po +17 -1
  88. package/lang/translations/kn.po +17 -1
  89. package/lang/translations/ko.po +17 -1
  90. package/lang/translations/ku.po +17 -1
  91. package/lang/translations/lt.po +17 -1
  92. package/lang/translations/lv.po +17 -1
  93. package/lang/translations/nb.po +17 -1
  94. package/lang/translations/ne.po +17 -1
  95. package/lang/translations/nl.po +21 -5
  96. package/lang/translations/no.po +17 -1
  97. package/lang/translations/pl.po +17 -1
  98. package/lang/translations/pt-br.po +17 -1
  99. package/lang/translations/pt.po +17 -1
  100. package/lang/translations/ro.po +17 -1
  101. package/lang/translations/ru.po +17 -1
  102. package/lang/translations/si.po +17 -1
  103. package/lang/translations/sk.po +17 -1
  104. package/lang/translations/sq.po +17 -1
  105. package/lang/translations/sr-latn.po +17 -1
  106. package/lang/translations/sr.po +17 -1
  107. package/lang/translations/sv.po +17 -1
  108. package/lang/translations/tk.po +17 -1
  109. package/lang/translations/tr.po +17 -1
  110. package/lang/translations/ug.po +17 -1
  111. package/lang/translations/uk.po +17 -1
  112. package/lang/translations/uz.po +125 -0
  113. package/lang/translations/vi.po +17 -1
  114. package/lang/translations/zh-cn.po +17 -1
  115. package/lang/translations/zh.po +36 -20
  116. package/package.json +27 -27
  117. package/src/checktodolistcommand.js +1 -1
  118. package/src/converters.js +14 -15
  119. package/src/indentcommand.js +1 -1
  120. package/src/index.js +4 -4
  121. package/src/list.js +24 -1
  122. package/src/listcommand.js +2 -2
  123. package/src/listediting.js +1 -1
  124. package/src/listproperties.js +96 -0
  125. package/src/{liststyleediting.js → listpropertiesediting.js} +299 -122
  126. package/src/listpropertiesui.js +314 -0
  127. package/src/listreversedcommand.js +64 -0
  128. package/src/liststartcommand.js +63 -0
  129. package/src/liststyle.js +12 -6
  130. package/src/liststylecommand.js +5 -22
  131. package/src/listui.js +1 -1
  132. package/src/todolist.js +1 -1
  133. package/src/todolistconverters.js +1 -1
  134. package/src/todolistediting.js +1 -1
  135. package/src/todolistui.js +1 -1
  136. package/src/ui/collapsibleview.js +152 -0
  137. package/src/ui/listpropertiesview.js +405 -0
  138. package/src/utils.js +49 -4
  139. package/theme/collapsible.css +10 -0
  140. package/theme/listproperties.css +10 -0
  141. package/theme/liststyles.css +2 -6
  142. package/theme/todolist.css +1 -1
  143. package/CHANGELOG.md +0 -272
  144. package/src/liststyleui.js +0 -225
@@ -0,0 +1,152 @@
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/ui/collapsibleview
8
+ */
9
+
10
+ import { View, ButtonView } from 'ckeditor5/src/ui';
11
+
12
+ // eslint-disable-next-line ckeditor5-rules/ckeditor-imports
13
+ import dropdownArrowIcon from '@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg';
14
+
15
+ import '../../theme/collapsible.css';
16
+
17
+ /**
18
+ * A collapsible UI component. Consists of a labeled button and a container which can be collapsed
19
+ * by clicking the button. The collapsible container can be a host to other UI views.
20
+ *
21
+ * @protected
22
+ * @extends module:ui/view~View
23
+ */
24
+ export default class CollapsibleView extends View {
25
+ /**
26
+ * Creates an instance of the collapsible view.
27
+ *
28
+ * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
29
+ * @param {Array.<module:ui/view~View>} [childViews] An optional array of initial child views to be inserted
30
+ * into the collapsible.
31
+ */
32
+ constructor( locale, childViews ) {
33
+ super( locale );
34
+
35
+ const bind = this.bindTemplate;
36
+
37
+ /**
38
+ * `true` when the container with {@link #children} is collapsed. `false` otherwise.
39
+ *
40
+ * @observable
41
+ * @member {Boolean} #isCollapsed
42
+ */
43
+ this.set( 'isCollapsed', false );
44
+
45
+ /**
46
+ * The text label of the {@link #buttonView}.
47
+ *
48
+ * @observable
49
+ * @member {String} #label
50
+ * @default 'Show more'
51
+ */
52
+ this.set( 'label', '' );
53
+
54
+ /**
55
+ * The main button that, when clicked, collapses or expands the container with {@link #children}.
56
+ *
57
+ * @readonly
58
+ * @member {module:ui/button/buttonview~ButtonView} #buttonView
59
+ */
60
+ this.buttonView = this._createButtonView();
61
+
62
+ /**
63
+ * A collection of the child views that can be collapsed by clicking the {@link #buttonView}.
64
+ *
65
+ * @readonly
66
+ * @member {module:ui/viewcollection~ViewCollection} #children
67
+ */
68
+ this.children = this.createCollection();
69
+
70
+ /**
71
+ * The ID of the label inside the {@link #buttonView} that describes the collapsible
72
+ * container for assistive technologies. Set after the button was {@link #render rendered}.
73
+ *
74
+ * @private
75
+ * @readonly
76
+ * @observable
77
+ * @member {String} #_collapsibleAriaLabelUid
78
+ */
79
+ this.set( '_collapsibleAriaLabelUid' );
80
+
81
+ if ( childViews ) {
82
+ this.children.addMany( childViews );
83
+ }
84
+
85
+ this.setTemplate( {
86
+ tag: 'div',
87
+ attributes: {
88
+ class: [
89
+ 'ck',
90
+ 'ck-collapsible',
91
+ bind.if( 'isCollapsed', 'ck-collapsible_collapsed' )
92
+ ]
93
+ },
94
+ children: [
95
+ this.buttonView,
96
+ {
97
+ tag: 'div',
98
+ attributes: {
99
+ class: [
100
+ 'ck',
101
+ 'ck-collapsible__children'
102
+ ],
103
+ role: 'region',
104
+ hidden: bind.if( 'isCollapsed', 'hidden' ),
105
+ 'aria-labelledby': bind.to( '_collapsibleAriaLabelUid' )
106
+ },
107
+ children: this.children
108
+ }
109
+ ]
110
+ } );
111
+ }
112
+
113
+ /**
114
+ * @inheritDoc
115
+ */
116
+ render() {
117
+ super.render();
118
+
119
+ this._collapsibleAriaLabelUid = this.buttonView.labelView.element.id;
120
+ }
121
+
122
+ /**
123
+ * Creates the main {@link #buttonView} of the collapsible.
124
+ *
125
+ * @private
126
+ * @returns {module:ui/button/buttonview~ButtonView}
127
+ */
128
+ _createButtonView() {
129
+ const buttonView = new ButtonView( this.locale );
130
+ const bind = buttonView.bindTemplate;
131
+
132
+ buttonView.set( {
133
+ withText: true,
134
+ icon: dropdownArrowIcon
135
+ } );
136
+
137
+ buttonView.extendTemplate( {
138
+ attributes: {
139
+ 'aria-expanded': bind.to( 'isOn', value => String( value ) )
140
+ }
141
+ } );
142
+
143
+ buttonView.bind( 'label' ).to( this );
144
+ buttonView.bind( 'isOn' ).to( this, 'isCollapsed', isCollapsed => !isCollapsed );
145
+
146
+ buttonView.on( 'execute', () => {
147
+ this.isCollapsed = !this.isCollapsed;
148
+ } );
149
+
150
+ return buttonView;
151
+ }
152
+ }
@@ -0,0 +1,405 @@
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/ui/listpropertiesview
8
+ */
9
+
10
+ import {
11
+ View,
12
+ ViewCollection,
13
+ FocusCycler,
14
+ SwitchButtonView,
15
+ LabeledFieldView,
16
+ createLabeledInputNumber
17
+ } from 'ckeditor5/src/ui';
18
+ import {
19
+ FocusTracker,
20
+ KeystrokeHandler
21
+ } from 'ckeditor5/src/utils';
22
+
23
+ import CollapsibleView from './collapsibleview';
24
+
25
+ import '../../theme/listproperties.css';
26
+
27
+ /**
28
+ * The list properties view to be displayed in the list dropdown.
29
+ *
30
+ * Contains a grid of available list styles and, for numbered list, also the list start index and reversed fields.
31
+ *
32
+ * @extends module:ui/view~View
33
+ */
34
+ export default class ListPropertiesView extends View {
35
+ /**
36
+ * Creates an instance of the list properties view.
37
+ *
38
+ * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
39
+ * @param {Object} options Options of the view.
40
+ * @param {Object.<String,Boolean>} options.enabledProperties An object containing the configuration of enabled list property names.
41
+ * Allows conditional rendering the sub-components of the properties view.
42
+ * @param {Array.<module:ui/button/buttonview~ButtonView>|null} options.styleButtonViews A list of style buttons to be rendered
43
+ * inside the styles grid. The grid will not be rendered when `enabledProperties` does not include the `'styles'` key.
44
+ * @param {String} options.styleGridAriaLabel An assistive technologies label set on the grid of styles (if the grid is rendered).
45
+ */
46
+ constructor( locale, { enabledProperties, styleButtonViews, styleGridAriaLabel } ) {
47
+ super( locale );
48
+
49
+ const elementCssClasses = [
50
+ 'ck',
51
+ 'ck-list-properties'
52
+ ];
53
+
54
+ /**
55
+ * A collection of the child views.
56
+ *
57
+ * @readonly
58
+ * @member {module:ui/viewcollection~ViewCollection}
59
+ */
60
+ this.children = this.createCollection();
61
+
62
+ /**
63
+ * A view that renders the grid of list styles.
64
+ *
65
+ * @readonly
66
+ * @member {module:ui/view~View|null}
67
+ */
68
+ this.stylesView = null;
69
+
70
+ /**
71
+ * A collapsible view that hosts additional list property fields ({@link #startIndexFieldView} and
72
+ * {@link #reversedSwitchButtonView}) to visually separate them from the {@link #stylesView grid of styles}.
73
+ *
74
+ * **Note**: Only present when:
75
+ * * the view represents **numbered** list properties,
76
+ * * and the {@link #stylesView} is rendered,
77
+ * * and either {@link #startIndexFieldView} or {@link #reversedSwitchButtonView} is rendered.
78
+ *
79
+ * @readonly
80
+ * @member {module:list/ui/collapsibleview~CollapsibleView|null}
81
+ */
82
+ this.additionalPropertiesCollapsibleView = null;
83
+
84
+ /**
85
+ * A labeled number field allowing the user to set the start index of the list.
86
+ *
87
+ * **Note**: Only present when the view represents **numbered** list properties.
88
+ *
89
+ * @readonly
90
+ * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView|null}
91
+ */
92
+ this.startIndexFieldView = null;
93
+
94
+ /**
95
+ * A switch button allowing the user to make the edited list reversed.
96
+ *
97
+ * **Note**: Only present when the view represents **numbered** list properties.
98
+ *
99
+ * @readonly
100
+ * @member {module:ui/button/switchbuttonview~SwitchButtonView|null}
101
+ */
102
+ this.reversedSwitchButtonView = null;
103
+
104
+ /**
105
+ * Tracks information about the DOM focus in the view.
106
+ *
107
+ * @readonly
108
+ * @member {module:utils/focustracker~FocusTracker}
109
+ */
110
+ this.focusTracker = new FocusTracker();
111
+
112
+ /**
113
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
114
+ *
115
+ * @readonly
116
+ * @member {module:utils/keystrokehandler~KeystrokeHandler}
117
+ */
118
+ this.keystrokes = new KeystrokeHandler();
119
+
120
+ /**
121
+ * A collection of views that can be focused in the properties view.
122
+ *
123
+ * @readonly
124
+ * @member {module:ui/viewcollection~ViewCollection}
125
+ */
126
+ this.focusables = new ViewCollection();
127
+
128
+ /**
129
+ * Helps cycling over {@link #focusables} in the view.
130
+ *
131
+ * @readonly
132
+ * @protected
133
+ * @member {module:ui/focuscycler~FocusCycler}
134
+ */
135
+ this.focusCycler = new FocusCycler( {
136
+ focusables: this.focusables,
137
+ focusTracker: this.focusTracker,
138
+ keystrokeHandler: this.keystrokes,
139
+ actions: {
140
+ // Navigate #children backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
141
+ focusPrevious: 'shift + tab',
142
+
143
+ // Navigate #children forwards using the <kbd>Tab</kbd> key.
144
+ focusNext: 'tab'
145
+ }
146
+ } );
147
+
148
+ // The rendering of the styles grid is conditional. When there is no styles grid, the view will render without collapsible
149
+ // for numbered list properties, hence simplifying the layout.
150
+ if ( enabledProperties.styles ) {
151
+ this.stylesView = this._createStylesView( styleButtonViews, styleGridAriaLabel );
152
+ this.children.add( this.stylesView );
153
+ } else {
154
+ elementCssClasses.push( 'ck-list-properties_without-styles' );
155
+ }
156
+
157
+ // The rendering of the numbered list property views is also conditional. It only makes sense for the numbered list
158
+ // dropdown. The unordered list does not have such properties.
159
+ if ( enabledProperties.startIndex || enabledProperties.reversed ) {
160
+ this._addNumberedListPropertyViews( enabledProperties, styleButtonViews );
161
+
162
+ elementCssClasses.push( 'ck-list-properties_with-numbered-properties' );
163
+ }
164
+
165
+ this.setTemplate( {
166
+ tag: 'div',
167
+ attributes: {
168
+ class: elementCssClasses
169
+ },
170
+ children: this.children
171
+ } );
172
+ }
173
+
174
+ /**
175
+ * @inheritDoc
176
+ */
177
+ render() {
178
+ super.render();
179
+
180
+ if ( this.stylesView ) {
181
+ for ( const styleButtonView of this.stylesView.children ) {
182
+ // Register the view as focusable.
183
+ this.focusables.add( styleButtonView );
184
+
185
+ // Register the view in the focus tracker.
186
+ this.focusTracker.add( styleButtonView.element );
187
+ }
188
+
189
+ // Register the collapsible toggle button to the focus system.
190
+ if ( this.startIndexFieldView || this.reversedSwitchButtonView ) {
191
+ this.focusables.add( this.children.last.buttonView );
192
+ this.focusTracker.add( this.children.last.buttonView.element );
193
+ }
194
+ }
195
+
196
+ if ( this.startIndexFieldView ) {
197
+ this.focusables.add( this.startIndexFieldView );
198
+ this.focusTracker.add( this.startIndexFieldView.element );
199
+
200
+ // Intercept the `selectstart` event, which is blocked by default because of the default behavior
201
+ // of the DropdownView#panelView.
202
+ // TODO: blocking `selectstart` in the #panelView should be configurable per–drop–down instance.
203
+ this.listenTo( this.startIndexFieldView.element, 'selectstart', ( evt, domEvt ) => {
204
+ domEvt.stopPropagation();
205
+ }, { priority: 'high' } );
206
+
207
+ const stopPropagation = data => data.stopPropagation();
208
+
209
+ // Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
210
+ // keystroke handler would take over the key management in the input. We need to prevent
211
+ // this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
212
+ this.keystrokes.set( 'arrowright', stopPropagation );
213
+ this.keystrokes.set( 'arrowleft', stopPropagation );
214
+ this.keystrokes.set( 'arrowup', stopPropagation );
215
+ this.keystrokes.set( 'arrowdown', stopPropagation );
216
+ }
217
+
218
+ if ( this.reversedSwitchButtonView ) {
219
+ this.focusables.add( this.reversedSwitchButtonView );
220
+ this.focusTracker.add( this.reversedSwitchButtonView.element );
221
+ }
222
+
223
+ // Start listening for the keystrokes coming from #element.
224
+ this.keystrokes.listenTo( this.element );
225
+ }
226
+
227
+ /**
228
+ * @inheritDoc
229
+ */
230
+ focus() {
231
+ this.focusCycler.focusFirst();
232
+ }
233
+
234
+ /**
235
+ * @inheritDoc
236
+ */
237
+ focusLast() {
238
+ this.focusCycler.focusLast();
239
+ }
240
+
241
+ /**
242
+ * @inheritDoc
243
+ */
244
+ destroy() {
245
+ super.destroy();
246
+
247
+ this.focusTracker.destroy();
248
+ this.keystrokes.destroy();
249
+ }
250
+
251
+ /**
252
+ * Creates the list styles grid.
253
+ *
254
+ * @protected
255
+ * @param {Array.<module:ui/button/buttonview~ButtonView>} styleButtons Buttons to be placed in the grid.
256
+ * @param {String} styleGridAriaLabel The assistive technology label of the grid.
257
+ * @returns {module:ui/view~View}
258
+ */
259
+ _createStylesView( styleButtons, styleGridAriaLabel ) {
260
+ const stylesView = new View( this.locale );
261
+
262
+ stylesView.children = stylesView.createCollection( this.locale );
263
+ stylesView.children.addMany( styleButtons );
264
+
265
+ stylesView.setTemplate( {
266
+ tag: 'div',
267
+ attributes: {
268
+ 'aria-label': styleGridAriaLabel,
269
+ class: [
270
+ 'ck',
271
+ 'ck-list-styles-list'
272
+ ]
273
+ },
274
+ children: stylesView.children
275
+ } );
276
+
277
+ stylesView.children.delegate( 'execute' ).to( this );
278
+
279
+ return stylesView;
280
+ }
281
+
282
+ /**
283
+ * Renders {@link #startIndexFieldView} and/or {@link #reversedSwitchButtonView} depending on the configuration of the properties view.
284
+ *
285
+ * @private
286
+ * @param {Object.<String,Boolean>} options.enabledProperties An object containing the configuration of enabled list property names
287
+ * (see {@link #constructor}).
288
+ */
289
+ _addNumberedListPropertyViews( enabledProperties ) {
290
+ const t = this.locale.t;
291
+ const numberedPropertyViews = [];
292
+
293
+ if ( enabledProperties.startIndex ) {
294
+ this.startIndexFieldView = this._createStartIndexField();
295
+ numberedPropertyViews.push( this.startIndexFieldView );
296
+ }
297
+
298
+ if ( enabledProperties.reversed ) {
299
+ this.reversedSwitchButtonView = this._createReversedSwitchButton();
300
+ numberedPropertyViews.push( this.reversedSwitchButtonView );
301
+ }
302
+
303
+ // When there are some style buttons, pack the numbered list properties into a collapsible to separate them.
304
+ if ( enabledProperties.styles ) {
305
+ this.additionalPropertiesCollapsibleView = new CollapsibleView( this.locale, numberedPropertyViews );
306
+
307
+ this.additionalPropertiesCollapsibleView.set( {
308
+ label: t( 'List properties' ),
309
+ isCollapsed: true
310
+ } );
311
+
312
+ // Don't enable the collapsible view unless either start index or reversed field is enabled (e.g. when no list is selected).
313
+ this.additionalPropertiesCollapsibleView.buttonView.bind( 'isEnabled' ).toMany(
314
+ numberedPropertyViews, 'isEnabled', ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) );
315
+
316
+ // Automatically collapse the additional properties collapsible when either start index or reversed field gets disabled.
317
+ this.additionalPropertiesCollapsibleView.buttonView.on( 'change:isEnabled', ( evt, data, isEnabled ) => {
318
+ if ( !isEnabled ) {
319
+ this.additionalPropertiesCollapsibleView.isCollapsed = true;
320
+ }
321
+ } );
322
+
323
+ this.children.add( this.additionalPropertiesCollapsibleView );
324
+ } else {
325
+ this.children.addMany( numberedPropertyViews );
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Creates the list start index labeled field.
331
+ *
332
+ * @private
333
+ * @protected
334
+ * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
335
+ */
336
+ _createStartIndexField() {
337
+ const t = this.locale.t;
338
+ const startIndexFieldView = new LabeledFieldView( this.locale, createLabeledInputNumber );
339
+
340
+ startIndexFieldView.set( {
341
+ label: t( 'Start at' ),
342
+ class: 'ck-numbered-list-properties__start-index'
343
+ } );
344
+
345
+ startIndexFieldView.fieldView.set( {
346
+ min: 1,
347
+ step: 1,
348
+ value: 1,
349
+ inputMode: 'numeric'
350
+ } );
351
+
352
+ startIndexFieldView.fieldView.on( 'input', () => {
353
+ const inputElement = startIndexFieldView.fieldView.element;
354
+ const startIndex = inputElement.valueAsNumber;
355
+
356
+ if ( Number.isNaN( startIndex ) ) {
357
+ return;
358
+ }
359
+
360
+ if ( !inputElement.checkValidity() ) {
361
+ startIndexFieldView.errorText = t( 'Start index must be greater than 0.' );
362
+ } else {
363
+ this.fire( 'listStart', { startIndex } );
364
+ }
365
+ } );
366
+
367
+ return startIndexFieldView;
368
+ }
369
+
370
+ /**
371
+ * Creates the reversed list switch button.
372
+ *
373
+ * @private
374
+ * @protected
375
+ * @returns {module:ui/button/switchbuttonview~SwitchButtonView}
376
+ */
377
+ _createReversedSwitchButton() {
378
+ const t = this.locale.t;
379
+ const reversedButtonView = new SwitchButtonView( this.locale );
380
+
381
+ reversedButtonView.set( {
382
+ withText: true,
383
+ label: t( 'Reversed order' ),
384
+ class: 'ck-numbered-list-properties__reversed-order'
385
+ } );
386
+
387
+ reversedButtonView.delegate( 'execute' ).to( this, 'listReversed' );
388
+
389
+ return reversedButtonView;
390
+ }
391
+
392
+ /**
393
+ * Fired when the list start index value has changed via {@link #startIndexFieldView}.
394
+ *
395
+ * @event listStart
396
+ * @param {Object} data
397
+ * @param {Number} data.startIndex The new start index of the list.
398
+ */
399
+
400
+ /**
401
+ * Fired when the list order has changed (reversed) via {@link #reversedSwitchButtonView}.
402
+ *
403
+ * @event listReversed
404
+ */
405
+ }
package/src/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
 
@@ -285,9 +285,9 @@ export function findNestedList( viewElement ) {
285
285
  }
286
286
 
287
287
  /**
288
- * Returns an array with all `listItem` elements that represents the same list.
288
+ * Returns an array with all `listItem` elements that represent the same list.
289
289
  *
290
- * It means that values for `listIndent`, `listType`, and `listStyle` for all items are equal.
290
+ * It means that values of `listIndent`, `listType`, `listStyle`, `listReversed` and `listStart` for all items are equal.
291
291
  *
292
292
  * @param {module:engine/model/position~Position} position Starting position.
293
293
  * @param {'forward'|'backward'} direction Walking direction.
@@ -350,11 +350,21 @@ export function getSiblingNodes( position, direction ) {
350
350
  // ○ List item 3. [listType=bulleted]
351
351
  // ○ List item 4. [listType=bulleted]
352
352
  //
353
- // Abort searching when found a different list style.
353
+ // Abort searching when found a different list style,
354
354
  if ( element.getAttribute( 'listStyle' ) !== listItem.getAttribute( 'listStyle' ) ) {
355
355
  break;
356
356
  }
357
357
 
358
+ // ... different direction
359
+ if ( element.getAttribute( 'listReversed' ) !== listItem.getAttribute( 'listReversed' ) ) {
360
+ break;
361
+ }
362
+
363
+ // ... and different start index
364
+ if ( element.getAttribute( 'listStart' ) !== listItem.getAttribute( 'listStart' ) ) {
365
+ break;
366
+ }
367
+
358
368
  if ( direction === 'backward' ) {
359
369
  items.unshift( element );
360
370
  } else {
@@ -365,6 +375,41 @@ export function getSiblingNodes( position, direction ) {
365
375
  return items;
366
376
  }
367
377
 
378
+ /**
379
+ * Returns an array with all `listItem` elements in the model selection.
380
+ *
381
+ * It returns all the items even if only part of the list is selected, including items that belong to nested lists.
382
+ * If no list is selected, returns an empty array.
383
+ * The order of elements is not specified.
384
+ *
385
+ * @protected
386
+ * @param {module:engine/model/model~Model} model
387
+ * @returns {Array.<module:engine/model/element~Element>}
388
+ */
389
+ export function getSelectedListItems( model ) {
390
+ const document = model.document;
391
+
392
+ // For all selected blocks find all list items that are being selected
393
+ // and update the `listStyle` attribute in those lists.
394
+ let listItems = [ ...document.selection.getSelectedBlocks() ]
395
+ .filter( element => element.is( 'element', 'listItem' ) )
396
+ .map( element => {
397
+ const position = model.change( writer => writer.createPositionAt( element, 0 ) );
398
+
399
+ return [
400
+ ...getSiblingNodes( position, 'backward' ),
401
+ ...getSiblingNodes( position, 'forward' )
402
+ ];
403
+ } )
404
+ .flat();
405
+
406
+ // Since `getSelectedBlocks()` can return items that belong to the same list, and
407
+ // `getSiblingNodes()` returns the entire list, we need to remove duplicated items.
408
+ listItems = [ ...new Set( listItems ) ];
409
+
410
+ return listItems;
411
+ }
412
+
368
413
  // Implementation of getFillerOffset for view list item element.
369
414
  //
370
415
  // @returns {Number|null} Block filler offset or `null` if block filler is not needed.
@@ -0,0 +1,10 @@
1
+ /*
2
+ * 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
+ .ck.ck-collapsible.ck-collapsible_collapsed {
7
+ & > .ck-collapsible__children {
8
+ display: none;
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ * 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
+ * Note: This file should contain the wireframe styles only. But since there are no such styles,
8
+ * it acts as a message to the builder telling that it should look for the corresponding styles
9
+ * **in the theme** when compiling the editor.
10
+ */
@@ -1,12 +1,8 @@
1
1
  /*
2
- * Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * 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
- .ck.ck-list-styles-dropdown > .ck-dropdown__panel > .ck-toolbar > .ck-toolbar__items {
7
- /*
8
- * Use the benefits of the toolbar (e.g. out-of-the-box keyboard navigation) but make it look
9
- * like a panel with thumbnails (previews).
10
- */
6
+ .ck.ck-list-styles-list {
11
7
  display: grid;
12
8
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * 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