@ckeditor/ckeditor5-ui 39.0.2 → 40.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 (226) hide show
  1. package/lang/contexts.json +5 -1
  2. package/lang/translations/ar.po +16 -0
  3. package/lang/translations/ast.po +16 -0
  4. package/lang/translations/az.po +16 -0
  5. package/lang/translations/bg.po +16 -0
  6. package/lang/translations/bn.po +16 -0
  7. package/lang/translations/ca.po +16 -0
  8. package/lang/translations/cs.po +16 -0
  9. package/lang/translations/da.po +16 -0
  10. package/lang/translations/de-ch.po +16 -0
  11. package/lang/translations/de.po +16 -0
  12. package/lang/translations/el.po +16 -0
  13. package/lang/translations/en-au.po +16 -0
  14. package/lang/translations/en-gb.po +16 -0
  15. package/lang/translations/en.po +16 -0
  16. package/lang/translations/eo.po +16 -0
  17. package/lang/translations/es.po +16 -0
  18. package/lang/translations/et.po +16 -0
  19. package/lang/translations/eu.po +16 -0
  20. package/lang/translations/fa.po +16 -0
  21. package/lang/translations/fi.po +16 -0
  22. package/lang/translations/fr.po +16 -0
  23. package/lang/translations/gl.po +16 -0
  24. package/lang/translations/he.po +16 -0
  25. package/lang/translations/hi.po +16 -0
  26. package/lang/translations/hr.po +16 -0
  27. package/lang/translations/hu.po +16 -0
  28. package/lang/translations/id.po +16 -0
  29. package/lang/translations/it.po +16 -0
  30. package/lang/translations/ja.po +16 -0
  31. package/lang/translations/km.po +16 -0
  32. package/lang/translations/kn.po +16 -0
  33. package/lang/translations/ko.po +16 -0
  34. package/lang/translations/ku.po +16 -0
  35. package/lang/translations/lt.po +16 -0
  36. package/lang/translations/lv.po +16 -0
  37. package/lang/translations/ms.po +16 -0
  38. package/lang/translations/nb.po +16 -0
  39. package/lang/translations/ne.po +16 -0
  40. package/lang/translations/nl.po +16 -0
  41. package/lang/translations/no.po +16 -0
  42. package/lang/translations/pl.po +16 -0
  43. package/lang/translations/pt-br.po +16 -0
  44. package/lang/translations/pt.po +16 -0
  45. package/lang/translations/ro.po +16 -0
  46. package/lang/translations/ru.po +16 -0
  47. package/lang/translations/sk.po +16 -0
  48. package/lang/translations/sl.po +16 -0
  49. package/lang/translations/sq.po +16 -0
  50. package/lang/translations/sr-latn.po +16 -0
  51. package/lang/translations/sr.po +16 -0
  52. package/lang/translations/sv.po +16 -0
  53. package/lang/translations/th.po +16 -0
  54. package/lang/translations/tk.po +16 -0
  55. package/lang/translations/tr.po +16 -0
  56. package/lang/translations/tt.po +16 -0
  57. package/lang/translations/ug.po +16 -0
  58. package/lang/translations/uk.po +16 -0
  59. package/lang/translations/ur.po +16 -0
  60. package/lang/translations/uz.po +16 -0
  61. package/lang/translations/vi.po +16 -0
  62. package/lang/translations/zh-cn.po +16 -0
  63. package/lang/translations/zh.po +16 -0
  64. package/package.json +3 -3
  65. package/src/augmentation.d.ts +86 -86
  66. package/src/augmentation.js +5 -5
  67. package/src/autocomplete/autocompleteview.d.ts +81 -0
  68. package/src/autocomplete/autocompleteview.js +146 -0
  69. package/src/bindings/addkeyboardhandlingforgrid.d.ts +27 -27
  70. package/src/bindings/addkeyboardhandlingforgrid.js +107 -107
  71. package/src/bindings/clickoutsidehandler.d.ts +28 -28
  72. package/src/bindings/clickoutsidehandler.js +36 -36
  73. package/src/bindings/csstransitiondisablermixin.d.ts +40 -40
  74. package/src/bindings/csstransitiondisablermixin.js +55 -55
  75. package/src/bindings/injectcsstransitiondisabler.d.ts +59 -59
  76. package/src/bindings/injectcsstransitiondisabler.js +71 -71
  77. package/src/bindings/preventdefault.d.ts +33 -33
  78. package/src/bindings/preventdefault.js +34 -34
  79. package/src/bindings/submithandler.d.ts +57 -57
  80. package/src/bindings/submithandler.js +47 -47
  81. package/src/button/button.d.ts +178 -178
  82. package/src/button/button.js +5 -5
  83. package/src/button/buttonlabel.d.ts +34 -0
  84. package/src/button/buttonlabel.js +5 -0
  85. package/src/button/buttonlabelview.d.ts +31 -0
  86. package/src/button/buttonlabelview.js +42 -0
  87. package/src/button/buttonview.d.ts +185 -177
  88. package/src/button/buttonview.js +219 -231
  89. package/src/button/switchbuttonview.d.ts +45 -45
  90. package/src/button/switchbuttonview.js +75 -75
  91. package/src/colorgrid/colorgridview.d.ts +132 -132
  92. package/src/colorgrid/colorgridview.js +124 -124
  93. package/src/colorgrid/colortileview.d.ts +28 -28
  94. package/src/colorgrid/colortileview.js +40 -40
  95. package/src/colorgrid/utils.d.ts +47 -47
  96. package/src/colorgrid/utils.js +84 -84
  97. package/src/colorpicker/colorpickerview.d.ts +137 -137
  98. package/src/colorpicker/colorpickerview.js +270 -270
  99. package/src/colorpicker/utils.d.ts +43 -43
  100. package/src/colorpicker/utils.js +99 -99
  101. package/src/colorselector/colorgridsfragmentview.d.ts +194 -194
  102. package/src/colorselector/colorgridsfragmentview.js +289 -289
  103. package/src/colorselector/colorpickerfragmentview.d.ts +128 -128
  104. package/src/colorselector/colorpickerfragmentview.js +205 -205
  105. package/src/colorselector/colorselectorview.d.ts +242 -242
  106. package/src/colorselector/colorselectorview.js +256 -256
  107. package/src/colorselector/documentcolorcollection.d.ts +70 -70
  108. package/src/colorselector/documentcolorcollection.js +42 -42
  109. package/src/componentfactory.d.ts +81 -81
  110. package/src/componentfactory.js +104 -104
  111. package/src/dropdown/button/dropdownbutton.d.ts +25 -25
  112. package/src/dropdown/button/dropdownbutton.js +5 -5
  113. package/src/dropdown/button/dropdownbuttonview.d.ts +48 -48
  114. package/src/dropdown/button/dropdownbuttonview.js +66 -66
  115. package/src/dropdown/button/splitbuttonview.d.ts +161 -161
  116. package/src/dropdown/button/splitbuttonview.js +152 -152
  117. package/src/dropdown/dropdownpanelfocusable.d.ts +21 -21
  118. package/src/dropdown/dropdownpanelfocusable.js +5 -5
  119. package/src/dropdown/dropdownpanelview.d.ts +62 -62
  120. package/src/dropdown/dropdownpanelview.js +97 -97
  121. package/src/dropdown/dropdownview.d.ts +315 -315
  122. package/src/dropdown/dropdownview.js +379 -378
  123. package/src/dropdown/utils.d.ts +235 -221
  124. package/src/dropdown/utils.js +458 -437
  125. package/src/editableui/editableuiview.d.ts +72 -72
  126. package/src/editableui/editableuiview.js +112 -112
  127. package/src/editableui/inline/inlineeditableuiview.d.ts +40 -40
  128. package/src/editableui/inline/inlineeditableuiview.js +48 -48
  129. package/src/editorui/bodycollection.d.ts +55 -55
  130. package/src/editorui/bodycollection.js +84 -84
  131. package/src/editorui/boxed/boxededitoruiview.d.ts +40 -40
  132. package/src/editorui/boxed/boxededitoruiview.js +81 -81
  133. package/src/editorui/editorui.d.ts +282 -282
  134. package/src/editorui/editorui.js +410 -410
  135. package/src/editorui/editoruiview.d.ts +39 -39
  136. package/src/editorui/editoruiview.js +38 -38
  137. package/src/editorui/poweredby.d.ts +71 -71
  138. package/src/editorui/poweredby.js +276 -299
  139. package/src/focuscycler.d.ts +226 -183
  140. package/src/focuscycler.js +245 -220
  141. package/src/formheader/formheaderview.d.ts +59 -53
  142. package/src/formheader/formheaderview.js +69 -63
  143. package/src/highlightedtext/highlightedtextview.d.ts +38 -0
  144. package/src/highlightedtext/highlightedtextview.js +102 -0
  145. package/src/icon/iconview.d.ts +85 -78
  146. package/src/icon/iconview.js +114 -112
  147. package/src/iframe/iframeview.d.ts +50 -50
  148. package/src/iframe/iframeview.js +63 -63
  149. package/src/index.d.ts +73 -63
  150. package/src/index.js +70 -62
  151. package/src/input/inputbase.d.ts +107 -0
  152. package/src/input/inputbase.js +110 -0
  153. package/src/input/inputview.d.ts +36 -121
  154. package/src/input/inputview.js +24 -106
  155. package/src/inputnumber/inputnumberview.d.ts +49 -49
  156. package/src/inputnumber/inputnumberview.js +40 -40
  157. package/src/inputtext/inputtextview.d.ts +18 -18
  158. package/src/inputtext/inputtextview.js +27 -27
  159. package/src/label/labelview.d.ts +36 -36
  160. package/src/label/labelview.js +41 -41
  161. package/src/labeledfield/labeledfieldview.d.ts +187 -182
  162. package/src/labeledfield/labeledfieldview.js +157 -157
  163. package/src/labeledfield/utils.d.ts +123 -93
  164. package/src/labeledfield/utils.js +176 -131
  165. package/src/labeledinput/labeledinputview.d.ts +125 -125
  166. package/src/labeledinput/labeledinputview.js +125 -125
  167. package/src/list/listitemgroupview.d.ts +51 -0
  168. package/src/list/listitemgroupview.js +75 -0
  169. package/src/list/listitemview.d.ts +36 -35
  170. package/src/list/listitemview.js +42 -40
  171. package/src/list/listseparatorview.d.ts +18 -18
  172. package/src/list/listseparatorview.js +28 -28
  173. package/src/list/listview.d.ts +122 -65
  174. package/src/list/listview.js +187 -90
  175. package/src/model.d.ts +22 -22
  176. package/src/model.js +31 -31
  177. package/src/notification/notification.d.ts +211 -211
  178. package/src/notification/notification.js +187 -187
  179. package/src/panel/balloon/balloonpanelview.d.ts +685 -685
  180. package/src/panel/balloon/balloonpanelview.js +1010 -988
  181. package/src/panel/balloon/contextualballoon.d.ts +299 -299
  182. package/src/panel/balloon/contextualballoon.js +572 -572
  183. package/src/panel/sticky/stickypanelview.d.ts +156 -158
  184. package/src/panel/sticky/stickypanelview.js +234 -231
  185. package/src/search/filteredview.d.ts +31 -0
  186. package/src/search/filteredview.js +5 -0
  187. package/src/search/searchinfoview.d.ts +45 -0
  188. package/src/search/searchinfoview.js +59 -0
  189. package/src/search/searchresultsview.d.ts +54 -0
  190. package/src/search/searchresultsview.js +65 -0
  191. package/src/search/text/searchtextqueryview.d.ts +76 -0
  192. package/src/search/text/searchtextqueryview.js +75 -0
  193. package/src/search/text/searchtextview.d.ts +219 -0
  194. package/src/search/text/searchtextview.js +201 -0
  195. package/src/spinner/spinnerview.d.ts +25 -0
  196. package/src/spinner/spinnerview.js +38 -0
  197. package/src/template.d.ts +942 -942
  198. package/src/template.js +1294 -1294
  199. package/src/textarea/textareaview.d.ts +88 -0
  200. package/src/textarea/textareaview.js +140 -0
  201. package/src/toolbar/balloon/balloontoolbar.d.ts +122 -122
  202. package/src/toolbar/balloon/balloontoolbar.js +300 -300
  203. package/src/toolbar/block/blockbuttonview.d.ts +35 -35
  204. package/src/toolbar/block/blockbuttonview.js +41 -41
  205. package/src/toolbar/block/blocktoolbar.d.ts +161 -161
  206. package/src/toolbar/block/blocktoolbar.js +395 -391
  207. package/src/toolbar/normalizetoolbarconfig.d.ts +40 -39
  208. package/src/toolbar/normalizetoolbarconfig.js +51 -51
  209. package/src/toolbar/toolbarlinebreakview.d.ts +18 -18
  210. package/src/toolbar/toolbarlinebreakview.js +28 -28
  211. package/src/toolbar/toolbarseparatorview.d.ts +18 -18
  212. package/src/toolbar/toolbarseparatorview.js +28 -28
  213. package/src/toolbar/toolbarview.d.ts +266 -265
  214. package/src/toolbar/toolbarview.js +719 -717
  215. package/src/tooltipmanager.d.ts +180 -180
  216. package/src/tooltipmanager.js +353 -353
  217. package/src/view.d.ts +422 -422
  218. package/src/view.js +396 -396
  219. package/src/viewcollection.d.ts +139 -139
  220. package/src/viewcollection.js +206 -206
  221. package/theme/components/autocomplete/autocomplete.css +22 -0
  222. package/theme/components/formheader/formheader.css +8 -0
  223. package/theme/components/highlightedtext/highlightedtext.css +12 -0
  224. package/theme/components/search/search.css +43 -0
  225. package/theme/components/spinner/spinner.css +23 -0
  226. package/theme/components/textarea/textarea.css +10 -0
