@vaadin/menu-bar 24.0.0-alpha9 → 24.0.0-beta2
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 +40 -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
|
@@ -1,315 +0,0 @@
|
|
|
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 { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
8
|
-
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @polymerMixin
|
|
12
|
-
* @mixes ResizeMixin
|
|
13
|
-
* @mixes ControllerMixin
|
|
14
|
-
*/
|
|
15
|
-
export const ButtonsMixin = (superClass) =>
|
|
16
|
-
class extends ResizeMixin(ControllerMixin(superClass)) {
|
|
17
|
-
static get properties() {
|
|
18
|
-
return {
|
|
19
|
-
/**
|
|
20
|
-
* @type {boolean}
|
|
21
|
-
* @protected
|
|
22
|
-
*/
|
|
23
|
-
_hasOverflow: {
|
|
24
|
-
type: Boolean,
|
|
25
|
-
value: false,
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
/** @protected */
|
|
29
|
-
_overflow: {
|
|
30
|
-
type: Object,
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
static get observers() {
|
|
36
|
-
return [
|
|
37
|
-
'__hasOverflowChanged(_hasOverflow, _overflow)',
|
|
38
|
-
'__i18nChanged(i18n, _overflow)',
|
|
39
|
-
'_menuItemsChanged(items, _overflow, items.splices)',
|
|
40
|
-
];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Override getter from `ResizeMixin` to observe parent.
|
|
45
|
-
*
|
|
46
|
-
* @protected
|
|
47
|
-
* @override
|
|
48
|
-
*/
|
|
49
|
-
get _observeParent() {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** @protected */
|
|
54
|
-
ready() {
|
|
55
|
-
super.ready();
|
|
56
|
-
|
|
57
|
-
this.setAttribute('role', 'menubar');
|
|
58
|
-
|
|
59
|
-
this._overflowController = new SlotController(this, 'overflow', 'vaadin-menu-bar-button', {
|
|
60
|
-
initializer: (btn) => {
|
|
61
|
-
btn.setAttribute('hidden', '');
|
|
62
|
-
|
|
63
|
-
const dots = document.createElement('div');
|
|
64
|
-
dots.setAttribute('aria-hidden', 'true');
|
|
65
|
-
dots.textContent = '···';
|
|
66
|
-
btn.appendChild(dots);
|
|
67
|
-
|
|
68
|
-
this._overflow = btn;
|
|
69
|
-
this._initButtonAttrs(btn);
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
this.addController(this._overflowController);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @return {!Array<!HTMLElement>}
|
|
77
|
-
* @protected
|
|
78
|
-
*/
|
|
79
|
-
get _buttons() {
|
|
80
|
-
return Array.from(this.querySelectorAll('vaadin-menu-bar-button'));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @return {!HTMLElement}
|
|
85
|
-
* @protected
|
|
86
|
-
*/
|
|
87
|
-
get _container() {
|
|
88
|
-
return this.shadowRoot.querySelector('[part="container"]');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** @private */
|
|
92
|
-
__hasOverflowChanged(hasOverflow, overflow) {
|
|
93
|
-
if (overflow) {
|
|
94
|
-
overflow.toggleAttribute('hidden', !hasOverflow);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** @private */
|
|
99
|
-
_menuItemsChanged(items, overflow) {
|
|
100
|
-
if (!overflow) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (items !== this._oldItems) {
|
|
105
|
-
this._oldItems = items;
|
|
106
|
-
this.__renderButtons(items);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** @private */
|
|
111
|
-
__i18nChanged(i18n, overflow) {
|
|
112
|
-
if (overflow && i18n && i18n.moreOptions !== undefined) {
|
|
113
|
-
if (i18n.moreOptions) {
|
|
114
|
-
overflow.setAttribute('aria-label', i18n.moreOptions);
|
|
115
|
-
} else {
|
|
116
|
-
overflow.removeAttribute('aria-label');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** @private */
|
|
122
|
-
__getOverflowCount(overflow) {
|
|
123
|
-
// We can't use optional chaining due to webpack 4
|
|
124
|
-
return (overflow.item && overflow.item.children && overflow.item.children.length) || 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** @private */
|
|
128
|
-
__restoreButtons(buttons) {
|
|
129
|
-
for (let i = 0; i < buttons.length; i++) {
|
|
130
|
-
const btn = buttons[i];
|
|
131
|
-
btn.disabled = (btn.item && btn.item.disabled) || this.disabled;
|
|
132
|
-
btn.style.visibility = '';
|
|
133
|
-
btn.style.position = '';
|
|
134
|
-
|
|
135
|
-
// Teleport item component back from "overflow" sub-menu
|
|
136
|
-
const item = btn.item && btn.item.component;
|
|
137
|
-
if (item instanceof HTMLElement && item.getAttribute('role') === 'menuitem') {
|
|
138
|
-
btn.appendChild(item);
|
|
139
|
-
item.removeAttribute('role');
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
this.__updateOverflow([]);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** @private */
|
|
146
|
-
__updateOverflow(items) {
|
|
147
|
-
this._overflow.item = { children: items };
|
|
148
|
-
this._hasOverflow = items.length > 0;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** @private */
|
|
152
|
-
__setOverflowItems(buttons, overflow) {
|
|
153
|
-
const container = this._container;
|
|
154
|
-
|
|
155
|
-
if (container.offsetWidth < container.scrollWidth) {
|
|
156
|
-
this._hasOverflow = true;
|
|
157
|
-
|
|
158
|
-
const isRTL = this.__isRTL;
|
|
159
|
-
|
|
160
|
-
let i;
|
|
161
|
-
for (i = buttons.length; i > 0; i--) {
|
|
162
|
-
const btn = buttons[i - 1];
|
|
163
|
-
const btnStyle = getComputedStyle(btn);
|
|
164
|
-
|
|
165
|
-
// If this button isn't overflowing, then the rest aren't either
|
|
166
|
-
if (
|
|
167
|
-
(!isRTL && btn.offsetLeft + btn.offsetWidth < container.offsetWidth - overflow.offsetWidth) ||
|
|
168
|
-
(isRTL && btn.offsetLeft >= overflow.offsetWidth)
|
|
169
|
-
) {
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
btn.disabled = true;
|
|
174
|
-
btn.style.visibility = 'hidden';
|
|
175
|
-
btn.style.position = 'absolute';
|
|
176
|
-
// Save width for buttons with component
|
|
177
|
-
btn.style.width = btnStyle.width;
|
|
178
|
-
}
|
|
179
|
-
const items = buttons.filter((_, idx) => idx >= i).map((b) => b.item);
|
|
180
|
-
this.__updateOverflow(items);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** @private */
|
|
185
|
-
__detectOverflow() {
|
|
186
|
-
const overflow = this._overflow;
|
|
187
|
-
const buttons = this._buttons.filter((btn) => btn !== overflow);
|
|
188
|
-
const oldOverflowCount = this.__getOverflowCount(overflow);
|
|
189
|
-
|
|
190
|
-
// Reset all buttons in the menu bar and the overflow button
|
|
191
|
-
this.__restoreButtons(buttons);
|
|
192
|
-
|
|
193
|
-
// Hide any overflowing buttons and put them in the 'overflow' button
|
|
194
|
-
this.__setOverflowItems(buttons, overflow);
|
|
195
|
-
|
|
196
|
-
const newOverflowCount = this.__getOverflowCount(overflow);
|
|
197
|
-
if (oldOverflowCount !== newOverflowCount && this._subMenu.opened) {
|
|
198
|
-
this._subMenu.close();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const isSingleButton = newOverflowCount === buttons.length || (newOverflowCount === 0 && buttons.length === 1);
|
|
202
|
-
this.toggleAttribute('has-single-button', isSingleButton);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** @protected */
|
|
206
|
-
_removeButtons() {
|
|
207
|
-
this._buttons.forEach((button) => {
|
|
208
|
-
if (button !== this._overflow) {
|
|
209
|
-
this.removeChild(button);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** @protected */
|
|
215
|
-
_initButton(item) {
|
|
216
|
-
const button = document.createElement('vaadin-menu-bar-button');
|
|
217
|
-
|
|
218
|
-
const itemCopy = { ...item };
|
|
219
|
-
button.item = itemCopy;
|
|
220
|
-
|
|
221
|
-
if (item.component) {
|
|
222
|
-
const component = this.__getComponent(itemCopy);
|
|
223
|
-
itemCopy.component = component;
|
|
224
|
-
// Save item for overflow menu
|
|
225
|
-
component.item = itemCopy;
|
|
226
|
-
button.appendChild(component);
|
|
227
|
-
} else if (item.text) {
|
|
228
|
-
button.textContent = item.text;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return button;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** @protected */
|
|
235
|
-
_initButtonAttrs(button) {
|
|
236
|
-
button.setAttribute('role', 'menuitem');
|
|
237
|
-
|
|
238
|
-
if (button === this._overflow || (button.item && button.item.children)) {
|
|
239
|
-
button.setAttribute('aria-haspopup', 'true');
|
|
240
|
-
button.setAttribute('aria-expanded', 'false');
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** @protected */
|
|
245
|
-
_setButtonDisabled(button, disabled) {
|
|
246
|
-
button.disabled = disabled;
|
|
247
|
-
button.setAttribute('tabindex', disabled ? '-1' : '0');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** @protected */
|
|
251
|
-
_setButtonTheme(btn, hostTheme) {
|
|
252
|
-
let theme = hostTheme;
|
|
253
|
-
|
|
254
|
-
// Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
|
|
255
|
-
const itemTheme = btn.item && btn.item.theme;
|
|
256
|
-
if (itemTheme != null) {
|
|
257
|
-
theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (theme) {
|
|
261
|
-
btn.setAttribute('theme', theme);
|
|
262
|
-
} else {
|
|
263
|
-
btn.removeAttribute('theme');
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** @private */
|
|
268
|
-
__getComponent(item) {
|
|
269
|
-
const itemComponent = item.component;
|
|
270
|
-
let component;
|
|
271
|
-
|
|
272
|
-
const isElement = itemComponent instanceof HTMLElement;
|
|
273
|
-
// Use existing item component, if any
|
|
274
|
-
if (isElement && itemComponent.localName === 'vaadin-context-menu-item') {
|
|
275
|
-
component = itemComponent;
|
|
276
|
-
} else {
|
|
277
|
-
component = document.createElement('vaadin-context-menu-item');
|
|
278
|
-
component.appendChild(isElement ? itemComponent : document.createElement(itemComponent));
|
|
279
|
-
}
|
|
280
|
-
if (item.text) {
|
|
281
|
-
const node = component.firstChild || component;
|
|
282
|
-
node.textContent = item.text;
|
|
283
|
-
}
|
|
284
|
-
component.setAttribute('theme', 'menu-bar-item');
|
|
285
|
-
return component;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/** @private */
|
|
289
|
-
__renderButtons(items = []) {
|
|
290
|
-
this._removeButtons();
|
|
291
|
-
|
|
292
|
-
/* Empty array, do nothing */
|
|
293
|
-
if (items.length === 0) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
items.forEach((item) => {
|
|
298
|
-
const button = this._initButton(item);
|
|
299
|
-
this.insertBefore(button, this._overflow);
|
|
300
|
-
this._setButtonDisabled(button, item.disabled);
|
|
301
|
-
this._initButtonAttrs(button);
|
|
302
|
-
this._setButtonTheme(button, this._theme);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
this.__detectOverflow();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* @protected
|
|
310
|
-
* @override
|
|
311
|
-
*/
|
|
312
|
-
_onResize() {
|
|
313
|
-
this.__detectOverflow();
|
|
314
|
-
}
|
|
315
|
-
};
|