@ckeditor/ckeditor5-core 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.
- package/lang/translations/es-co.po +49 -0
- package/lang/translations/gl.po +1 -1
- package/lang/translations/tt.po +1 -1
- package/package.json +17 -17
- package/src/editor/editorconfig.jsdoc +94 -10
- package/src/editor/editorui.js +309 -2
- package/src/index.js +12 -1
- package/theme/icons/bold.svg +1 -0
- package/theme/icons/importexport.svg +1 -0
- package/theme/icons/paragraph.svg +1 -0
- package/theme/icons/plus.svg +1 -0
- package/theme/icons/text.svg +1 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# !!! IMPORTANT !!!
|
|
4
|
+
#
|
|
5
|
+
# Before you edit this file, please keep in mind that contributing to the project
|
|
6
|
+
# translations is possible ONLY via the Transifex online service.
|
|
7
|
+
#
|
|
8
|
+
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
|
9
|
+
#
|
|
10
|
+
# To learn more, check out the official contributor's guide:
|
|
11
|
+
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
|
12
|
+
#
|
|
13
|
+
msgid ""
|
|
14
|
+
msgstr ""
|
|
15
|
+
"Language-Team: Spanish (Colombia) (https://www.transifex.com/ckeditor/teams/11143/es_CO/)\n"
|
|
16
|
+
"Language: es_CO\n"
|
|
17
|
+
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
|
18
|
+
|
|
19
|
+
msgctxt "Label for the Cancel button."
|
|
20
|
+
msgid "Cancel"
|
|
21
|
+
msgstr "Cancelar"
|
|
22
|
+
|
|
23
|
+
msgctxt "The label used by a button next to the color palette in the color picker that removes the color (resets it to an empty value, example usages in font color or table properties)."
|
|
24
|
+
msgid "Remove color"
|
|
25
|
+
msgstr "Quitar color"
|
|
26
|
+
|
|
27
|
+
msgctxt "The label used by a button next to the color palette in the color picker that restores the default value if the default table properties are specified."
|
|
28
|
+
msgid "Restore default"
|
|
29
|
+
msgstr "Restaurar valores predeterminados"
|
|
30
|
+
|
|
31
|
+
msgctxt "Label for the Save button."
|
|
32
|
+
msgid "Save"
|
|
33
|
+
msgstr "Guardar"
|
|
34
|
+
|
|
35
|
+
msgctxt "Label of a toolbar button which reveals more toolbar items."
|
|
36
|
+
msgid "Show more items"
|
|
37
|
+
msgstr "Mostrar más elementos"
|
|
38
|
+
|
|
39
|
+
msgctxt "Label for an ‘X of Y’ status of a typical next/previous navigation. For instance, ‘Page 5 of 20’ or 'Search result 5 of 20'."
|
|
40
|
+
msgid "%0 of %1"
|
|
41
|
+
msgstr "%0 de %1"
|
|
42
|
+
|
|
43
|
+
msgctxt "A generic error message displayed on upload failure. The file name is concatenated to this text."
|
|
44
|
+
msgid "Cannot upload file:"
|
|
45
|
+
msgstr "No se pudo cargar el archivo:"
|
|
46
|
+
|
|
47
|
+
msgctxt "Accessible label of the specific editing area of the editor acting as a root of the entire application."
|
|
48
|
+
msgid "Rich Text Editor. Editing area: %0"
|
|
49
|
+
msgstr "Editor de texto enriquecido. Área de edición: %0"
|
package/lang/translations/gl.po
CHANGED
|
@@ -46,4 +46,4 @@ msgstr "Non é posíbel cargar o ficheiro:"
|
|
|
46
46
|
|
|
47
47
|
msgctxt "Accessible label of the specific editing area of the editor acting as a root of the entire application."
|
|
48
48
|
msgid "Rich Text Editor. Editing area: %0"
|
|
49
|
-
msgstr ""
|
|
49
|
+
msgstr "Editor de texto mellorado. Área de edición: %0"
|
package/lang/translations/tt.po
CHANGED
|
@@ -18,7 +18,7 @@ msgstr ""
|
|
|
18
18
|
|
|
19
19
|
msgctxt "Label for the Cancel button."
|
|
20
20
|
msgid "Cancel"
|
|
21
|
-
msgstr ""
|
|
21
|
+
msgstr "Баш тарт"
|
|
22
22
|
|
|
23
23
|
msgctxt "The label used by a button next to the color palette in the color picker that removes the color (resets it to an empty value, example usages in font color or table properties)."
|
|
24
24
|
msgid "Remove color"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-core",
|
|
3
|
-
"version": "35.0
|
|
3
|
+
"version": "35.2.0",
|
|
4
4
|
"description": "The core architecture of CKEditor 5 – the best browser-based rich text editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wysiwyg",
|
|
@@ -23,25 +23,25 @@
|
|
|
23
23
|
],
|
|
24
24
|
"main": "src/index.js",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@ckeditor/ckeditor5-engine": "^35.0
|
|
27
|
-
"@ckeditor/ckeditor5-ui": "^35.0
|
|
28
|
-
"@ckeditor/ckeditor5-utils": "^35.0
|
|
26
|
+
"@ckeditor/ckeditor5-engine": "^35.2.0",
|
|
27
|
+
"@ckeditor/ckeditor5-ui": "^35.2.0",
|
|
28
|
+
"@ckeditor/ckeditor5-utils": "^35.2.0",
|
|
29
29
|
"lodash-es": "^4.17.15"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@ckeditor/ckeditor5-autoformat": "^35.0
|
|
33
|
-
"@ckeditor/ckeditor5-basic-styles": "^35.0
|
|
34
|
-
"@ckeditor/ckeditor5-block-quote": "^35.0
|
|
35
|
-
"@ckeditor/ckeditor5-editor-classic": "^35.0
|
|
36
|
-
"@ckeditor/ckeditor5-essentials": "^35.0
|
|
37
|
-
"@ckeditor/ckeditor5-heading": "^35.0
|
|
38
|
-
"@ckeditor/ckeditor5-image": "^35.0
|
|
39
|
-
"@ckeditor/ckeditor5-indent": "^35.0
|
|
40
|
-
"@ckeditor/ckeditor5-link": "^35.0
|
|
41
|
-
"@ckeditor/ckeditor5-list": "^35.0
|
|
42
|
-
"@ckeditor/ckeditor5-media-embed": "^35.0
|
|
43
|
-
"@ckeditor/ckeditor5-paragraph": "^35.0
|
|
44
|
-
"@ckeditor/ckeditor5-table": "^35.0
|
|
32
|
+
"@ckeditor/ckeditor5-autoformat": "^35.2.0",
|
|
33
|
+
"@ckeditor/ckeditor5-basic-styles": "^35.2.0",
|
|
34
|
+
"@ckeditor/ckeditor5-block-quote": "^35.2.0",
|
|
35
|
+
"@ckeditor/ckeditor5-editor-classic": "^35.2.0",
|
|
36
|
+
"@ckeditor/ckeditor5-essentials": "^35.2.0",
|
|
37
|
+
"@ckeditor/ckeditor5-heading": "^35.2.0",
|
|
38
|
+
"@ckeditor/ckeditor5-image": "^35.2.0",
|
|
39
|
+
"@ckeditor/ckeditor5-indent": "^35.2.0",
|
|
40
|
+
"@ckeditor/ckeditor5-link": "^35.2.0",
|
|
41
|
+
"@ckeditor/ckeditor5-list": "^35.2.0",
|
|
42
|
+
"@ckeditor/ckeditor5-media-embed": "^35.2.0",
|
|
43
|
+
"@ckeditor/ckeditor5-paragraph": "^35.2.0",
|
|
44
|
+
"@ckeditor/ckeditor5-table": "^35.2.0"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=14.0.0",
|
|
@@ -110,7 +110,8 @@
|
|
|
110
110
|
*
|
|
111
111
|
* **Note:** This configuration works only for simple plugins which utilize the
|
|
112
112
|
* {@link module:core/plugin~PluginInterface plugin interface} and have no dependencies. To extend a
|
|
113
|
-
* build with complex features, create a {@glink installation/getting-started/quick-start-other#creating-custom-builds-with-online-builder
|
|
113
|
+
* build with complex features, create a {@glink installation/getting-started/quick-start-other#creating-custom-builds-with-online-builder
|
|
114
|
+
* custom build}.
|
|
114
115
|
*
|
|
115
116
|
* **Note:** Make sure you include the new features in you toolbar configuration. Learn more
|
|
116
117
|
* about the {@glink features/toolbar/toolbar toolbar setup}.
|
|
@@ -119,7 +120,8 @@
|
|
|
119
120
|
*/
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
|
-
* The list of plugins which should not be loaded despite being available in an {@glink installation/getting-started/predefined-builds
|
|
123
|
+
* The list of plugins which should not be loaded despite being available in an {@glink installation/getting-started/predefined-builds
|
|
124
|
+
* editor build}.
|
|
123
125
|
*
|
|
124
126
|
* const config = {
|
|
125
127
|
* removePlugins: [ 'Bold', 'Italic' ]
|
|
@@ -169,11 +171,90 @@
|
|
|
169
171
|
*
|
|
170
172
|
* Line break will work only in the extended format when `shouldNotGroupWhenFull` option is set to `true`.
|
|
171
173
|
*
|
|
172
|
-
*
|
|
174
|
+
* **Note**: To save space in your toolbar you can group several items into a dropdown:
|
|
175
|
+
*
|
|
176
|
+
* toolbar: [
|
|
177
|
+
* {
|
|
178
|
+
* label: 'Basic styles',
|
|
179
|
+
* icon: 'text',
|
|
180
|
+
* items: [ 'bold', 'italic', ... ]
|
|
181
|
+
* },
|
|
182
|
+
* '|',
|
|
183
|
+
* 'undo', 'redo'
|
|
184
|
+
* ]
|
|
185
|
+
*
|
|
186
|
+
* The code above will create a "Basic styles" dropdown with a "text" icon containing "bold" and "italic" buttons.
|
|
187
|
+
* You can customize the look of the dropdown by setting the `withText`, `icon`, and `tooltip` properties:
|
|
188
|
+
*
|
|
189
|
+
* * **Displaying a label**
|
|
190
|
+
*
|
|
191
|
+
* For instance, to hide the icon and to display the label only, you can use the following configuration:
|
|
192
|
+
*
|
|
193
|
+
* {
|
|
194
|
+
* label: 'Basic styles',
|
|
195
|
+
* // Show the textual label of the drop-down. Note that the "icon" property is not configured.
|
|
196
|
+
* withText: true,
|
|
197
|
+
* items: [ 'bold', 'italic', ... ]
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* * **Selecting an icon**
|
|
201
|
+
*
|
|
202
|
+
* You can use one of the common icons provided by the editor (`'bold'`, `'plus'`, `'text'`, `'importExport'`, `'alignLeft'`,
|
|
203
|
+
* `'paragraph'`, `'threeVerticalDots'`):
|
|
204
|
+
*
|
|
205
|
+
* {
|
|
206
|
+
* label: '...',
|
|
207
|
+
* // A "plus" sign icon works best for content insertion tools.
|
|
208
|
+
* icon: 'plus',
|
|
209
|
+
* items: [ ... ]
|
|
210
|
+
* }
|
|
211
|
+
*
|
|
212
|
+
* If no icon is specified, `'threeVerticalDots'` will be used as a default:
|
|
213
|
+
*
|
|
214
|
+
* // No icon specified, using a default one.
|
|
215
|
+
* {
|
|
216
|
+
* label: 'Default icon',
|
|
217
|
+
* items: [ ... ]
|
|
218
|
+
* }
|
|
219
|
+
*
|
|
220
|
+
* If `icon: false` is configured, no icon will be displayed at all and the text label will show up instead:
|
|
221
|
+
*
|
|
222
|
+
* // This drop-down has no icon. The text label will be displayed instead.
|
|
223
|
+
* {
|
|
224
|
+
* label: 'No icon',
|
|
225
|
+
* icon: false,
|
|
226
|
+
* items: [ ... ]
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* You can also set a custom icon for the drop-down by passing an SVG string:
|
|
230
|
+
*
|
|
231
|
+
* {
|
|
232
|
+
* label: '...',
|
|
233
|
+
* // If you want your icon to change the color dynamically (e.g. when the dropdown opens), avoid fill="..."
|
|
234
|
+
* // and stroke="..." styling attributes. Use solid shapes and avoid paths with strokes.
|
|
235
|
+
* icon: '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">...</svg>',
|
|
236
|
+
* items: [ ... ]
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* * **Customizing the tooltip**
|
|
240
|
+
*
|
|
241
|
+
* By default, the tooltip of the button shares its text with the label. You can customize it to better describe your dropdown
|
|
242
|
+
* using the `tooltip` property ({@link module:ui/button/buttonview~ButtonView#tooltip learn more}):
|
|
243
|
+
*
|
|
244
|
+
* {
|
|
245
|
+
* label: 'Drop-down label',
|
|
246
|
+
* tooltip: 'Custom tooltip of the drop-down',
|
|
247
|
+
* icon: '...',
|
|
248
|
+
* items: [ ... ]
|
|
249
|
+
* }
|
|
250
|
+
*
|
|
251
|
+
* * **`toolbar.viewportTopOffset` (deprecated)** – The offset (in pixels) from the top of the viewport used when positioning a
|
|
252
|
+
* sticky toolbar.
|
|
173
253
|
* Useful when a page with which the editor is being integrated has some other sticky or fixed elements
|
|
174
254
|
* (e.g. the top menu). Thanks to setting the toolbar offset the toolbar will not be positioned underneath or above the page's UI.
|
|
175
255
|
*
|
|
176
|
-
* **This property has been deprecated and will be removed in the future versions of CKEditor. Please use
|
|
256
|
+
* **This property has been deprecated and will be removed in the future versions of CKEditor. Please use
|
|
257
|
+
* `{@link module:core/editor/editorconfig~EditorConfig#ui EditorConfig#ui.viewportOffset}` instead.**
|
|
177
258
|
*
|
|
178
259
|
* * **`toolbar.shouldNotGroupWhenFull`** – When set to `true`, the toolbar will stop grouping items
|
|
179
260
|
* and let them wrap to the next line if there is not enough space to display them in a single row.
|
|
@@ -315,27 +396,30 @@
|
|
|
315
396
|
*
|
|
316
397
|
* Options which can be set using the UI config:
|
|
317
398
|
*
|
|
318
|
-
* * **`ui.viewportOffset`** – The offset (in pixels) of the viewport from every direction used when positioning a sticky toolbar
|
|
399
|
+
* * **`ui.viewportOffset`** – The offset (in pixels) of the viewport from every direction used when positioning a sticky toolbar
|
|
400
|
+
* or other absolutely positioned UI elements.
|
|
319
401
|
* Useful when a page with which the editor is being integrated has some other sticky or fixed elements
|
|
320
|
-
* (e.g. the top menu). Thanks to setting the UI viewport offset the toolbar and other contextual balloons will not be positioned
|
|
402
|
+
* (e.g. the top menu). Thanks to setting the UI viewport offset the toolbar and other contextual balloons will not be positioned
|
|
403
|
+
* underneath or above the page's UI.
|
|
321
404
|
*
|
|
322
405
|
* ui: {
|
|
323
406
|
* viewportOffset: { top: 10, right: 10, bottom: 10, left: 10 }
|
|
324
407
|
* }
|
|
325
408
|
*
|
|
326
|
-
* **Note:** If you want to modify the viewport offset in runtime (after editor was created), you can do that by overriding
|
|
409
|
+
* **Note:** If you want to modify the viewport offset in runtime (after editor was created), you can do that by overriding
|
|
410
|
+
* {@link module:core/editor/editorui~EditorUI#viewportOffset `editor.ui.viewportOffset`}.
|
|
327
411
|
*
|
|
328
412
|
* @member {Object} module:core/editor/editorconfig~EditorConfig#ui
|
|
329
413
|
*/
|
|
330
414
|
|
|
331
|
-
|
|
415
|
+
/**
|
|
332
416
|
* Enables updating the source element after the editor destroy.
|
|
333
417
|
*
|
|
334
418
|
* Enabling this option might have some security implications, as the editor doesn't have control over all data
|
|
335
419
|
* in the output.
|
|
336
420
|
*
|
|
337
|
-
* Be careful, especially while using
|
|
338
|
-
* {@glink features/markdown Markdown}, {@glink features/general-html-support General HTML Support} or
|
|
421
|
+
* Be careful, especially while using
|
|
422
|
+
* {@glink features/markdown Markdown}, {@glink features/general-html-support General HTML Support} or
|
|
339
423
|
* {@glink features/html-embed HTML embed} features.
|
|
340
424
|
*
|
|
341
425
|
* @member {Boolean} module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy
|
package/src/editor/editorui.js
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
|
|
13
13
|
import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
|
|
14
|
+
import TooltipManager from '@ckeditor/ckeditor5-ui/src/tooltipmanager';
|
|
14
15
|
|
|
15
16
|
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
|
16
17
|
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
18
|
+
import isVisible from '@ckeditor/ckeditor5-utils/src/dom/isvisible';
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* A class providing the minimal interface that is required to successfully bootstrap any editor UI.
|
|
@@ -53,6 +55,14 @@ export default class EditorUI {
|
|
|
53
55
|
*/
|
|
54
56
|
this.focusTracker = new FocusTracker();
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Manages the tooltips displayed on mouseover and focus across the UI.
|
|
60
|
+
*
|
|
61
|
+
* @readonly
|
|
62
|
+
* @member {module:ui/tooltipmanager~TooltipManager}
|
|
63
|
+
*/
|
|
64
|
+
this.tooltipManager = new TooltipManager( editor );
|
|
65
|
+
|
|
56
66
|
/**
|
|
57
67
|
* Stores viewport offsets from every direction.
|
|
58
68
|
*
|
|
@@ -86,6 +96,18 @@ export default class EditorUI {
|
|
|
86
96
|
*/
|
|
87
97
|
this.set( 'viewportOffset', this._readViewportOffsetFromConfig() );
|
|
88
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Indicates the UI is ready. Set `true` after {@link #event:ready} event is fired.
|
|
101
|
+
*
|
|
102
|
+
* @readonly
|
|
103
|
+
* @default false
|
|
104
|
+
* @member {Boolean} #isReady
|
|
105
|
+
*/
|
|
106
|
+
this.isReady = false;
|
|
107
|
+
this.once( 'ready', () => {
|
|
108
|
+
this.isReady = true;
|
|
109
|
+
} );
|
|
110
|
+
|
|
89
111
|
/**
|
|
90
112
|
* Stores all editable elements used by the editor instance.
|
|
91
113
|
*
|
|
@@ -94,8 +116,18 @@ export default class EditorUI {
|
|
|
94
116
|
*/
|
|
95
117
|
this._editableElementsMap = new Map();
|
|
96
118
|
|
|
119
|
+
/**
|
|
120
|
+
* All available & focusable toolbars.
|
|
121
|
+
*
|
|
122
|
+
* @private
|
|
123
|
+
* @type {Array.<module:core/editor/editorui~FocusableToolbarDefinition>}
|
|
124
|
+
*/
|
|
125
|
+
this._focusableToolbarDefinitions = [];
|
|
126
|
+
|
|
97
127
|
// Informs UI components that should be refreshed after layout change.
|
|
98
128
|
this.listenTo( editor.editing.view.document, 'layoutChanged', () => this.update() );
|
|
129
|
+
|
|
130
|
+
this._initFocusTracking();
|
|
99
131
|
}
|
|
100
132
|
|
|
101
133
|
/**
|
|
@@ -134,6 +166,7 @@ export default class EditorUI {
|
|
|
134
166
|
this.stopListening();
|
|
135
167
|
|
|
136
168
|
this.focusTracker.destroy();
|
|
169
|
+
this.tooltipManager.destroy( this.editor );
|
|
137
170
|
|
|
138
171
|
// Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
|
|
139
172
|
for ( const domElement of this._editableElementsMap.values() ) {
|
|
@@ -141,11 +174,14 @@ export default class EditorUI {
|
|
|
141
174
|
}
|
|
142
175
|
|
|
143
176
|
this._editableElementsMap = new Map();
|
|
177
|
+
this._focusableToolbarDefinitions = [];
|
|
144
178
|
}
|
|
145
179
|
|
|
146
180
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
181
|
+
* Stores the native DOM editable element used by the editor under a unique name.
|
|
182
|
+
*
|
|
183
|
+
* Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable
|
|
184
|
+
* editable area, they can use the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke to navigate over editor toolbars. See {@link #addToolbar}.
|
|
149
185
|
*
|
|
150
186
|
* @param {String} rootName The unique name of the editable element.
|
|
151
187
|
* @param {HTMLElement} domElement The native DOM editable element.
|
|
@@ -160,6 +196,28 @@ export default class EditorUI {
|
|
|
160
196
|
if ( !domElement.ckeditorInstance ) {
|
|
161
197
|
domElement.ckeditorInstance = this.editor;
|
|
162
198
|
}
|
|
199
|
+
|
|
200
|
+
// Register the element so it becomes available for Alt+F10 and Esc navigation.
|
|
201
|
+
this.focusTracker.add( domElement );
|
|
202
|
+
|
|
203
|
+
const setUpKeystrokeHandler = () => {
|
|
204
|
+
// The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).
|
|
205
|
+
// Do not duplicate listeners.
|
|
206
|
+
if ( this.editor.editing.view.getDomRoot( rootName ) ) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.editor.keystrokes.listenTo( domElement );
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// For editable elements set by features after EditorUI is ready (e.g. source editing).
|
|
214
|
+
if ( this.isReady ) {
|
|
215
|
+
setUpKeystrokeHandler();
|
|
216
|
+
}
|
|
217
|
+
// For editable elements set while the editor is being created (e.g. DOM roots).
|
|
218
|
+
else {
|
|
219
|
+
this.once( 'ready', setUpKeystrokeHandler );
|
|
220
|
+
}
|
|
163
221
|
}
|
|
164
222
|
|
|
165
223
|
/**
|
|
@@ -181,6 +239,35 @@ export default class EditorUI {
|
|
|
181
239
|
return this._editableElementsMap.keys();
|
|
182
240
|
}
|
|
183
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.
|
|
244
|
+
*
|
|
245
|
+
* Focusable toolbars can be accessed (focused) by users by pressing the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
|
|
246
|
+
* Successive keystroke presses navigate over available toolbars.
|
|
247
|
+
*
|
|
248
|
+
* @param {module:ui/toolbar/toolbarview~ToolbarView} toolbarView A instance of the toolbar to be registered.
|
|
249
|
+
* @param {Object} [options]
|
|
250
|
+
* @param {Boolean} [options.isContextual] Set `true` if the toolbar is attached to the content of the editor. Such toolbar takes
|
|
251
|
+
* a precedence over other toolbars when a user pressed <kbd>Alt</kbd> + <kbd>F10</kbd>.
|
|
252
|
+
* @param {Function} [options.beforeFocus] Specify a callback executed before the toolbar instance DOM element gains focus
|
|
253
|
+
* upon the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
|
|
254
|
+
* @param {Function} [options.afterBlur] Specify a callback executed after the toolbar instance DOM element loses focus upon
|
|
255
|
+
* <kbd>Esc</kbd> keystroke but before the focus goes back to the {@link #setEditableElement editable element}.
|
|
256
|
+
*/
|
|
257
|
+
addToolbar( toolbarView, options = {} ) {
|
|
258
|
+
if ( toolbarView.isRendered ) {
|
|
259
|
+
this.focusTracker.add( toolbarView.element );
|
|
260
|
+
this.editor.keystrokes.listenTo( toolbarView.element );
|
|
261
|
+
} else {
|
|
262
|
+
toolbarView.once( 'render', () => {
|
|
263
|
+
this.focusTracker.add( toolbarView.element );
|
|
264
|
+
this.editor.keystrokes.listenTo( toolbarView.element );
|
|
265
|
+
} );
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this._focusableToolbarDefinitions.push( { toolbarView, options } );
|
|
269
|
+
}
|
|
270
|
+
|
|
184
271
|
/**
|
|
185
272
|
* Stores all editable elements used by the editor instance.
|
|
186
273
|
*
|
|
@@ -254,6 +341,177 @@ export default class EditorUI {
|
|
|
254
341
|
return { top: 0 };
|
|
255
342
|
}
|
|
256
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Starts listening for <kbd>Alt</kbd> + <kbd>F10</kbd> and <kbd>Esc</kbd> keystrokes in the context of focusable
|
|
346
|
+
* {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}
|
|
347
|
+
* to allow users navigate across the UI.
|
|
348
|
+
*
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
_initFocusTracking() {
|
|
352
|
+
const editor = this.editor;
|
|
353
|
+
const editingView = editor.editing.view;
|
|
354
|
+
|
|
355
|
+
let lastFocusedForeignElement;
|
|
356
|
+
let candidateDefinitions;
|
|
357
|
+
|
|
358
|
+
// Focus the next focusable toolbar on <kbd>Alt</kbd> + <kbd>F10</kbd>.
|
|
359
|
+
editor.keystrokes.set( 'Alt+F10', ( data, cancel ) => {
|
|
360
|
+
const focusedElement = this.focusTracker.focusedElement;
|
|
361
|
+
|
|
362
|
+
// Focus moved out of a DOM element that
|
|
363
|
+
// * is not a toolbar,
|
|
364
|
+
// * does not belong to the editing view (e.g. source editing).
|
|
365
|
+
if (
|
|
366
|
+
Array.from( this._editableElementsMap.values() ).includes( focusedElement ) &&
|
|
367
|
+
!Array.from( editingView.domRoots.values() ).includes( focusedElement )
|
|
368
|
+
) {
|
|
369
|
+
lastFocusedForeignElement = focusedElement;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();
|
|
373
|
+
|
|
374
|
+
// * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.
|
|
375
|
+
// This ensures, the navigation works always the same and no pair of toolbars takes over
|
|
376
|
+
// (e.g. image and table toolbars when a selected image is inside a cell).
|
|
377
|
+
// * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,
|
|
378
|
+
// there were no candidates so they must be obtained (#12339).
|
|
379
|
+
if ( !currentFocusedToolbarDefinition || !candidateDefinitions ) {
|
|
380
|
+
candidateDefinitions = this._getFocusableCandidateToolbarDefinitions( currentFocusedToolbarDefinition );
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.
|
|
384
|
+
// This prevents an infinite loop.
|
|
385
|
+
for ( let i = 0; i < candidateDefinitions.length; i++ ) {
|
|
386
|
+
const candidateDefinition = candidateDefinitions.shift();
|
|
387
|
+
|
|
388
|
+
// Put the first definition to the back of the array. This allows circular navigation over all toolbars
|
|
389
|
+
// on successive presses of Alt+F10.
|
|
390
|
+
candidateDefinitions.push( candidateDefinition );
|
|
391
|
+
|
|
392
|
+
// Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the
|
|
393
|
+
// first item as per ToolbarView#focus() if the user navigated inside the toolbar.
|
|
394
|
+
if (
|
|
395
|
+
candidateDefinition !== currentFocusedToolbarDefinition &&
|
|
396
|
+
this._focusFocusableCandidateToolbar( candidateDefinition )
|
|
397
|
+
) {
|
|
398
|
+
// Clean up after a current visible toolbar when switching to the next one.
|
|
399
|
+
if ( currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur ) {
|
|
400
|
+
currentFocusedToolbarDefinition.options.afterBlur();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
cancel();
|
|
408
|
+
} );
|
|
409
|
+
|
|
410
|
+
// Blur the focused toolbar on <kbd>Esc</kbd> and bring the focus back to its origin.
|
|
411
|
+
editor.keystrokes.set( 'Esc', ( data, cancel ) => {
|
|
412
|
+
const focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();
|
|
413
|
+
|
|
414
|
+
if ( !focusedToolbarDef ) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Bring focus back to where it came from before focusing the toolbar:
|
|
419
|
+
// 1. If it came from outside the engine view (e.g. source editing), move it there.
|
|
420
|
+
if ( lastFocusedForeignElement ) {
|
|
421
|
+
lastFocusedForeignElement.focus();
|
|
422
|
+
lastFocusedForeignElement = null;
|
|
423
|
+
}
|
|
424
|
+
// 2. There are two possibilities left:
|
|
425
|
+
// 2.1. It could be that the focus went from an editable element in the view (root or nested).
|
|
426
|
+
// 2.2. It could be the focus went straight to the toolbar before even focusing the editing area.
|
|
427
|
+
// In either case, just focus the view editing. The focus will land where it belongs.
|
|
428
|
+
else {
|
|
429
|
+
editor.editing.view.focus();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Clean up after the toolbar if there is anything to do there.
|
|
433
|
+
if ( focusedToolbarDef.options.afterBlur ) {
|
|
434
|
+
focusedToolbarDef.options.afterBlur();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
cancel();
|
|
438
|
+
} );
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.
|
|
443
|
+
*
|
|
444
|
+
* Focusable toolbars candidates are either:
|
|
445
|
+
* * already visible,
|
|
446
|
+
* * have `beforeFocus()` set in their {@link module:core/editor/editorui~FocusableToolbarDefinition definition} that suggests that
|
|
447
|
+
* they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible
|
|
448
|
+
* at this stage because it depends on its implementation, that in turn depends on the editing context (selection).
|
|
449
|
+
*
|
|
450
|
+
* **Note**: Contextual toolbars take precedence over regular toolbars.
|
|
451
|
+
*
|
|
452
|
+
* @private
|
|
453
|
+
* @returns {Array.<module:core/editor/editorui~FocusableToolbarDefinition>}
|
|
454
|
+
*/
|
|
455
|
+
_getFocusableCandidateToolbarDefinitions() {
|
|
456
|
+
const definitions = [];
|
|
457
|
+
|
|
458
|
+
for ( const toolbarDef of this._focusableToolbarDefinitions ) {
|
|
459
|
+
const { toolbarView, options } = toolbarDef;
|
|
460
|
+
|
|
461
|
+
if ( isVisible( toolbarView.element ) || options.beforeFocus ) {
|
|
462
|
+
definitions.push( toolbarDef );
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.
|
|
467
|
+
// For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.
|
|
468
|
+
definitions.sort( ( defA, defB ) => getToolbarDefinitionWeight( defA ) - getToolbarDefinitionWeight( defB ) );
|
|
469
|
+
|
|
470
|
+
return definitions;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).
|
|
475
|
+
*
|
|
476
|
+
* `null` is returned when no toolbar is currently focused.
|
|
477
|
+
*
|
|
478
|
+
* @private
|
|
479
|
+
* @returns {module:core/editor/editorui~FocusableToolbarDefinition|null}
|
|
480
|
+
*/
|
|
481
|
+
_getCurrentFocusedToolbarDefinition() {
|
|
482
|
+
for ( const definition of this._focusableToolbarDefinitions ) {
|
|
483
|
+
if ( definition.toolbarView.element && definition.toolbarView.element.contains( this.focusTracker.focusedElement ) ) {
|
|
484
|
+
return definition;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Focuses a focusable toolbar candidate using its definition.
|
|
493
|
+
*
|
|
494
|
+
* @private
|
|
495
|
+
* @param {module:core/editor/editorui~FocusableToolbarDefinition} candidateToolbarDefinition A definition of the toolbar to focus.
|
|
496
|
+
* @returns {Boolean} `true` when the toolbar candidate was focused. `false` otherwise.
|
|
497
|
+
*/
|
|
498
|
+
_focusFocusableCandidateToolbar( candidateToolbarDefinition ) {
|
|
499
|
+
const { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;
|
|
500
|
+
|
|
501
|
+
if ( beforeFocus ) {
|
|
502
|
+
beforeFocus();
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// If it didn't show up after beforeFocus(), it's not focusable at all.
|
|
506
|
+
if ( !isVisible( toolbarView.element ) ) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
toolbarView.focus();
|
|
511
|
+
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
|
|
257
515
|
/**
|
|
258
516
|
* Fired when the editor UI is ready.
|
|
259
517
|
*
|
|
@@ -273,3 +531,52 @@ export default class EditorUI {
|
|
|
273
531
|
}
|
|
274
532
|
|
|
275
533
|
mix( EditorUI, ObservableMixin );
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* A definition of a focusable toolbar. Used by {@link module:core/editor/editorui~EditorUI#addToolbar}.
|
|
537
|
+
*
|
|
538
|
+
* @private
|
|
539
|
+
* @interface module:core/editor/editorui~FocusableToolbarDefinition
|
|
540
|
+
*/
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* An instance of a focusable toolbar view.
|
|
544
|
+
*
|
|
545
|
+
* @member {module:ui/toolbar/toolbarview~ToolbarView} #toolbarView
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Options of a focusable toolbar view:
|
|
550
|
+
*
|
|
551
|
+
* * `isContextual`: Marks the higher priority toolbar. For example when there are 2 visible toolbars,
|
|
552
|
+
* it allows to distinguish which toolbar should be focused first after the `alt+f10` keystroke
|
|
553
|
+
* * `beforeFocus`: A callback executed before the `ToolbarView` gains focus upon the `Alt+F10` keystroke.
|
|
554
|
+
* * `afterBlur`: A callback executed after `ToolbarView` loses focus upon `Esc` keystroke but before the focus goes back to the `origin`.
|
|
555
|
+
*
|
|
556
|
+
* @member {Object} #options
|
|
557
|
+
*/
|
|
558
|
+
|
|
559
|
+
// Returns a number (weight) for a toolbar definition. Visible toolbars have a higher priority and so do
|
|
560
|
+
// contextual toolbars (displayed in the context of a content, for instance, an image toolbar).
|
|
561
|
+
//
|
|
562
|
+
// A standard invisible toolbar is the heaviest. A visible contextual toolbar is the lightest.
|
|
563
|
+
//
|
|
564
|
+
// @private
|
|
565
|
+
// @param {module:core/editor/editorui~FocusableToolbarDefinition} toolbarDef A toolbar definition to be weighted.
|
|
566
|
+
// @returns {Number}
|
|
567
|
+
function getToolbarDefinitionWeight( toolbarDef ) {
|
|
568
|
+
const { toolbarView, options } = toolbarDef;
|
|
569
|
+
let weight = 10;
|
|
570
|
+
|
|
571
|
+
// Prioritize already visible toolbars. They should get focused first.
|
|
572
|
+
if ( isVisible( toolbarView.element ) ) {
|
|
573
|
+
weight--;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Prioritize contextual toolbars. They are displayed at the selection.
|
|
577
|
+
if ( options.isContextual ) {
|
|
578
|
+
weight--;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return weight;
|
|
582
|
+
}
|
package/src/index.js
CHANGED
|
@@ -58,14 +58,25 @@ import pilcrow from './../theme/icons/pilcrow.svg';
|
|
|
58
58
|
import quote from './../theme/icons/quote.svg';
|
|
59
59
|
import threeVerticalDots from './../theme/icons/three-vertical-dots.svg';
|
|
60
60
|
|
|
61
|
+
import bold from './../theme/icons/bold.svg';
|
|
62
|
+
import paragraph from './../theme/icons/paragraph.svg';
|
|
63
|
+
import plus from './../theme/icons/plus.svg';
|
|
64
|
+
import text from './../theme/icons/text.svg';
|
|
65
|
+
import importExport from './../theme/icons/importexport.svg';
|
|
66
|
+
|
|
61
67
|
export const icons = {
|
|
68
|
+
bold,
|
|
62
69
|
cancel,
|
|
63
70
|
caption,
|
|
64
71
|
check,
|
|
65
72
|
cog,
|
|
66
73
|
eraser,
|
|
67
|
-
lowVision,
|
|
68
74
|
image,
|
|
75
|
+
lowVision,
|
|
76
|
+
importExport,
|
|
77
|
+
paragraph,
|
|
78
|
+
plus,
|
|
79
|
+
text,
|
|
69
80
|
|
|
70
81
|
alignBottom,
|
|
71
82
|
alignMiddle,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.187 17H5.773c-.637 0-1.092-.138-1.364-.415-.273-.277-.409-.718-.409-1.323V4.738c0-.617.14-1.062.419-1.332.279-.27.73-.406 1.354-.406h4.68c.69 0 1.288.041 1.793.124.506.083.96.242 1.36.478.341.197.644.447.906.75a3.262 3.262 0 0 1 .808 2.162c0 1.401-.722 2.426-2.167 3.075C15.05 10.175 16 11.315 16 13.01a3.756 3.756 0 0 1-2.296 3.504 6.1 6.1 0 0 1-1.517.377c-.571.073-1.238.11-2 .11zm-.217-6.217H7v4.087h3.069c1.977 0 2.965-.69 2.965-2.072 0-.707-.256-1.22-.768-1.537-.512-.319-1.277-.478-2.296-.478zM7 5.13v3.619h2.606c.729 0 1.292-.067 1.69-.2a1.6 1.6 0 0 0 .91-.765c.165-.267.247-.566.247-.897 0-.707-.26-1.176-.778-1.409-.519-.232-1.31-.348-2.375-.348H7z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path clip-rule="evenodd" d="M19 4.5 14 0H3v12.673l.868-1.041c.185-.222.4-.402.632-.54V1.5h8v5h5v7.626a2.24 2.24 0 0 1 1.5.822V4.5ZM14 5V2l3.3 3H14Zm-3.692 12.5c.062.105.133.206.213.303L11.52 19H8v-.876a2.243 2.243 0 0 0 1.82-.624h.488Zm7.518-.657a.75.75 0 0 0-1.152-.96L15.5 17.29V12H14v5.29l-1.174-1.408a.75.75 0 0 0-1.152.96l2.346 2.816a.95.95 0 0 0 1.46 0l2.346-2.815Zm-15.056-.38a.75.75 0 0 1-.096-1.056l2.346-2.815a.95.95 0 0 1 1.46 0l2.346 2.815a.75.75 0 1 1-1.152.96L6.5 14.96V20H5v-5.04l-1.174 1.408a.75.75 0 0 1-1.056.096Z"/></g><defs><clipPath id="a"><path d="M0 0h20v20H0z"/></clipPath></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.5 5.5H7v5h3.5a2.5 2.5 0 1 0 0-5zM5 3h6.5v.025a5 5 0 0 1 0 9.95V13H7v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a1 1 0 0 0-1 1v6H3a1 1 0 1 0 0 2h6v6a1 1 0 1 0 2 0v-6h6a1 1 0 1 0 0-2h-6V3a1 1 0 0 0-1-1Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="M9.816 11.5 7.038 4.785 4.261 11.5h5.555Zm.62 1.5H3.641l-1.666 4.028H.312l5.789-14h1.875l5.789 14h-1.663L10.436 13Z"/><path clip-rule="evenodd" d="m12.09 17-.534-1.292.848-1.971.545 1.319L12.113 17h-.023Zm1.142-5.187.545 1.319L15.5 9.13l1.858 4.316h-3.45l.398.965h3.467L18.887 17H20l-3.873-9h-1.254l-1.641 3.813Z"/></g><defs><clipPath id="a"><path d="M0 0h20v20H0z"/></clipPath></defs></svg>
|