@vaadin/menu-bar 24.0.0-alpha9 → 24.0.0-beta1
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/package.json +10 -10
- package/src/vaadin-menu-bar-button.js +1 -1
- package/src/vaadin-menu-bar-item.d.ts +21 -0
- package/src/vaadin-menu-bar-item.js +55 -0
- package/src/vaadin-menu-bar-list-box.d.ts +22 -0
- package/src/vaadin-menu-bar-list-box.js +81 -0
- package/src/{vaadin-menu-bar-interactions-mixin.d.ts → vaadin-menu-bar-mixin.d.ts} +16 -4
- package/src/{vaadin-menu-bar-interactions-mixin.js → vaadin-menu-bar-mixin.js} +322 -30
- package/src/vaadin-menu-bar-overlay.d.ts +20 -0
- package/src/vaadin-menu-bar-overlay.js +26 -0
- package/src/vaadin-menu-bar-submenu.js +39 -0
- package/src/vaadin-menu-bar.d.ts +14 -7
- package/src/vaadin-menu-bar.js +22 -14
- package/theme/lumo/vaadin-menu-bar-button-styles.js +4 -4
- package/theme/lumo/vaadin-menu-bar-item-styles.js +21 -21
- package/theme/lumo/vaadin-menu-bar-list-box-styles.js +5 -0
- package/theme/lumo/vaadin-menu-bar-overlay-styles.js +11 -9
- package/theme/lumo/vaadin-menu-bar.js +2 -1
- package/theme/material/vaadin-menu-bar-button-styles.js +3 -3
- package/theme/material/vaadin-menu-bar-item-styles.js +19 -19
- package/theme/material/vaadin-menu-bar-list-box-styles.js +5 -0
- package/theme/material/vaadin-menu-bar-overlay-styles.js +11 -9
- package/theme/material/vaadin-menu-bar.js +2 -1
- package/web-types.json +31 -9
- package/web-types.lit.json +13 -6
- package/src/vaadin-menu-bar-buttons-mixin.d.ts +0 -22
- package/src/vaadin-menu-bar-buttons-mixin.js +0 -315
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/menu-bar",
|
|
3
|
-
"version": "24.0.0-
|
|
3
|
+
"version": "24.0.0-beta1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,22 +37,22 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
39
39
|
"@polymer/polymer": "^3.0.0",
|
|
40
|
-
"@vaadin/button": "24.0.0-
|
|
41
|
-
"@vaadin/component-base": "24.0.0-
|
|
42
|
-
"@vaadin/context-menu": "24.0.0-
|
|
43
|
-
"@vaadin/vaadin-lumo-styles": "24.0.0-
|
|
44
|
-
"@vaadin/vaadin-material-styles": "24.0.0-
|
|
45
|
-
"@vaadin/vaadin-themable-mixin": "24.0.0-
|
|
40
|
+
"@vaadin/button": "24.0.0-beta1",
|
|
41
|
+
"@vaadin/component-base": "24.0.0-beta1",
|
|
42
|
+
"@vaadin/context-menu": "24.0.0-beta1",
|
|
43
|
+
"@vaadin/vaadin-lumo-styles": "24.0.0-beta1",
|
|
44
|
+
"@vaadin/vaadin-material-styles": "24.0.0-beta1",
|
|
45
|
+
"@vaadin/vaadin-themable-mixin": "24.0.0-beta1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@esm-bundle/chai": "^4.3.4",
|
|
49
|
-
"@vaadin/icon": "24.0.0-
|
|
50
|
-
"@vaadin/testing-helpers": "^0.
|
|
49
|
+
"@vaadin/icon": "24.0.0-beta1",
|
|
50
|
+
"@vaadin/testing-helpers": "^0.4.0",
|
|
51
51
|
"sinon": "^13.0.2"
|
|
52
52
|
},
|
|
53
53
|
"web-types": [
|
|
54
54
|
"web-types.json",
|
|
55
55
|
"web-types.lit.json"
|
|
56
56
|
],
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "c5b48921a62482746df8e46994b37e1490fec27e"
|
|
58
58
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
7
|
+
import { ItemMixin } from '@vaadin/item/src/vaadin-item-mixin.js';
|
|
8
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
12
|
+
*/
|
|
13
|
+
declare class MenuBarItem extends ItemMixin(DirMixin(ThemableMixin(HTMLElement))) {}
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'vaadin-menu-bar-item': MenuBarItem;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { MenuBarItem };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
7
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
8
|
+
import { ItemMixin } from '@vaadin/item/src/vaadin-item-mixin.js';
|
|
9
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
13
|
+
*
|
|
14
|
+
* @extends HTMLElement
|
|
15
|
+
* @mixes DirMixin
|
|
16
|
+
* @mixes ItemMixin
|
|
17
|
+
* @mixes ThemableMixin
|
|
18
|
+
* @protected
|
|
19
|
+
*/
|
|
20
|
+
class MenuBarItem extends ItemMixin(ThemableMixin(DirMixin(PolymerElement))) {
|
|
21
|
+
static get is() {
|
|
22
|
+
return 'vaadin-menu-bar-item';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static get template() {
|
|
26
|
+
return html`
|
|
27
|
+
<style>
|
|
28
|
+
:host {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:host([hidden]) {
|
|
33
|
+
display: none !important;
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
36
|
+
<span part="checkmark" aria-hidden="true"></span>
|
|
37
|
+
<div part="content">
|
|
38
|
+
<slot></slot>
|
|
39
|
+
</div>
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @protected */
|
|
44
|
+
connectedCallback() {
|
|
45
|
+
super.connectedCallback();
|
|
46
|
+
|
|
47
|
+
// Set role in `connectedCallback()` instead of `ready()`
|
|
48
|
+
// because the role is removed when teleporting to button.
|
|
49
|
+
this.setAttribute('role', 'menuitem');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
customElements.define(MenuBarItem.is, MenuBarItem);
|
|
54
|
+
|
|
55
|
+
export { MenuBarItem };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
8
|
+
import { ListMixin } from '@vaadin/component-base/src/list-mixin.js';
|
|
9
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
13
|
+
*/
|
|
14
|
+
declare class MenuBarListBox extends ListMixin(DirMixin(ThemableMixin(ControllerMixin(HTMLElement)))) {}
|
|
15
|
+
|
|
16
|
+
declare global {
|
|
17
|
+
interface HTMLElementTagNameMap {
|
|
18
|
+
'vaadin-menu-bar-list-box': MenuBarListBox;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { MenuBarListBox };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
7
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
8
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
|
+
import { ListMixin } from '@vaadin/component-base/src/list-mixin.js';
|
|
10
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
14
|
+
*
|
|
15
|
+
* @extends HTMLElement
|
|
16
|
+
* @mixes ControllerMixin
|
|
17
|
+
* @mixes DirMixin
|
|
18
|
+
* @mixes ListMixin
|
|
19
|
+
* @mixes ThemableMixin
|
|
20
|
+
* @protected
|
|
21
|
+
*/
|
|
22
|
+
class MenuBarListBox extends ListMixin(ThemableMixin(DirMixin(ControllerMixin(PolymerElement)))) {
|
|
23
|
+
static get is() {
|
|
24
|
+
return 'vaadin-menu-bar-list-box';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static get template() {
|
|
28
|
+
return html`
|
|
29
|
+
<style>
|
|
30
|
+
:host {
|
|
31
|
+
display: flex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:host([hidden]) {
|
|
35
|
+
display: none !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[part='items'] {
|
|
39
|
+
height: 100%;
|
|
40
|
+
width: 100%;
|
|
41
|
+
overflow-y: auto;
|
|
42
|
+
-webkit-overflow-scrolling: touch;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
<div part="items">
|
|
46
|
+
<slot></slot>
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static get properties() {
|
|
52
|
+
return {
|
|
53
|
+
// We don't need to define this property since super default is vertical,
|
|
54
|
+
// but we don't want it to be modified, or be shown in the API docs.
|
|
55
|
+
/** @private */
|
|
56
|
+
orientation: {
|
|
57
|
+
readOnly: true,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @return {!HTMLElement}
|
|
64
|
+
* @protected
|
|
65
|
+
* @override
|
|
66
|
+
*/
|
|
67
|
+
get _scrollerElement() {
|
|
68
|
+
return this.shadowRoot.querySelector('[part="items"]');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** @protected */
|
|
72
|
+
ready() {
|
|
73
|
+
super.ready();
|
|
74
|
+
|
|
75
|
+
this.setAttribute('role', 'menu');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
customElements.define(MenuBarListBox.is, MenuBarListBox);
|
|
80
|
+
|
|
81
|
+
export { MenuBarListBox };
|
|
@@ -4,22 +4,34 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
8
|
import type { FocusMixinClass } from '@vaadin/component-base/src/focus-mixin.js';
|
|
8
9
|
import type { KeyboardDirectionMixinClass } from '@vaadin/component-base/src/keyboard-direction-mixin.js';
|
|
9
10
|
import type { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
11
|
+
import type { ResizeMixinClass } from '@vaadin/component-base/src/resize-mixin.js';
|
|
10
12
|
|
|
11
|
-
export declare function
|
|
13
|
+
export declare function MenuBarMixin<T extends Constructor<HTMLElement>>(
|
|
12
14
|
base: T,
|
|
13
|
-
): Constructor<
|
|
14
|
-
Constructor<
|
|
15
|
+
): Constructor<ControllerMixinClass> &
|
|
16
|
+
Constructor<FocusMixinClass> &
|
|
15
17
|
Constructor<KeyboardDirectionMixinClass> &
|
|
16
18
|
Constructor<KeyboardMixinClass> &
|
|
19
|
+
Constructor<MenuBarMixinClass> &
|
|
20
|
+
Constructor<ResizeMixinClass> &
|
|
17
21
|
T;
|
|
18
22
|
|
|
19
|
-
export declare class
|
|
23
|
+
export declare class MenuBarMixinClass {
|
|
20
24
|
/**
|
|
21
25
|
* If true, the submenu will open on hover (mouseover) instead of click.
|
|
22
26
|
* @attr {boolean} open-on-hover
|
|
23
27
|
*/
|
|
24
28
|
openOnHover: boolean | null | undefined;
|
|
29
|
+
|
|
30
|
+
protected readonly _buttons: HTMLElement[];
|
|
31
|
+
|
|
32
|
+
protected readonly _container: HTMLElement;
|
|
33
|
+
|
|
34
|
+
protected readonly _overflow: HTMLElement;
|
|
35
|
+
|
|
36
|
+
protected _hasOverflow: boolean;
|
|
25
37
|
}
|
|
@@ -3,17 +3,22 @@
|
|
|
3
3
|
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
6
7
|
import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
|
|
7
8
|
import { isElementFocused, isKeyboardActive } from '@vaadin/component-base/src/focus-utils.js';
|
|
8
9
|
import { KeyboardDirectionMixin } from '@vaadin/component-base/src/keyboard-direction-mixin.js';
|
|
10
|
+
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
11
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* @polymerMixin
|
|
15
|
+
* @mixes ControllerMixin
|
|
12
16
|
* @mixes FocusMixin
|
|
13
|
-
* @mixes
|
|
17
|
+
* @mixes KeyboardDirectionMixin
|
|
18
|
+
* @mixes ResizeMixin
|
|
14
19
|
*/
|
|
15
|
-
export const
|
|
16
|
-
class
|
|
20
|
+
export const MenuBarMixin = (superClass) =>
|
|
21
|
+
class MenuBarMixinClass extends KeyboardDirectionMixin(ResizeMixin(FocusMixin(ControllerMixin(superClass)))) {
|
|
17
22
|
static get properties() {
|
|
18
23
|
return {
|
|
19
24
|
/**
|
|
@@ -23,34 +28,40 @@ export const InteractionsMixin = (superClass) =>
|
|
|
23
28
|
openOnHover: {
|
|
24
29
|
type: Boolean,
|
|
25
30
|
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @type {boolean}
|
|
34
|
+
* @protected
|
|
35
|
+
*/
|
|
36
|
+
_hasOverflow: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
value: false,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/** @protected */
|
|
42
|
+
_overflow: {
|
|
43
|
+
type: Object,
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/** @protected */
|
|
47
|
+
_container: {
|
|
48
|
+
type: Object,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
static get observers() {
|
|
35
|
-
return [
|
|
54
|
+
return [
|
|
55
|
+
'_itemsChanged(items, items.splices)',
|
|
56
|
+
'__hasOverflowChanged(_hasOverflow, _overflow)',
|
|
57
|
+
'__i18nChanged(i18n, _overflow)',
|
|
58
|
+
'_menuItemsChanged(items, _overflow, _container, items.splices)',
|
|
59
|
+
];
|
|
36
60
|
}
|
|
37
61
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.addEventListener('mousedown', () => this._hideTooltip());
|
|
43
|
-
this.addEventListener('mouseleave', () => this._hideTooltip());
|
|
44
|
-
|
|
45
|
-
this._subMenu.addEventListener('item-selected', this.__onItemSelected.bind(this));
|
|
46
|
-
this._subMenu.addEventListener('close-all-menus', this.__onEscapeClose.bind(this));
|
|
47
|
-
|
|
48
|
-
const overlay = this._subMenu.$.overlay;
|
|
49
|
-
overlay.addEventListener('keydown', this.__boundOnContextMenuKeydown);
|
|
50
|
-
|
|
51
|
-
const container = this._container;
|
|
52
|
-
container.addEventListener('click', this.__onButtonClick.bind(this));
|
|
53
|
-
container.addEventListener('mouseover', (e) => this._onMouseOver(e));
|
|
62
|
+
constructor() {
|
|
63
|
+
super();
|
|
64
|
+
this.__boundOnContextMenuKeydown = this.__onContextMenuKeydown.bind(this);
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
/**
|
|
@@ -77,6 +88,66 @@ export const InteractionsMixin = (superClass) =>
|
|
|
77
88
|
return false;
|
|
78
89
|
}
|
|
79
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Override getter from `ResizeMixin` to observe parent.
|
|
93
|
+
*
|
|
94
|
+
* @protected
|
|
95
|
+
* @override
|
|
96
|
+
*/
|
|
97
|
+
get _observeParent() {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @return {!Array<!HTMLElement>}
|
|
103
|
+
* @protected
|
|
104
|
+
*/
|
|
105
|
+
get _buttons() {
|
|
106
|
+
return Array.from(this.querySelectorAll('vaadin-menu-bar-button'));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @private */
|
|
110
|
+
get _subMenu() {
|
|
111
|
+
return this.shadowRoot.querySelector('vaadin-menu-bar-submenu');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @protected */
|
|
115
|
+
ready() {
|
|
116
|
+
super.ready();
|
|
117
|
+
|
|
118
|
+
this.setAttribute('role', 'menubar');
|
|
119
|
+
|
|
120
|
+
this._overflowController = new SlotController(this, 'overflow', 'vaadin-menu-bar-button', {
|
|
121
|
+
initializer: (btn) => {
|
|
122
|
+
btn.setAttribute('hidden', '');
|
|
123
|
+
|
|
124
|
+
const dots = document.createElement('div');
|
|
125
|
+
dots.setAttribute('aria-hidden', 'true');
|
|
126
|
+
dots.innerHTML = '·'.repeat(3);
|
|
127
|
+
btn.appendChild(dots);
|
|
128
|
+
|
|
129
|
+
this._overflow = btn;
|
|
130
|
+
this._initButtonAttrs(btn);
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
this.addController(this._overflowController);
|
|
134
|
+
|
|
135
|
+
this.addEventListener('mousedown', () => this._hideTooltip());
|
|
136
|
+
this.addEventListener('mouseleave', () => this._hideTooltip());
|
|
137
|
+
|
|
138
|
+
this._subMenu.addEventListener('item-selected', this.__onItemSelected.bind(this));
|
|
139
|
+
this._subMenu.addEventListener('close-all-menus', this.__onEscapeClose.bind(this));
|
|
140
|
+
|
|
141
|
+
const overlay = this._subMenu.$.overlay;
|
|
142
|
+
overlay.addEventListener('keydown', this.__boundOnContextMenuKeydown);
|
|
143
|
+
|
|
144
|
+
const container = this.shadowRoot.querySelector('[part="container"]');
|
|
145
|
+
container.addEventListener('click', this.__onButtonClick.bind(this));
|
|
146
|
+
container.addEventListener('mouseover', (e) => this._onMouseOver(e));
|
|
147
|
+
|
|
148
|
+
this._container = container;
|
|
149
|
+
}
|
|
150
|
+
|
|
80
151
|
/**
|
|
81
152
|
* Override method inherited from `KeyboardDirectionMixin`
|
|
82
153
|
* to use the list of menu-bar buttons as items.
|
|
@@ -95,6 +166,232 @@ export const InteractionsMixin = (superClass) =>
|
|
|
95
166
|
this._hideTooltip(true);
|
|
96
167
|
}
|
|
97
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Implement callback from `ResizeMixin` to update buttons
|
|
171
|
+
* and detect whether to show or hide the overflow button.
|
|
172
|
+
*
|
|
173
|
+
* @protected
|
|
174
|
+
* @override
|
|
175
|
+
*/
|
|
176
|
+
_onResize() {
|
|
177
|
+
this.__detectOverflow();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** @private */
|
|
181
|
+
__hasOverflowChanged(hasOverflow, overflow) {
|
|
182
|
+
if (overflow) {
|
|
183
|
+
overflow.toggleAttribute('hidden', !hasOverflow);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** @private */
|
|
188
|
+
_menuItemsChanged(items, overflow, container) {
|
|
189
|
+
if (!overflow || !container) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (items !== this._oldItems) {
|
|
194
|
+
this._oldItems = items;
|
|
195
|
+
this.__renderButtons(items);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @private */
|
|
200
|
+
__i18nChanged(i18n, overflow) {
|
|
201
|
+
if (overflow && i18n && i18n.moreOptions !== undefined) {
|
|
202
|
+
if (i18n.moreOptions) {
|
|
203
|
+
overflow.setAttribute('aria-label', i18n.moreOptions);
|
|
204
|
+
} else {
|
|
205
|
+
overflow.removeAttribute('aria-label');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** @private */
|
|
211
|
+
__getOverflowCount(overflow) {
|
|
212
|
+
// We can't use optional chaining due to webpack 4
|
|
213
|
+
return (overflow.item && overflow.item.children && overflow.item.children.length) || 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** @private */
|
|
217
|
+
__restoreButtons(buttons) {
|
|
218
|
+
buttons.forEach((button) => {
|
|
219
|
+
button.disabled = (button.item && button.item.disabled) || this.disabled;
|
|
220
|
+
button.style.visibility = '';
|
|
221
|
+
button.style.position = '';
|
|
222
|
+
|
|
223
|
+
// Teleport item component back from "overflow" sub-menu
|
|
224
|
+
const item = button.item && button.item.component;
|
|
225
|
+
if (item instanceof HTMLElement && item.getAttribute('role') === 'menuitem') {
|
|
226
|
+
button.appendChild(item);
|
|
227
|
+
item.removeAttribute('role');
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this.__updateOverflow([]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** @private */
|
|
234
|
+
__updateOverflow(items) {
|
|
235
|
+
this._overflow.item = { children: items };
|
|
236
|
+
this._hasOverflow = items.length > 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** @private */
|
|
240
|
+
__setOverflowItems(buttons, overflow) {
|
|
241
|
+
const container = this._container;
|
|
242
|
+
|
|
243
|
+
if (container.offsetWidth < container.scrollWidth) {
|
|
244
|
+
this._hasOverflow = true;
|
|
245
|
+
|
|
246
|
+
const isRTL = this.__isRTL;
|
|
247
|
+
|
|
248
|
+
let i;
|
|
249
|
+
for (i = buttons.length; i > 0; i--) {
|
|
250
|
+
const btn = buttons[i - 1];
|
|
251
|
+
const btnStyle = getComputedStyle(btn);
|
|
252
|
+
|
|
253
|
+
// If this button isn't overflowing, then the rest aren't either
|
|
254
|
+
if (
|
|
255
|
+
(!isRTL && btn.offsetLeft + btn.offsetWidth < container.offsetWidth - overflow.offsetWidth) ||
|
|
256
|
+
(isRTL && btn.offsetLeft >= overflow.offsetWidth)
|
|
257
|
+
) {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
btn.disabled = true;
|
|
262
|
+
btn.style.visibility = 'hidden';
|
|
263
|
+
btn.style.position = 'absolute';
|
|
264
|
+
// Save width for buttons with component
|
|
265
|
+
btn.style.width = btnStyle.width;
|
|
266
|
+
}
|
|
267
|
+
const items = buttons.filter((_, idx) => idx >= i).map((b) => b.item);
|
|
268
|
+
this.__updateOverflow(items);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** @private */
|
|
273
|
+
__detectOverflow() {
|
|
274
|
+
const overflow = this._overflow;
|
|
275
|
+
const buttons = this._buttons.filter((btn) => btn !== overflow);
|
|
276
|
+
const oldOverflowCount = this.__getOverflowCount(overflow);
|
|
277
|
+
|
|
278
|
+
// Reset all buttons in the menu bar and the overflow button
|
|
279
|
+
this.__restoreButtons(buttons);
|
|
280
|
+
|
|
281
|
+
// Hide any overflowing buttons and put them in the 'overflow' button
|
|
282
|
+
this.__setOverflowItems(buttons, overflow);
|
|
283
|
+
|
|
284
|
+
const newOverflowCount = this.__getOverflowCount(overflow);
|
|
285
|
+
if (oldOverflowCount !== newOverflowCount && this._subMenu.opened) {
|
|
286
|
+
this._subMenu.close();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const isSingleButton = newOverflowCount === buttons.length || (newOverflowCount === 0 && buttons.length === 1);
|
|
290
|
+
this.toggleAttribute('has-single-button', isSingleButton);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** @protected */
|
|
294
|
+
_removeButtons() {
|
|
295
|
+
this._buttons.forEach((button) => {
|
|
296
|
+
if (button !== this._overflow) {
|
|
297
|
+
this.removeChild(button);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** @protected */
|
|
303
|
+
_initButton(item) {
|
|
304
|
+
const button = document.createElement('vaadin-menu-bar-button');
|
|
305
|
+
|
|
306
|
+
const itemCopy = { ...item };
|
|
307
|
+
button.item = itemCopy;
|
|
308
|
+
|
|
309
|
+
if (item.component) {
|
|
310
|
+
const component = this.__getComponent(itemCopy);
|
|
311
|
+
itemCopy.component = component;
|
|
312
|
+
// Save item for overflow menu
|
|
313
|
+
component.item = itemCopy;
|
|
314
|
+
button.appendChild(component);
|
|
315
|
+
} else if (item.text) {
|
|
316
|
+
button.textContent = item.text;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return button;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** @protected */
|
|
323
|
+
_initButtonAttrs(button) {
|
|
324
|
+
button.setAttribute('role', 'menuitem');
|
|
325
|
+
|
|
326
|
+
if (button === this._overflow || (button.item && button.item.children)) {
|
|
327
|
+
button.setAttribute('aria-haspopup', 'true');
|
|
328
|
+
button.setAttribute('aria-expanded', 'false');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** @protected */
|
|
333
|
+
_setButtonDisabled(button, disabled) {
|
|
334
|
+
button.disabled = disabled;
|
|
335
|
+
button.setAttribute('tabindex', disabled ? '-1' : '0');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** @protected */
|
|
339
|
+
_setButtonTheme(btn, hostTheme) {
|
|
340
|
+
let theme = hostTheme;
|
|
341
|
+
|
|
342
|
+
// Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
|
|
343
|
+
const itemTheme = btn.item && btn.item.theme;
|
|
344
|
+
if (itemTheme != null) {
|
|
345
|
+
theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (theme) {
|
|
349
|
+
btn.setAttribute('theme', theme);
|
|
350
|
+
} else {
|
|
351
|
+
btn.removeAttribute('theme');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** @private */
|
|
356
|
+
__getComponent(item) {
|
|
357
|
+
const itemComponent = item.component;
|
|
358
|
+
let component;
|
|
359
|
+
|
|
360
|
+
const isElement = itemComponent instanceof HTMLElement;
|
|
361
|
+
// Use existing item component, if any
|
|
362
|
+
if (isElement && itemComponent.localName === 'vaadin-menu-bar-item') {
|
|
363
|
+
component = itemComponent;
|
|
364
|
+
} else {
|
|
365
|
+
component = document.createElement('vaadin-menu-bar-item');
|
|
366
|
+
component.appendChild(isElement ? itemComponent : document.createElement(itemComponent));
|
|
367
|
+
}
|
|
368
|
+
if (item.text) {
|
|
369
|
+
const node = component.firstChild || component;
|
|
370
|
+
node.textContent = item.text;
|
|
371
|
+
}
|
|
372
|
+
return component;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** @private */
|
|
376
|
+
__renderButtons(items = []) {
|
|
377
|
+
this._removeButtons();
|
|
378
|
+
|
|
379
|
+
/* Empty array, do nothing */
|
|
380
|
+
if (items.length === 0) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
items.forEach((item) => {
|
|
385
|
+
const button = this._initButton(item);
|
|
386
|
+
this.insertBefore(button, this._overflow);
|
|
387
|
+
this._setButtonDisabled(button, item.disabled);
|
|
388
|
+
this._initButtonAttrs(button);
|
|
389
|
+
this._setButtonTheme(button, this._theme);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
this.__detectOverflow();
|
|
393
|
+
}
|
|
394
|
+
|
|
98
395
|
/**
|
|
99
396
|
* @param {HTMLElement} button
|
|
100
397
|
* @protected
|
|
@@ -273,11 +570,6 @@ export const InteractionsMixin = (superClass) =>
|
|
|
273
570
|
}
|
|
274
571
|
}
|
|
275
572
|
|
|
276
|
-
/** @private */
|
|
277
|
-
get _subMenu() {
|
|
278
|
-
return this.shadowRoot.querySelector('vaadin-menu-bar-submenu');
|
|
279
|
-
}
|
|
280
|
-
|
|
281
573
|
/** @private */
|
|
282
574
|
_itemsChanged() {
|
|
283
575
|
const subMenu = this._subMenu;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { MenuOverlayMixin } from '@vaadin/context-menu/src/vaadin-menu-overlay-mixin.js';
|
|
7
|
+
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
11
|
+
*/
|
|
12
|
+
declare class MenuBarOverlay extends MenuOverlayMixin(Overlay) {}
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface HTMLElementTagNameMap {
|
|
16
|
+
'vaadin-menu-bar-overlay': MenuBarOverlay;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { MenuBarOverlay };
|