@ckeditor/ckeditor5-ui 35.0.1 → 35.2.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 (88) hide show
  1. package/lang/contexts.json +3 -1
  2. package/lang/translations/ar.po +8 -0
  3. package/lang/translations/ast.po +8 -0
  4. package/lang/translations/az.po +8 -0
  5. package/lang/translations/bg.po +8 -0
  6. package/lang/translations/bn.po +8 -0
  7. package/lang/translations/ca.po +8 -0
  8. package/lang/translations/cs.po +8 -0
  9. package/lang/translations/da.po +8 -0
  10. package/lang/translations/de-ch.po +8 -0
  11. package/lang/translations/de.po +8 -0
  12. package/lang/translations/el.po +8 -0
  13. package/lang/translations/en-au.po +9 -1
  14. package/lang/translations/en-gb.po +8 -0
  15. package/lang/translations/en.po +8 -0
  16. package/lang/translations/eo.po +8 -0
  17. package/lang/translations/es.po +8 -0
  18. package/lang/translations/et.po +8 -0
  19. package/lang/translations/eu.po +8 -0
  20. package/lang/translations/fa.po +8 -0
  21. package/lang/translations/fi.po +8 -0
  22. package/lang/translations/fr.po +8 -0
  23. package/lang/translations/gl.po +9 -1
  24. package/lang/translations/he.po +8 -0
  25. package/lang/translations/hi.po +8 -0
  26. package/lang/translations/hr.po +8 -0
  27. package/lang/translations/hu.po +8 -0
  28. package/lang/translations/id.po +8 -0
  29. package/lang/translations/it.po +8 -0
  30. package/lang/translations/ja.po +8 -0
  31. package/lang/translations/km.po +8 -0
  32. package/lang/translations/kn.po +8 -0
  33. package/lang/translations/ko.po +11 -3
  34. package/lang/translations/ku.po +8 -0
  35. package/lang/translations/lt.po +8 -0
  36. package/lang/translations/lv.po +8 -0
  37. package/lang/translations/ms.po +8 -0
  38. package/lang/translations/nb.po +8 -0
  39. package/lang/translations/ne.po +8 -0
  40. package/lang/translations/nl.po +8 -0
  41. package/lang/translations/no.po +8 -0
  42. package/lang/translations/pl.po +8 -0
  43. package/lang/translations/pt-br.po +8 -0
  44. package/lang/translations/pt.po +8 -0
  45. package/lang/translations/ro.po +8 -0
  46. package/lang/translations/ru.po +8 -0
  47. package/lang/translations/sk.po +8 -0
  48. package/lang/translations/sl.po +8 -0
  49. package/lang/translations/sq.po +8 -0
  50. package/lang/translations/sr-latn.po +9 -1
  51. package/lang/translations/sr.po +9 -1
  52. package/lang/translations/sv.po +8 -0
  53. package/lang/translations/th.po +8 -0
  54. package/lang/translations/tk.po +8 -0
  55. package/lang/translations/tr.po +8 -0
  56. package/lang/translations/tt.po +113 -0
  57. package/lang/translations/ug.po +8 -0
  58. package/lang/translations/uk.po +8 -0
  59. package/lang/translations/ur.po +8 -0
  60. package/lang/translations/uz.po +8 -0
  61. package/lang/translations/vi.po +8 -0
  62. package/lang/translations/zh-cn.po +8 -0
  63. package/lang/translations/zh.po +8 -0
  64. package/package.json +21 -19
  65. package/src/bindings/addkeyboardhandlingforgrid.js +76 -0
  66. package/src/button/button.jsdoc +2 -2
  67. package/src/button/buttonview.js +5 -29
  68. package/src/colorgrid/colorgridview.js +19 -25
  69. package/src/dropdown/button/dropdownbuttonview.js +2 -1
  70. package/src/dropdown/button/splitbuttonview.js +4 -1
  71. package/src/dropdown/dropdownview.js +16 -9
  72. package/src/dropdown/utils.js +58 -4
  73. package/src/icon/iconview.js +3 -1
  74. package/src/index.js +2 -2
  75. package/src/list/listitemview.js +14 -1
  76. package/src/panel/balloon/balloonpanelview.js +128 -74
  77. package/src/toolbar/balloon/balloontoolbar.js +17 -5
  78. package/src/toolbar/block/blocktoolbar.js +36 -0
  79. package/src/toolbar/toolbarview.js +132 -21
  80. package/src/tooltipmanager.js +430 -0
  81. package/theme/components/button/button.css +0 -11
  82. package/theme/components/dropdown/dropdown.css +0 -12
  83. package/theme/components/dropdown/splitbutton.css +0 -7
  84. package/theme/components/responsive-form/responsiveform.css +28 -16
  85. package/theme/components/tooltip/tooltip.css +3 -26
  86. package/src/toolbar/enabletoolbarkeyboardfocus.js +0 -64
  87. package/src/tooltip/tooltipview.js +0 -107
  88. package/theme/components/tooltip/mixins/_tooltip.css +0 -54
