@ckeditor/ckeditor5-font 37.1.0 → 38.0.0-rc.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 (131) hide show
  1. package/LICENSE.md +6 -0
  2. package/build/font.js +2 -2
  3. package/build/translations/ar.js +1 -1
  4. package/build/translations/az.js +1 -1
  5. package/build/translations/bg.js +1 -1
  6. package/build/translations/bn.js +1 -1
  7. package/build/translations/bs.js +1 -1
  8. package/build/translations/ca.js +1 -1
  9. package/build/translations/cs.js +1 -1
  10. package/build/translations/da.js +1 -1
  11. package/build/translations/de-ch.js +1 -1
  12. package/build/translations/de.js +1 -1
  13. package/build/translations/el.js +1 -1
  14. package/build/translations/en-au.js +1 -1
  15. package/build/translations/en-gb.js +1 -1
  16. package/build/translations/es.js +1 -1
  17. package/build/translations/et.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/jv.js +1 -1
  30. package/build/translations/ko.js +1 -1
  31. package/build/translations/ku.js +1 -1
  32. package/build/translations/lt.js +1 -1
  33. package/build/translations/lv.js +1 -1
  34. package/build/translations/ms.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/sk.js +1 -1
  45. package/build/translations/sl.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/th.js +1 -1
  51. package/build/translations/tk.js +1 -1
  52. package/build/translations/tr.js +1 -1
  53. package/build/translations/ug.js +1 -1
  54. package/build/translations/uk.js +1 -1
  55. package/build/translations/ur.js +1 -1
  56. package/build/translations/uz.js +1 -1
  57. package/build/translations/vi.js +1 -1
  58. package/build/translations/zh-cn.js +1 -1
  59. package/build/translations/zh.js +1 -1
  60. package/lang/contexts.json +2 -1
  61. package/lang/translations/ar.po +5 -1
  62. package/lang/translations/az.po +5 -1
  63. package/lang/translations/bg.po +5 -1
  64. package/lang/translations/bn.po +5 -1
  65. package/lang/translations/bs.po +5 -1
  66. package/lang/translations/ca.po +5 -1
  67. package/lang/translations/cs.po +5 -1
  68. package/lang/translations/da.po +5 -1
  69. package/lang/translations/de-ch.po +5 -1
  70. package/lang/translations/de.po +5 -1
  71. package/lang/translations/el.po +5 -1
  72. package/lang/translations/en-au.po +5 -1
  73. package/lang/translations/en-gb.po +5 -1
  74. package/lang/translations/en.po +4 -0
  75. package/lang/translations/es.po +5 -1
  76. package/lang/translations/et.po +5 -1
  77. package/lang/translations/fa.po +5 -1
  78. package/lang/translations/fi.po +5 -1
  79. package/lang/translations/fr.po +5 -1
  80. package/lang/translations/gl.po +5 -1
  81. package/lang/translations/he.po +5 -1
  82. package/lang/translations/hi.po +5 -1
  83. package/lang/translations/hr.po +5 -1
  84. package/lang/translations/hu.po +5 -1
  85. package/lang/translations/id.po +5 -1
  86. package/lang/translations/it.po +5 -1
  87. package/lang/translations/ja.po +5 -1
  88. package/lang/translations/jv.po +5 -1
  89. package/lang/translations/ko.po +5 -1
  90. package/lang/translations/ku.po +5 -1
  91. package/lang/translations/lt.po +5 -1
  92. package/lang/translations/lv.po +5 -1
  93. package/lang/translations/ms.po +5 -1
  94. package/lang/translations/nb.po +5 -1
  95. package/lang/translations/ne.po +5 -1
  96. package/lang/translations/nl.po +5 -1
  97. package/lang/translations/no.po +5 -1
  98. package/lang/translations/pl.po +5 -1
  99. package/lang/translations/pt-br.po +5 -1
  100. package/lang/translations/pt.po +5 -1
  101. package/lang/translations/ro.po +5 -1
  102. package/lang/translations/ru.po +5 -1
  103. package/lang/translations/sk.po +5 -1
  104. package/lang/translations/sl.po +5 -1
  105. package/lang/translations/sq.po +5 -1
  106. package/lang/translations/sr-latn.po +5 -1
  107. package/lang/translations/sr.po +5 -1
  108. package/lang/translations/sv.po +5 -1
  109. package/lang/translations/th.po +5 -1
  110. package/lang/translations/tk.po +5 -1
  111. package/lang/translations/tr.po +5 -1
  112. package/lang/translations/ug.po +5 -1
  113. package/lang/translations/uk.po +5 -1
  114. package/lang/translations/ur.po +5 -1
  115. package/lang/translations/uz.po +5 -1
  116. package/lang/translations/vi.po +5 -1
  117. package/lang/translations/zh-cn.po +5 -1
  118. package/lang/translations/zh.po +5 -1
  119. package/package.json +12 -11
  120. package/src/fontcommand.d.ts +2 -0
  121. package/src/fontcommand.js +14 -2
  122. package/src/fontconfig.d.ts +7 -1
  123. package/src/fontfamily/fontfamilyui.js +7 -2
  124. package/src/fontsize/fontsizeui.js +7 -2
  125. package/src/ui/colortableview.d.ts +326 -34
  126. package/src/ui/colortableview.js +432 -47
  127. package/src/ui/colorui.d.ts +5 -1
  128. package/src/ui/colorui.js +40 -8
  129. package/src/utils.d.ts +4 -2
  130. package/src/utils.js +10 -3
  131. package/theme/fontcolor.css +62 -1
