@ckeditor/ckeditor5-ui 42.0.2 → 43.0.0-alpha.1
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/CHANGELOG.md +1 -539
- package/dist/button/button.d.ts +0 -6
- package/dist/button/buttonview.d.ts +16 -4
- package/dist/button/filedialogbuttonview.d.ts +42 -15
- package/dist/button/listitembuttonview.d.ts +82 -0
- package/dist/editorui/accessibilityhelp/accessibilityhelp.d.ts +1 -1
- package/dist/editorui/editorui.d.ts +57 -0
- package/dist/editorui/editoruiview.d.ts +5 -0
- package/dist/focuscycler.d.ts +53 -7
- package/dist/formheader/formheaderview.d.ts +1 -2
- package/dist/highlightedtext/buttonlabelwithhighlightview.d.ts +34 -0
- package/dist/highlightedtext/labelwithhighlightview.d.ts +30 -0
- package/dist/index-editor.css +32 -0
- package/dist/index.css +42 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4934 -4291
- package/dist/index.js.map +1 -1
- package/dist/menubar/menubarmenubuttonview.d.ts +2 -2
- package/dist/menubar/menubarmenulistitembuttonview.d.ts +2 -2
- package/dist/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +2 -2
- package/dist/menubar/menubarmenulistview.d.ts +5 -0
- package/dist/menubar/menubarview.d.ts +10 -1
- package/dist/menubar/utils.d.ts +16 -10
- package/dist/panel/balloon/balloonpanelview.d.ts +13 -1
- package/dist/search/filtergroupanditemnames.d.ts +19 -0
- package/dist/toolbar/block/blocktoolbar.d.ts +14 -0
- package/dist/tooltipmanager.d.ts +0 -7
- package/dist/translations/sr-latn.js +1 -1
- package/dist/translations/sr-latn.umd.js +1 -1
- package/lang/translations/sr-latn.po +17 -17
- package/package.json +3 -3
- package/src/arialiveannouncer.js +0 -1
- package/src/bindings/draggableviewmixin.js +1 -1
- package/src/button/button.d.ts +0 -6
- package/src/button/buttonview.d.ts +16 -4
- package/src/button/buttonview.js +32 -2
- package/src/button/filedialogbuttonview.d.ts +42 -15
- package/src/button/filedialogbuttonview.js +69 -27
- package/src/button/listitembuttonview.d.ts +78 -0
- package/src/button/listitembuttonview.js +129 -0
- package/src/colorpicker/colorpickerview.js +5 -0
- package/src/colorselector/colorgridsfragmentview.js +9 -5
- package/src/dialog/dialog.js +4 -1
- package/src/dialog/dialogview.js +1 -14
- package/src/dropdown/utils.js +23 -3
- package/src/editorui/accessibilityhelp/accessibilityhelp.d.ts +1 -1
- package/src/editorui/accessibilityhelp/accessibilityhelp.js +20 -12
- package/src/editorui/bodycollection.js +2 -1
- package/src/editorui/editorui.d.ts +57 -0
- package/src/editorui/editorui.js +104 -12
- package/src/editorui/editoruiview.d.ts +5 -0
- package/src/focuscycler.d.ts +53 -7
- package/src/focuscycler.js +79 -1
- package/src/formheader/formheaderview.d.ts +1 -2
- package/src/formheader/formheaderview.js +1 -2
- package/src/highlightedtext/buttonlabelwithhighlightview.d.ts +30 -0
- package/src/highlightedtext/buttonlabelwithhighlightview.js +31 -0
- package/src/highlightedtext/labelwithhighlightview.d.ts +26 -0
- package/src/highlightedtext/labelwithhighlightview.js +33 -0
- package/src/index.d.ts +6 -2
- package/src/index.js +6 -2
- package/src/menubar/menubarmenubuttonview.d.ts +2 -2
- package/src/menubar/menubarmenubuttonview.js +2 -2
- package/src/menubar/menubarmenulistitembuttonview.d.ts +2 -2
- package/src/menubar/menubarmenulistitembuttonview.js +2 -2
- package/src/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +2 -2
- package/src/menubar/menubarmenulistitemfiledialogbuttonview.js +2 -2
- package/src/menubar/menubarmenulistview.d.ts +5 -0
- package/src/menubar/menubarmenulistview.js +49 -0
- package/src/menubar/menubarview.d.ts +10 -1
- package/src/menubar/menubarview.js +11 -4
- package/src/menubar/utils.d.ts +16 -10
- package/src/menubar/utils.js +84 -53
- package/src/panel/balloon/balloonpanelview.d.ts +13 -1
- package/src/panel/balloon/balloonpanelview.js +41 -3
- package/src/search/filtergroupanditemnames.d.ts +15 -0
- package/src/search/filtergroupanditemnames.js +38 -0
- package/src/search/text/searchtextview.js +1 -0
- package/src/toolbar/balloon/balloontoolbar.js +1 -1
- package/src/toolbar/block/blocktoolbar.d.ts +14 -0
- package/src/toolbar/block/blocktoolbar.js +83 -3
- package/src/tooltipmanager.d.ts +0 -7
- package/src/tooltipmanager.js +1 -18
- package/theme/components/button/listitembutton.css +38 -0
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* @module ui/menubar/menubarmenulistitemfiledialogbuttonview
|
|
7
7
|
*/
|
|
8
8
|
import type { Locale } from '@ckeditor/ckeditor5-utils';
|
|
9
|
-
import
|
|
9
|
+
import { FileDialogListItemButtonView } from '../button/filedialogbuttonview.js';
|
|
10
10
|
import '../../theme/components/menubar/menubarmenulistitembutton.css';
|
|
11
11
|
/**
|
|
12
12
|
* A menu bar list file dialog button view. Buttons like this one execute user actions.
|
|
13
13
|
*
|
|
14
14
|
* This component provides a button that opens the native file selection dialog.
|
|
15
15
|
*/
|
|
16
|
-
export default class MenuBarMenuListItemFileDialogButtonView extends
|
|
16
|
+
export default class MenuBarMenuListItemFileDialogButtonView extends FileDialogListItemButtonView {
|
|
17
17
|
/**
|
|
18
18
|
* Creates an instance of the menu bar list button view.
|
|
19
19
|
*
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { FileDialogListItemButtonView } from '../button/filedialogbuttonview.js';
|
|
6
6
|
import '../../theme/components/menubar/menubarmenulistitembutton.css';
|
|
7
7
|
/**
|
|
8
8
|
* A menu bar list file dialog button view. Buttons like this one execute user actions.
|
|
9
9
|
*
|
|
10
10
|
* This component provides a button that opens the native file selection dialog.
|
|
11
11
|
*/
|
|
12
|
-
export default class MenuBarMenuListItemFileDialogButtonView extends
|
|
12
|
+
export default class MenuBarMenuListItemFileDialogButtonView extends FileDialogListItemButtonView {
|
|
13
13
|
/**
|
|
14
14
|
* Creates an instance of the menu bar list button view.
|
|
15
15
|
*
|
|
@@ -21,4 +21,9 @@ export default class MenuBarMenuListView extends ListView {
|
|
|
21
21
|
* @param locale The localization services instance.
|
|
22
22
|
*/
|
|
23
23
|
constructor(locale: Locale);
|
|
24
|
+
/**
|
|
25
|
+
* This method adds empty space if there is any toggleable item in the list.
|
|
26
|
+
* It makes the list properly aligned.
|
|
27
|
+
*/
|
|
28
|
+
private _setItemsCheckSpace;
|
|
24
29
|
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
+
import ListItemView from '../list/listitemview.js';
|
|
5
6
|
import ListView from '../list/listview.js';
|
|
7
|
+
import ListItemButtonView from '../button/listitembuttonview.js';
|
|
8
|
+
import ButtonView from '../button/buttonview.js';
|
|
6
9
|
/**
|
|
7
10
|
* A list of menu bar items, a child of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView#panelView}.
|
|
8
11
|
*
|
|
@@ -19,5 +22,51 @@ export default class MenuBarMenuListView extends ListView {
|
|
|
19
22
|
constructor(locale) {
|
|
20
23
|
super(locale);
|
|
21
24
|
this.role = 'menu';
|
|
25
|
+
this.items.on('change', this._setItemsCheckSpace.bind(this));
|
|
22
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* This method adds empty space if there is any toggleable item in the list.
|
|
29
|
+
* It makes the list properly aligned.
|
|
30
|
+
*/
|
|
31
|
+
_setItemsCheckSpace() {
|
|
32
|
+
const hasAnyToggleableItem = (Array
|
|
33
|
+
.from(this.items)
|
|
34
|
+
.some(item => {
|
|
35
|
+
const listButtonView = pickListButtonMenuViewIfPresent(item);
|
|
36
|
+
return listButtonView && listButtonView.isToggleable;
|
|
37
|
+
}));
|
|
38
|
+
this.items.forEach(item => {
|
|
39
|
+
const listButtonView = pickListButtonMenuViewIfPresent(item);
|
|
40
|
+
if (listButtonView) {
|
|
41
|
+
listButtonView.hasCheckSpace = hasAnyToggleableItem;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Picks the first button menu view from the given item if present.
|
|
48
|
+
*
|
|
49
|
+
* @param item The item to check for a button menu view.
|
|
50
|
+
* @returns The first button menu view found in the item, or `null` if not found.
|
|
51
|
+
*/
|
|
52
|
+
function pickListButtonMenuViewIfPresent(item) {
|
|
53
|
+
if (!(item instanceof ListItemView)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return item
|
|
57
|
+
.children
|
|
58
|
+
.map(child => isNestedMenuLikeView(child) ? child.buttonView : child)
|
|
59
|
+
.find(item => item instanceof ListItemButtonView);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Checks if the given item is a nested menu-like view. `MenuBarMenuView` imports this file
|
|
63
|
+
* so to avoid circular dependencies, this function is defined in more generic way.
|
|
64
|
+
*
|
|
65
|
+
* @param item The item to check.
|
|
66
|
+
* @returns `true` if the item is a nested menu-like view, `false` otherwise.
|
|
67
|
+
*/
|
|
68
|
+
function isNestedMenuLikeView(item) {
|
|
69
|
+
return (typeof item === 'object' &&
|
|
70
|
+
'buttonView' in item &&
|
|
71
|
+
item.buttonView instanceof ButtonView);
|
|
23
72
|
}
|
|
@@ -28,6 +28,15 @@ export default class MenuBarView extends View implements FocusableView {
|
|
|
28
28
|
* @observable
|
|
29
29
|
*/
|
|
30
30
|
isOpen: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Indicates whether the menu bar has been interacted with using the keyboard.
|
|
33
|
+
*
|
|
34
|
+
* It is useful for showing focus outlines while hovering over the menu bar when
|
|
35
|
+
* interaction with the keyboard was detected.
|
|
36
|
+
*
|
|
37
|
+
* @observable
|
|
38
|
+
*/
|
|
39
|
+
isFocusBorderEnabled: boolean;
|
|
31
40
|
/**
|
|
32
41
|
* A list of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instances registered in the menu bar.
|
|
33
42
|
*
|
|
@@ -47,7 +56,7 @@ export default class MenuBarView extends View implements FocusableView {
|
|
|
47
56
|
* See the {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar} in the editor
|
|
48
57
|
* configuration reference to learn how to configure the menu bar.
|
|
49
58
|
*/
|
|
50
|
-
fillFromConfig(config: NormalizedMenuBarConfigObject, componentFactory: ComponentFactory): void;
|
|
59
|
+
fillFromConfig(config: NormalizedMenuBarConfigObject, componentFactory: ComponentFactory, extraItems?: Array<MenuBarConfigAddedItem | MenuBarConfigAddedGroup | MenuBarConfigAddedMenu>): void;
|
|
51
60
|
/**
|
|
52
61
|
* @inheritDoc
|
|
53
62
|
*/
|
|
@@ -37,7 +37,11 @@ export default class MenuBarView extends View {
|
|
|
37
37
|
*/
|
|
38
38
|
this.menus = [];
|
|
39
39
|
const t = locale.t;
|
|
40
|
-
this.
|
|
40
|
+
const bind = this.bindTemplate;
|
|
41
|
+
this.set({
|
|
42
|
+
isOpen: false,
|
|
43
|
+
isFocusBorderEnabled: false
|
|
44
|
+
});
|
|
41
45
|
this._setupIsOpenUpdater();
|
|
42
46
|
this.children = this.createCollection();
|
|
43
47
|
// @if CK_DEBUG_MENU_BAR // // Logs events in the main event bus of the component.
|
|
@@ -49,7 +53,8 @@ export default class MenuBarView extends View {
|
|
|
49
53
|
attributes: {
|
|
50
54
|
class: [
|
|
51
55
|
'ck',
|
|
52
|
-
'ck-menu-bar'
|
|
56
|
+
'ck-menu-bar',
|
|
57
|
+
bind.if('isFocusBorderEnabled', 'ck-menu-bar_focus-border-enabled')
|
|
53
58
|
],
|
|
54
59
|
'aria-label': t('Editor menu bar'),
|
|
55
60
|
role: 'menubar'
|
|
@@ -64,12 +69,13 @@ export default class MenuBarView extends View {
|
|
|
64
69
|
* See the {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar} in the editor
|
|
65
70
|
* configuration reference to learn how to configure the menu bar.
|
|
66
71
|
*/
|
|
67
|
-
fillFromConfig(config, componentFactory) {
|
|
72
|
+
fillFromConfig(config, componentFactory, extraItems = []) {
|
|
68
73
|
const locale = this.locale;
|
|
69
74
|
const processedConfig = processMenuBarConfig({
|
|
70
75
|
normalizedConfig: config,
|
|
71
76
|
locale,
|
|
72
|
-
componentFactory
|
|
77
|
+
componentFactory,
|
|
78
|
+
extraItems
|
|
73
79
|
});
|
|
74
80
|
const topLevelCategoryMenuViews = processedConfig.items.map(menuDefinition => this._createMenu({
|
|
75
81
|
componentFactory,
|
|
@@ -87,6 +93,7 @@ export default class MenuBarView extends View {
|
|
|
87
93
|
MenuBarBehaviors.closeMenuWhenAnotherOnTheSameLevelOpens(this);
|
|
88
94
|
MenuBarBehaviors.focusCycleMenusOnArrows(this);
|
|
89
95
|
MenuBarBehaviors.closeOnClickOutside(this);
|
|
96
|
+
MenuBarBehaviors.enableFocusHighlightOnInteraction(this);
|
|
90
97
|
}
|
|
91
98
|
/**
|
|
92
99
|
* Focuses the menu bar.
|
package/src/menubar/utils.d.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
import type MenuBarMenuView from './menubarmenuview.js';
|
|
6
|
-
import type { default as MenuBarView, MenuBarConfig, MenuBarConfigObject, NormalizedMenuBarConfigObject } from './menubarview.js';
|
|
6
|
+
import type { default as MenuBarView, MenuBarConfig, MenuBarConfigObject, MenuBarConfigAddedGroup, MenuBarConfigAddedMenu, NormalizedMenuBarConfigObject, MenuBarConfigAddedItem } from './menubarview.js';
|
|
7
7
|
import type ComponentFactory from '../componentfactory.js';
|
|
8
|
-
import type { Editor } from '@ckeditor/ckeditor5-core';
|
|
9
8
|
import { type Locale, type PositioningFunction } from '@ckeditor/ckeditor5-utils';
|
|
10
9
|
type DeepReadonly<T> = Readonly<{
|
|
11
10
|
[K in keyof T]: T[K] extends string ? Readonly<T[K]> : T[K] extends Array<infer A> ? Readonly<Array<DeepReadonly<A>>> : DeepReadonly<T[K]>;
|
|
@@ -44,6 +43,11 @@ export declare const MenuBarBehaviors: {
|
|
|
44
43
|
* Closes the bar when the user clicked outside of it (page body, editor root, etc.).
|
|
45
44
|
*/
|
|
46
45
|
closeOnClickOutside(menuBarView: MenuBarView): void;
|
|
46
|
+
/**
|
|
47
|
+
* Tracks the keyboard focus interaction on the menu bar view. It is used to determine if the nested items
|
|
48
|
+
* of the menu bar should render focus rings after first interaction with the keyboard.
|
|
49
|
+
*/
|
|
50
|
+
enableFocusHighlightOnInteraction(menuBarView: MenuBarView): void;
|
|
47
51
|
};
|
|
48
52
|
/**
|
|
49
53
|
* Behaviors of the {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} component.
|
|
@@ -233,6 +237,12 @@ export declare const MenuBarMenuViewPanelPositioningFunctions: Record<string, Po
|
|
|
233
237
|
* ]
|
|
234
238
|
* },
|
|
235
239
|
* {
|
|
240
|
+
* groupId: 'previewMergeFields',
|
|
241
|
+
* items: [
|
|
242
|
+
* 'menuBar:previewMergeFields'
|
|
243
|
+
* ]
|
|
244
|
+
* },
|
|
245
|
+
* {
|
|
236
246
|
* groupId: 'restrictedEditingException',
|
|
237
247
|
* items: [
|
|
238
248
|
* 'menuBar:restrictedEditingException'
|
|
@@ -257,7 +267,8 @@ export declare const MenuBarMenuViewPanelPositioningFunctions: Record<string, Po
|
|
|
257
267
|
* groupId: 'insertInline',
|
|
258
268
|
* items: [
|
|
259
269
|
* 'menuBar:link',
|
|
260
|
-
* 'menuBar:comment'
|
|
270
|
+
* 'menuBar:comment',
|
|
271
|
+
* 'menuBar:insertMergeField'
|
|
261
272
|
* ]
|
|
262
273
|
* },
|
|
263
274
|
* {
|
|
@@ -427,15 +438,10 @@ export declare function normalizeMenuBarConfig(config: Readonly<MenuBarConfig>):
|
|
|
427
438
|
* * Purged empty menus,
|
|
428
439
|
* * Localized top-level menu labels.
|
|
429
440
|
*/
|
|
430
|
-
export declare function processMenuBarConfig({ normalizedConfig, locale, componentFactory }: {
|
|
441
|
+
export declare function processMenuBarConfig({ normalizedConfig, locale, componentFactory, extraItems }: {
|
|
431
442
|
normalizedConfig: NormalizedMenuBarConfigObject;
|
|
432
443
|
locale: Locale;
|
|
433
444
|
componentFactory: ComponentFactory;
|
|
445
|
+
extraItems: Array<MenuBarConfigAddedItem | MenuBarConfigAddedGroup | MenuBarConfigAddedMenu>;
|
|
434
446
|
}): NormalizedMenuBarConfigObject;
|
|
435
|
-
/**
|
|
436
|
-
* Initializes menu bar for given editor.
|
|
437
|
-
*
|
|
438
|
-
* @internal
|
|
439
|
-
*/
|
|
440
|
-
export declare function _initMenuBar(editor: Editor, menuBarView: MenuBarView): void;
|
|
441
447
|
export {};
|
package/src/menubar/utils.js
CHANGED
|
@@ -21,20 +21,24 @@ export const MenuBarBehaviors = {
|
|
|
21
21
|
*/
|
|
22
22
|
toggleMenusAndFocusItemsOnHover(menuBarView) {
|
|
23
23
|
menuBarView.on('menu:mouseenter', evt => {
|
|
24
|
-
// This
|
|
25
|
-
|
|
24
|
+
// This behavior should be activated when one of condition is present:
|
|
25
|
+
// 1. The user opened any submenu of menubar and hover over items in the menu bar.
|
|
26
|
+
// 2. The user focused whole menubar using keyboard interaction and enabled focus borders and hover over items in the menu bar.
|
|
27
|
+
if (!menuBarView.isFocusBorderEnabled && !menuBarView.isOpen) {
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
if (menuBarView.isOpen) {
|
|
31
|
+
for (const menuView of menuBarView.menus) {
|
|
32
|
+
// @if CK_DEBUG_MENU_BAR // const wasOpen = menuView.isOpen;
|
|
33
|
+
const pathLeaf = evt.path[0];
|
|
34
|
+
const isListItemContainingMenu = pathLeaf instanceof MenuBarMenuListItemView && pathLeaf.children.first === menuView;
|
|
35
|
+
menuView.isOpen = (evt.path.includes(menuView) || isListItemContainingMenu) && menuView.isEnabled;
|
|
36
|
+
// @if CK_DEBUG_MENU_BAR // if ( wasOpen !== menuView.isOpen ) {
|
|
37
|
+
// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] toggleMenusAndFocusItemsOnHover(): Toggle',
|
|
38
|
+
// @if CK_DEBUG_MENU_BAR // logMenu( menuView ), 'isOpen', menuView.isOpen
|
|
39
|
+
// @if CK_DEBUG_MENU_BAR // );
|
|
40
|
+
// @if CK_DEBUG_MENU_BAR // }
|
|
41
|
+
}
|
|
38
42
|
}
|
|
39
43
|
evt.source.focus();
|
|
40
44
|
});
|
|
@@ -111,6 +115,40 @@ export const MenuBarBehaviors = {
|
|
|
111
115
|
callback: () => menuBarView.close(),
|
|
112
116
|
contextElements: () => menuBarView.children.map(child => child.element)
|
|
113
117
|
});
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Tracks the keyboard focus interaction on the menu bar view. It is used to determine if the nested items
|
|
121
|
+
* of the menu bar should render focus rings after first interaction with the keyboard.
|
|
122
|
+
*/
|
|
123
|
+
enableFocusHighlightOnInteraction(menuBarView) {
|
|
124
|
+
let isKeyPressed = false;
|
|
125
|
+
menuBarView.on('change:isOpen', (_, evt, isOpen) => {
|
|
126
|
+
if (!isOpen) {
|
|
127
|
+
menuBarView.isFocusBorderEnabled = false;
|
|
128
|
+
// Reset the flag when the menu bar is closed, menu items tend to intercept `keyup` event
|
|
129
|
+
// and sometimes, after pressing `enter` on focused item, `isKeyPressed` stuck in `true` state.
|
|
130
|
+
isKeyPressed = false;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// After clicking menu bar list item the focus is moved to the newly opened submenu.
|
|
134
|
+
// We need to enable focus border for the submenu items because after pressing arrow down it will
|
|
135
|
+
// focus second item instead of first which is not super intuitive.
|
|
136
|
+
menuBarView.listenTo(menuBarView.element, 'click', () => {
|
|
137
|
+
if (menuBarView.isOpen && menuBarView.element.matches(':focus-within')) {
|
|
138
|
+
menuBarView.isFocusBorderEnabled = true;
|
|
139
|
+
}
|
|
140
|
+
}, { useCapture: true });
|
|
141
|
+
menuBarView.listenTo(menuBarView.element, 'keydown', () => {
|
|
142
|
+
isKeyPressed = true;
|
|
143
|
+
}, { useCapture: true });
|
|
144
|
+
menuBarView.listenTo(menuBarView.element, 'keyup', () => {
|
|
145
|
+
isKeyPressed = false;
|
|
146
|
+
}, { useCapture: true });
|
|
147
|
+
menuBarView.listenTo(menuBarView.element, 'focus', () => {
|
|
148
|
+
if (isKeyPressed) {
|
|
149
|
+
menuBarView.isFocusBorderEnabled = true;
|
|
150
|
+
}
|
|
151
|
+
}, { useCapture: true });
|
|
114
152
|
}
|
|
115
153
|
};
|
|
116
154
|
/**
|
|
@@ -156,7 +194,9 @@ export const MenuBarMenuBehaviors = {
|
|
|
156
194
|
openOnButtonClick(menuView) {
|
|
157
195
|
menuView.buttonView.on('execute', () => {
|
|
158
196
|
menuView.isOpen = true;
|
|
159
|
-
menuView.
|
|
197
|
+
if (menuView.parentMenuView) {
|
|
198
|
+
menuView.panelView.focus();
|
|
199
|
+
}
|
|
160
200
|
});
|
|
161
201
|
},
|
|
162
202
|
/**
|
|
@@ -165,9 +205,6 @@ export const MenuBarMenuBehaviors = {
|
|
|
165
205
|
toggleOnButtonClick(menuView) {
|
|
166
206
|
menuView.buttonView.on('execute', () => {
|
|
167
207
|
menuView.isOpen = !menuView.isOpen;
|
|
168
|
-
if (menuView.isOpen) {
|
|
169
|
-
menuView.panelView.focus();
|
|
170
|
-
}
|
|
171
208
|
});
|
|
172
209
|
},
|
|
173
210
|
/**
|
|
@@ -420,6 +457,12 @@ export const MenuBarMenuViewPanelPositioningFunctions = {
|
|
|
420
457
|
* ]
|
|
421
458
|
* },
|
|
422
459
|
* {
|
|
460
|
+
* groupId: 'previewMergeFields',
|
|
461
|
+
* items: [
|
|
462
|
+
* 'menuBar:previewMergeFields'
|
|
463
|
+
* ]
|
|
464
|
+
* },
|
|
465
|
+
* {
|
|
423
466
|
* groupId: 'restrictedEditingException',
|
|
424
467
|
* items: [
|
|
425
468
|
* 'menuBar:restrictedEditingException'
|
|
@@ -444,7 +487,8 @@ export const MenuBarMenuViewPanelPositioningFunctions = {
|
|
|
444
487
|
* groupId: 'insertInline',
|
|
445
488
|
* items: [
|
|
446
489
|
* 'menuBar:link',
|
|
447
|
-
* 'menuBar:comment'
|
|
490
|
+
* 'menuBar:comment',
|
|
491
|
+
* 'menuBar:insertMergeField'
|
|
448
492
|
* ]
|
|
449
493
|
* },
|
|
450
494
|
* {
|
|
@@ -670,9 +714,15 @@ export const DefaultMenuBarItems = [
|
|
|
670
714
|
]
|
|
671
715
|
},
|
|
672
716
|
{
|
|
673
|
-
groupId: '
|
|
717
|
+
groupId: 'previewMergeFields',
|
|
674
718
|
items: [
|
|
675
|
-
'menuBar:
|
|
719
|
+
'menuBar:previewMergeFields'
|
|
720
|
+
]
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
groupId: 'restrictedEditing',
|
|
724
|
+
items: [
|
|
725
|
+
'menuBar:restrictedEditing'
|
|
676
726
|
]
|
|
677
727
|
}
|
|
678
728
|
]
|
|
@@ -694,7 +744,8 @@ export const DefaultMenuBarItems = [
|
|
|
694
744
|
groupId: 'insertInline',
|
|
695
745
|
items: [
|
|
696
746
|
'menuBar:link',
|
|
697
|
-
'menuBar:comment'
|
|
747
|
+
'menuBar:comment',
|
|
748
|
+
'menuBar:insertMergeField'
|
|
698
749
|
]
|
|
699
750
|
},
|
|
700
751
|
{
|
|
@@ -702,6 +753,7 @@ export const DefaultMenuBarItems = [
|
|
|
702
753
|
items: [
|
|
703
754
|
'menuBar:mediaEmbed',
|
|
704
755
|
'menuBar:insertTemplate',
|
|
756
|
+
'menuBar:specialCharacters',
|
|
705
757
|
'menuBar:blockQuote',
|
|
706
758
|
'menuBar:codeBlock',
|
|
707
759
|
'menuBar:htmlEmbed'
|
|
@@ -716,9 +768,9 @@ export const DefaultMenuBarItems = [
|
|
|
716
768
|
]
|
|
717
769
|
},
|
|
718
770
|
{
|
|
719
|
-
groupId: '
|
|
771
|
+
groupId: 'restrictedEditingException',
|
|
720
772
|
items: [
|
|
721
|
-
'menuBar:
|
|
773
|
+
'menuBar:restrictedEditingException'
|
|
722
774
|
]
|
|
723
775
|
}
|
|
724
776
|
]
|
|
@@ -885,10 +937,11 @@ export function normalizeMenuBarConfig(config) {
|
|
|
885
937
|
* * Purged empty menus,
|
|
886
938
|
* * Localized top-level menu labels.
|
|
887
939
|
*/
|
|
888
|
-
export function processMenuBarConfig({ normalizedConfig, locale, componentFactory }) {
|
|
940
|
+
export function processMenuBarConfig({ normalizedConfig, locale, componentFactory, extraItems }) {
|
|
889
941
|
const configClone = cloneDeep(normalizedConfig);
|
|
942
|
+
handleAdditions(normalizedConfig, configClone, extraItems);
|
|
890
943
|
handleRemovals(normalizedConfig, configClone);
|
|
891
|
-
handleAdditions(normalizedConfig, configClone);
|
|
944
|
+
handleAdditions(normalizedConfig, configClone, configClone.addItems);
|
|
892
945
|
purgeUnavailableComponents(normalizedConfig, configClone, componentFactory);
|
|
893
946
|
purgeEmptyMenus(normalizedConfig, configClone);
|
|
894
947
|
localizeMenuLabels(configClone, locale);
|
|
@@ -953,13 +1006,15 @@ function handleRemovals(originalConfig, config) {
|
|
|
953
1006
|
}
|
|
954
1007
|
}
|
|
955
1008
|
/**
|
|
956
|
-
*
|
|
1009
|
+
* Adds provided items to config. It allows for adding menus, groups, and items at arbitrary
|
|
957
1010
|
* positions in the menu bar. If the position does not exist, a warning is logged.
|
|
958
1011
|
*/
|
|
959
|
-
function handleAdditions(originalConfig, config) {
|
|
960
|
-
const itemsToBeAdded = config.addItems;
|
|
1012
|
+
function handleAdditions(originalConfig, config, items) {
|
|
961
1013
|
const successFullyAddedItems = [];
|
|
962
|
-
|
|
1014
|
+
if (items.length == 0) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
for (const itemToAdd of items) {
|
|
963
1018
|
const relation = getRelationFromPosition(itemToAdd.position);
|
|
964
1019
|
const relativeId = getRelativeIdFromPosition(itemToAdd.position);
|
|
965
1020
|
// Adding a menu.
|
|
@@ -1038,7 +1093,7 @@ function handleAdditions(originalConfig, config) {
|
|
|
1038
1093
|
}
|
|
1039
1094
|
}
|
|
1040
1095
|
}
|
|
1041
|
-
for (const addedItemConfig of
|
|
1096
|
+
for (const addedItemConfig of items) {
|
|
1042
1097
|
if (!successFullyAddedItems.includes(addedItemConfig)) {
|
|
1043
1098
|
/**
|
|
1044
1099
|
* There was a problem processing the configuration of the menu bar. The configured item could not be added
|
|
@@ -1322,27 +1377,3 @@ function getIdFromGroupItem(item) {
|
|
|
1322
1377
|
function isMenuDefinition(definition) {
|
|
1323
1378
|
return typeof definition === 'object' && 'menuId' in definition;
|
|
1324
1379
|
}
|
|
1325
|
-
/**
|
|
1326
|
-
* Initializes menu bar for given editor.
|
|
1327
|
-
*
|
|
1328
|
-
* @internal
|
|
1329
|
-
*/
|
|
1330
|
-
export function _initMenuBar(editor, menuBarView) {
|
|
1331
|
-
const menuBarViewElement = menuBarView.element;
|
|
1332
|
-
editor.ui.focusTracker.add(menuBarViewElement);
|
|
1333
|
-
editor.keystrokes.listenTo(menuBarViewElement);
|
|
1334
|
-
const normalizedMenuBarConfig = normalizeMenuBarConfig(editor.config.get('menuBar') || {});
|
|
1335
|
-
menuBarView.fillFromConfig(normalizedMenuBarConfig, editor.ui.componentFactory);
|
|
1336
|
-
editor.keystrokes.set('Esc', (data, cancel) => {
|
|
1337
|
-
if (menuBarViewElement.contains(editor.ui.focusTracker.focusedElement)) {
|
|
1338
|
-
editor.editing.view.focus();
|
|
1339
|
-
cancel();
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
editor.keystrokes.set('Alt+F9', (data, cancel) => {
|
|
1343
|
-
if (!menuBarViewElement.contains(editor.ui.focusTracker.focusedElement)) {
|
|
1344
|
-
menuBarView.focus();
|
|
1345
|
-
cancel();
|
|
1346
|
-
}
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
@@ -110,10 +110,18 @@ export default class BalloonPanelView extends View {
|
|
|
110
110
|
* @private
|
|
111
111
|
*/
|
|
112
112
|
private _pinWhenIsVisibleCallback;
|
|
113
|
+
/**
|
|
114
|
+
* An instance of resize observer used to detect if target element is still visible.
|
|
115
|
+
*/
|
|
116
|
+
private _resizeObserver;
|
|
113
117
|
/**
|
|
114
118
|
* @inheritDoc
|
|
115
119
|
*/
|
|
116
120
|
constructor(locale?: Locale);
|
|
121
|
+
/**
|
|
122
|
+
* @inheritDoc
|
|
123
|
+
*/
|
|
124
|
+
destroy(): void;
|
|
117
125
|
/**
|
|
118
126
|
* Shows the panel.
|
|
119
127
|
*
|
|
@@ -157,8 +165,10 @@ export default class BalloonPanelView extends View {
|
|
|
157
165
|
*
|
|
158
166
|
* @param options Positioning options compatible with {@link module:utils/dom/position~getOptimalPosition}.
|
|
159
167
|
* Default `positions` array is {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
|
|
168
|
+
* @returns Whether the balloon was shown and successfully attached or not. Attaching can fail if the target
|
|
169
|
+
* provided in the options is invisible (e.g. element detached from DOM).
|
|
160
170
|
*/
|
|
161
|
-
attachTo(options: Partial<PositionOptions>):
|
|
171
|
+
attachTo(options: Partial<PositionOptions>): boolean;
|
|
162
172
|
/**
|
|
163
173
|
* Works the same way as the {@link #attachTo} method except that the position of the panel is
|
|
164
174
|
* continuously updated when:
|
|
@@ -202,6 +212,8 @@ export default class BalloonPanelView extends View {
|
|
|
202
212
|
* Starts managing the pinned state of the panel. See {@link #pin}.
|
|
203
213
|
*
|
|
204
214
|
* @param options Positioning options compatible with {@link module:utils/dom/position~getOptimalPosition}.
|
|
215
|
+
* @returns Whether the balloon was shown and successfully attached or not. Attaching can fail if the target
|
|
216
|
+
* provided in the options is invisible (e.g. element detached from DOM).
|
|
205
217
|
*/
|
|
206
218
|
private _startPinning;
|
|
207
219
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module ui/panel/balloon/balloonpanelview
|
|
7
7
|
*/
|
|
8
8
|
import View from '../../view.js';
|
|
9
|
-
import { getOptimalPosition, global, isRange, toUnit } from '@ckeditor/ckeditor5-utils';
|
|
9
|
+
import { getOptimalPosition, global, isRange, toUnit, isVisible, ResizeObserver } from '@ckeditor/ckeditor5-utils';
|
|
10
10
|
import { isElement } from 'lodash-es';
|
|
11
11
|
import '../../../theme/components/panel/balloonpanel.css';
|
|
12
12
|
const toPx = /* #__PURE__ */ toUnit('px');
|
|
@@ -79,6 +79,7 @@ class BalloonPanelView extends View {
|
|
|
79
79
|
this.set('withArrow', true);
|
|
80
80
|
this.set('class', undefined);
|
|
81
81
|
this._pinWhenIsVisibleCallback = null;
|
|
82
|
+
this._resizeObserver = null;
|
|
82
83
|
this.content = this.createCollection();
|
|
83
84
|
this.setTemplate({
|
|
84
85
|
tag: 'div',
|
|
@@ -99,6 +100,13 @@ class BalloonPanelView extends View {
|
|
|
99
100
|
children: this.content
|
|
100
101
|
});
|
|
101
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* @inheritDoc
|
|
105
|
+
*/
|
|
106
|
+
destroy() {
|
|
107
|
+
this.hide();
|
|
108
|
+
super.destroy();
|
|
109
|
+
}
|
|
102
110
|
/**
|
|
103
111
|
* Shows the panel.
|
|
104
112
|
*
|
|
@@ -146,8 +154,14 @@ class BalloonPanelView extends View {
|
|
|
146
154
|
*
|
|
147
155
|
* @param options Positioning options compatible with {@link module:utils/dom/position~getOptimalPosition}.
|
|
148
156
|
* Default `positions` array is {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
|
|
157
|
+
* @returns Whether the balloon was shown and successfully attached or not. Attaching can fail if the target
|
|
158
|
+
* provided in the options is invisible (e.g. element detached from DOM).
|
|
149
159
|
*/
|
|
150
160
|
attachTo(options) {
|
|
161
|
+
const target = getDomElement(options.target);
|
|
162
|
+
if (target && !isVisible(target)) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
151
165
|
this.show();
|
|
152
166
|
const defaultPositions = BalloonPanelView.defaultPositions;
|
|
153
167
|
const positionOptions = Object.assign({}, {
|
|
@@ -180,6 +194,7 @@ class BalloonPanelView extends View {
|
|
|
180
194
|
this.left = left;
|
|
181
195
|
this.position = position;
|
|
182
196
|
this.withArrow = withArrow;
|
|
197
|
+
return true;
|
|
183
198
|
}
|
|
184
199
|
/**
|
|
185
200
|
* Works the same way as the {@link #attachTo} method except that the position of the panel is
|
|
@@ -217,6 +232,9 @@ class BalloonPanelView extends View {
|
|
|
217
232
|
*/
|
|
218
233
|
pin(options) {
|
|
219
234
|
this.unpin();
|
|
235
|
+
if (!this._startPinning(options)) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
220
238
|
this._pinWhenIsVisibleCallback = () => {
|
|
221
239
|
if (this.isVisible) {
|
|
222
240
|
this._startPinning(options);
|
|
@@ -225,7 +243,6 @@ class BalloonPanelView extends View {
|
|
|
225
243
|
this._stopPinning();
|
|
226
244
|
}
|
|
227
245
|
};
|
|
228
|
-
this._startPinning(options);
|
|
229
246
|
// Control the state of the listeners depending on whether the panel is visible
|
|
230
247
|
// or not.
|
|
231
248
|
// TODO: Use on() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
|
|
@@ -249,9 +266,13 @@ class BalloonPanelView extends View {
|
|
|
249
266
|
* Starts managing the pinned state of the panel. See {@link #pin}.
|
|
250
267
|
*
|
|
251
268
|
* @param options Positioning options compatible with {@link module:utils/dom/position~getOptimalPosition}.
|
|
269
|
+
* @returns Whether the balloon was shown and successfully attached or not. Attaching can fail if the target
|
|
270
|
+
* provided in the options is invisible (e.g. element detached from DOM).
|
|
252
271
|
*/
|
|
253
272
|
_startPinning(options) {
|
|
254
|
-
this.attachTo(options)
|
|
273
|
+
if (!this.attachTo(options)) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
255
276
|
const targetElement = getDomElement(options.target);
|
|
256
277
|
const limiterElement = options.limiter ? getDomElement(options.limiter) : global.document.body;
|
|
257
278
|
// Then we need to listen on scroll event of eny element in the document.
|
|
@@ -271,6 +292,19 @@ class BalloonPanelView extends View {
|
|
|
271
292
|
this.listenTo(global.window, 'resize', () => {
|
|
272
293
|
this.attachTo(options);
|
|
273
294
|
});
|
|
295
|
+
// Hide the panel if the target element is no longer visible.
|
|
296
|
+
if (targetElement && !this._resizeObserver) {
|
|
297
|
+
const checkVisibility = () => {
|
|
298
|
+
// If the target element is no longer visible, hide the panel.
|
|
299
|
+
if (!isVisible(targetElement)) {
|
|
300
|
+
this.unpin();
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
// Element is being resized to 0x0 after it's parent became hidden,
|
|
304
|
+
// so we need to check size in order to determine if it's visible or not.
|
|
305
|
+
this._resizeObserver = new ResizeObserver(targetElement, checkVisibility);
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
274
308
|
}
|
|
275
309
|
/**
|
|
276
310
|
* Stops managing the pinned state of the panel. See {@link #pin}.
|
|
@@ -278,6 +312,10 @@ class BalloonPanelView extends View {
|
|
|
278
312
|
_stopPinning() {
|
|
279
313
|
this.stopListening(global.document, 'scroll');
|
|
280
314
|
this.stopListening(global.window, 'resize');
|
|
315
|
+
if (this._resizeObserver) {
|
|
316
|
+
this._resizeObserver.destroy();
|
|
317
|
+
this._resizeObserver = null;
|
|
318
|
+
}
|
|
281
319
|
}
|
|
282
320
|
/**
|
|
283
321
|
* Returns available {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
|
|
@@ -0,0 +1,15 @@
|
|
|
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 type ViewCollection from '../viewcollection.js';
|
|
6
|
+
import type ListItemGroupView from '../list/listitemgroupview.js';
|
|
7
|
+
import type ListItemView from '../list/listitemview.js';
|
|
8
|
+
import type ListSeparatorView from '../list/listseparatorview.js';
|
|
9
|
+
/**
|
|
10
|
+
* A filter function that returns matching item and group names in the list view.
|
|
11
|
+
*/
|
|
12
|
+
export default function filterGroupAndItemNames(regExp: RegExp | null, items: ViewCollection<ListItemGroupView | ListItemView | ListSeparatorView>): {
|
|
13
|
+
resultsCount: number;
|
|
14
|
+
totalItemsCount: number;
|
|
15
|
+
};
|