@@ -1,410 +1,410 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, 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/editorui/editorui
7
- */
8
- /* globals console */
9
- import ComponentFactory from '../componentfactory';
10
- import TooltipManager from '../tooltipmanager';
11
- import PoweredBy from './poweredby';
12
- import { ObservableMixin, isVisible, FocusTracker } from '@ckeditor/ckeditor5-utils';
13
- /**
14
- * A class providing the minimal interface that is required to successfully bootstrap any editor UI.
15
- */
16
- export default class EditorUI extends ObservableMixin() {
17
- /**
18
- * Creates an instance of the editor UI class.
19
- *
20
- * @param editor The editor instance.
21
- */
22
- constructor(editor) {
23
- super();
24
- /**
25
- * Indicates the UI is ready. Set `true` after {@link #event:ready} event is fired.
26
- *
27
- * @readonly
28
- * @default false
29
- */
30
- this.isReady = false;
31
- /**
32
- * Stores all editable elements used by the editor instance.
33
- */
34
- this._editableElementsMap = new Map();
35
- /**
36
- * All available & focusable toolbars.
37
- */
38
- this._focusableToolbarDefinitions = [];
39
- const editingView = editor.editing.view;
40
- this.editor = editor;
41
- this.componentFactory = new ComponentFactory(editor);
42
- this.focusTracker = new FocusTracker();
43
- this.tooltipManager = new TooltipManager(editor);
44
- this.poweredBy = new PoweredBy(editor);
45
- this.set('viewportOffset', this._readViewportOffsetFromConfig());
46
- this.once('ready', () => {
47
- this.isReady = true;
48
- });
49
- // Informs UI components that should be refreshed after layout change.
50
- this.listenTo(editingView.document, 'layoutChanged', this.update.bind(this));
51
- this.listenTo(editingView, 'scrollToTheSelection', this._handleScrollToTheSelection.bind(this));
52
- this._initFocusTracking();
53
- }
54
- /**
55
- * The main (outermost) DOM element of the editor UI.
56
- *
57
- * For example, in {@link module:editor-classic/classiceditor~ClassicEditor} it is a `<div>` which
58
- * wraps the editable element and the toolbar. In {@link module:editor-inline/inlineeditor~InlineEditor}
59
- * it is the editable element itself (as there is no other wrapper). However, in
60
- * {@link module:editor-decoupled/decouplededitor~DecoupledEditor} it is set to `null` because this editor does not
61
- * come with a single "main" HTML element (its editable element and toolbar are separate).
62
- *
63
- * This property can be understood as a shorthand for retrieving the element that a specific editor integration
64
- * considers to be its main DOM element.
65
- */
66
- get element() {
67
- return null;
68
- }
69
- /**
70
- * Fires the {@link module:ui/editorui/editorui~EditorUI#event:update `update`} event.
71
- *
72
- * This method should be called when the editor UI (e.g. positions of its balloons) needs to be updated due to
73
- * some environmental change which CKEditor 5 is not aware of (e.g. resize of a container in which it is used).
74
- */
75
- update() {
76
- this.fire('update');
77
- }
78
- /**
79
- * Destroys the UI.
80
- */
81
- destroy() {
82
- this.stopListening();
83
- this.focusTracker.destroy();
84
- this.tooltipManager.destroy(this.editor);
85
- this.poweredBy.destroy();
86
- // Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
87
- for (const domElement of this._editableElementsMap.values()) {
88
- domElement.ckeditorInstance = null;
89
- this.editor.keystrokes.stopListening(domElement);
90
- }
91
- this._editableElementsMap = new Map();
92
- this._focusableToolbarDefinitions = [];
93
- }
94
- /**
95
- * Stores the native DOM editable element used by the editor under a unique name.
96
- *
97
- * Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable
98
- * editable area, they can use the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke to navigate over editor toolbars. See {@link #addToolbar}.
99
- *
100
- * @param rootName The unique name of the editable element.
101
- * @param domElement The native DOM editable element.
102
- */
103
- setEditableElement(rootName, domElement) {
104
- this._editableElementsMap.set(rootName, domElement);
105
- // Put a reference to the CKEditor instance in the editable native DOM element.
106
- // It helps 3rd–party software (browser extensions, other libraries) access and recognize
107
- // CKEditor 5 instances (editing roots) and use their API (there is no global editor
108
- // instance registry).
109
- if (!domElement.ckeditorInstance) {
110
- domElement.ckeditorInstance = this.editor;
111
- }
112
- // Register the element, so it becomes available for Alt+F10 and Esc navigation.
113
- this.focusTracker.add(domElement);
114
- const setUpKeystrokeHandler = () => {
115
- // The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).
116
- // Do not duplicate listeners.
117
- if (this.editor.editing.view.getDomRoot(rootName)) {
118
- return;
119
- }
120
- this.editor.keystrokes.listenTo(domElement);
121
- };
122
- // For editable elements set by features after EditorUI is ready (e.g. source editing).
123
- if (this.isReady) {
124
- setUpKeystrokeHandler();
125
- }
126
- // For editable elements set while the editor is being created (e.g. DOM roots).
127
- else {
128
- this.once('ready', setUpKeystrokeHandler);
129
- }
130
- }
131
- /**
132
- * Removes the editable from the editor UI. Removes all handlers added by {@link #setEditableElement}.
133
- *
134
- * @param rootName The name of the editable element to remove.
135
- */
136
- removeEditableElement(rootName) {
137
- const domElement = this._editableElementsMap.get(rootName);
138
- if (!domElement) {
139
- return;
140
- }
141
- this._editableElementsMap.delete(rootName);
142
- this.editor.keystrokes.stopListening(domElement);
143
- this.focusTracker.remove(domElement);
144
- domElement.ckeditorInstance = null;
145
- }
146
- /**
147
- * Returns the editable editor element with the given name or null if editable does not exist.
148
- *
149
- * @param rootName The editable name.
150
- */
151
- getEditableElement(rootName = 'main') {
152
- return this._editableElementsMap.get(rootName);
153
- }
154
- /**
155
- * Returns array of names of all editor editable elements.
156
- */
157
- getEditableElementsNames() {
158
- return this._editableElementsMap.keys();
159
- }
160
- /**
161
- * Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.
162
- *
163
- * Focusable toolbars can be accessed (focused) by users by pressing the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
164
- * Successive keystroke presses navigate over available toolbars.
165
- *
166
- * @param toolbarView A instance of the toolbar to be registered.
167
- */
168
- addToolbar(toolbarView, options = {}) {
169
- if (toolbarView.isRendered) {
170
- this.focusTracker.add(toolbarView.element);
171
- this.editor.keystrokes.listenTo(toolbarView.element);
172
- }
173
- else {
174
- toolbarView.once('render', () => {
175
- this.focusTracker.add(toolbarView.element);
176
- this.editor.keystrokes.listenTo(toolbarView.element);
177
- });
178
- }
179
- this._focusableToolbarDefinitions.push({ toolbarView, options });
180
- }
181
- /**
182
- * Stores all editable elements used by the editor instance.
183
- *
184
- * @deprecated
185
- */
186
- get _editableElements() {
187
- /**
188
- * The {@link module:ui/editorui/editorui~EditorUI#_editableElements `EditorUI#_editableElements`} property has been
189
- * deprecated and will be removed in the near future. Please use
190
- * {@link module:ui/editorui/editorui~EditorUI#setEditableElement `setEditableElement()`} and
191
- * {@link module:ui/editorui/editorui~EditorUI#getEditableElement `getEditableElement()`} methods instead.
192
- *
193
- * @error editor-ui-deprecated-editable-elements
194
- * @param editorUI Editor UI instance the deprecated property belongs to.
195
- */
196
- console.warn('editor-ui-deprecated-editable-elements: ' +
197
- 'The EditorUI#_editableElements property has been deprecated and will be removed in the near future.', { editorUI: this });
198
- return this._editableElementsMap;
199
- }
200
- /**
201
- * Returns viewport offsets object:
202
- *
203
- * ```js
204
- * {
205
- * top: Number,
206
- * right: Number,
207
- * bottom: Number,
208
- * left: Number
209
- * }
210
- * ```
211
- *
212
- * Only top property is currently supported.
213
- */
214
- _readViewportOffsetFromConfig() {
215
- const editor = this.editor;
216
- const viewportOffsetConfig = editor.config.get('ui.viewportOffset');
217
- if (viewportOffsetConfig) {
218
- return viewportOffsetConfig;
219
- }
220
- // Not present in EditorConfig type, because it's legacy. Hence the `as` expression.
221
- const legacyOffsetConfig = editor.config.get('toolbar.viewportTopOffset');
222
- // Fall back to deprecated toolbar config.
223
- if (legacyOffsetConfig) {
224
- /**
225
- * The {@link module:core/editor/editorconfig~EditorConfig#toolbar `EditorConfig#toolbar.viewportTopOffset`}
226
- * property has been deprecated and will be removed in the near future. Please use
227
- * {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset`} instead.
228
- *
229
- * @error editor-ui-deprecated-viewport-offset-config
230
- */
231
- console.warn('editor-ui-deprecated-viewport-offset-config: ' +
232
- 'The `toolbar.vieportTopOffset` configuration option is deprecated. ' +
233
- 'It will be removed from future CKEditor versions. Use `ui.viewportOffset.top` instead.');
234
- return { top: legacyOffsetConfig };
235
- }
236
- // More keys to come in the future.
237
- return { top: 0 };
238
- }
239
- /**
240
- * Starts listening for <kbd>Alt</kbd> + <kbd>F10</kbd> and <kbd>Esc</kbd> keystrokes in the context of focusable
241
- * {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}
242
- * to allow users navigate across the UI.
243
- */
244
- _initFocusTracking() {
245
- const editor = this.editor;
246
- const editingView = editor.editing.view;
247
- let lastFocusedForeignElement;
248
- let candidateDefinitions;
249
- // Focus the next focusable toolbar on <kbd>Alt</kbd> + <kbd>F10</kbd>.
250
- editor.keystrokes.set('Alt+F10', (data, cancel) => {
251
- const focusedElement = this.focusTracker.focusedElement;
252
- // Focus moved out of a DOM element that
253
- // * is not a toolbar,
254
- // * does not belong to the editing view (e.g. source editing).
255
- if (Array.from(this._editableElementsMap.values()).includes(focusedElement) &&
256
- !Array.from(editingView.domRoots.values()).includes(focusedElement)) {
257
- lastFocusedForeignElement = focusedElement;
258
- }
259
- const currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();
260
- // * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.
261
- // This ensures, the navigation works always the same and no pair of toolbars takes over
262
- // (e.g. image and table toolbars when a selected image is inside a cell).
263
- // * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,
264
- // there were no candidates so they must be obtained (#12339).
265
- if (!currentFocusedToolbarDefinition || !candidateDefinitions) {
266
- candidateDefinitions = this._getFocusableCandidateToolbarDefinitions();
267
- }
268
- // In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.
269
- // This prevents an infinite loop.
270
- for (let i = 0; i < candidateDefinitions.length; i++) {
271
- const candidateDefinition = candidateDefinitions.shift();
272
- // Put the first definition to the back of the array. This allows circular navigation over all toolbars
273
- // on successive presses of Alt+F10.
274
- candidateDefinitions.push(candidateDefinition);
275
- // Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the
276
- // first item as per ToolbarView#focus() if the user navigated inside the toolbar.
277
- if (candidateDefinition !== currentFocusedToolbarDefinition &&
278
- this._focusFocusableCandidateToolbar(candidateDefinition)) {
279
- // Clean up after a current visible toolbar when switching to the next one.
280
- if (currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur) {
281
- currentFocusedToolbarDefinition.options.afterBlur();
282
- }
283
- break;
284
- }
285
- }
286
- cancel();
287
- });
288
- // Blur the focused toolbar on <kbd>Esc</kbd> and bring the focus back to its origin.
289
- editor.keystrokes.set('Esc', (data, cancel) => {
290
- const focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();
291
- if (!focusedToolbarDef) {
292
- return;
293
- }
294
- // Bring focus back to where it came from before focusing the toolbar:
295
- // 1. If it came from outside the engine view (e.g. source editing), move it there.
296
- if (lastFocusedForeignElement) {
297
- lastFocusedForeignElement.focus();
298
- lastFocusedForeignElement = null;
299
- }
300
- // 2. There are two possibilities left:
301
- // 2.1. It could be that the focus went from an editable element in the view (root or nested).
302
- // 2.2. It could be the focus went straight to the toolbar before even focusing the editing area.
303
- // In either case, just focus the view editing. The focus will land where it belongs.
304
- else {
305
- editor.editing.view.focus();
306
- }
307
- // Clean up after the toolbar if there is anything to do there.
308
- if (focusedToolbarDef.options.afterBlur) {
309
- focusedToolbarDef.options.afterBlur();
310
- }
311
- cancel();
312
- });
313
- }
314
- /**
315
- * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.
316
- *
317
- * Focusable toolbars candidates are either:
318
- * * already visible,
319
- * * have `beforeFocus()` set in their {@link module:ui/editorui/editorui~FocusableToolbarDefinition definition} that suggests that
320
- * they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible
321
- * at this stage because it depends on its implementation, that in turn depends on the editing context (selection).
322
- *
323
- * **Note**: Contextual toolbars take precedence over regular toolbars.
324
- */
325
- _getFocusableCandidateToolbarDefinitions() {
326
- const definitions = [];
327
- for (const toolbarDef of this._focusableToolbarDefinitions) {
328
- const { toolbarView, options } = toolbarDef;
329
- if (isVisible(toolbarView.element) || options.beforeFocus) {
330
- definitions.push(toolbarDef);
331
- }
332
- }
333
- // Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.
334
- // For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.
335
- definitions.sort((defA, defB) => getToolbarDefinitionWeight(defA) - getToolbarDefinitionWeight(defB));
336
- return definitions;
337
- }
338
- /**
339
- * Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).
340
- *
341
- * `null` is returned when no toolbar is currently focused.
342
- */
343
- _getCurrentFocusedToolbarDefinition() {
344
- for (const definition of this._focusableToolbarDefinitions) {
345
- if (definition.toolbarView.element && definition.toolbarView.element.contains(this.focusTracker.focusedElement)) {
346
- return definition;
347
- }
348
- }
349
- return null;
350
- }
351
- /**
352
- * Focuses a focusable toolbar candidate using its definition.
353
- *
354
- * @param candidateToolbarDefinition A definition of the toolbar to focus.
355
- * @returns `true` when the toolbar candidate was focused. `false` otherwise.
356
- */
357
- _focusFocusableCandidateToolbar(candidateToolbarDefinition) {
358
- const { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;
359
- if (beforeFocus) {
360
- beforeFocus();
361
- }
362
- // If it didn't show up after beforeFocus(), it's not focusable at all.
363
- if (!isVisible(toolbarView.element)) {
364
- return false;
365
- }
366
- toolbarView.focus();
367
- return true;
368
- }
369
- /**
370
- * Provides an integration between {@link #viewportOffset} and {@link module:utils/dom/scroll~scrollViewportToShowTarget}.
371
- * It allows the UI-agnostic engine method to consider user-configured viewport offsets specific for the integration.
372
- *
373
- * @param evt The `scrollToTheSelection` event info.
374
- * @param data The payload carried by the `scrollToTheSelection` event.
375
- */
376
- _handleScrollToTheSelection(evt, data) {
377
- const configuredViewportOffset = {
378
- top: 0,
379
- bottom: 0,
380
- left: 0,
381
- right: 0,
382
- ...this.viewportOffset
383
- };
384
- data.viewportOffset.top += configuredViewportOffset.top;
385
- data.viewportOffset.bottom += configuredViewportOffset.bottom;
386
- data.viewportOffset.left += configuredViewportOffset.left;
387
- data.viewportOffset.right += configuredViewportOffset.right;
388
- }
389
- }
390
- /**
391
- * Returns a number (weight) for a toolbar definition. Visible toolbars have a higher priority and so do
392
- * contextual toolbars (displayed in the context of a content, for instance, an image toolbar).
393
- *
394
- * A standard invisible toolbar is the heaviest. A visible contextual toolbar is the lightest.
395
- *
396
- * @param toolbarDef A toolbar definition to be weighted.
397
- */
398
- function getToolbarDefinitionWeight(toolbarDef) {
399
- const { toolbarView, options } = toolbarDef;
400
- let weight = 10;
401
- // Prioritize already visible toolbars. They should get focused first.
402
- if (isVisible(toolbarView.element)) {
403
- weight--;
404
- }
405
- // Prioritize contextual toolbars. They are displayed at the selection.
406
- if (options.isContextual) {
407
- weight--;
408
- }
409
- return weight;
410
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, 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/editorui/editorui
7
+ */
8
+ /* globals console */
9
+ import ComponentFactory from '../componentfactory';
10
+ import TooltipManager from '../tooltipmanager';
11
+ import PoweredBy from './poweredby';
12
+ import { ObservableMixin, isVisible, FocusTracker } from '@ckeditor/ckeditor5-utils';
13
+ /**
14
+ * A class providing the minimal interface that is required to successfully bootstrap any editor UI.
15
+ */
16
+ export default class EditorUI extends ObservableMixin() {
17
+ /**
18
+ * Creates an instance of the editor UI class.
19
+ *
20
+ * @param editor The editor instance.
21
+ */
22
+ constructor(editor) {
23
+ super();
24
+ /**
25
+ * Indicates the UI is ready. Set `true` after {@link #event:ready} event is fired.
26
+ *
27
+ * @readonly
28
+ * @default false
29
+ */
30
+ this.isReady = false;
31
+ /**
32
+ * Stores all editable elements used by the editor instance.
33
+ */
34
+ this._editableElementsMap = new Map();
35
+ /**
36
+ * All available & focusable toolbars.
37
+ */
38
+ this._focusableToolbarDefinitions = [];
39
+ const editingView = editor.editing.view;
40
+ this.editor = editor;
41
+ this.componentFactory = new ComponentFactory(editor);
42
+ this.focusTracker = new FocusTracker();
43
+ this.tooltipManager = new TooltipManager(editor);
44
+ this.poweredBy = new PoweredBy(editor);
45
+ this.set('viewportOffset', this._readViewportOffsetFromConfig());
46
+ this.once('ready', () => {
47
+ this.isReady = true;
48
+ });
49
+ // Informs UI components that should be refreshed after layout change.
50
+ this.listenTo(editingView.document, 'layoutChanged', this.update.bind(this));
51
+ this.listenTo(editingView, 'scrollToTheSelection', this._handleScrollToTheSelection.bind(this));
52
+ this._initFocusTracking();
53
+ }
54
+ /**
55
+ * The main (outermost) DOM element of the editor UI.
56
+ *
57
+ * For example, in {@link module:editor-classic/classiceditor~ClassicEditor} it is a `<div>` which
58
+ * wraps the editable element and the toolbar. In {@link module:editor-inline/inlineeditor~InlineEditor}
59
+ * it is the editable element itself (as there is no other wrapper). However, in
60
+ * {@link module:editor-decoupled/decouplededitor~DecoupledEditor} it is set to `null` because this editor does not
61
+ * come with a single "main" HTML element (its editable element and toolbar are separate).
62
+ *
63
+ * This property can be understood as a shorthand for retrieving the element that a specific editor integration
64
+ * considers to be its main DOM element.
65
+ */
66
+ get element() {
67
+ return null;
68
+ }
69
+ /**
70
+ * Fires the {@link module:ui/editorui/editorui~EditorUI#event:update `update`} event.
71
+ *
72
+ * This method should be called when the editor UI (e.g. positions of its balloons) needs to be updated due to
73
+ * some environmental change which CKEditor 5 is not aware of (e.g. resize of a container in which it is used).
74
+ */
75
+ update() {
76
+ this.fire('update');
77
+ }
78
+ /**
79
+ * Destroys the UI.
80
+ */
81
+ destroy() {
82
+ this.stopListening();
83
+ this.focusTracker.destroy();
84
+ this.tooltipManager.destroy(this.editor);
85
+ this.poweredBy.destroy();
86
+ // Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
87
+ for (const domElement of this._editableElementsMap.values()) {
88
+ domElement.ckeditorInstance = null;
89
+ this.editor.keystrokes.stopListening(domElement);
90
+ }
91
+ this._editableElementsMap = new Map();
92
+ this._focusableToolbarDefinitions = [];
93
+ }
94
+ /**
95
+ * Stores the native DOM editable element used by the editor under a unique name.
96
+ *
97
+ * Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable
98
+ * editable area, they can use the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke to navigate over editor toolbars. See {@link #addToolbar}.
99
+ *
100
+ * @param rootName The unique name of the editable element.
101
+ * @param domElement The native DOM editable element.
102
+ */
103
+ setEditableElement(rootName, domElement) {
104
+ this._editableElementsMap.set(rootName, domElement);
105
+ // Put a reference to the CKEditor instance in the editable native DOM element.
106
+ // It helps 3rd–party software (browser extensions, other libraries) access and recognize
107
+ // CKEditor 5 instances (editing roots) and use their API (there is no global editor
108
+ // instance registry).
109
+ if (!domElement.ckeditorInstance) {
110
+ domElement.ckeditorInstance = this.editor;
111
+ }
112
+ // Register the element, so it becomes available for Alt+F10 and Esc navigation.
113
+ this.focusTracker.add(domElement);
114
+ const setUpKeystrokeHandler = () => {
115
+ // The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).
116
+ // Do not duplicate listeners.
117
+ if (this.editor.editing.view.getDomRoot(rootName)) {
118
+ return;
119
+ }
120
+ this.editor.keystrokes.listenTo(domElement);
121
+ };
122
+ // For editable elements set by features after EditorUI is ready (e.g. source editing).
123
+ if (this.isReady) {
124
+ setUpKeystrokeHandler();
125
+ }
126
+ // For editable elements set while the editor is being created (e.g. DOM roots).
127
+ else {
128
+ this.once('ready', setUpKeystrokeHandler);
129
+ }
130
+ }
131
+ /**
132
+ * Removes the editable from the editor UI. Removes all handlers added by {@link #setEditableElement}.
133
+ *
134
+ * @param rootName The name of the editable element to remove.
135
+ */
136
+ removeEditableElement(rootName) {
137
+ const domElement = this._editableElementsMap.get(rootName);
138
+ if (!domElement) {
139
+ return;
140
+ }
141
+ this._editableElementsMap.delete(rootName);
142
+ this.editor.keystrokes.stopListening(domElement);
143
+ this.focusTracker.remove(domElement);
144
+ domElement.ckeditorInstance = null;
145
+ }
146
+ /**
147
+ * Returns the editable editor element with the given name or null if editable does not exist.
148
+ *
149
+ * @param rootName The editable name.
150
+ */
151
+ getEditableElement(rootName = 'main') {
152
+ return this._editableElementsMap.get(rootName);
153
+ }
154
+ /**
155
+ * Returns array of names of all editor editable elements.
156
+ */
157
+ getEditableElementsNames() {
158
+ return this._editableElementsMap.keys();
159
+ }
160
+ /**
161
+ * Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.
162
+ *
163
+ * Focusable toolbars can be accessed (focused) by users by pressing the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
164
+ * Successive keystroke presses navigate over available toolbars.
165
+ *
166
+ * @param toolbarView A instance of the toolbar to be registered.
167
+ */
168
+ addToolbar(toolbarView, options = {}) {
169
+ if (toolbarView.isRendered) {
170
+ this.focusTracker.add(toolbarView.element);
171
+ this.editor.keystrokes.listenTo(toolbarView.element);
172
+ }
173
+ else {
174
+ toolbarView.once('render', () => {
175
+ this.focusTracker.add(toolbarView.element);
176
+ this.editor.keystrokes.listenTo(toolbarView.element);
177
+ });
178
+ }
179
+ this._focusableToolbarDefinitions.push({ toolbarView, options });
180
+ }
181
+ /**
182
+ * Stores all editable elements used by the editor instance.
183
+ *
184
+ * @deprecated
185
+ */
186
+ get _editableElements() {
187
+ /**
188
+ * The {@link module:ui/editorui/editorui~EditorUI#_editableElements `EditorUI#_editableElements`} property has been
189
+ * deprecated and will be removed in the near future. Please use
190
+ * {@link module:ui/editorui/editorui~EditorUI#setEditableElement `setEditableElement()`} and
191
+ * {@link module:ui/editorui/editorui~EditorUI#getEditableElement `getEditableElement()`} methods instead.
192
+ *
193
+ * @error editor-ui-deprecated-editable-elements
194
+ * @param editorUI Editor UI instance the deprecated property belongs to.
195
+ */
196
+ console.warn('editor-ui-deprecated-editable-elements: ' +
197
+ 'The EditorUI#_editableElements property has been deprecated and will be removed in the near future.', { editorUI: this });
198
+ return this._editableElementsMap;
199
+ }
200
+ /**
201
+ * Returns viewport offsets object:
202
+ *
203
+ * ```js
204
+ * {
205
+ * top: Number,
206
+ * right: Number,
207
+ * bottom: Number,
208
+ * left: Number
209
+ * }
210
+ * ```
211
+ *
212
+ * Only top property is currently supported.
213
+ */
214
+ _readViewportOffsetFromConfig() {
215
+ const editor = this.editor;
216
+ const viewportOffsetConfig = editor.config.get('ui.viewportOffset');
217
+ if (viewportOffsetConfig) {
218
+ return viewportOffsetConfig;
219
+ }
220
+ // Not present in EditorConfig type, because it's legacy. Hence the `as` expression.
221
+ const legacyOffsetConfig = editor.config.get('toolbar.viewportTopOffset');
222
+ // Fall back to deprecated toolbar config.
223
+ if (legacyOffsetConfig) {
224
+ /**
225
+ * The {@link module:core/editor/editorconfig~EditorConfig#toolbar `EditorConfig#toolbar.viewportTopOffset`}
226
+ * property has been deprecated and will be removed in the near future. Please use
227
+ * {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset`} instead.
228
+ *
229
+ * @error editor-ui-deprecated-viewport-offset-config
230
+ */
231
+ console.warn('editor-ui-deprecated-viewport-offset-config: ' +
232
+ 'The `toolbar.vieportTopOffset` configuration option is deprecated. ' +
233
+ 'It will be removed from future CKEditor versions. Use `ui.viewportOffset.top` instead.');
234
+ return { top: legacyOffsetConfig };
235
+ }
236
+ // More keys to come in the future.
237
+ return { top: 0 };
238
+ }
239
+ /**
240
+ * Starts listening for <kbd>Alt</kbd> + <kbd>F10</kbd> and <kbd>Esc</kbd> keystrokes in the context of focusable
241
+ * {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}
242
+ * to allow users navigate across the UI.
243
+ */
244
+ _initFocusTracking() {
245
+ const editor = this.editor;
246
+ const editingView = editor.editing.view;
247
+ let lastFocusedForeignElement;
248
+ let candidateDefinitions;
249
+ // Focus the next focusable toolbar on <kbd>Alt</kbd> + <kbd>F10</kbd>.
250
+ editor.keystrokes.set('Alt+F10', (data, cancel) => {
251
+ const focusedElement = this.focusTracker.focusedElement;
252
+ // Focus moved out of a DOM element that
253
+ // * is not a toolbar,
254
+ // * does not belong to the editing view (e.g. source editing).
255
+ if (Array.from(this._editableElementsMap.values()).includes(focusedElement) &&
256
+ !Array.from(editingView.domRoots.values()).includes(focusedElement)) {
257
+ lastFocusedForeignElement = focusedElement;
258
+ }
259
+ const currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();
260
+ // * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.
261
+ // This ensures, the navigation works always the same and no pair of toolbars takes over
262
+ // (e.g. image and table toolbars when a selected image is inside a cell).
263
+ // * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,
264
+ // there were no candidates so they must be obtained (#12339).
265
+ if (!currentFocusedToolbarDefinition || !candidateDefinitions) {
266
+ candidateDefinitions = this._getFocusableCandidateToolbarDefinitions();
267
+ }
268
+ // In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.
269
+ // This prevents an infinite loop.
270
+ for (let i = 0; i < candidateDefinitions.length; i++) {
271
+ const candidateDefinition = candidateDefinitions.shift();
272
+ // Put the first definition to the back of the array. This allows circular navigation over all toolbars
273
+ // on successive presses of Alt+F10.
274
+ candidateDefinitions.push(candidateDefinition);
275
+ // Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the
276
+ // first item as per ToolbarView#focus() if the user navigated inside the toolbar.
277
+ if (candidateDefinition !== currentFocusedToolbarDefinition &&
278
+ this._focusFocusableCandidateToolbar(candidateDefinition)) {
279
+ // Clean up after a current visible toolbar when switching to the next one.
280
+ if (currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur) {
281
+ currentFocusedToolbarDefinition.options.afterBlur();
282
+ }
283
+ break;
284
+ }
285
+ }
286
+ cancel();
287
+ });
288
+ // Blur the focused toolbar on <kbd>Esc</kbd> and bring the focus back to its origin.
289
+ editor.keystrokes.set('Esc', (data, cancel) => {
290
+ const focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();
291
+ if (!focusedToolbarDef) {
292
+ return;
293
+ }
294
+ // Bring focus back to where it came from before focusing the toolbar:
295
+ // 1. If it came from outside the engine view (e.g. source editing), move it there.
296
+ if (lastFocusedForeignElement) {
297
+ lastFocusedForeignElement.focus();
298
+ lastFocusedForeignElement = null;
299
+ }
300
+ // 2. There are two possibilities left:
301
+ // 2.1. It could be that the focus went from an editable element in the view (root or nested).
302
+ // 2.2. It could be the focus went straight to the toolbar before even focusing the editing area.
303
+ // In either case, just focus the view editing. The focus will land where it belongs.
304
+ else {
305
+ editor.editing.view.focus();
306
+ }
307
+ // Clean up after the toolbar if there is anything to do there.
308
+ if (focusedToolbarDef.options.afterBlur) {
309
+ focusedToolbarDef.options.afterBlur();
310
+ }
311
+ cancel();
312
+ });
313
+ }
314
+ /**
315
+ * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.
316
+ *
317
+ * Focusable toolbars candidates are either:
318
+ * * already visible,
319
+ * * have `beforeFocus()` set in their {@link module:ui/editorui/editorui~FocusableToolbarDefinition definition} that suggests that
320
+ * they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible
321
+ * at this stage because it depends on its implementation, that in turn depends on the editing context (selection).
322
+ *
323
+ * **Note**: Contextual toolbars take precedence over regular toolbars.
324
+ */
325
+ _getFocusableCandidateToolbarDefinitions() {
326
+ const definitions = [];
327
+ for (const toolbarDef of this._focusableToolbarDefinitions) {
328
+ const { toolbarView, options } = toolbarDef;
329
+ if (isVisible(toolbarView.element) || options.beforeFocus) {
330
+ definitions.push(toolbarDef);
331
+ }
332
+ }
333
+ // Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.
334
+ // For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.
335
+ definitions.sort((defA, defB) => getToolbarDefinitionWeight(defA) - getToolbarDefinitionWeight(defB));
336
+ return definitions;
337
+ }
338
+ /**
339
+ * Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).
340
+ *
341
+ * `null` is returned when no toolbar is currently focused.
342
+ */
343
+ _getCurrentFocusedToolbarDefinition() {
344
+ for (const definition of this._focusableToolbarDefinitions) {
345
+ if (definition.toolbarView.element && definition.toolbarView.element.contains(this.focusTracker.focusedElement)) {
346
+ return definition;
347
+ }
348
+ }
349
+ return null;
350
+ }
351
+ /**
352
+ * Focuses a focusable toolbar candidate using its definition.
353
+ *
354
+ * @param candidateToolbarDefinition A definition of the toolbar to focus.
355
+ * @returns `true` when the toolbar candidate was focused. `false` otherwise.
356
+ */
357
+ _focusFocusableCandidateToolbar(candidateToolbarDefinition) {
358
+ const { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;
359
+ if (beforeFocus) {
360
+ beforeFocus();
361
+ }
362
+ // If it didn't show up after beforeFocus(), it's not focusable at all.
363
+ if (!isVisible(toolbarView.element)) {
364
+ return false;
365
+ }
366
+ toolbarView.focus();
367
+ return true;
368
+ }
369
+ /**
370
+ * Provides an integration between {@link #viewportOffset} and {@link module:utils/dom/scroll~scrollViewportToShowTarget}.
371
+ * It allows the UI-agnostic engine method to consider user-configured viewport offsets specific for the integration.
372
+ *
373
+ * @param evt The `scrollToTheSelection` event info.
374
+ * @param data The payload carried by the `scrollToTheSelection` event.
375
+ */
376
+ _handleScrollToTheSelection(evt, data) {
377
+ const configuredViewportOffset = {
378
+ top: 0,
379
+ bottom: 0,
380
+ left: 0,
381
+ right: 0,
382
+ ...this.viewportOffset
383
+ };
384
+ data.viewportOffset.top += configuredViewportOffset.top;
385
+ data.viewportOffset.bottom += configuredViewportOffset.bottom;
386
+ data.viewportOffset.left += configuredViewportOffset.left;
387
+ data.viewportOffset.right += configuredViewportOffset.right;
388
+ }
389
+ }
390
+ /**
391
+ * Returns a number (weight) for a toolbar definition. Visible toolbars have a higher priority and so do
392
+ * contextual toolbars (displayed in the context of a content, for instance, an image toolbar).
393
+ *
394
+ * A standard invisible toolbar is the heaviest. A visible contextual toolbar is the lightest.
395
+ *
396
+ * @param toolbarDef A toolbar definition to be weighted.
397
+ */
398
+ function getToolbarDefinitionWeight(toolbarDef) {
399
+ const { toolbarView, options } = toolbarDef;
400
+ let weight = 10;
401
+ // Prioritize already visible toolbars. They should get focused first.
402
+ if (isVisible(toolbarView.element)) {
403
+ weight--;
404
+ }
405
+ // Prioritize contextual toolbars. They are displayed at the selection.
406
+ if (options.isContextual) {
407
+ weight--;
408
+ }
409
+ return weight;
410
+ }