@ckeditor/ckeditor5-ui 42.0.2 → 43.0.0-alpha.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 (85) hide show
  1. package/CHANGELOG.md +1 -539
  2. package/dist/button/button.d.ts +0 -6
  3. package/dist/button/buttonview.d.ts +16 -4
  4. package/dist/button/filedialogbuttonview.d.ts +42 -15
  5. package/dist/button/listitembuttonview.d.ts +82 -0
  6. package/dist/editorui/accessibilityhelp/accessibilityhelp.d.ts +1 -1
  7. package/dist/editorui/editorui.d.ts +57 -0
  8. package/dist/editorui/editoruiview.d.ts +5 -0
  9. package/dist/focuscycler.d.ts +53 -7
  10. package/dist/formheader/formheaderview.d.ts +1 -2
  11. package/dist/highlightedtext/buttonlabelwithhighlightview.d.ts +34 -0
  12. package/dist/highlightedtext/labelwithhighlightview.d.ts +30 -0
  13. package/dist/index-editor.css +32 -0
  14. package/dist/index.css +42 -0
  15. package/dist/index.css.map +1 -1
  16. package/dist/index.d.ts +6 -2
  17. package/dist/index.js +4934 -4291
  18. package/dist/index.js.map +1 -1
  19. package/dist/menubar/menubarmenubuttonview.d.ts +2 -2
  20. package/dist/menubar/menubarmenulistitembuttonview.d.ts +2 -2
  21. package/dist/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +2 -2
  22. package/dist/menubar/menubarmenulistview.d.ts +5 -0
  23. package/dist/menubar/menubarview.d.ts +10 -1
  24. package/dist/menubar/utils.d.ts +16 -10
  25. package/dist/panel/balloon/balloonpanelview.d.ts +13 -1
  26. package/dist/search/filtergroupanditemnames.d.ts +19 -0
  27. package/dist/toolbar/block/blocktoolbar.d.ts +14 -0
  28. package/dist/tooltipmanager.d.ts +0 -7
  29. package/dist/translations/sr-latn.js +1 -1
  30. package/dist/translations/sr-latn.umd.js +1 -1
  31. package/lang/translations/sr-latn.po +17 -17
  32. package/package.json +3 -3
  33. package/src/arialiveannouncer.js +0 -1
  34. package/src/bindings/draggableviewmixin.js +1 -1
  35. package/src/button/button.d.ts +0 -6
  36. package/src/button/buttonview.d.ts +16 -4
  37. package/src/button/buttonview.js +32 -2
  38. package/src/button/filedialogbuttonview.d.ts +42 -15
  39. package/src/button/filedialogbuttonview.js +69 -27
  40. package/src/button/listitembuttonview.d.ts +78 -0
  41. package/src/button/listitembuttonview.js +129 -0
  42. package/src/colorpicker/colorpickerview.js +5 -0
  43. package/src/colorselector/colorgridsfragmentview.js +9 -5
  44. package/src/dialog/dialog.js +4 -1
  45. package/src/dialog/dialogview.js +1 -14
  46. package/src/dropdown/utils.js +23 -3
  47. package/src/editorui/accessibilityhelp/accessibilityhelp.d.ts +1 -1
  48. package/src/editorui/accessibilityhelp/accessibilityhelp.js +20 -12
  49. package/src/editorui/bodycollection.js +2 -1
  50. package/src/editorui/editorui.d.ts +57 -0
  51. package/src/editorui/editorui.js +104 -12
  52. package/src/editorui/editoruiview.d.ts +5 -0
  53. package/src/focuscycler.d.ts +53 -7
  54. package/src/focuscycler.js +79 -1
  55. package/src/formheader/formheaderview.d.ts +1 -2
  56. package/src/formheader/formheaderview.js +1 -2
  57. package/src/highlightedtext/buttonlabelwithhighlightview.d.ts +30 -0
  58. package/src/highlightedtext/buttonlabelwithhighlightview.js +31 -0
  59. package/src/highlightedtext/labelwithhighlightview.d.ts +26 -0
  60. package/src/highlightedtext/labelwithhighlightview.js +33 -0
  61. package/src/index.d.ts +6 -2
  62. package/src/index.js +6 -2
  63. package/src/menubar/menubarmenubuttonview.d.ts +2 -2
  64. package/src/menubar/menubarmenubuttonview.js +2 -2
  65. package/src/menubar/menubarmenulistitembuttonview.d.ts +2 -2
  66. package/src/menubar/menubarmenulistitembuttonview.js +2 -2
  67. package/src/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +2 -2
  68. package/src/menubar/menubarmenulistitemfiledialogbuttonview.js +2 -2
  69. package/src/menubar/menubarmenulistview.d.ts +5 -0
  70. package/src/menubar/menubarmenulistview.js +49 -0
  71. package/src/menubar/menubarview.d.ts +10 -1
  72. package/src/menubar/menubarview.js +11 -4
  73. package/src/menubar/utils.d.ts +16 -10
  74. package/src/menubar/utils.js +84 -53
  75. package/src/panel/balloon/balloonpanelview.d.ts +13 -1
  76. package/src/panel/balloon/balloonpanelview.js +41 -3
  77. package/src/search/filtergroupanditemnames.d.ts +15 -0
  78. package/src/search/filtergroupanditemnames.js +38 -0
  79. package/src/search/text/searchtextview.js +1 -0
  80. package/src/toolbar/balloon/balloontoolbar.js +1 -1
  81. package/src/toolbar/block/blocktoolbar.d.ts +14 -0
  82. package/src/toolbar/block/blocktoolbar.js +83 -3
  83. package/src/tooltipmanager.d.ts +0 -7
  84. package/src/tooltipmanager.js +1 -18
  85. package/theme/components/button/listitembutton.css +38 -0
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import View from '../view.js';
9
9
  import ButtonView from './buttonview.js';
