@vaadin/menu-bar 25.0.0-alpha2 → 25.0.0-alpha21
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 +15 -16
- package/src/styles/vaadin-menu-bar-base-styles.d.ts +8 -0
- package/src/styles/vaadin-menu-bar-base-styles.js +56 -0
- package/src/styles/vaadin-menu-bar-button-base-styles.d.ts +8 -0
- package/src/styles/vaadin-menu-bar-button-base-styles.js +47 -0
- package/src/styles/vaadin-menu-bar-item-base-styles.d.ts +8 -0
- package/src/styles/vaadin-menu-bar-item-base-styles.js +8 -0
- package/src/styles/vaadin-menu-bar-overlay-base-styles.d.ts +8 -0
- package/src/styles/vaadin-menu-bar-overlay-base-styles.js +9 -0
- package/src/vaadin-menu-bar-button.d.ts +19 -0
- package/src/vaadin-menu-bar-button.js +4 -13
- package/src/vaadin-menu-bar-item.js +5 -11
- package/src/vaadin-menu-bar-list-box.js +5 -18
- package/src/vaadin-menu-bar-mixin.d.ts +5 -12
- package/src/vaadin-menu-bar-mixin.js +161 -161
- package/src/vaadin-menu-bar-overlay.js +7 -4
- package/src/vaadin-menu-bar-submenu.d.ts +20 -0
- package/src/vaadin-menu-bar-submenu.js +83 -8
- package/src/vaadin-menu-bar.d.ts +3 -6
- package/src/vaadin-menu-bar.js +11 -27
- package/vaadin-menu-bar.js +1 -1
- package/web-types.json +4 -26
- package/web-types.lit.json +4 -11
- package/src/vaadin-menu-bar-submenu-mixin.js +0 -66
- package/theme/lumo/vaadin-menu-bar-button-styles.d.ts +0 -1
- package/theme/lumo/vaadin-menu-bar-button-styles.js +0 -128
- package/theme/lumo/vaadin-menu-bar-button.d.ts +0 -2
- package/theme/lumo/vaadin-menu-bar-button.js +0 -2
- package/theme/lumo/vaadin-menu-bar-item-styles.d.ts +0 -2
- package/theme/lumo/vaadin-menu-bar-item-styles.js +0 -27
- package/theme/lumo/vaadin-menu-bar-list-box-styles.d.ts +0 -1
- package/theme/lumo/vaadin-menu-bar-list-box-styles.js +0 -5
- package/theme/lumo/vaadin-menu-bar-overlay-styles.d.ts +0 -1
- package/theme/lumo/vaadin-menu-bar-overlay-styles.js +0 -13
- package/theme/lumo/vaadin-menu-bar-styles.d.ts +0 -1
- package/theme/lumo/vaadin-menu-bar-styles.js +0 -17
- package/theme/lumo/vaadin-menu-bar.d.ts +0 -6
- package/theme/lumo/vaadin-menu-bar.js +0 -6
|
@@ -10,6 +10,8 @@ import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
|
|
|
10
10
|
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
11
11
|
import { isElementFocused, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
12
12
|
import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';
|
|
13
|
+
import { microTask } from '@vaadin/component-base/src/async.js';
|
|
14
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
13
15
|
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
14
16
|
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
15
17
|
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
@@ -74,10 +76,10 @@ export const MenuBarMixin = (superClass) =>
|
|
|
74
76
|
* @property {string} text - Text to be set as the menu button component's textContent.
|
|
75
77
|
* @property {string} tooltip - Text to be set as the menu button's tooltip.
|
|
76
78
|
* Requires a `<vaadin-tooltip slot="tooltip">` element to be added inside the `<vaadin-menu-bar>`.
|
|
77
|
-
* @property {
|
|
79
|
+
* @property {string | HTMLElement} component - The component to represent the button content.
|
|
78
80
|
* Either a tagName or an element instance. Defaults to "vaadin-menu-bar-item".
|
|
79
81
|
* @property {boolean} disabled - If true, the button is disabled and cannot be activated.
|
|
80
|
-
* @property {
|
|
82
|
+
* @property {string | string[]} theme - Theme(s) to be set as the theme attribute of the button, overriding any theme set on the menu bar.
|
|
81
83
|
* @property {SubMenuItem[]} children - Array of submenu items.
|
|
82
84
|
*/
|
|
83
85
|
|
|
@@ -85,7 +87,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
85
87
|
* @typedef SubMenuItem
|
|
86
88
|
* @type {object}
|
|
87
89
|
* @property {string} text - Text to be set as the menu item component's textContent.
|
|
88
|
-
* @property {
|
|
90
|
+
* @property {string | HTMLElement} component - The component to represent the item.
|
|
89
91
|
* Either a tagName or an element instance. Defaults to "vaadin-menu-bar-item".
|
|
90
92
|
* @property {boolean} disabled - If true, the item is disabled and cannot be selected.
|
|
91
93
|
* @property {boolean} checked - If true, the item shows a checkmark next to it.
|
|
@@ -134,12 +136,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
134
136
|
* which makes disabled buttons focusable and hoverable, while still
|
|
135
137
|
* preventing them from being triggered:
|
|
136
138
|
*
|
|
137
|
-
* ```
|
|
138
|
-
* // Set before any menu bar is attached to the DOM.
|
|
139
|
-
* window.Vaadin.featureFlags.accessibleDisabledButtons = true;
|
|
140
|
-
* ```
|
|
141
|
-
*
|
|
142
|
-
* ```
|
|
139
|
+
* ```js
|
|
143
140
|
* // Set before any menu bar is attached to the DOM.
|
|
144
141
|
* window.Vaadin.featureFlags.accessibleDisabledButtons = true;
|
|
145
142
|
* ```
|
|
@@ -152,16 +149,6 @@ export const MenuBarMixin = (superClass) =>
|
|
|
152
149
|
value: () => [],
|
|
153
150
|
},
|
|
154
151
|
|
|
155
|
-
/**
|
|
156
|
-
* A space-delimited list of CSS class names
|
|
157
|
-
* to set on each sub-menu overlay element.
|
|
158
|
-
*
|
|
159
|
-
* @attr {string} overlay-class
|
|
160
|
-
*/
|
|
161
|
-
overlayClass: {
|
|
162
|
-
type: String,
|
|
163
|
-
},
|
|
164
|
-
|
|
165
152
|
/**
|
|
166
153
|
* If true, the submenu will open on hover (mouseover) instead of click.
|
|
167
154
|
* @attr {boolean} open-on-hover
|
|
@@ -189,48 +176,16 @@ export const MenuBarMixin = (superClass) =>
|
|
|
189
176
|
type: Boolean,
|
|
190
177
|
sync: true,
|
|
191
178
|
},
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @type {boolean}
|
|
195
|
-
* @protected
|
|
196
|
-
*/
|
|
197
|
-
_hasOverflow: {
|
|
198
|
-
type: Boolean,
|
|
199
|
-
value: false,
|
|
200
|
-
sync: true,
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
/** @protected */
|
|
204
|
-
_overflow: {
|
|
205
|
-
type: Object,
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
/** @protected */
|
|
209
|
-
_container: {
|
|
210
|
-
type: Object,
|
|
211
|
-
sync: true,
|
|
212
|
-
},
|
|
213
179
|
};
|
|
214
180
|
}
|
|
215
181
|
|
|
216
|
-
static get observers() {
|
|
217
|
-
return [
|
|
218
|
-
'_themeChanged(_theme, _overflow, _container)',
|
|
219
|
-
'__hasOverflowChanged(_hasOverflow, _overflow)',
|
|
220
|
-
'__i18nChanged(__effectiveI18n, _overflow)',
|
|
221
|
-
'__updateButtons(items, disabled, _overflow, _container)',
|
|
222
|
-
'_reverseCollapseChanged(reverseCollapse, _overflow, _container)',
|
|
223
|
-
'_tabNavigationChanged(tabNavigation, _overflow, _container)',
|
|
224
|
-
];
|
|
225
|
-
}
|
|
226
|
-
|
|
227
182
|
/**
|
|
228
183
|
* The object used to localize this component. To change the default
|
|
229
184
|
* localization, replace this with an object that provides all properties, or
|
|
230
185
|
* just the individual properties you want to change.
|
|
231
186
|
*
|
|
232
187
|
* The object has the following JSON structure and default values:
|
|
233
|
-
* ```
|
|
188
|
+
* ```js
|
|
234
189
|
* {
|
|
235
190
|
* moreOptions: 'More options'
|
|
236
191
|
* }
|
|
@@ -305,8 +260,15 @@ export const MenuBarMixin = (superClass) =>
|
|
|
305
260
|
}
|
|
306
261
|
|
|
307
262
|
/** @private */
|
|
308
|
-
get
|
|
309
|
-
return this.
|
|
263
|
+
get _hasOverflow() {
|
|
264
|
+
return this._overflow && !this._overflow.hasAttribute('hidden');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** @private */
|
|
268
|
+
set _hasOverflow(hasOverflow) {
|
|
269
|
+
if (this._overflow) {
|
|
270
|
+
this._overflow.toggleAttribute('hidden', !hasOverflow);
|
|
271
|
+
}
|
|
310
272
|
}
|
|
311
273
|
|
|
312
274
|
/** @protected */
|
|
@@ -315,6 +277,20 @@ export const MenuBarMixin = (superClass) =>
|
|
|
315
277
|
|
|
316
278
|
this.setAttribute('role', 'menubar');
|
|
317
279
|
|
|
280
|
+
this._subMenuController = new SlotController(this, 'submenu', 'vaadin-menu-bar-submenu', {
|
|
281
|
+
initializer: (menu) => {
|
|
282
|
+
menu.setAttribute('is-root', '');
|
|
283
|
+
|
|
284
|
+
menu.addEventListener('item-selected', this.__onItemSelected.bind(this));
|
|
285
|
+
menu.addEventListener('close-all-menus', this.__onEscapeClose.bind(this));
|
|
286
|
+
|
|
287
|
+
const overlay = menu._overlayElement;
|
|
288
|
+
overlay._contentRoot.addEventListener('keydown', this.__boundOnContextMenuKeydown);
|
|
289
|
+
|
|
290
|
+
this._subMenu = menu;
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
318
294
|
this._overflowController = new SlotController(this, 'overflow', 'vaadin-menu-bar-button', {
|
|
319
295
|
initializer: (btn) => {
|
|
320
296
|
btn.setAttribute('hidden', '');
|
|
@@ -331,27 +307,47 @@ export const MenuBarMixin = (superClass) =>
|
|
|
331
307
|
this._overflow = btn;
|
|
332
308
|
},
|
|
333
309
|
});
|
|
310
|
+
|
|
311
|
+
this.addController(this._subMenuController);
|
|
334
312
|
this.addController(this._overflowController);
|
|
335
313
|
|
|
336
314
|
this.addEventListener('mousedown', () => this._hideTooltip(true));
|
|
337
315
|
this.addEventListener('mouseleave', () => this._hideTooltip());
|
|
338
316
|
|
|
339
|
-
this.
|
|
340
|
-
|
|
317
|
+
this._container = this.shadowRoot.querySelector('[part="container"]');
|
|
318
|
+
}
|
|
341
319
|
|
|
342
|
-
|
|
343
|
-
|
|
320
|
+
/** @protected */
|
|
321
|
+
updated(props) {
|
|
322
|
+
super.updated(props);
|
|
344
323
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
324
|
+
if (props.has('items') || props.has('_theme') || props.has('disabled')) {
|
|
325
|
+
this.__renderButtons(this.items);
|
|
326
|
+
}
|
|
348
327
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
328
|
+
if (props.has('items') || props.has('_theme') || props.has('reverseCollapse')) {
|
|
329
|
+
this.__scheduleOverflow();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (props.has('items')) {
|
|
333
|
+
this.__updateSubMenu();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (props.has('_theme')) {
|
|
337
|
+
this._themeChanged(this._theme);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (props.has('disabled')) {
|
|
341
|
+
this._overflow.toggleAttribute('disabled', this.disabled);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (props.has('tabNavigation')) {
|
|
345
|
+
this._tabNavigationChanged(this.tabNavigation);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (props.has('__effectiveI18n')) {
|
|
349
|
+
this.__i18nChanged(this.__effectiveI18n);
|
|
350
|
+
}
|
|
355
351
|
}
|
|
356
352
|
|
|
357
353
|
/**
|
|
@@ -380,87 +376,40 @@ export const MenuBarMixin = (superClass) =>
|
|
|
380
376
|
* @override
|
|
381
377
|
*/
|
|
382
378
|
_onResize() {
|
|
383
|
-
this.
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* A callback for the `_theme` property observer.
|
|
388
|
-
* It propagates the host theme to the buttons and the sub menu.
|
|
389
|
-
*
|
|
390
|
-
* @param {string | null} theme
|
|
391
|
-
* @private
|
|
392
|
-
*/
|
|
393
|
-
_themeChanged(theme, overflow, container) {
|
|
394
|
-
if (overflow && container) {
|
|
395
|
-
this.__renderButtons(this.items);
|
|
396
|
-
this.__detectOverflow();
|
|
397
|
-
|
|
398
|
-
if (theme) {
|
|
399
|
-
overflow.setAttribute('theme', theme);
|
|
400
|
-
this._subMenu.setAttribute('theme', theme);
|
|
401
|
-
} else {
|
|
402
|
-
overflow.removeAttribute('theme');
|
|
403
|
-
this._subMenu.removeAttribute('theme');
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* A callback for the 'reverseCollapse' property observer.
|
|
410
|
-
*
|
|
411
|
-
* @param {boolean | null} _reverseCollapse
|
|
412
|
-
* @private
|
|
413
|
-
*/
|
|
414
|
-
_reverseCollapseChanged(_reverseCollapse, overflow, container) {
|
|
415
|
-
if (overflow && container) {
|
|
416
|
-
this.__detectOverflow();
|
|
417
|
-
}
|
|
379
|
+
this.__scheduleOverflow();
|
|
418
380
|
}
|
|
419
381
|
|
|
420
382
|
/** @private */
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
this.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
this._setTabindex(btn, false);
|
|
429
|
-
}
|
|
430
|
-
btn.setAttribute('role', tabNavigation ? 'button' : 'menuitem');
|
|
431
|
-
});
|
|
383
|
+
_themeChanged(theme) {
|
|
384
|
+
if (theme) {
|
|
385
|
+
this._overflow.setAttribute('theme', theme);
|
|
386
|
+
this._subMenu.setAttribute('theme', theme);
|
|
387
|
+
} else {
|
|
388
|
+
this._overflow.removeAttribute('theme');
|
|
389
|
+
this._subMenu.removeAttribute('theme');
|
|
432
390
|
}
|
|
433
|
-
this.setAttribute('role', tabNavigation ? 'group' : 'menubar');
|
|
434
391
|
}
|
|
435
392
|
|
|
436
393
|
/** @private */
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
394
|
+
_tabNavigationChanged(tabNavigation) {
|
|
395
|
+
const target = this.querySelector('[tabindex="0"]');
|
|
396
|
+
this._buttons.forEach((btn) => {
|
|
397
|
+
if (target) {
|
|
398
|
+
this._setTabindex(btn, btn === target);
|
|
399
|
+
} else {
|
|
400
|
+
this._setTabindex(btn, false);
|
|
401
|
+
}
|
|
402
|
+
btn.setAttribute('role', tabNavigation ? 'button' : 'menuitem');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
this.setAttribute('role', tabNavigation ? 'group' : 'menubar');
|
|
441
406
|
}
|
|
442
407
|
|
|
443
408
|
/** @private */
|
|
444
|
-
|
|
445
|
-
if (!overflow || !container) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (items !== this._oldItems) {
|
|
450
|
-
this._oldItems = items;
|
|
451
|
-
this.__renderButtons(items);
|
|
452
|
-
this.__detectOverflow();
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (disabled !== this._oldDisabled) {
|
|
456
|
-
this._oldDisabled = disabled;
|
|
457
|
-
this.__renderButtons(items);
|
|
458
|
-
overflow.toggleAttribute('disabled', disabled);
|
|
459
|
-
}
|
|
460
|
-
|
|
409
|
+
__updateSubMenu() {
|
|
461
410
|
const subMenu = this._subMenu;
|
|
462
411
|
if (subMenu && subMenu.opened) {
|
|
463
|
-
const button = subMenu.
|
|
412
|
+
const button = subMenu._positionTarget;
|
|
464
413
|
|
|
465
414
|
// Close sub-menu if the corresponding button is no longer in the DOM,
|
|
466
415
|
// or if the item on it has been changed to no longer have children.
|
|
@@ -471,12 +420,12 @@ export const MenuBarMixin = (superClass) =>
|
|
|
471
420
|
}
|
|
472
421
|
|
|
473
422
|
/** @private */
|
|
474
|
-
__i18nChanged(effectiveI18n
|
|
475
|
-
if (
|
|
423
|
+
__i18nChanged(effectiveI18n) {
|
|
424
|
+
if (effectiveI18n && effectiveI18n.moreOptions !== undefined) {
|
|
476
425
|
if (effectiveI18n.moreOptions) {
|
|
477
|
-
|
|
426
|
+
this._overflow.setAttribute('aria-label', effectiveI18n.moreOptions);
|
|
478
427
|
} else {
|
|
479
|
-
|
|
428
|
+
this._overflow.removeAttribute('aria-label');
|
|
480
429
|
}
|
|
481
430
|
}
|
|
482
431
|
}
|
|
@@ -561,11 +510,14 @@ export const MenuBarMixin = (superClass) =>
|
|
|
561
510
|
}
|
|
562
511
|
|
|
563
512
|
/** @private */
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
513
|
+
__scheduleOverflow() {
|
|
514
|
+
this._overflowDebouncer = Debouncer.debounce(this._overflowDebouncer, microTask, () => {
|
|
515
|
+
this.__detectOverflow();
|
|
516
|
+
});
|
|
517
|
+
}
|
|
568
518
|
|
|
519
|
+
/** @private */
|
|
520
|
+
__detectOverflow() {
|
|
569
521
|
const overflow = this._overflow;
|
|
570
522
|
const buttons = this._buttons.filter((btn) => btn !== overflow);
|
|
571
523
|
const oldOverflowCount = this.__getOverflowCount(overflow);
|
|
@@ -707,6 +659,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
707
659
|
_hideTooltip(immediate) {
|
|
708
660
|
const tooltip = this._tooltipController && this._tooltipController.node;
|
|
709
661
|
if (tooltip) {
|
|
662
|
+
this._tooltipController.setContext({ item: null });
|
|
710
663
|
tooltip._stateController.close(immediate);
|
|
711
664
|
}
|
|
712
665
|
}
|
|
@@ -740,17 +693,18 @@ export const MenuBarMixin = (superClass) =>
|
|
|
740
693
|
* and open another one for the newly focused button.
|
|
741
694
|
*
|
|
742
695
|
* @param {Element} item
|
|
696
|
+
* @param {FocusOptions=} options
|
|
743
697
|
* @param {boolean} navigating
|
|
744
698
|
* @protected
|
|
745
699
|
* @override
|
|
746
700
|
*/
|
|
747
|
-
_focusItem(item, navigating) {
|
|
701
|
+
_focusItem(item, options, navigating) {
|
|
748
702
|
const wasExpanded = navigating && this.focused === this._expandedButton;
|
|
749
703
|
if (wasExpanded) {
|
|
750
704
|
this._close();
|
|
751
705
|
}
|
|
752
706
|
|
|
753
|
-
super._focusItem(item, navigating);
|
|
707
|
+
super._focusItem(item, options, navigating);
|
|
754
708
|
|
|
755
709
|
this._buttons.forEach((btn) => {
|
|
756
710
|
this._setTabindex(btn, btn === item);
|
|
@@ -770,6 +724,34 @@ export const MenuBarMixin = (superClass) =>
|
|
|
770
724
|
return Array.from(e.composedPath()).find((el) => el.localName === 'vaadin-menu-bar-button');
|
|
771
725
|
}
|
|
772
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Override method inherited from `FocusMixin`
|
|
729
|
+
*
|
|
730
|
+
* @override
|
|
731
|
+
* @protected
|
|
732
|
+
*/
|
|
733
|
+
_shouldSetFocus(event) {
|
|
734
|
+
// Ignore events from the submenu
|
|
735
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
return super._shouldSetFocus(event);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Override method inherited from `FocusMixin`
|
|
743
|
+
*
|
|
744
|
+
* @override
|
|
745
|
+
* @protected
|
|
746
|
+
*/
|
|
747
|
+
_shouldRemoveFocus(event) {
|
|
748
|
+
// Ignore events from the submenu
|
|
749
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
return super._shouldRemoveFocus(event);
|
|
753
|
+
}
|
|
754
|
+
|
|
773
755
|
/**
|
|
774
756
|
* Override method inherited from `FocusMixin`
|
|
775
757
|
*
|
|
@@ -779,7 +761,8 @@ export const MenuBarMixin = (superClass) =>
|
|
|
779
761
|
*/
|
|
780
762
|
_setFocused(focused) {
|
|
781
763
|
if (focused) {
|
|
782
|
-
const
|
|
764
|
+
const selector = this.tabNavigation ? '[focused]' : '[tabindex="0"]';
|
|
765
|
+
const target = this.querySelector(`vaadin-menu-bar-button${selector}`);
|
|
783
766
|
if (target) {
|
|
784
767
|
this._buttons.forEach((btn) => {
|
|
785
768
|
this._setTabindex(btn, btn === target);
|
|
@@ -852,6 +835,16 @@ export const MenuBarMixin = (superClass) =>
|
|
|
852
835
|
* @override
|
|
853
836
|
*/
|
|
854
837
|
_onKeyDown(event) {
|
|
838
|
+
// Ignore events from the submenu
|
|
839
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
this._handleKeyDown(event);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** @private */
|
|
847
|
+
_handleKeyDown(event) {
|
|
855
848
|
switch (event.key) {
|
|
856
849
|
case 'ArrowDown':
|
|
857
850
|
this._onArrowDown(event);
|
|
@@ -866,20 +859,25 @@ export const MenuBarMixin = (superClass) =>
|
|
|
866
859
|
}
|
|
867
860
|
|
|
868
861
|
/**
|
|
869
|
-
* @param {!MouseEvent}
|
|
862
|
+
* @param {!MouseEvent} event
|
|
870
863
|
* @protected
|
|
871
864
|
*/
|
|
872
|
-
_onMouseOver(
|
|
873
|
-
|
|
865
|
+
_onMouseOver(event) {
|
|
866
|
+
// Ignore events from the submenu
|
|
867
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const button = this._getButtonFromEvent(event);
|
|
874
872
|
if (!button) {
|
|
875
873
|
// Hide tooltip on mouseover to disabled button
|
|
876
874
|
this._hideTooltip();
|
|
877
875
|
} else if (button !== this._expandedButton) {
|
|
878
|
-
|
|
879
|
-
|
|
876
|
+
// Switch sub-menu when moving cursor over another button
|
|
877
|
+
// with children, regardless of whether openOnHover is set.
|
|
878
|
+
// If the button has no children, keep the sub-menu opened.
|
|
879
|
+
if (button.item.children && (this.openOnHover || this._subMenu.opened)) {
|
|
880
880
|
this.__openSubMenu(button, false);
|
|
881
|
-
} else if (isOpened) {
|
|
882
|
-
this._close();
|
|
883
881
|
}
|
|
884
882
|
|
|
885
883
|
if (button === this._overflow || (this.openOnHover && button.item.children)) {
|
|
@@ -902,11 +900,11 @@ export const MenuBarMixin = (superClass) =>
|
|
|
902
900
|
if (e.keyCode === 37 || (e.keyCode === 39 && !item._item.children)) {
|
|
903
901
|
// Prevent ArrowLeft from being handled in context-menu
|
|
904
902
|
e.stopImmediatePropagation();
|
|
905
|
-
this.
|
|
903
|
+
this._handleKeyDown(e);
|
|
906
904
|
}
|
|
907
905
|
|
|
908
906
|
if (e.key === 'Tab' && this.tabNavigation) {
|
|
909
|
-
this.
|
|
907
|
+
this._handleKeyDown(e);
|
|
910
908
|
}
|
|
911
909
|
}
|
|
912
910
|
}
|
|
@@ -948,6 +946,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
948
946
|
|
|
949
947
|
subMenu.items = items;
|
|
950
948
|
subMenu.listenOn = button;
|
|
949
|
+
subMenu._positionTarget = button;
|
|
951
950
|
const overlay = subMenu._overlayElement;
|
|
952
951
|
overlay.noVerticalOverlap = true;
|
|
953
952
|
|
|
@@ -957,7 +956,6 @@ export const MenuBarMixin = (superClass) =>
|
|
|
957
956
|
this._setExpanded(button, true);
|
|
958
957
|
|
|
959
958
|
this.style.pointerEvents = 'auto';
|
|
960
|
-
overlay.positionTarget = button;
|
|
961
959
|
|
|
962
960
|
button.dispatchEvent(
|
|
963
961
|
new CustomEvent('opensubmenu', {
|
|
@@ -975,7 +973,8 @@ export const MenuBarMixin = (superClass) =>
|
|
|
975
973
|
}
|
|
976
974
|
|
|
977
975
|
if (options.keepFocus) {
|
|
978
|
-
|
|
976
|
+
const focusOptions = { focusVisible: isKeyboardActive() };
|
|
977
|
+
this._focusItem(this._expandedButton, focusOptions, false);
|
|
979
978
|
}
|
|
980
979
|
|
|
981
980
|
// Do not focus item when open not from keyboard
|
|
@@ -989,13 +988,13 @@ export const MenuBarMixin = (superClass) =>
|
|
|
989
988
|
|
|
990
989
|
/** @private */
|
|
991
990
|
_focusFirstItem() {
|
|
992
|
-
const list = this._subMenu._overlayElement.firstElementChild;
|
|
991
|
+
const list = this._subMenu._overlayElement._contentRoot.firstElementChild;
|
|
993
992
|
list.focus();
|
|
994
993
|
}
|
|
995
994
|
|
|
996
995
|
/** @private */
|
|
997
996
|
_focusLastItem() {
|
|
998
|
-
const list = this._subMenu._overlayElement.firstElementChild;
|
|
997
|
+
const list = this._subMenu._overlayElement._contentRoot.firstElementChild;
|
|
999
998
|
const item = list.items[list.items.length - 1];
|
|
1000
999
|
if (item) {
|
|
1001
1000
|
item.focus();
|
|
@@ -1019,7 +1018,8 @@ export const MenuBarMixin = (superClass) =>
|
|
|
1019
1018
|
if (button && button.hasAttribute('expanded')) {
|
|
1020
1019
|
this._setExpanded(button, false);
|
|
1021
1020
|
if (restoreFocus) {
|
|
1022
|
-
|
|
1021
|
+
const focusOptions = { focusVisible: isKeyboardActive() };
|
|
1022
|
+
this._focusItem(button, focusOptions, false);
|
|
1023
1023
|
}
|
|
1024
1024
|
this._expandedButton = null;
|
|
1025
1025
|
}
|
|
@@ -8,10 +8,10 @@ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
|
8
8
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
9
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
10
|
import { MenuOverlayMixin } from '@vaadin/context-menu/src/vaadin-menu-overlay-mixin.js';
|
|
11
|
-
import { styles } from '@vaadin/context-menu/src/vaadin-menu-overlay-styles.js';
|
|
12
11
|
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
|
|
13
|
-
import {
|
|
12
|
+
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
14
13
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
14
|
+
import { menuBarOverlayStyles } from './styles/vaadin-menu-bar-overlay-base-styles.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
@@ -24,13 +24,15 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
24
24
|
* @mixes ThemableMixin
|
|
25
25
|
* @protected
|
|
26
26
|
*/
|
|
27
|
-
export class MenuBarOverlay extends MenuOverlayMixin(
|
|
27
|
+
export class MenuBarOverlay extends MenuOverlayMixin(
|
|
28
|
+
OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
|
|
29
|
+
) {
|
|
28
30
|
static get is() {
|
|
29
31
|
return 'vaadin-menu-bar-overlay';
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
static get styles() {
|
|
33
|
-
return
|
|
35
|
+
return menuBarOverlayStyles;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/** @protected */
|
|
@@ -40,6 +42,7 @@ export class MenuBarOverlay extends MenuOverlayMixin(OverlayMixin(DirMixin(Thema
|
|
|
40
42
|
<div part="overlay" id="overlay" tabindex="0">
|
|
41
43
|
<div part="content" id="content">
|
|
42
44
|
<slot></slot>
|
|
45
|
+
<slot name="submenu"></slot>
|
|
43
46
|
</div>
|
|
44
47
|
</div>
|
|
45
48
|
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2025 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { ContextMenuMixin } from '@vaadin/context-menu/src/vaadin-context-menu-mixin.js';
|
|
7
|
+
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
11
|
+
*/
|
|
12
|
+
declare class MenuBarSubmenu extends ContextMenuMixin(ThemePropertyMixin(HTMLElement)) {}
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface HTMLElementTagNameMap {
|
|
16
|
+
'vaadin-menu-bar-submenu': MenuBarSubmenu;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { MenuBarSubmenu };
|