@@ -79,6 +79,13 @@ export default class BalloonToolbar extends Plugin {
79
79
  this.focusTracker.add( this.toolbarView.element );
80
80
  } );
81
81
 
82
+ // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
83
+ editor.ui.addToolbar( this.toolbarView, {
84
+ beforeFocus: () => this.show( true ),
85
+ afterBlur: () => this.hide(),
86
+ isContextual: true
87
+ } );
88
+
82
89
  /**
83
90
  * An instance of the resize observer that allows to respond to changes in editable's geometry
84
91
  * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).
@@ -196,12 +203,14 @@ export default class BalloonToolbar extends Plugin {
196
203
  * @returns {module:ui/toolbar/toolbarview~ToolbarView}
197
204
  */
198
205
  _createToolbarView() {
206
+ const t = this.editor.locale.t;
199
207
  const shouldGroupWhenFull = !this._balloonConfig.shouldNotGroupWhenFull;
200
208
  const toolbarView = new ToolbarView( this.editor.locale, {
201
209
  shouldGroupWhenFull,
202
210
  isFloating: true
203
211
  } );
204
212
 
213
+ toolbarView.ariaLabel = t( 'Editor contextual toolbar' );
205
214
  toolbarView.render();
206
215
 
207
216
  return toolbarView;
@@ -211,8 +220,11 @@ export default class BalloonToolbar extends Plugin {
211
220
  * Shows the toolbar and attaches it to the selection.
212
221
  *
213
222
  * Fires {@link #event:show} event which can be stopped to prevent the toolbar from showing up.
223
+ *
224
+ * @param {Boolean} [showForCollapsedSelection=false] When set `true`, the toolbar will show despite collapsed selection in the
225
+ * editing view.
214
226
  */
215
- show() {
227
+ show( showForCollapsedSelection = false ) {
216
228
  const editor = this.editor;
217
229
  const selection = editor.model.document.selection;
218
230
  const schema = editor.model.schema;
@@ -223,7 +235,7 @@ export default class BalloonToolbar extends Plugin {
223
235
  }
224
236
 
225
237
  // Do not show the toolbar when the selection is collapsed.
226
- if ( selection.isCollapsed ) {
238
+ if ( selection.isCollapsed && !showForCollapsedSelection ) {
227
239
  return;
228
240
  }
229
241
 
@@ -361,8 +373,8 @@ export default class BalloonToolbar extends Plugin {
361
373
  const positions = isSafariIniOS ? generatePositions( {
362
374
  // 20px when zoomed out. Less then 20px when zoomed in; the "radius" of the native selection handle gets
363
375
  // smaller as the user zooms in. No less than the default v-offset, though.
364
- verticalOffset: Math.max(
365
- BalloonPanelView.arrowVerticalOffset,
376
+ heightOffset: Math.max(
377
+ BalloonPanelView.arrowHeightOffset,
366
378
  Math.round( 20 / global.window.visualViewport.scale )
367
379
  )
368
380
  } ) : BalloonPanelView.defaultPositions;
@@ -426,7 +438,7 @@ function selectionContainsOnlyMultipleSelectables( selection, schema ) {
426
438
  * You can also use `'|'` to create a separator between groups of items:
427
439
  *
428
440
  * const config = {
429
- * balloonToolbar: [ 'bold', 'italic', | 'undo', 'redo' ]
441
+ * balloonToolbar: [ 'bold', 'italic', '|', 'undo', 'redo' ]
430
442
  * };
431
443
  *
432
444
  * Read also about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
@@ -25,6 +25,7 @@ import normalizeToolbarConfig from '../normalizetoolbarconfig';
25
25
  import ResizeObserver from '@ckeditor/ckeditor5-utils/src/dom/resizeobserver';
26
26
 
27
27
  import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
28
+ import env from '@ckeditor/ckeditor5-utils/src/env';
28
29
 
29
30
  const toPx = toUnit( 'px' );
30
31
 
@@ -166,6 +167,12 @@ export default class BlockToolbar extends Plugin {
166
167
  this._hidePanel();
167
168
  }
168
169
  } );
170
+
171
+ // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
172
+ editor.ui.addToolbar( this.toolbarView, {
173
+ beforeFocus: () => this._showPanel(),
174
+ afterBlur: () => this._hidePanel()
175
+ } );
169
176
  }
170
177
 
171
178
  /**
@@ -221,12 +228,15 @@ export default class BlockToolbar extends Plugin {
221
228
  * @returns {module:ui/toolbar/toolbarview~ToolbarView}
222
229
  */
223
230
  _createToolbarView() {
231
+ const t = this.editor.locale.t;
224
232
  const shouldGroupWhenFull = !this._blockToolbarConfig.shouldNotGroupWhenFull;
225
233
  const toolbarView = new ToolbarView( this.editor.locale, {
226
234
  shouldGroupWhenFull,
227
235
  isFloating: true
228
236
  } );
229
237
 
238
+ toolbarView.ariaLabel = t( 'Editor block content toolbar' );
239
+
230
240
  // When toolbar lost focus then panel should hide.
231
241
  toolbarView.focusTracker.on( 'change:isFocused', ( evt, name, is ) => {
232
242
  if ( !is ) {
@@ -271,6 +281,7 @@ export default class BlockToolbar extends Plugin {
271
281
  const editor = this.editor;
272
282
  const t = editor.t;
273
283
  const buttonView = new BlockButtonView( editor.locale );
284
+ const bind = buttonView.bindTemplate;
274
285
 
275
286
  buttonView.set( {
276
287
  label: t( 'Edit block' ),
@@ -278,6 +289,23 @@ export default class BlockToolbar extends Plugin {
278
289
  withText: false
279
290
  } );
280
291
 
292
+ // Note that this piece over here overrides the default mousedown logic in ButtonView
293
+ // to make it work with BlockToolbar. See the implementation of the ButtonView class to learn more.
294
+ buttonView.extendTemplate( {
295
+ on: {
296
+ mousedown: bind.to( evt => {
297
+ // On Safari we have to force the focus on a button on click as it's the only browser
298
+ // that doesn't do that automatically. See #12115.
299
+ if ( env.isSafari && this.panelView.isVisible ) {
300
+ this.toolbarView.focus();
301
+ }
302
+
303
+ // Workaround to #12184, see https://github.com/ckeditor/ckeditor5/issues/12184#issuecomment-1199147964.
304
+ evt.preventDefault();
305
+ } )
306
+ }
307
+ } );
308
+
281
309
  // Bind the panelView observable properties to the buttonView.
282
310
  buttonView.bind( 'isOn' ).to( this.panelView, 'isVisible' );
283
311
  buttonView.bind( 'tooltip' ).to( this.panelView, 'isVisible', isVisible => !isVisible );
@@ -363,6 +391,14 @@ export default class BlockToolbar extends Plugin {
363
391
  * @private
364
392
  */
365
393
  _showPanel() {
394
+ // Usually, the only way to show the toolbar is by pressing the block button. It makes it impossible for
395
+ // the toolbar to show up when the button is invisible (feature does not make sense for the selection then).
396
+ // The toolbar navigation using Alt+F10 does not access the button but shows the panel directly using this method.
397
+ // So we need to check whether this is possible first.
398
+ if ( !this.buttonView.isVisible ) {
399
+ return;
400
+ }
401
+
366
402
  const wasVisible = this.panelView.isVisible;
367
403
 
368
404
  // So here's the thing: If there was no initial panelView#show() or these two were in different order, the toolbar
@@ -21,10 +21,24 @@ import global from '@ckeditor/ckeditor5-utils/src/dom/global';
21
21
  import { createDropdown, addToolbarToDropdown } from '../dropdown/utils';
22
22
  import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
23
23
  import normalizeToolbarConfig from './normalizetoolbarconfig';
24
+ import { isObject } from 'lodash-es';
25
+
24
26
  import threeVerticalDots from '@ckeditor/ckeditor5-core/theme/icons/three-vertical-dots.svg';
25
27
 
26
28
  import '../../theme/components/toolbar/toolbar.css';
27
29
 
30
+ import { icons } from '@ckeditor/ckeditor5-core';
31
+
32
+ const NESTED_TOOLBAR_ICONS = {
33
+ alignLeft: icons.alignLeft,
34
+ bold: icons.bold,
35
+ importExport: icons.importExport,
36
+ paragraph: icons.paragraph,
37
+ plus: icons.plus,
38
+ text: icons.text,
39
+ threeVerticalDots: icons.threeVerticalDots
40
+ };
41
+
28
42
  /**
29
43
  * The toolbar view class.
30
44
  *
@@ -286,18 +300,48 @@ export default class ToolbarView extends View {
286
300
  *
287
301
  * @param {Array.<String>|Object} itemsOrConfig The toolbar items or the entire toolbar configuration object.
288
302
  * @param {module:ui/componentfactory~ComponentFactory} factory A factory producing toolbar items.
303
+ * @param {Array.<String>} [removeItems] An array of items names to be removed from the configuration. When present, applies
304
+ * to this toolbar and all nested ones as well.
289
305
  */
290
- fillFromConfig( itemsOrConfig, factory ) {
306
+ fillFromConfig( itemsOrConfig, factory, removeItems ) {
291
307
  const config = normalizeToolbarConfig( itemsOrConfig );
308
+ const normalizedRemoveItems = removeItems || config.removeItems;
309
+ const itemsToAdd = this._cleanItemsConfiguration( config.items, factory, normalizedRemoveItems )
310
+ .map( name => {
311
+ if ( isObject( name ) ) {
312
+ return this._createNestedToolbarDropdown( name, factory, normalizedRemoveItems );
313
+ } else if ( name === '|' ) {
314
+ return new ToolbarSeparatorView();
315
+ } else if ( name === '-' ) {
316
+ return new ToolbarLineBreakView();
317
+ }
318
+
319
+ return factory.create( name );
320
+ } )
321
+ .filter( item => item );
322
+
323
+ this.items.addMany( itemsToAdd );
324
+ }
292
325
 
293
- const itemsToClean = config.items
326
+ /**
327
+ * Cleans up the {@link module:ui/toolbar/toolbarview~ToolbarView#items} of the toolbar by removing unwanted items and
328
+ * duplicated (obsolete) separators or line breaks.
329
+ *
330
+ * @private
331
+ * @param {Array.<String>} items The toolbar items configuration.
332
+ * @param {module:ui/componentfactory~ComponentFactory} factory A factory producing toolbar items.
333
+ * @param {Array.<String>} removeItems An array of items names to be removed from the configuration.
334
+ * @returns {Array.<String>} Items after the clean-up.
335
+ */
336
+ _cleanItemsConfiguration( items, factory, removeItems ) {
337
+ const filteredItems = items
294
338
  .filter( ( name, idx, items ) => {
295
339
  if ( name === '|' ) {
296
340
  return true;
297
341
  }
298
342
 
299
343
  // Items listed in `config.removeItems` should not be added to the toolbar.
300
- if ( config.removeItems.indexOf( name ) !== -1 ) {
344
+ if ( removeItems.indexOf( name ) !== -1 ) {
301
345
  return false;
302
346
  }
303
347
 
@@ -330,7 +374,7 @@ export default class ToolbarView extends View {
330
374
  }
331
375
 
332
376
  // For the items that cannot be instantiated we are sending warning message. We also filter them out.
333
- if ( !factory.has( name ) ) {
377
+ if ( !isObject( name ) && !factory.has( name ) ) {
334
378
  /**
335
379
  * There was a problem processing the configuration of the toolbar. The item with the given
336
380
  * name does not exist so it was omitted when rendering the toolbar.
@@ -356,19 +400,7 @@ export default class ToolbarView extends View {
356
400
  return true;
357
401
  } );
358
402
 
359
- const itemsToAdd = this._cleanSeparators( itemsToClean )
360
- // Instantiate toolbar items.
361
- .map( name => {
362
- if ( name === '|' ) {
363
- return new ToolbarSeparatorView();
364
- } else if ( name === '-' ) {
365
- return new ToolbarLineBreakView();
366
- }
367
-
368
- return factory.create( name );
369
- } );
370
-
371
- this.items.addMany( itemsToAdd );
403
+ return this._cleanSeparatorsAndLineBreaks( filteredItems );
372
404
  }
373
405
 
374
406
  /**
@@ -376,23 +408,29 @@ export default class ToolbarView extends View {
376
408
  *
377
409
  * @private
378
410
  * @param {Array.<String>} items
411
+ * @returns {Array.<String>} Toolbar items after the separator and line break clean-up.
379
412
  */
380
- _cleanSeparators( items ) {
413
+ _cleanSeparatorsAndLineBreaks( items ) {
381
414
  const nonSeparatorPredicate = item => ( item !== '-' && item !== '|' );
382
415
  const count = items.length;
383
416
 
384
417
  // Find an index of the first item that is not a separator.
385
- const firstCommandItem = items.findIndex( nonSeparatorPredicate );
418
+ const firstCommandItemIndex = items.findIndex( nonSeparatorPredicate );
419
+
420
+ // Items include separators only. There is no point in displaying them.
421
+ if ( firstCommandItemIndex === -1 ) {
422
+ return [];
423
+ }
386
424
 
387
425
  // Search from the end of the list, then convert found index back to the original direction.
388
- const lastCommandItem = count - items
426
+ const lastCommandItemIndex = count - items
389
427
  .slice()
390
428
  .reverse()
391
429
  .findIndex( nonSeparatorPredicate );
392
430
 
393
431
  return items
394
432
  // Return items without the leading and trailing separators.
395
- .slice( firstCommandItem, lastCommandItem )
433
+ .slice( firstCommandItemIndex, lastCommandItemIndex )
396
434
  // Remove duplicated separators.
397
435
  .filter( ( name, idx, items ) => {
398
436
  // Filter only separators.
@@ -405,6 +443,79 @@ export default class ToolbarView extends View {
405
443
  } );
406
444
  }
407
445
 
446
+ /**
447
+ * Creates a user-defined dropdown containing a toolbar with items.
448
+ *
449
+ * @private
450
+ * @param {Object} definition A definition of the nested toolbar dropdown.
451
+ * @param {String} definition.label A label of the dropdown.
452
+ * @param {String|Boolean} [definition.icon] An icon of the drop-down. One of 'bold', 'plus', 'text', 'importExport', 'alignLeft',
453
+ * 'paragraph' or an SVG string. When `false` is passed, no icon will be used.
454
+ * @param {Boolean} [definition.withText=false] When set `true`, the label of the dropdown will be visible. See
455
+ * {@link module:ui/button/buttonview~ButtonView#withText} to learn more.
456
+ * @param {Boolean|String|Function} [definition.tooltip=true] A tooltip of the dropdown button. See
457
+ * {@link module:ui/button/buttonview~ButtonView#tooltip} to learn more.
458
+ * @param {module:ui/componentfactory~ComponentFactory} componentFactory Component factory used to create items
459
+ * of the nested toolbar.
460
+ * @returns {module:ui/dropdown/dropdownview~DropdownView}
461
+ */
462
+ _createNestedToolbarDropdown( definition, componentFactory, removeItems ) {
463
+ let { label, icon, items, tooltip = true, withText = false } = definition;
464
+
465
+ items = this._cleanItemsConfiguration( items, componentFactory, removeItems );
466
+
467
+ // There is no point in rendering a dropdown without items.
468
+ if ( !items.length ) {
469
+ return null;
470
+ }
471
+
472
+ const locale = this.locale;
473
+ const dropdownView = createDropdown( locale );
474
+
475
+ if ( !label ) {
476
+ /**
477
+ * A dropdown definition in the toolbar configuration is missing a text label.
478
+ *
479
+ * Without a label, the dropdown becomes inaccessible to users relying on assistive technologies.
480
+ * Make sure the `label` property is set in your drop-down configuration:
481
+ *
482
+ * {
483
+ * label: 'A human-readable label',
484
+ * icon: '...',
485
+ * items: [ ... ]
486
+ * },
487
+ *
488
+ * Learn more about {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar configuration}.
489
+ *
490
+ * @error toolbarview-nested-toolbar-dropdown-missing-label
491
+ */
492
+ logWarning( 'toolbarview-nested-toolbar-dropdown-missing-label', definition );
493
+ }
494
+
495
+ dropdownView.class = 'ck-toolbar__nested-toolbar-dropdown';
496
+ dropdownView.buttonView.set( {
497
+ label,
498
+ tooltip,
499
+ withText: !!withText
500
+ } );
501
+
502
+ // Allow disabling icon by passing false.
503
+ if ( icon !== false ) {
504
+ // A pre-defined icon picked by name, SVG string, a fallback (default) icon.
505
+ dropdownView.buttonView.icon = NESTED_TOOLBAR_ICONS[ icon ] || icon || NESTED_TOOLBAR_ICONS.threeVerticalDots;
506
+ }
507
+ // If the icon is disabled, display the label automatically.
508
+ else {
509
+ dropdownView.buttonView.withText = true;
510
+ }
511
+
512
+ addToolbarToDropdown( dropdownView, [] );
513
+
514
+ dropdownView.toolbarView.fillFromConfig( items, componentFactory, removeItems );
515
+
516
+ return dropdownView;
517
+ }
518
+
408
519
  /**
409
520
  * Fired when some toolbar {@link #items} were grouped or ungrouped as a result of some change
410
521
  * in the toolbar geometry.