@@ -6,16 +6,15 @@
6
6
  * @module font/ui/colortableview
7
7
  */
8
8
  import { icons } from 'ckeditor5/src/core';
9
- import { ButtonView, ColorGridView, ColorTileView, FocusCycler, LabelView, Template, View, ViewCollection } from 'ckeditor5/src/ui';
9
+ import { ButtonView, ColorGridView, ColorTileView, FocusCycler, LabelView, Template, View, ViewCollection, ColorPickerView, ColorPaletteIcon } from 'ckeditor5/src/ui';
10
10
  import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
11
11
  import DocumentColorCollection from '../documentcolorcollection';
12
12
  import '../../theme/fontcolor.css';
13
13
  /**
14
14
  * A class which represents a view with the following sub–components:
15
15
  *
16
- * * A remove color button,
17
- * * A static {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors defined in the configuration,
18
- * * A dynamic {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors used in the document.
16
+ * * A {@link module:font/ui/colortableview~ColorTableView#colorGridsPageView color grids component},
17
+ * * A {@link module:font/ui/colortableview~ColorTableView#colorPickerPageView color picker component}.
19
18
  */
20
19
  export default class ColorTableView extends View {
21
20
  /**
@@ -25,21 +24,18 @@ export default class ColorTableView extends View {
25
24
  * @param colors An array with definitions of colors to be displayed in the table.
26
25
  * @param columns The number of columns in the color grid.
27
26
  * @param removeButtonLabel The label of the button responsible for removing the color.
27
+ * @param colorPickerLabel The label of the button responsible for color picker appearing.
28
28
  * @param documentColorsLabel The label for the section with the document colors.
29
29
  * @param documentColorsCount The number of colors in the document colors section inside the color dropdown.
30
+ * @param colorPickerConfig The configuration of color picker feature.
30
31
  */
31
- constructor(locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount }) {
32
+ constructor(locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount, colorPickerLabel, colorPickerConfig }) {
32
33
  super(locale);
33
34
  this.items = this.createCollection();
34
- this.colorDefinitions = colors;
35
35
  this.focusTracker = new FocusTracker();
36
36
  this.keystrokes = new KeystrokeHandler();
37
- this.set('selectedColor', undefined);
38
- this.removeButtonLabel = removeButtonLabel;
39
- this.columns = columns;
40
- this.documentColors = new DocumentColorCollection();
41
- this.documentColorsCount = documentColorsCount;
42
37
  this._focusables = new ViewCollection();
38
+ this._colorPickerConfig = colorPickerConfig;
43
39
  this._focusCycler = new FocusCycler({
44
40
  focusables: this._focusables,
45
41
  focusTracker: this.focusTracker,
@@ -51,7 +47,36 @@ export default class ColorTableView extends View {
51
47
  focusNext: 'tab'
52
48
  }
53
49
  });
54
- this._documentColorsLabel = documentColorsLabel;
50
+ this.colorGridsPageView = new ColorGridsPageView(locale, {
51
+ colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount, colorPickerLabel,
52
+ focusTracker: this.focusTracker,
53
+ focusables: this._focusables
54
+ });
55
+ this.colorPickerPageView = new ColorPickerPageView(locale, {
56
+ focusables: this._focusables,
57
+ focusTracker: this.focusTracker,
58
+ keystrokes: this.keystrokes,
59
+ colorPickerConfig
60
+ });
61
+ this.set('_isColorGridsPageVisible', true);
62
+ this.set('_isColorPickerPageVisible', false);
63
+ this.set('selectedColor', undefined);
64
+ this.colorGridsPageView.bind('isVisible').to(this, '_isColorGridsPageVisible');
65
+ this.colorPickerPageView.bind('isVisible').to(this, '_isColorPickerPageVisible');
66
+ /**
67
+ * This is kind of bindings. Unfortunately we could not use this.bind() method because the same property
68
+ * can not be binded twice. So this is work around how to bind 'selectedColor' property between components.
69
+ */
70
+ this.on('change:selectedColor', (evt, evtName, data) => {
71
+ this.colorGridsPageView.set('selectedColor', data);
72
+ this.colorPickerPageView.set('selectedColor', data);
73
+ });
74
+ this.colorGridsPageView.on('change:selectedColor', (evt, evtName, data) => {
75
+ this.set('selectedColor', data);
76
+ });
77
+ this.colorPickerPageView.on('change:selectedColor', (evt, evtName, data) => {
78
+ this.set('selectedColor', data);
79
+ });
55
80
  this.setTemplate({
56
81
  tag: 'div',
57
82
  attributes: {
@@ -62,7 +87,160 @@ export default class ColorTableView extends View {
62
87
  },
63
88
  children: this.items
64
89
  });
65
- this.items.add(this._createRemoveColorButton());
90
+ }
91
+ /**
92
+ * @inheritDoc
93
+ */
94
+ render() {
95
+ super.render();
96
+ // Start listening for the keystrokes coming from #element.
97
+ this.keystrokes.listenTo(this.element);
98
+ }
99
+ /**
100
+ * @inheritDoc
101
+ */
102
+ destroy() {
103
+ super.destroy();
104
+ this.focusTracker.destroy();
105
+ this.keystrokes.destroy();
106
+ }
107
+ /**
108
+ * Appends static and document color grid views.
109
+ */
110
+ appendGrids() {
111
+ if (this.items.length) {
112
+ return;
113
+ }
114
+ this.items.add(this.colorGridsPageView);
115
+ this.colorGridsPageView.delegate('execute').to(this);
116
+ this.colorGridsPageView.delegate('showColorPicker').to(this);
117
+ }
118
+ /**
119
+ * Renders UI in dropdown. Which sub-components are rendered
120
+ * depends on the component configuration.
121
+ */
122
+ appendUI() {
123
+ this.appendGrids();
124
+ if (this._colorPickerConfig) {
125
+ this._appendColorPicker();
126
+ }
127
+ }
128
+ /**
129
+ * Show "Color picker" and hide "Color grids".
130
+ */
131
+ showColorPicker() {
132
+ if (!this.colorPickerPageView.colorPickerView) {
133
+ return;
134
+ }
135
+ this.set('_isColorPickerPageVisible', true);
136
+ this.colorPickerPageView.focus();
137
+ this.set('_isColorGridsPageVisible', false);
138
+ }
139
+ /**
140
+ * Show "Color grids" and hide "Color picker".
141
+ */
142
+ showColorGrids() {
143
+ this.set('_isColorGridsPageVisible', true);
144
+ this.set('_isColorPickerPageVisible', false);
145
+ }
146
+ /**
147
+ * Focuses the first focusable element in {@link #items}.
148
+ */
149
+ focus() {
150
+ this._focusCycler.focusFirst();
151
+ }
152
+ /**
153
+ * Focuses the last focusable element in {@link #items}.
154
+ */
155
+ focusLast() {
156
+ this._focusCycler.focusLast();
157
+ }
158
+ /**
159
+ * Scans through the editor model and searches for text node attributes with the given attribute name.
160
+ * Found entries are set as document colors.
161
+ *
162
+ * All the previously stored document colors will be lost in the process.
163
+ *
164
+ * @param model The model used as a source to obtain the document colors.
165
+ * @param attributeName Determines the name of the related model's attribute for a given dropdown.
166
+ */
167
+ updateDocumentColors(model, attributeName) {
168
+ this.colorGridsPageView.updateDocumentColors(model, attributeName);
169
+ }
170
+ /**
171
+ * Refreshes the state of the selected color in one or both {@link module:ui/colorgrid/colorgridview~ColorGridView}s
172
+ * available in the {@link module:font/ui/colortableview~ColorTableView}. It guarantees that the selection will occur only in one
173
+ * of them.
174
+ */
175
+ updateSelectedColors() {
176
+ this.colorGridsPageView.updateSelectedColors();
177
+ }
178
+ /**
179
+ * Appends the color picker view.
180
+ */
181
+ _appendColorPicker() {
182
+ if (this.items.length === 2) {
183
+ return;
184
+ }
185
+ this.items.add(this.colorPickerPageView);
186
+ if (this.colorGridsPageView.colorPickerButtonView) {
187
+ this.colorGridsPageView.colorPickerButtonView.on('execute', () => {
188
+ this.showColorPicker();
189
+ });
190
+ }
191
+ this.colorGridsPageView.addColorPickerButton();
192
+ this.colorPickerPageView.delegate('execute').to(this);
193
+ this.colorPickerPageView.delegate('cancel').to(this);
194
+ }
195
+ }
196
+ /**
197
+ * A class which represents a view with the following sub–components:
198
+ *
199
+ * * A remove color button,
200
+ * * A static {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors defined in the configuration,
201
+ * * A dynamic {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors used in the document.
202
+ * * If color picker is configured, the "Color Picker" button is visible too.
203
+ */
204
+ class ColorGridsPageView extends View {
205
+ /**
206
+ * Creates a view to be inserted as a child of {@link module:ui/dropdown/dropdownview~DropdownView}.
207
+ *
208
+ * @param locale The localization services instance.
209
+ * @param colors An array with definitions of colors to be displayed in the table.
210
+ * @param columns The number of columns in the color grid.
211
+ * @param removeButtonLabel The label of the button responsible for removing the color.
212
+ * @param colorPickerLabel The label of the button responsible for color picker appearing.
213
+ * @param documentColorsLabel The label for the section with the document colors.
214
+ * @param documentColorsCount The number of colors in the document colors section inside the color dropdown.
215
+ * @param focusTracker Tracks information about the DOM focus in the list.
216
+ * @param focusables A collection of views that can be focused in the view.
217
+ */
218
+ constructor(locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount, colorPickerLabel, focusTracker, focusables }) {
219
+ super(locale);
220
+ const bind = this.bindTemplate;
221
+ this.set('isVisible', true);
222
+ this.focusTracker = focusTracker;
223
+ this.items = this.createCollection();
224
+ this.colorDefinitions = colors;
225
+ this.columns = columns;
226
+ this.documentColors = new DocumentColorCollection();
227
+ this.documentColorsCount = documentColorsCount;
228
+ this._focusables = focusables;
229
+ this._removeButtonLabel = removeButtonLabel;
230
+ this._colorPickerLabel = colorPickerLabel;
231
+ this._documentColorsLabel = documentColorsLabel;
232
+ this.setTemplate({
233
+ tag: 'div',
234
+ attributes: {
235
+ class: [
236
+ 'ck-color-grids-page-view',
237
+ bind.if('isVisible', 'ck-hidden', value => !value)
238
+ ]
239
+ },
240
+ children: this.items
241
+ });
242
+ this.removeColorButtonView = this._createRemoveColorButton();
243
+ this.items.add(this.removeColorButtonView);
66
244
  }