10
+ import ListItemButtonView from './listitembuttonview.js';
10
11
  /**
11
12
  * The file dialog button view.
12
13
  *
@@ -31,34 +32,75 @@ import ButtonView from './buttonview.js';
31
32
  * } );
32
33
  * ```
33
34
  */
34
- export default class FileDialogButtonView extends ButtonView {
35
- /**
36
- * @inheritDoc
37
- */
38
- constructor(locale) {
39
- super(locale);
40
- // For backward compatibility.
41
- this.buttonView = this;
42
- this._fileInputView = new FileInputView(locale);
43
- this._fileInputView.bind('acceptedType').to(this);
44
- this._fileInputView.bind('allowMultipleFiles').to(this);
45
- this._fileInputView.delegate('done').to(this);
46
- this.on('execute', () => {
47
- this._fileInputView.open();
48
- });
49
- this.extendTemplate({
50
- attributes: {
51
- class: 'ck-file-dialog-button'
52
- }
53
- });
54
- }
55
- /**
56
- * @inheritDoc
57
- */
58
- render() {
59
- super.render();
60
- this.children.add(this._fileInputView);
35
+ export default class FileDialogButtonView extends /* #__PURE__ */ FileDialogViewMixin(ButtonView) {
36
+ }
37
+ /**
38
+ * The file dialog button view used in a lists.
39
+ *
40
+ * This component provides a button that opens the native file selection dialog.
41
+ * It can be used to implement the UI of a file upload feature.
42
+ *
43
+ * ```ts
44
+ * const view = new FileDialogListItemButtonView( locale );
45
+ *
46
+ * view.set( {
47
+ * acceptedType: 'image/*',
48
+ * allowMultipleFiles: true
49
+ * label: t( 'Insert image' ),
50
+ * icon: imageIcon,
51
+ * tooltip: true
52
+ * } );
53
+ *
54
+ * view.on( 'done', ( evt, files ) => {
55
+ * for ( const file of Array.from( files ) ) {
56
+ * console.log( 'Selected file', file );
57
+ * }
58
+ * } );
59
+ * ```
60
+ */
61
+ export class FileDialogListItemButtonView extends /* #__PURE__ */ FileDialogViewMixin(ListItemButtonView) {
62
+ }
63
+ /**
64
+ * Mixin function that enhances a base button view class with file dialog functionality. It is used
65
+ * to create a button view class that opens the native select file dialog when clicked.
66
+ *
67
+ * The enhanced view includes a button and a hidden file input. When the button is clicked, the file dialog is opened.
68
+ * The mixin adds properties and methods to the base class to handle the file selection.
69
+ *
70
+ * @param view The base class to be enhanced with file dialog functionality.
71
+ * @returns A new class that extends the base class and includes the file dialog functionality.
72
+ */
73
+ function FileDialogViewMixin(view) {
74
+ class FileDialogView extends view {
75
+ /**
76
+ * @inheritDoc
77
+ */
78
+ constructor(...args) {
79
+ super(...args);
80
+ // For backward compatibility.
81
+ this.buttonView = this;
82
+ this._fileInputView = new FileInputView(this.locale);
83
+ this._fileInputView.bind('acceptedType').to(this);
84
+ this._fileInputView.bind('allowMultipleFiles').to(this);
85
+ this._fileInputView.delegate('done').to(this);
86
+ this.on('execute', () => {
87
+ this._fileInputView.open();
88
+ });
89
+ this.extendTemplate({
90
+ attributes: {
91
+ class: 'ck-file-dialog-button'
92
+ }
93
+ });
94
+ }
95
+ /**
96
+ * @inheritDoc
97
+ */
98
+ render() {
99
+ super.render();
100
+ this.children.add(this._fileInputView);
101
+ }
61
102
  }
103
+ return FileDialogView;
62
104
  }
63
105
  /**
64
106
  * The hidden file input view class.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2024, 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
+ * @module ui/button/listitembuttonview
7
+ */
8
+ import type { Locale } from '@ckeditor/ckeditor5-utils';
9
+ import type ButtonLabel from './buttonlabel.js';
10
+ import type ViewCollection from '../viewcollection.js';
11
+ import ButtonView from './buttonview.js';
12
+ import View from '../view.js';
13
+ import '../../theme/components/button/listitembutton.css';
14
+ /**
15
+ * Button that is used as dropdown list item entry.
16
+ */
17
+ export default class ListItemButtonView extends ButtonView {
18
+ /**
19
+ * Indicates whether the button view has reserved space for a check holder.
20
+ *
21
+ * @observable
22
+ */
23
+ hasCheckSpace: boolean;
24
+ /**
25
+ * The flag that indicates if the button should render a check holder.
26
+ *
27
+ * @internal
28
+ * @readonly
29
+ * @observable
30
+ */
31
+ _hasCheck: boolean;
32
+ /**
33
+ * Holds the view for the check icon of a button list item.
34
+ */
35
+ private readonly _checkIconHolderView;
36
+ /**
37
+ * @inheritDoc
38
+ */
39
+ constructor(locale?: Locale, labelView?: ButtonLabel);
40
+ /**
41
+ * @inheritDoc
42
+ */
43
+ render(): void;
44
+ /**
45
+ * Renders the check icon if the button is toggleable.
46
+ */
47
+ private _watchCheckIconHolderMount;
48
+ }
49
+ export declare class CheckIconHolderView extends View {
50
+ /**
51
+ * Collection of child views.
52
+ */
53
+ readonly children: ViewCollection<View>;
54
+ /**
55
+ * Indicates whether the button is in the "on" state.
56
+ */
57
+ isOn: boolean;
58
+ /**
59
+ * The view for the check icon of the button list item.
60
+ */
61
+ private readonly _checkIconView;
62
+ /**
63
+ * @inheritDoc
64
+ */
65
+ constructor();
66
+ /**
67
+ * @inheritDoc
68
+ */
69
+ render(): void;
70
+ /**
71
+ * Renders the check icon if the button is toggleable.
72
+ */
73
+ private _watchCheckIconMount;
74
+ /**
75
+ * Creates a check icon view.
76
+ */
77
+ private _createCheckIconView;
78
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2024, 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
+ import { icons } from '@ckeditor/ckeditor5-core';
6
+ import ButtonView from './buttonview.js';
7
+ import ButtonLabelView from './buttonlabelview.js';
8
+ import IconView from '../icon/iconview.js';
9
+ import View from '../view.js';
10
+ import '../../theme/components/button/listitembutton.css';
11
+ /**
12
+ * Button that is used as dropdown list item entry.
13
+ */
14
+ export default class ListItemButtonView extends ButtonView {
15
+ /**
16
+ * @inheritDoc
17
+ */
18
+ constructor(locale, labelView = new ButtonLabelView()) {
19
+ super(locale, labelView);
20
+ /**
21
+ * Holds the view for the check icon of a button list item.
22
+ */
23
+ this._checkIconHolderView = new CheckIconHolderView();
24
+ this.set({
25
+ hasCheckSpace: false,
26
+ _hasCheck: this.isToggleable
27
+ });
28
+ const bind = this.bindTemplate;
29
+ this.extendTemplate({
30
+ attributes: {
31
+ class: [
32
+ 'ck-list-item-button',
33
+ bind.if('isToggleable', 'ck-list-item-button_toggleable')
34
+ ]
35
+ }
36
+ });
37
+ this.bind('_hasCheck').to(this, 'hasCheckSpace', this, 'isToggleable', (hasCheckSpace, isToggleable) => hasCheckSpace || isToggleable);
38
+ }
39
+ /**
40
+ * @inheritDoc
41
+ */
42
+ render() {
43
+ super.render();
44
+ if (this._hasCheck) {
45
+ this.children.add(this._checkIconHolderView, 0);
46
+ }
47
+ this._watchCheckIconHolderMount();
48
+ }
49
+ /**
50
+ * Renders the check icon if the button is toggleable.
51
+ */
52
+ _watchCheckIconHolderMount() {
53
+ this._checkIconHolderView
54
+ .bind('isOn')
55
+ .to(this, 'isOn', value => this.isToggleable && value);
56
+ this.on('change:_hasCheck', (evt, propertyName, hasCheck) => {
57
+ const { children, _checkIconHolderView } = this;
58
+ if (hasCheck) {
59
+ children.add(_checkIconHolderView, 0);
60
+ }
61
+ else {
62
+ children.remove(_checkIconHolderView);
63
+ }
64
+ });
65
+ }
66
+ }
67
+ export class CheckIconHolderView extends View {
68
+ /**
69
+ * @inheritDoc
70
+ */
71
+ constructor() {
72
+ super();
73
+ /**
74
+ * The view for the check icon of the button list item.
75
+ */
76
+ this._checkIconView = this._createCheckIconView();
77
+ const bind = this.bindTemplate;
78
+ this.children = this.createCollection();
79
+ this.set('isOn', false);
80
+ this.setTemplate({
81
+ tag: 'span',
82
+ children: this.children,
83
+ attributes: {
84
+ class: [
85
+ 'ck',
86
+ 'ck-list-item-button__check-holder',
87
+ bind.to('isOn', isOn => isOn ? 'ck-on' : 'ck-off')
88
+ ]
89
+ }
90
+ });
91
+ }
92
+ /**
93
+ * @inheritDoc
94
+ */
95
+ render() {
96
+ super.render();
97
+ if (this.isOn) {
98
+ this.children.add(this._checkIconView, 0);
99
+ }
100
+ this._watchCheckIconMount();
101
+ }
102
+ /**
103
+ * Renders the check icon if the button is toggleable.
104
+ */
105
+ _watchCheckIconMount() {
106
+ this.on('change:isOn', (evt, propertyName, isOn) => {
107
+ const { children, _checkIconView } = this;
108
+ if (isOn && !children.has(_checkIconView)) {
109
+ children.add(_checkIconView);
110
+ }
111
+ else if (!isOn && children.has(_checkIconView)) {
112
+ children.remove(_checkIconView);
113
+ }
114
+ });
115
+ }
116
+ /**
117
+ * Creates a check icon view.
118
+ */
119
+ _createCheckIconView() {
120
+ const iconView = new IconView();
121
+ iconView.content = icons.check;
122
+ iconView.extendTemplate({
123
+ attributes: {
124
+ class: 'ck-list-item-button__check-icon'
125
+ }
126
+ });
127
+ return iconView;
128
+ }
129
+ }
@@ -194,6 +194,11 @@ export default class ColorPickerView extends View {
194
194
  */
195
195
  isValid() {
196
196
  const { t } = this.locale;
197
+ // If the input is hidden, it's always valid, because there is no way to select
198
+ // invalid color value using diagram color picker.
199
+ if (this._config.hideInput) {
200
+ return true;
201
+ }
197
202
  this.resetValidationStatus();
198
203
  // One error per field is enough.
199
204
  if (!this.hexInputRow.getParsedColor()) {
@@ -9,7 +9,6 @@ import View from '../view.js';
9
9
  import ButtonView from '../button/buttonview.js';
10
10
  import ColorGridView from '../colorgrid/colorgridview.js';
11
11
  import ColorTileView from '../colorgrid/colortileview.js';
12
- import LabelView from '../label/labelview.js';
13
12
  import Template from '../template.js';
14
13
  import DocumentColorCollection from './documentcolorcollection.js';
15
14
  import { icons } from '@ckeditor/ckeditor5-core';
@@ -115,16 +114,21 @@ export default class ColorGridsFragmentView extends View {
115
114
  if (this.documentColorsCount) {
116
115
  // Create a label for document colors.
117
116
  const bind = Template.bind(this.documentColors, this.documentColors);
118
- const label = new LabelView(this.locale);
119
- label.text = this._documentColorsLabel;
120
- label.extendTemplate({
117
+ const label = new View(this.locale);
118
+ label.setTemplate({
119
+ tag: 'span',
121
120
  attributes: {
122
121
  class: [
123
122
  'ck',
124
123
  'ck-color-grid__label',
125
124
  bind.if('isEmpty', 'ck-hidden')
126
125
  ]
127
- }
126
+ },
127
+ children: [
128
+ {
129
+ text: this._documentColorsLabel
130
+ }
131
+ ]
128
132
  });
129
133
  this.items.add(label);
130
134
  this.documentColorsGrid = this._createDocumentColorsGrid();
@@ -23,7 +23,10 @@ export default class Dialog extends Plugin {
23
23
  this._initShowHideListeners();
24
24
  this._initFocusToggler();
25
25
  this._initMultiRootIntegration();
26
- this.set('id', null);
26
+ this.set({
27
+ id: null,
28
+ isOpen: false
29
+ });
27
30
  // Add the information about the keystroke to the accessibility database.
28
31
  editor.accessibility.addKeystrokeInfos({
29
32
  categoryId: 'navigation',
@@ -413,20 +413,7 @@ class DialogView extends /* #__PURE__ */ DraggableViewMixin(View) {
413
413
  this._focusables.add(focusable);
414
414
  this.focusTracker.add(focusable.element);
415
415
  if (isViewWithFocusCycler(focusable)) {
416
- this.listenTo(focusable.focusCycler, 'forwardCycle', evt => {
417
- this._focusCycler.focusNext();
418
- // Stop the event propagation only if there are more focusables.
419
- if (this._focusCycler.next !== this._focusCycler.focusables.get(this._focusCycler.current)) {
420
- evt.stop();
421
- }
422
- });
423
- this.listenTo(focusable.focusCycler, 'backwardCycle', evt => {
424
- this._focusCycler.focusPrevious();
425
- // Stop the event propagation only if there are more focusables.
426
- if (this._focusCycler.previous !== this._focusCycler.focusables.get(this._focusCycler.current)) {
427
- evt.stop();
428
- }
429
- });
416
+ this._focusCycler.chain(focusable.focusCycler);
430
417
  }
431
418
  });
432
419
  }
@@ -12,7 +12,6 @@ import ToolbarView from '../toolbar/toolbarview.js';
12
12
  import ListView from '../list/listview.js';
13
13
  import ListItemView from '../list/listitemview.js';
14
14
  import ListSeparatorView from '../list/listseparatorview.js';
15
- import ButtonView from '../button/buttonview.js';
16
15
  import SplitButtonView from './button/splitbuttonview.js';
17
16
  import SwitchButtonView from '../button/switchbuttonview.js';
18
17
  import ViewCollection from '../viewcollection.js';
@@ -21,6 +20,7 @@ import { global, priorities, logWarning } from '@ckeditor/ckeditor5-utils';
21
20
  import '../../theme/components/dropdown/toolbardropdown.css';
22
21
  import '../../theme/components/dropdown/listdropdown.css';
23
22
  import ListItemGroupView from '../list/listitemgroupview.js';
23
+ import ListItemButtonView from '../button/listitembuttonview.js';
24
24
  /**
25
25
  * A helper for creating dropdowns. It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown},
26
26
  * with a {@link module:ui/dropdown/button/dropdownbutton~DropdownButton button},
@@ -427,6 +427,23 @@ function focusDropdownPanelOnOpen(dropdownView) {
427
427
  * @param locale
428
428
  */
429
429
  function bindViewCollectionItemsToDefinitions(dropdownView, listItems, definitions, locale) {
430
+ // List item checkboxes have a reserved space for the check icon, so we need to know if there are any checkboxes in the list
431
+ // to adjust the layout accordingly. It'd look weird if the items on the list were not aligned horizontally.
432
+ //
433
+ // Possible theoretical performance problem if many items are added one by one, as this will be called for each item.
434
+ listItems.on('change', () => {
435
+ // Filter-map. Check all items, leave only these that have buttons and return the buttons.
436
+ const listItemButtons = [...listItems].reduce((acc, item) => {
437
+ if (item instanceof ListItemView && item.children.first instanceof ListItemButtonView) {
438
+ acc.push(item.children.first);
439
+ }
440
+ return acc;
441
+ }, []);
442
+ const hasAnyCheckboxOnList = listItemButtons.some(button => button.isToggleable);
443
+ listItemButtons.forEach(item => {
444
+ item.hasCheckSpace = hasAnyCheckboxOnList;
445
+ });
446
+ });
430
447
  listItems.bindTo(definitions).using(def => {
431
448
  if (def.type === 'separator') {
432
449
  return new ListSeparatorView(locale);
@@ -439,11 +456,14 @@ function bindViewCollectionItemsToDefinitions(dropdownView, listItems, definitio
439
456
  return groupView;
440
457
  }
441
458
  else if (def.type === 'button' || def.type === 'switchbutton') {
459
+ const isToggleable = def.model.role === 'menuitemcheckbox' || def.model.role === 'menuitemradio';
442
460
  const listItemView = new ListItemView(locale);
443
461
  let buttonView;
444
462
  if (def.type === 'button') {
445
- buttonView = new ButtonView(locale);
446
- buttonView.bind('ariaChecked').to(buttonView, 'isOn');
463
+ buttonView = new ListItemButtonView(locale);
464
+ buttonView.set({
465
+ isToggleable
466
+ });
447
467
  }
448
468
  else {
449
469
  buttonView = new SwitchButtonView(locale);
@@ -47,5 +47,5 @@ export default class AccessibilityHelp extends Plugin {
47
47
  /**
48
48
  * Shows the accessibility help dialog. Also, creates {@link #contentView} on demand.
49
49
  */
50
- private _showDialog;
50
+ private _toggleDialog;
51
51
  }
@@ -63,7 +63,7 @@ export default class AccessibilityHelp extends Plugin {
63
63
  return button;
64
64
  });
65
65
  editor.keystrokes.set('Alt+0', (evt, cancel) => {
66
- this._showDialog();
66
+ this._toggleDialog();
67
67
  cancel();
68
68
  });
69
69
  this._setupRootLabels();
@@ -73,13 +73,16 @@ export default class AccessibilityHelp extends Plugin {
73
73
  */
74
74
  _createButton(ButtonClass) {
75
75
  const editor = this.editor;
76
+ const dialog = editor.plugins.get('Dialog');
76
77
  const locale = editor.locale;
77
78
  const view = new ButtonClass(locale);
78
79
  view.set({
79
80
  keystroke: 'Alt+0',
80
- icon: accessibilityIcon
81
+ icon: accessibilityIcon,
82
+ isToggleable: true
81
83
  });
82
- view.on('execute', () => this._showDialog());
84
+ view.on('execute', () => this._toggleDialog());
85
+ view.bind('isOn').to(dialog, 'id', id => id === 'accessibilityHelp');
83
86
  return view;
84
87
  }
85
88
  /**
@@ -110,20 +113,25 @@ export default class AccessibilityHelp extends Plugin {
110
113
  /**
111
114
  * Shows the accessibility help dialog. Also, creates {@link #contentView} on demand.
112
115
  */
113
- _showDialog() {
116
+ _toggleDialog() {
114
117
  const editor = this.editor;
115
118
  const dialog = editor.plugins.get('Dialog');
116
119
  const t = editor.locale.t;
117
120
  if (!this.contentView) {
118
121
  this.contentView = new AccessibilityHelpContentView(editor.locale, editor.accessibility.keystrokeInfos);
119
122
  }
120
- dialog.show({
121
- id: 'accessibilityHelp',
122
- className: 'ck-accessibility-help-dialog',
123
- title: t('Accessibility help'),
124
- icon: accessibilityIcon,
125
- hasCloseButton: true,
126
- content: this.contentView
127
- });
123
+ if (dialog.id === 'accessibilityHelp') {
124
+ dialog.hide();
125
+ }
126
+ else {
127
+ dialog.show({
128
+ id: 'accessibilityHelp',
129
+ className: 'ck-accessibility-help-dialog',
130
+ title: t('Accessibility help'),
131
+ icon: accessibilityIcon,
132
+ hasCloseButton: true,
133
+ content: this.contentView
134
+ });
135
+ }
128
136
  }
129
137
  }
@@ -56,7 +56,8 @@ export default class BodyCollection extends ViewCollection {
56
56
  'ck-body',
57
57
  'ck-rounded-corners'
58
58
  ],
59
- dir: this.locale.uiLanguageDirection
59
+ dir: this.locale.uiLanguageDirection,
60
+ role: 'application'
60
61
  },
61
62
  children: this
62
63
  }).render();
@@ -13,6 +13,7 @@ import type EditorUIView from './editoruiview.js';
13
13
  import type ToolbarView from '../toolbar/toolbarview.js';
14
14
  import { FocusTracker } from '@ckeditor/ckeditor5-utils';
15
15
  import type { Editor } from '@ckeditor/ckeditor5-core';
16
+ import type { default as MenuBarView, MenuBarConfigAddedGroup, MenuBarConfigAddedItem, MenuBarConfigAddedMenu } from '../menubar/menubarview.js';
16
17
  declare const EditorUI_base: {
17
18
  new (): import("@ckeditor/ckeditor5-utils").Observable;
18
19
  prototype: import("@ckeditor/ckeditor5-utils").Observable;
@@ -100,6 +101,14 @@ export default abstract class EditorUI extends /* #__PURE__ */ EditorUI_base {
100
101
  * All available & focusable toolbars.
101
102
  */
102
103
  private _focusableToolbarDefinitions;
104
+ /**
105
+ * All additional menu bar items, groups or menus that have their default location defined.
106
+ */
107
+ private _extraMenuBarElements;
108
+ /**
109
+ * The last focused element to which focus should return on `Esc` press.
110
+ */
111
+ private _lastFocusedForeignElement;
103
112
  /**
104
113
  * Creates an instance of the editor UI class.
105
114
  *
@@ -165,12 +174,56 @@ export default abstract class EditorUI extends /* #__PURE__ */ EditorUI_base {
165
174
  * @param toolbarView A instance of the toolbar to be registered.
166
175
  */
167
176
  addToolbar(toolbarView: ToolbarView, options?: FocusableToolbarOptions): void;
177
+ /**
178
+ * Registers an extra menu bar element, which could be a single item, a group of items, or a menu containing groups.
179
+ *
180
+ * ```ts
181
+ * // Register a new menu bar item.
182
+ * editor.ui.extendMenuBar( {
183
+ * item: 'menuBar:customFunctionButton',
184
+ * position: 'after:menuBar:bold'
185
+ * } );
186
+ *
187
+ * // Register a new menu bar group.
188
+ * editor.ui.extendMenuBar( {
189
+ * group: {
190
+ * groupId: 'customGroup',
191
+ * items: [
192
+ * 'menuBar:customFunctionButton'
193
+ * ]
194
+ * },
195
+ * position: 'start:help'
196
+ * } );
197
+ *
198
+ * // Register a new menu bar menu.
199
+ * editor.ui.extendMenuBar( {
200
+ * menu: {
201
+ * menuId: 'customMenu',
202
+ * label: 'customMenu',
203
+ * groups: [
204
+ * {
205
+ * groupId: 'customGroup',
206
+ * items: [
207
+ * 'menuBar:customFunctionButton'
208
+ * ]
209
+ * }
210
+ * ]
211
+ * },
212
+ * position: 'after:help'
213
+ * } );
214
+ * ```
215
+ */
216
+ extendMenuBar(config: MenuBarConfigAddedItem | MenuBarConfigAddedGroup | MenuBarConfigAddedMenu): void;
168
217
  /**
169
218
  * Stores all editable elements used by the editor instance.
170
219
  *
171
220
  * @deprecated
172
221
  */
173
222
  protected get _editableElements(): unknown;
223
+ /**
224
+ * Initializes menu bar.
225
+ */
226
+ protected _initMenuBar(menuBarView: MenuBarView): void;
174
227
  /**
175
228
  * Returns viewport offsets object:
176
229
  *
@@ -192,6 +245,10 @@ export default abstract class EditorUI extends /* #__PURE__ */ EditorUI_base {
192
245
  * to allow users navigate across the UI.
193
246
  */
194
247
  private _initFocusTracking;
248
+ /**
249
+ * Saves last focused element that doen not belong to editing view to restore focus on `Esc`.
250
+ */
251
+ private _saveLastFocusedForeignElement;
195
252
  /**
196
253
  * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.
197
254
  *