@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.
Files changed (38) hide show
  1. package/package.json +15 -16
  2. package/src/styles/vaadin-menu-bar-base-styles.d.ts +8 -0
  3. package/src/styles/vaadin-menu-bar-base-styles.js +56 -0
  4. package/src/styles/vaadin-menu-bar-button-base-styles.d.ts +8 -0
  5. package/src/styles/vaadin-menu-bar-button-base-styles.js +47 -0
  6. package/src/styles/vaadin-menu-bar-item-base-styles.d.ts +8 -0
  7. package/src/styles/vaadin-menu-bar-item-base-styles.js +8 -0
  8. package/src/styles/vaadin-menu-bar-overlay-base-styles.d.ts +8 -0
  9. package/src/styles/vaadin-menu-bar-overlay-base-styles.js +9 -0
  10. package/src/vaadin-menu-bar-button.d.ts +19 -0
  11. package/src/vaadin-menu-bar-button.js +4 -13
  12. package/src/vaadin-menu-bar-item.js +5 -11
  13. package/src/vaadin-menu-bar-list-box.js +5 -18
  14. package/src/vaadin-menu-bar-mixin.d.ts +5 -12
  15. package/src/vaadin-menu-bar-mixin.js +161 -161
  16. package/src/vaadin-menu-bar-overlay.js +7 -4
  17. package/src/vaadin-menu-bar-submenu.d.ts +20 -0
  18. package/src/vaadin-menu-bar-submenu.js +83 -8
  19. package/src/vaadin-menu-bar.d.ts +3 -6
  20. package/src/vaadin-menu-bar.js +11 -27
  21. package/vaadin-menu-bar.js +1 -1
  22. package/web-types.json +4 -26
  23. package/web-types.lit.json +4 -11
  24. package/src/vaadin-menu-bar-submenu-mixin.js +0 -66
  25. package/theme/lumo/vaadin-menu-bar-button-styles.d.ts +0 -1
  26. package/theme/lumo/vaadin-menu-bar-button-styles.js +0 -128
  27. package/theme/lumo/vaadin-menu-bar-button.d.ts +0 -2
  28. package/theme/lumo/vaadin-menu-bar-button.js +0 -2
  29. package/theme/lumo/vaadin-menu-bar-item-styles.d.ts +0 -2
  30. package/theme/lumo/vaadin-menu-bar-item-styles.js +0 -27
  31. package/theme/lumo/vaadin-menu-bar-list-box-styles.d.ts +0 -1
  32. package/theme/lumo/vaadin-menu-bar-list-box-styles.js +0 -5
  33. package/theme/lumo/vaadin-menu-bar-overlay-styles.d.ts +0 -1
  34. package/theme/lumo/vaadin-menu-bar-overlay-styles.js +0 -13
  35. package/theme/lumo/vaadin-menu-bar-styles.d.ts +0 -1
  36. package/theme/lumo/vaadin-menu-bar-styles.js +0 -17
  37. package/theme/lumo/vaadin-menu-bar.d.ts +0 -6
  38. 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 {union: string | object} component - The component to represent the button content.
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 {union: string | string[]} theme - Theme(s) to be set as the theme attribute of the button, overriding any theme set on the menu bar.
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 {union: string | object} component - The component to represent the item.
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 _subMenu() {
309
- return this.shadowRoot.querySelector('vaadin-menu-bar-submenu');
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._subMenu.addEventListener('item-selected', this.__onItemSelected.bind(this));
340
- this._subMenu.addEventListener('close-all-menus', this.__onEscapeClose.bind(this));
317
+ this._container = this.shadowRoot.querySelector('[part="container"]');
318
+ }
341
319
 
342
- const overlay = this._subMenu._overlayElement;
343
- overlay.addEventListener('keydown', this.__boundOnContextMenuKeydown);
320
+ /** @protected */
321
+ updated(props) {
322
+ super.updated(props);
344
323
 
345
- const container = this.shadowRoot.querySelector('[part="container"]');
346
- container.addEventListener('click', this.__onButtonClick.bind(this));
347
- container.addEventListener('mouseover', (e) => this._onMouseOver(e));
324
+ if (props.has('items') || props.has('_theme') || props.has('disabled')) {
325
+ this.__renderButtons(this.items);
326
+ }
348
327
 
349
- // Delay setting container to avoid rendering buttons immediately,
350
- // which would also trigger detecting overflow and force re-layout
351
- // See https://github.com/vaadin/web-components/issues/7271
352
- queueMicrotask(() => {
353
- this._container = container;
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.__detectOverflow();
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
- _tabNavigationChanged(tabNavigation, overflow, container) {
422
- if (overflow && container) {
423
- const target = this.querySelector('[tabindex="0"]');
424
- this._buttons.forEach((btn) => {
425
- if (target) {
426
- this._setTabindex(btn, btn === target);
427
- } else {
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
- __hasOverflowChanged(hasOverflow, overflow) {
438
- if (overflow) {
439
- overflow.toggleAttribute('hidden', !hasOverflow);
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
- __updateButtons(items, disabled, overflow, container) {
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._overlayElement.positionTarget;
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, overflow) {
475
- if (overflow && effectiveI18n && effectiveI18n.moreOptions !== undefined) {
423
+ __i18nChanged(effectiveI18n) {
424
+ if (effectiveI18n && effectiveI18n.moreOptions !== undefined) {
476
425
  if (effectiveI18n.moreOptions) {
477
- overflow.setAttribute('aria-label', effectiveI18n.moreOptions);
426
+ this._overflow.setAttribute('aria-label', effectiveI18n.moreOptions);
478
427
  } else {
479
- overflow.removeAttribute('aria-label');
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
- __detectOverflow() {
565
- if (!this._container) {
566
- return;
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 target = this.tabNavigation ? this.querySelector('[focused]') : this.querySelector('[tabindex="0"]');
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} e
862
+ * @param {!MouseEvent} event
870
863
  * @protected
871
864
  */
872
- _onMouseOver(e) {
873
- const button = this._getButtonFromEvent(e);
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
- const isOpened = this._subMenu.opened;
879
- if (button.item.children && (this.openOnHover || isOpened)) {
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._onKeyDown(e);
903
+ this._handleKeyDown(e);
906
904
  }
907
905
 
908
906
  if (e.key === 'Tab' && this.tabNavigation) {
909
- this._onKeyDown(e);
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
- this._focusItem(this._expandedButton, false);
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
- this._focusItem(button, false);
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 { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
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(OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LitElement))))) {
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 [overlayStyles, styles];
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 };