67
245
  /**
68
246
  * Scans through the editor model and searches for text node attributes with the given attribute name.
@@ -109,28 +287,8 @@ export default class ColorTableView extends View {
109
287
  */
110
288
  render() {
111
289
  super.render();
112
- // Start listening for the keystrokes coming from #element.
113
- this.keystrokes.listenTo(this.element);
114
- }
115
- /**
116
- * @inheritDoc
117
- */
118
- destroy() {
119
- super.destroy();
120
- this.focusTracker.destroy();
121
- this.keystrokes.destroy();
122
- }
123
- /**
124
- * Appends {@link #staticColorsGrid} and {@link #documentColorsGrid} views.
125
- */
126
- appendGrids() {
127
- if (this.staticColorsGrid) {
128
- return;
129
- }
130
290
  this.staticColorsGrid = this._createStaticColorsGrid();
131
291
  this.items.add(this.staticColorsGrid);
132
- this.focusTracker.add(this.staticColorsGrid.element);
133
- this._focusables.add(this.staticColorsGrid);
134
292
  if (this.documentColorsCount) {
135
293
  // Create a label for document colors.
136
294
  const bind = Template.bind(this.documentColors, this.documentColors);
@@ -148,21 +306,62 @@ export default class ColorTableView extends View {
148
306
  this.items.add(label);
149
307
  this.documentColorsGrid = this._createDocumentColorsGrid();
150
308
  this.items.add(this.documentColorsGrid);
151
- this.focusTracker.add(this.documentColorsGrid.element);
152
- this._focusables.add(this.documentColorsGrid);
153
309
  }
310
+ this._createColorPickerButton();
311
+ this._addColorTablesElementsToFocusTracker();
312
+ this.focus();
154
313
  }
155
314
  /**
156
- * Focuses the first focusable element in {@link #items}.
315
+ * Focuses the component.
157
316
  */
158
317
  focus() {
159
- this._focusCycler.focusFirst();
318
+ this.removeColorButtonView.focus();
160
319
  }
161
320
  /**
162
- * Focuses the last focusable element in {@link #items}.
321
+ * @inheritDoc
163
322
  */
164
- focusLast() {
165
- this._focusCycler.focusLast();
323
+ destroy() {
324
+ super.destroy();
325
+ }
326
+ /**
327
+ * Handles displaying the color picker button (if it was previously created) and making it focusable.
328
+ */
329
+ addColorPickerButton() {
330
+ if (this.colorPickerButtonView) {
331
+ this.items.add(this.colorPickerButtonView);
332
+ this.focusTracker.add(this.colorPickerButtonView.element);
333
+ this._focusables.add(this.colorPickerButtonView);
334
+ }
335
+ }
336
+ /**
337
+ * Adds color table elements to focus tracker.
338
+ */
339
+ _addColorTablesElementsToFocusTracker() {
340
+ this.focusTracker.add(this.removeColorButtonView.element);
341
+ this._focusables.add(this.removeColorButtonView);
342
+ if (this.staticColorsGrid) {
343
+ this.focusTracker.add(this.staticColorsGrid.element);
344
+ this._focusables.add(this.staticColorsGrid);
345
+ }
346
+ if (this.documentColorsGrid) {
347
+ this.focusTracker.add(this.documentColorsGrid.element);
348
+ this._focusables.add(this.documentColorsGrid);
349
+ }
350
+ }
351
+ /**
352
+ * Creates the button responsible for displaying the color picker component.
353
+ */
354
+ _createColorPickerButton() {
355
+ this.colorPickerButtonView = new ButtonView();
356
+ this.colorPickerButtonView.set({
357
+ label: this._colorPickerLabel,
358
+ withText: true,
359
+ icon: ColorPaletteIcon,
360
+ class: 'ck-color-table__color-picker'
361
+ });
362
+ this.colorPickerButtonView.on('execute', () => {
363
+ this.fire('showColorPicker');
364
+ });
166
365
  }
167
366
  /**
168
367
  * Adds the remove color button as a child of the current view.
@@ -172,15 +371,16 @@ export default class ColorTableView extends View {
172
371
  buttonView.set({
173
372
  withText: true,
174
373
  icon: icons.eraser,
175
- label: this.removeButtonLabel
374
+ label: this._removeButtonLabel
176
375
  });
177
376
  buttonView.class = 'ck-color-table__remove-color';
178
377
  buttonView.on('execute', () => {
179
- this.fire('execute', { value: null });
378
+ this.fire('execute', {
379
+ value: null,
380
+ source: 'removeColorButton'
381
+ });
180
382
  });
181
383
  buttonView.render();
182
- this.focusTracker.add(buttonView.element);
183
- this._focusables.add(buttonView);
184
384
  return buttonView;
185
385
  }
186
386
  /**
@@ -191,7 +391,12 @@ export default class ColorTableView extends View {
191
391
  colorDefinitions: this.colorDefinitions,
192
392
  columns: this.columns
193
393
  });
194
- colorGrid.delegate('execute').to(this);
394
+ colorGrid.on('execute', (evt, data) => {
395
+ this.fire('execute', {
396
+ value: data.value,
397
+ source: 'staticColorsGrid'
398
+ });
399
+ });
195
400
  return colorGrid;
196
401
  }
197
402
  /**
@@ -202,7 +407,6 @@ export default class ColorTableView extends View {
202
407
  const documentColorsGrid = new ColorGridView(this.locale, {
203
408
  columns: this.columns
204
409
  });
205
- documentColorsGrid.delegate('execute').to(this);
206
410
  documentColorsGrid.extendTemplate({
207
411
  attributes: {
208
412
  class: bind.if('isEmpty', 'ck-hidden')
@@ -222,7 +426,8 @@ export default class ColorTableView extends View {
222
426
  }
223
427
  colorTile.on('execute', () => {
224
428
  this.fire('execute', {
225
- value: colorObj.color
429
+ value: colorObj.color,
430
+ source: 'documentColorsGrid'
226
431
  });
227
432
  });
228
433
  return colorTile;
@@ -258,3 +463,183 @@ export default class ColorTableView extends View {
258
463
  }
259
464
  }
260
465
  }
466
+ /**
467
+ * A class which represents a color picker component view with the following sub–components:
468
+ *
469
+ * * Color picker saturation and hue sliders,
470
+ * * Input accepting colors in HEX format,
471
+ * * "Save" and "Cancel" action buttons.
472
+ */
473
+ class ColorPickerPageView extends View {
474
+ /**
475
+ * @param locale The localization services instance.
476
+ * @param focusTracker Tracks information about the DOM focus in the list.
477
+ * @param focusables A collection of views that can be focused in the view..
478
+ * @param keystrokes An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
479
+ * @param colorPickerConfig The configuration of color picker feature.
480
+ */
481
+ constructor(locale, { focusTracker, focusables, keystrokes, colorPickerConfig }) {
482
+ super(locale);
483
+ this.items = this.createCollection();
484
+ this.focusTracker = focusTracker;
485
+ this.keystrokes = keystrokes;
486
+ this.set('isVisible', false);
487
+ this.set('selectedColor', undefined);
488
+ this._focusables = focusables;
489
+ this._pickerConfig = colorPickerConfig;
490
+ const bind = this.bindTemplate;
491
+ const { saveButtonView, cancelButtonView } = this._createActionButtons();
492
+ this.saveButtonView = saveButtonView;
493
+ this.cancelButtonView = cancelButtonView;
494
+ this.actionBarView = this._createActionBarView({ saveButtonView, cancelButtonView });
495
+ this.setTemplate({
496
+ tag: 'div',
497
+ attributes: {
498
+ class: [
499
+ 'ck-color-picker-page-view',
500
+ bind.if('isVisible', 'ck-hidden', value => !value)
501
+ ]
502
+ },
503
+ children: this.items
504
+ });
505
+ }
506
+ /**
507
+ * @inheritDoc
508
+ */
509
+ render() {
510
+ super.render();
511
+ const colorPickerView = new ColorPickerView(this.locale, this._pickerConfig);
512
+ this.colorPickerView = colorPickerView;
513
+ this.colorPickerView.render();
514
+ if (this.selectedColor) {
515
+ colorPickerView.color = this.selectedColor;
516
+ }
517
+ this.listenTo(this, 'change:selectedColor', (evt, name, value) => {
518
+ colorPickerView.color = value;
519
+ });
520
+ this.items.add(this.colorPickerView);
521
+ this.items.add(this.actionBarView);
522
+ this._addColorPickersElementsToFocusTracker();
523
+ this._stopPropagationOnArrowsKeys();
524
+ this._executeOnEnterPress();
525
+ this._executeUponColorChange();
526
+ }
527
+ /**
528
+ * @inheritDoc
529
+ */
530
+ destroy() {
531
+ super.destroy();
532
+ }
533
+ /**
534
+ * Focuses the color picker.
535
+ */
536
+ focus() {
537
+ this.colorPickerView.focus();
538
+ }
539
+ /**
540
+ * When color picker is focused and "enter" is pressed it executes command.
541
+ */
542
+ _executeOnEnterPress() {
543
+ this.keystrokes.set('enter', evt => {
544
+ if (this.isVisible && this.focusTracker.focusedElement !== this.cancelButtonView.element) {
545
+ this.fire('execute', {
546
+ value: this.selectedColor
547
+ });
548
+ evt.stopPropagation();
549
+ evt.preventDefault();
550
+ }
551
+ });
552
+ }
553
+ /**
554
+ * Removes default behavior of arrow keys in dropdown.
555
+ */
556
+ _stopPropagationOnArrowsKeys() {
557
+ const stopPropagation = (data) => data.stopPropagation();
558
+ this.keystrokes.set('arrowright', stopPropagation);
559
+ this.keystrokes.set('arrowleft', stopPropagation);
560
+ this.keystrokes.set('arrowup', stopPropagation);
561
+ this.keystrokes.set('arrowdown', stopPropagation);
562
+ }
563
+ /**
564
+ * Adds color picker elements to focus tracker.
565
+ */
566
+ _addColorPickersElementsToFocusTracker() {
567
+ for (const slider of this.colorPickerView.slidersView) {
568
+ this.focusTracker.add(slider.element);
569
+ this._focusables.add(slider);
570
+ }
571
+ this.focusTracker.add(this.colorPickerView.hexInputRow.children.get(1).element);
572
+ this._focusables.add(this.colorPickerView.hexInputRow.children.get(1));
573
+ this.focusTracker.add(this.saveButtonView.element);
574
+ this._focusables.add(this.saveButtonView);
575
+ this.focusTracker.add(this.cancelButtonView.element);
576
+ this._focusables.add(this.cancelButtonView);
577
+ }
578
+ /**
579
+ * Creates bar containing "Save" and "Cancel" buttons.
580
+ */
581
+ _createActionBarView({ saveButtonView, cancelButtonView }) {
582
+ const actionBarRow = new View();
583
+ const children = this.createCollection();
584
+ children.add(saveButtonView);
585
+ children.add(cancelButtonView);
586
+ actionBarRow.setTemplate({
587
+ tag: 'div',
588
+ attributes: {
589
+ class: [
590
+ 'ck',
591
+ 'ck-color-table_action-bar'
592
+ ]
593
+ },
594
+ children
595
+ });
596
+ return actionBarRow;
597
+ }
598
+ /**
599
+ * Creates "Save" and "Cancel" buttons.
600
+ */
601
+ _createActionButtons() {
602
+ const locale = this.locale;
603
+ const t = locale.t;
604
+ const saveButtonView = new ButtonView(locale);
605
+ const cancelButtonView = new ButtonView(locale);
606
+ saveButtonView.set({
607
+ icon: icons.check,
608
+ class: 'ck-button-save',
609
+ withText: false,
610
+ label: t('Accept'),
611
+ type: 'submit'
612
+ });
613
+ cancelButtonView.set({
614
+ icon: icons.cancel,
615
+ class: 'ck-button-cancel',
616
+ withText: false,
617
+ label: t('Cancel')
618
+ });
619
+ saveButtonView.on('execute', () => {
620
+ this.fire('execute', {
621
+ source: 'saveButton',
622
+ value: this.selectedColor
623
+ });
624
+ });
625
+ cancelButtonView.on('execute', () => {
626
+ this.fire('cancel');
627
+ });
628
+ return {
629
+ saveButtonView, cancelButtonView
630
+ };
631
+ }
632
+ /**
633
+ * Fires the `execute` event if color in color picker changed.
634
+ *
635
+ * @fires execute
636
+ */
637
+ _executeUponColorChange() {
638
+ this.colorPickerView.on('change:color', (evt, evtName, newValue) => {
639
+ this.fire('execute', {
640
+ value: newValue,
641
+ source: 'colorPicker'
642
+ });
643
+ });
644
+ }
645
+ }
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Plugin, type Editor } from 'ckeditor5/src/core';
9
9
  import { type FONT_BACKGROUND_COLOR, type FONT_COLOR } from '../utils';
10
- import type ColorTableView from './colortableview';
10
+ import { type default as ColorTableView } from './colortableview';
11
11
  /**
12
12
  * The color UI plugin which isolates the common logic responsible for displaying dropdowns with color grids.
13
13
  *
@@ -40,6 +40,10 @@ export default class ColorUI extends Plugin {
40
40
  * Keeps a reference to {@link module:font/ui/colortableview~ColorTableView}.
41
41
  */
42
42
  colorTableView: ColorTableView | undefined;
43
+ /**
44
+ * Keeps all changes in color picker in one batch while dropdown is open.
45
+ */
46
+ private _undoStepBatch;
43
47
  /**
44
48
  * Creates a plugin which introduces a dropdown with a pre–configured {@link module:font/ui/colortableview~ColorTableView}.
45
49
  *
package/src/ui/colorui.js CHANGED
@@ -42,12 +42,16 @@ export default class ColorUI extends Plugin {
42
42
  const locale = editor.locale;
43
43
  const t = locale.t;
44
44
  const command = editor.commands.get(this.commandName);
45
- const colorsConfig = normalizeColorOptions((editor.config.get(this.componentName)).colors);
45
+ const componentConfig = editor.config.get(this.componentName);
46
+ const colorsConfig = normalizeColorOptions(componentConfig.colors);
46
47
  const localizedColors = getLocalizedColorOptions(locale, colorsConfig);
47
- const documentColorsCount = editor.config.get(`${this.componentName}.documentColors`);
48
+ const documentColorsCount = componentConfig.documentColors;
49
+ const hasColorPicker = componentConfig.colorPicker !== false;
48
50
  // Register the UI component.
49
51
  editor.ui.componentFactory.add(this.componentName, locale => {
50
52
  const dropdownView = createDropdown(locale);
53
+ // Font color dropdown rendering is deferred once it gets open to improve performance (#6192).
54
+ let dropdownContentRendered = false;
51
55
  this.colorTableView = addColorTableToDropdown({
52
56
  dropdownView,
53
57
  colors: localizedColors.map(option => ({
@@ -59,8 +63,10 @@ export default class ColorUI extends Plugin {
59
63
  })),
60
64
  columns: this.columns,
61
65
  removeButtonLabel: t('Remove color'),
66
+ colorPickerLabel: t('Color picker'),
62
67
  documentColorsLabel: documentColorsCount !== 0 ? t('Document colors') : '',
63
- documentColorsCount: documentColorsCount === undefined ? this.columns : documentColorsCount
68
+ documentColorsCount: documentColorsCount === undefined ? this.columns : documentColorsCount,
69
+ colorPickerConfig: hasColorPicker ? (componentConfig.colorPicker || {}) : false
64
70
  });
65
71
  this.colorTableView.bind('selectedColor').to(command, 'value');
66
72
  dropdownView.buttonView.set({
@@ -74,22 +80,48 @@ export default class ColorUI extends Plugin {
74
80
  }
75
81
  });
76
82
  dropdownView.bind('isEnabled').to(command);
77
- dropdownView.on('execute', (evt, data) => {
78
- editor.execute(this.commandName, data);
83
+ this.colorTableView.on('execute', (evt, data) => {
84
+ if (dropdownView.isOpen) {
85
+ editor.execute(this.commandName, {
86
+ value: data.value,
87
+ batch: this._undoStepBatch
88
+ });
89
+ }
90
+ if (data.source !== 'colorPicker') {
91
+ editor.editing.view.focus();
92
+ }
93
+ });
94
+ this.colorTableView.on('showColorPicker', () => {
95
+ this._undoStepBatch = editor.model.createBatch();
96
+ });
97
+ this.colorTableView.on('cancel', () => {
98
+ if (this._undoStepBatch.operations.length) {
99
+ // We need to close the dropdown before the undo batch.
100
+ // Otherwise, ColorUI treats undo as a selected color change,
101
+ // propagating the update to the whole selection.
102
+ // That's an issue if spans with various colors were selected.
103
+ dropdownView.isOpen = false;
104
+ editor.execute('undo', this._undoStepBatch);
105
+ }
79
106
  editor.editing.view.focus();
80
107
  });
81
108
  dropdownView.on('change:isOpen', (evt, name, isVisible) => {
82
- // Grids rendering is deferred (#6192).
83
- dropdownView.colorTableView.appendGrids();
109
+ if (!dropdownContentRendered) {
110
+ dropdownContentRendered = true;
111
+ dropdownView.colorTableView.appendUI();
112
+ }
84
113
  if (isVisible) {
85
114
  if (documentColorsCount !== 0) {
86
115
  this.colorTableView.updateDocumentColors(editor.model, this.componentName);
87
116
  }
88
117
  this.colorTableView.updateSelectedColors();
89
118
  }
119
+ else {
120
+ this.colorTableView.showColorGrids();
121
+ }
90
122
  });
91
123
  // Accessibility: focus the first active color when opening the dropdown.
92
- focusChildOnDropdownOpen(dropdownView, () => dropdownView.colorTableView.staticColorsGrid.items.find((item) => item.isOn));
124
+ focusChildOnDropdownOpen(dropdownView, () => dropdownView.colorTableView.colorGridsPageView.staticColorsGrid.items.find((item) => item.isOn));
93
125
  return dropdownView;
94
126
  });
95
127
  }