@vaadin/menu-bar 25.0.0-alpha8 → 25.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.
Files changed (40) hide show
  1. package/package.json +15 -18
  2. package/src/styles/vaadin-menu-bar-base-styles.js +7 -7
  3. package/src/styles/vaadin-menu-bar-button-base-styles.js +1 -1
  4. package/src/vaadin-menu-bar-button.d.ts +19 -0
  5. package/src/vaadin-menu-bar-button.js +3 -1
  6. package/src/vaadin-menu-bar-item.js +1 -1
  7. package/src/vaadin-menu-bar-list-box.js +1 -1
  8. package/src/vaadin-menu-bar-mixin.d.ts +5 -12
  9. package/src/vaadin-menu-bar-mixin.js +71 -43
  10. package/src/vaadin-menu-bar-overlay.js +2 -1
  11. package/src/vaadin-menu-bar-submenu.d.ts +20 -0
  12. package/src/vaadin-menu-bar-submenu.js +83 -8
  13. package/src/vaadin-menu-bar.d.ts +1 -4
  14. package/src/vaadin-menu-bar.js +2 -5
  15. package/vaadin-menu-bar.js +1 -1
  16. package/web-types.json +4 -26
  17. package/web-types.lit.json +4 -11
  18. package/src/styles/vaadin-menu-bar-button-core-styles.d.ts +0 -8
  19. package/src/styles/vaadin-menu-bar-button-core-styles.js +0 -16
  20. package/src/styles/vaadin-menu-bar-core-styles.d.ts +0 -8
  21. package/src/styles/vaadin-menu-bar-core-styles.js +0 -24
  22. package/src/styles/vaadin-menu-bar-item-core-styles.d.ts +0 -8
  23. package/src/styles/vaadin-menu-bar-item-core-styles.js +0 -8
  24. package/src/styles/vaadin-menu-bar-overlay-core-styles.d.ts +0 -8
  25. package/src/styles/vaadin-menu-bar-overlay-core-styles.js +0 -9
  26. package/src/vaadin-menu-bar-submenu-mixin.js +0 -66
  27. package/theme/lumo/vaadin-menu-bar-button-styles.d.ts +0 -1
  28. package/theme/lumo/vaadin-menu-bar-button-styles.js +0 -128
  29. package/theme/lumo/vaadin-menu-bar-button.d.ts +0 -2
  30. package/theme/lumo/vaadin-menu-bar-button.js +0 -2
  31. package/theme/lumo/vaadin-menu-bar-item-styles.d.ts +0 -2
  32. package/theme/lumo/vaadin-menu-bar-item-styles.js +0 -27
  33. package/theme/lumo/vaadin-menu-bar-list-box-styles.d.ts +0 -1
  34. package/theme/lumo/vaadin-menu-bar-list-box-styles.js +0 -5
  35. package/theme/lumo/vaadin-menu-bar-overlay-styles.d.ts +0 -1
  36. package/theme/lumo/vaadin-menu-bar-overlay-styles.js +0 -13
  37. package/theme/lumo/vaadin-menu-bar-styles.d.ts +0 -1
  38. package/theme/lumo/vaadin-menu-bar-styles.js +0 -17
  39. package/theme/lumo/vaadin-menu-bar.d.ts +0 -6
  40. package/theme/lumo/vaadin-menu-bar.js +0 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/menu-bar",
3
- "version": "25.0.0-alpha8",
3
+ "version": "25.0.0-beta1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,9 +21,6 @@
21
21
  "type": "module",
22
22
  "files": [
23
23
  "src",
24
- "!src/styles/*-base-styles.d.ts",
25
- "!src/styles/*-base-styles.js",
26
- "theme",
27
24
  "vaadin-*.d.ts",
28
25
  "vaadin-*.js",
29
26
  "web-types.json",
@@ -37,27 +34,27 @@
37
34
  ],
38
35
  "dependencies": {
39
36
  "@open-wc/dedupe-mixin": "^1.3.0",
40
- "@vaadin/a11y-base": "25.0.0-alpha8",
41
- "@vaadin/button": "25.0.0-alpha8",
42
- "@vaadin/component-base": "25.0.0-alpha8",
43
- "@vaadin/context-menu": "25.0.0-alpha8",
44
- "@vaadin/item": "25.0.0-alpha8",
45
- "@vaadin/list-box": "25.0.0-alpha8",
46
- "@vaadin/overlay": "25.0.0-alpha8",
47
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha8",
48
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha8",
37
+ "@vaadin/a11y-base": "25.0.0-beta1",
38
+ "@vaadin/button": "25.0.0-beta1",
39
+ "@vaadin/component-base": "25.0.0-beta1",
40
+ "@vaadin/context-menu": "25.0.0-beta1",
41
+ "@vaadin/item": "25.0.0-beta1",
42
+ "@vaadin/list-box": "25.0.0-beta1",
43
+ "@vaadin/overlay": "25.0.0-beta1",
44
+ "@vaadin/vaadin-themable-mixin": "25.0.0-beta1",
49
45
  "lit": "^3.0.0"
50
46
  },
51
47
  "devDependencies": {
52
- "@vaadin/chai-plugins": "25.0.0-alpha8",
53
- "@vaadin/icon": "25.0.0-alpha8",
54
- "@vaadin/test-runner-commands": "25.0.0-alpha8",
48
+ "@vaadin/chai-plugins": "25.0.0-beta1",
49
+ "@vaadin/icon": "25.0.0-beta1",
50
+ "@vaadin/test-runner-commands": "25.0.0-beta1",
55
51
  "@vaadin/testing-helpers": "^2.0.0",
56
- "sinon": "^18.0.0"
52
+ "@vaadin/vaadin-lumo-styles": "25.0.0-beta1",
53
+ "sinon": "^21.0.0"
57
54
  },
58
55
  "web-types": [
59
56
  "web-types.json",
60
57
  "web-types.lit.json"
61
58
  ],
62
- "gitHead": "ebf53673d5f639d2b1b6f2b31f640f530643ee2f"
59
+ "gitHead": "1d20cf54e582d1f2e209126d4586f8b4c01c50e0"
63
60
  }
@@ -17,17 +17,15 @@ export const menuBarStyles = css`
17
17
  [part='container'] {
18
18
  display: flex;
19
19
  flex-wrap: nowrap;
20
- margin: calc((var(--vaadin-focus-ring-width) + 1px) * -1);
20
+ margin: calc((var(--vaadin-focus-ring-width) + 2px) * -1);
21
21
  overflow: hidden;
22
- padding: calc(var(--vaadin-focus-ring-width) + 1px);
22
+ padding: calc(var(--vaadin-focus-ring-width) + 2px);
23
23
  position: relative;
24
24
  width: 100%;
25
- /* stylelint-disable length-zero-no-unit */
26
25
  --_gap: var(--vaadin-menu-bar-gap, 0px);
27
26
  --_bw: var(--vaadin-button-border-width, 1px);
28
27
  gap: var(--_gap);
29
28
  --_rad-button: var(--vaadin-button-border-radius, var(--vaadin-radius-m));
30
- --_rad: min(var(--_gap) * 1000, var(--_rad-button));
31
29
  }
32
30
 
33
31
  ::slotted(vaadin-menu-bar-button:not(:first-of-type)) {
@@ -35,16 +33,18 @@ export const menuBarStyles = css`
35
33
  }
36
34
 
37
35
  ::slotted(vaadin-menu-bar-button) {
38
- border-radius: var(--_rad);
36
+ border-radius: 0;
39
37
  }
40
38
 
41
39
  ::slotted([first-visible]),
42
- :host([has-single-button]) ::slotted([slot='overflow']) {
40
+ :host([has-single-button]) ::slotted([slot='overflow']),
41
+ ::slotted(vaadin-menu-bar-button[theme~='tertiary']) {
43
42
  border-start-start-radius: var(--_rad-button);
44
43
  border-end-start-radius: var(--_rad-button);
45
44
  }
46
45
 
47
- ::slotted(:is([last-visible], [slot='overflow'])) {
46
+ ::slotted(:is([last-visible], [slot='overflow'])),
47
+ ::slotted(vaadin-menu-bar-button[theme~='tertiary']) {
48
48
  border-start-end-radius: var(--_rad-button);
49
49
  border-end-end-radius: var(--_rad-button);
50
50
  }
@@ -32,7 +32,7 @@ export const menuBarButtonStyles = css`
32
32
  background: currentColor;
33
33
  content: '';
34
34
  height: var(--vaadin-icon-size, 1lh);
35
- mask-image: var(--_vaadin-icon-chevron-down);
35
+ mask: var(--_vaadin-icon-chevron-down) 50% / var(--vaadin-icon-visual-size, 100%) no-repeat;
36
36
  width: var(--vaadin-icon-size, 1lh);
37
37
  }
38
38
 
@@ -0,0 +1,19 @@
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 { Button } from '@vaadin/button/src/vaadin-button.js';
7
+
8
+ /**
9
+ * An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
10
+ */
11
+ declare class MenuBarButton extends Button {}
12
+
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ 'vaadin-menu-bar-button': MenuBarButton;
16
+ }
17
+ }
18
+
19
+ export { MenuBarButton };
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { Button } from '@vaadin/button/src/vaadin-button.js';
7
7
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
8
- import { menuBarButtonStyles } from './styles/vaadin-menu-bar-button-core-styles.js';
8
+ import { menuBarButtonStyles } from './styles/vaadin-menu-bar-button-base-styles.js';
9
9
 
10
10
  /**
11
11
  * An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
@@ -54,3 +54,5 @@ class MenuBarButton extends Button {
54
54
  }
55
55
 
56
56
  defineCustomElement(MenuBarButton);
57
+
58
+ export { MenuBarButton };
@@ -10,7 +10,7 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
10
10
  import { ItemMixin } from '@vaadin/item/src/vaadin-item-mixin.js';
11
11
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
12
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
13
- import { menuBarItemStyles } from './styles/vaadin-menu-bar-item-core-styles.js';
13
+ import { menuBarItemStyles } from './styles/vaadin-menu-bar-item-base-styles.js';
14
14
 
15
15
  /**
16
16
  * An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
@@ -8,7 +8,7 @@ import { ListMixin } from '@vaadin/a11y-base/src/list-mixin.js';
8
8
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
9
9
  import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
10
10
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
11
- import { listBoxStyles } from '@vaadin/list-box/src/styles/vaadin-list-box-core-styles.js';
11
+ import { listBoxStyles } from '@vaadin/list-box/src/styles/vaadin-list-box-base-styles.js';
12
12
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
13
13
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14
14
 
@@ -10,6 +10,7 @@ import type { KeyboardDirectionMixinClass } from '@vaadin/a11y-base/src/keyboard
10
10
  import type { KeyboardMixinClass } from '@vaadin/a11y-base/src/keyboard-mixin.js';
11
11
  import type { I18nMixinClass } from '@vaadin/component-base/src/i18n-mixin.js';
12
12
  import type { ResizeMixinClass } from '@vaadin/component-base/src/resize-mixin.js';
13
+ import type { MenuBarButton } from './vaadin-menu-bar-button.js';
13
14
 
14
15
  export type MenuBarItem<TItemData extends object = object> = {
15
16
  /**
@@ -113,7 +114,7 @@ export declare class MenuBarMixinClass<TItem extends MenuBarItem = MenuBarItem>
113
114
  * which makes disabled buttons focusable and hoverable, while still
114
115
  * preventing them from being triggered:
115
116
  *
116
- * ```
117
+ * ```js
117
118
  * // Set before any menu bar is attached to the DOM.
118
119
  * window.Vaadin.featureFlags.accessibleDisabledButtons = true;
119
120
  * ```
@@ -126,7 +127,7 @@ export declare class MenuBarMixinClass<TItem extends MenuBarItem = MenuBarItem>
126
127
  * just the individual properties you want to change.
127
128
  *
128
129
  * The object has the following JSON structure and default values:
129
- * ```
130
+ * ```js
130
131
  * {
131
132
  * moreOptions: 'More options'
132
133
  * }
@@ -134,14 +135,6 @@ export declare class MenuBarMixinClass<TItem extends MenuBarItem = MenuBarItem>
134
135
  */
135
136
  i18n: MenuBarI18n;
136
137
 
137
- /**
138
- * A space-delimited list of CSS class names
139
- * to set on each sub-menu overlay element.
140
- *
141
- * @attr {string} overlay-class
142
- */
143
- overlayClass: string;
144
-
145
138
  /**
146
139
  * If true, the submenu will open on hover (mouseover) instead of click.
147
140
  * @attr {boolean} open-on-hover
@@ -167,11 +160,11 @@ export declare class MenuBarMixinClass<TItem extends MenuBarItem = MenuBarItem>
167
160
  */
168
161
  close(): void;
169
162
 
170
- protected readonly _buttons: HTMLElement[];
163
+ protected readonly _buttons: MenuBarButton[];
171
164
 
172
165
  protected readonly _container: HTMLElement;
173
166
 
174
- protected readonly _overflow: HTMLElement;
167
+ protected readonly _overflow: MenuBarButton;
175
168
 
176
169
  protected _hasOverflow: boolean;
177
170
  }
@@ -76,10 +76,10 @@ export const MenuBarMixin = (superClass) =>
76
76
  * @property {string} text - Text to be set as the menu button component's textContent.
77
77
  * @property {string} tooltip - Text to be set as the menu button's tooltip.
78
78
  * Requires a `<vaadin-tooltip slot="tooltip">` element to be added inside the `<vaadin-menu-bar>`.
79
- * @property {union: string | object} component - The component to represent the button content.
79
+ * @property {string | HTMLElement} component - The component to represent the button content.
80
80
  * Either a tagName or an element instance. Defaults to "vaadin-menu-bar-item".
81
81
  * @property {boolean} disabled - If true, the button is disabled and cannot be activated.
82
- * @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.
83
83
  * @property {SubMenuItem[]} children - Array of submenu items.
84
84
  */
85
85
 
@@ -87,7 +87,7 @@ export const MenuBarMixin = (superClass) =>
87
87
  * @typedef SubMenuItem
88
88
  * @type {object}
89
89
  * @property {string} text - Text to be set as the menu item component's textContent.
90
- * @property {union: string | object} component - The component to represent the item.
90
+ * @property {string | HTMLElement} component - The component to represent the item.
91
91
  * Either a tagName or an element instance. Defaults to "vaadin-menu-bar-item".
92
92
  * @property {boolean} disabled - If true, the item is disabled and cannot be selected.
93
93
  * @property {boolean} checked - If true, the item shows a checkmark next to it.
@@ -136,12 +136,7 @@ export const MenuBarMixin = (superClass) =>
136
136
  * which makes disabled buttons focusable and hoverable, while still
137
137
  * preventing them from being triggered:
138
138
  *
139
- * ```
140
- * // Set before any menu bar is attached to the DOM.
141
- * window.Vaadin.featureFlags.accessibleDisabledButtons = true;
142
- * ```
143
- *
144
- * ```
139
+ * ```js
145
140
  * // Set before any menu bar is attached to the DOM.
146
141
  * window.Vaadin.featureFlags.accessibleDisabledButtons = true;
147
142
  * ```
@@ -154,16 +149,6 @@ export const MenuBarMixin = (superClass) =>
154
149
  value: () => [],
155
150
  },
156
151
 
157
- /**
158
- * A space-delimited list of CSS class names
159
- * to set on each sub-menu overlay element.
160
- *
161
- * @attr {string} overlay-class
162
- */
163
- overlayClass: {
164
- type: String,
165
- },
166
-
167
152
  /**
168
153
  * If true, the submenu will open on hover (mouseover) instead of click.
169
154
  * @attr {boolean} open-on-hover
@@ -200,7 +185,7 @@ export const MenuBarMixin = (superClass) =>
200
185
  * just the individual properties you want to change.
201
186
  *
202
187
  * The object has the following JSON structure and default values:
203
- * ```
188
+ * ```js
204
189
  * {
205
190
  * moreOptions: 'More options'
206
191
  * }
@@ -300,7 +285,7 @@ export const MenuBarMixin = (superClass) =>
300
285
  menu.addEventListener('close-all-menus', this.__onEscapeClose.bind(this));
301
286
 
302
287
  const overlay = menu._overlayElement;
303
- overlay.addEventListener('keydown', this.__boundOnContextMenuKeydown);
288
+ overlay._contentRoot.addEventListener('keydown', this.__boundOnContextMenuKeydown);
304
289
 
305
290
  this._subMenu = menu;
306
291
  },
@@ -348,10 +333,6 @@ export const MenuBarMixin = (superClass) =>
348
333
  this.__updateSubMenu();
349
334
  }
350
335
 
351
- if (props.has('overlayClass')) {
352
- this._subMenu.overlayClass = this.overlayClass;
353
- }
354
-
355
336
  if (props.has('_theme')) {
356
337
  this._themeChanged(this._theme);
357
338
  }
@@ -428,7 +409,7 @@ export const MenuBarMixin = (superClass) =>
428
409
  __updateSubMenu() {
429
410
  const subMenu = this._subMenu;
430
411
  if (subMenu && subMenu.opened) {
431
- const button = subMenu._overlayElement.positionTarget;
412
+ const button = subMenu._positionTarget;
432
413
 
433
414
  // Close sub-menu if the corresponding button is no longer in the DOM,
434
415
  // or if the item on it has been changed to no longer have children.
@@ -712,17 +693,18 @@ export const MenuBarMixin = (superClass) =>
712
693
  * and open another one for the newly focused button.
713
694
  *
714
695
  * @param {Element} item
696
+ * @param {FocusOptions=} options
715
697
  * @param {boolean} navigating
716
698
  * @protected
717
699
  * @override
718
700
  */
719
- _focusItem(item, navigating) {
701
+ _focusItem(item, options, navigating) {
720
702
  const wasExpanded = navigating && this.focused === this._expandedButton;
721
703
  if (wasExpanded) {
722
704
  this._close();
723
705
  }
724
706
 
725
- super._focusItem(item, navigating);
707
+ super._focusItem(item, options, navigating);
726
708
 
727
709
  this._buttons.forEach((btn) => {
728
710
  this._setTabindex(btn, btn === item);
@@ -742,6 +724,34 @@ export const MenuBarMixin = (superClass) =>
742
724
  return Array.from(e.composedPath()).find((el) => el.localName === 'vaadin-menu-bar-button');
743
725
  }
744
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
+
745
755
  /**
746
756
  * Override method inherited from `FocusMixin`
747
757
  *
@@ -751,7 +761,8 @@ export const MenuBarMixin = (superClass) =>
751
761
  */
752
762
  _setFocused(focused) {
753
763
  if (focused) {
754
- 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}`);
755
766
  if (target) {
756
767
  this._buttons.forEach((btn) => {
757
768
  this._setTabindex(btn, btn === target);
@@ -824,6 +835,16 @@ export const MenuBarMixin = (superClass) =>
824
835
  * @override
825
836
  */
826
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) {
827
848
  switch (event.key) {
828
849
  case 'ArrowDown':
829
850
  this._onArrowDown(event);
@@ -838,20 +859,25 @@ export const MenuBarMixin = (superClass) =>
838
859
  }
839
860
 
840
861
  /**
841
- * @param {!MouseEvent} e
862
+ * @param {!MouseEvent} event
842
863
  * @protected
843
864
  */
844
- _onMouseOver(e) {
845
- 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);
846
872
  if (!button) {
847
873
  // Hide tooltip on mouseover to disabled button
848
874
  this._hideTooltip();
849
875
  } else if (button !== this._expandedButton) {
850
- const isOpened = this._subMenu.opened;
851
- 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)) {
852
880
  this.__openSubMenu(button, false);
853
- } else if (isOpened) {
854
- this._close();
855
881
  }
856
882
 
857
883
  if (button === this._overflow || (this.openOnHover && button.item.children)) {
@@ -874,11 +900,11 @@ export const MenuBarMixin = (superClass) =>
874
900
  if (e.keyCode === 37 || (e.keyCode === 39 && !item._item.children)) {
875
901
  // Prevent ArrowLeft from being handled in context-menu
876
902
  e.stopImmediatePropagation();
877
- this._onKeyDown(e);
903
+ this._handleKeyDown(e);
878
904
  }
879
905
 
880
906
  if (e.key === 'Tab' && this.tabNavigation) {
881
- this._onKeyDown(e);
907
+ this._handleKeyDown(e);
882
908
  }
883
909
  }
884
910
  }
@@ -920,6 +946,7 @@ export const MenuBarMixin = (superClass) =>
920
946
 
921
947
  subMenu.items = items;
922
948
  subMenu.listenOn = button;
949
+ subMenu._positionTarget = button;
923
950
  const overlay = subMenu._overlayElement;
924
951
  overlay.noVerticalOverlap = true;
925
952
 
@@ -929,7 +956,6 @@ export const MenuBarMixin = (superClass) =>
929
956
  this._setExpanded(button, true);
930
957
 
931
958
  this.style.pointerEvents = 'auto';
932
- overlay.positionTarget = button;
933
959
 
934
960
  button.dispatchEvent(
935
961
  new CustomEvent('opensubmenu', {
@@ -947,7 +973,8 @@ export const MenuBarMixin = (superClass) =>
947
973
  }
948
974
 
949
975
  if (options.keepFocus) {
950
- this._focusItem(this._expandedButton, false);
976
+ const focusOptions = { focusVisible: isKeyboardActive() };
977
+ this._focusItem(this._expandedButton, focusOptions, false);
951
978
  }
952
979
 
953
980
  // Do not focus item when open not from keyboard
@@ -961,13 +988,13 @@ export const MenuBarMixin = (superClass) =>
961
988
 
962
989
  /** @private */
963
990
  _focusFirstItem() {
964
- const list = this._subMenu._overlayElement.firstElementChild;
991
+ const list = this._subMenu._overlayElement._contentRoot.firstElementChild;
965
992
  list.focus();
966
993
  }
967
994
 
968
995
  /** @private */
969
996
  _focusLastItem() {
970
- const list = this._subMenu._overlayElement.firstElementChild;
997
+ const list = this._subMenu._overlayElement._contentRoot.firstElementChild;
971
998
  const item = list.items[list.items.length - 1];
972
999
  if (item) {
973
1000
  item.focus();
@@ -991,7 +1018,8 @@ export const MenuBarMixin = (superClass) =>
991
1018
  if (button && button.hasAttribute('expanded')) {
992
1019
  this._setExpanded(button, false);
993
1020
  if (restoreFocus) {
994
- this._focusItem(button, false);
1021
+ const focusOptions = { focusVisible: isKeyboardActive() };
1022
+ this._focusItem(button, focusOptions, false);
995
1023
  }
996
1024
  this._expandedButton = null;
997
1025
  }
@@ -11,7 +11,7 @@ import { MenuOverlayMixin } from '@vaadin/context-menu/src/vaadin-menu-overlay-m
11
11
  import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
12
12
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
13
13
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14
- import { menuBarOverlayStyles } from './styles/vaadin-menu-bar-overlay-core-styles.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.
@@ -42,6 +42,7 @@ export class MenuBarOverlay extends MenuOverlayMixin(
42
42
  <div part="overlay" id="overlay" tabindex="0">
43
43
  <div part="content" id="content">
44
44
  <slot></slot>
45
+ <slot name="submenu"></slot>
45
46
  </div>
46
47
  </div>
47
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 };
@@ -7,21 +7,22 @@ import './vaadin-menu-bar-item.js';
7
7
  import './vaadin-menu-bar-list-box.js';
8
8
  import './vaadin-menu-bar-overlay.js';
9
9
  import { css, html, LitElement } from 'lit';
10
+ import { ifDefined } from 'lit/directives/if-defined.js';
10
11
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
11
12
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
13
+ import { ContextMenuMixin } from '@vaadin/context-menu/src/vaadin-context-menu-mixin.js';
12
14
  import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
13
- import { SubMenuMixin } from './vaadin-menu-bar-submenu-mixin.js';
14
15
 
15
16
  /**
16
17
  * An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
17
18
  *
18
19
  * @customElement
19
20
  * @extends HTMLElement
20
- * @mixes SubMenuMixin
21
+ * @mixes ContextMenuMixin
21
22
  * @mixes ThemePropertyMixin
22
23
  * @protected
23
24
  */
24
- class MenuBarSubmenu extends SubMenuMixin(ThemePropertyMixin(PolylitMixin(LitElement))) {
25
+ class MenuBarSubmenu extends ContextMenuMixin(ThemePropertyMixin(PolylitMixin(LitElement))) {
25
26
  static get is() {
26
27
  return 'vaadin-menu-bar-submenu';
27
28
  }
@@ -38,20 +39,94 @@ class MenuBarSubmenu extends SubMenuMixin(ThemePropertyMixin(PolylitMixin(LitEle
38
39
  `;
39
40
  }
40
41
 
42
+ static get properties() {
43
+ return {
44
+ isRoot: {
45
+ type: Boolean,
46
+ reflectToAttribute: true,
47
+ sync: true,
48
+ },
49
+ };
50
+ }
51
+
52
+ constructor() {
53
+ super();
54
+
55
+ this.openOn = 'opensubmenu';
56
+ }
57
+
58
+ /**
59
+ * Tag name prefix used by overlay, list-box and items.
60
+ * @protected
61
+ * @return {string}
62
+ */
63
+ get _tagNamePrefix() {
64
+ return 'vaadin-menu-bar';
65
+ }
66
+
41
67
  /** @protected */
42
68
  render() {
43
- return html`<slot id="slot"></slot>`;
69
+ return html`
70
+ <vaadin-menu-bar-overlay
71
+ id="overlay"
72
+ .owner="${this}"
73
+ .opened="${this.opened}"
74
+ .model="${this._context}"
75
+ .modeless="${this._modeless}"
76
+ .renderer="${this.__itemsRenderer}"
77
+ .withBackdrop="${this._phone}"
78
+ .positionTarget="${this._positionTarget}"
79
+ ?no-horizontal-overlap="${!this.isRoot}"
80
+ ?phone="${this._phone}"
81
+ theme="${ifDefined(this._theme)}"
82
+ exportparts="backdrop, overlay, content"
83
+ @opened-changed="${this._onOverlayOpened}"
84
+ @vaadin-overlay-open="${this._onVaadinOverlayOpen}"
85
+ >
86
+ <slot name="overlay"></slot>
87
+ <slot name="submenu" slot="submenu"></slot>
88
+ </vaadin-menu-bar-overlay>
89
+ `;
44
90
  }
45
91
 
46
92
  /**
93
+ * Overriding the observer to not add global "contextmenu" listener.
94
+ * @override
95
+ */
96
+ _openedChanged() {
97
+ // Do nothing
98
+ }
99
+
100
+ /**
101
+ * Overriding the public method to reset expanded button state.
102
+ */
103
+ close() {
104
+ super.close();
105
+
106
+ // Only handle 1st level submenu
107
+ if (this.hasAttribute('is-root')) {
108
+ this.parentElement._close();
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Override method from `ContextMenuMixin` to prevent closing
114
+ * sub-menu on the same click event that was used to open it.
115
+ *
116
+ * @param {Event} event
117
+ * @return {boolean}
47
118
  * @protected
48
119
  * @override
49
120
  */
50
- createRenderRoot() {
51
- const root = super.createRenderRoot();
52
- root.appendChild(this._overlayElement);
53
- return root;
121
+ _shouldCloseOnOutsideClick(event) {
122
+ if (this.hasAttribute('is-root') && event.composedPath().includes(this.listenOn)) {
123
+ return false;
124
+ }
125
+
126
+ return super._shouldCloseOnOutsideClick(event);
54
127
  }
55
128
  }
56
129
 
57
130
  defineCustomElement(MenuBarSubmenu);
131
+
132
+ export { MenuBarSubmenu };
@@ -65,7 +65,7 @@ export interface MenuBarEventMap<TItem extends MenuBarItem = MenuBarItem>
65
65
  * - `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](#/elements/vaadin-button).
66
66
  * - `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
67
67
  * - `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
68
- * - `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
68
+ * - `<vaadin-menu-bar-submenu>` - has the same API as [`<vaadin-context-menu>`](#/elements/vaadin-context-menu).
69
69
  *
70
70
  * The `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes
71
71
  * on top of the built-in `<vaadin-item>` state attributes:
@@ -74,9 +74,6 @@ export interface MenuBarEventMap<TItem extends MenuBarItem = MenuBarItem>
74
74
  * ---------- |-------------
75
75
  * `expanded` | Expanded parent item.
76
76
  *
77
- * Note: the `theme` attribute value set on `<vaadin-menu-bar>` is
78
- * propagated to the internal components listed above.
79
- *
80
77
  * @fires {CustomEvent} item-selected - Fired when a submenu item or menu bar button without children is clicked.
81
78
  */
82
79
  declare class MenuBar<TItem extends MenuBarItem = MenuBarItem> extends HTMLElement {
@@ -12,7 +12,7 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
12
12
  import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
13
13
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
14
14
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
15
- import { menuBarStyles } from './styles/vaadin-menu-bar-core-styles.js';
15
+ import { menuBarStyles } from './styles/vaadin-menu-bar-base-styles.js';
16
16
  import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
17
17
 
18
18
  /**
@@ -57,7 +57,7 @@ import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
57
57
  * - `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](#/elements/vaadin-button).
58
58
  * - `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
59
59
  * - `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
60
- * - `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
60
+ * - `<vaadin-menu-bar-submenu>` - has the same API as [`<vaadin-context-menu>`](#/elements/vaadin-context-menu).
61
61
  *
62
62
  * The `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes
63
63
  * on top of the built-in `<vaadin-item>` state attributes:
@@ -66,9 +66,6 @@ import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
66
66
  * ---------- |-------------
67
67
  * `expanded` | Expanded parent item.
68
68
  *
69
- * Note: the `theme` attribute value set on `<vaadin-menu-bar>` is
70
- * propagated to the internal components listed above.
71
- *
72
69
  * @fires {CustomEvent<boolean>} item-selected - Fired when a submenu item or menu bar button without children is clicked.
73
70
  *
74
71
  * @customElement
@@ -1,2 +1,2 @@
1
- import './theme/lumo/vaadin-menu-bar.js';
1
+ import './src/vaadin-menu-bar.js';
2
2
  export * from './src/vaadin-menu-bar.js';
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/menu-bar",
4
- "version": "25.0.0-alpha8",
4
+ "version": "25.0.0-beta1",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "elements": [
9
9
  {
10
10
  "name": "vaadin-menu-bar",
11
- "description": "`<vaadin-menu-bar>` is a Web Component providing a set of horizontally stacked buttons offering\nthe user quick access to a consistent set of commands. Each button can toggle a submenu with\nsupport for additional levels of nested menus.\n\nTo create the menu bar, first add the component to the page:\n\n```html\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```js\ndocument.querySelector('vaadin-menu-bar').items = [{text: 'File'}, {text: 'Edit'}];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n------------------|----------------\n`container` | The container wrapping menu bar buttons.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------|----------------------------------\n`disabled` | Set when the menu bar is disabled\n`has-single-button` | Set when there is only one button visible\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-menu-bar>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-overlay).\n\nThe `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.\n\nNote: the `theme` attribute value set on `<vaadin-menu-bar>` is\npropagated to the internal components listed above.",
11
+ "description": "`<vaadin-menu-bar>` is a Web Component providing a set of horizontally stacked buttons offering\nthe user quick access to a consistent set of commands. Each button can toggle a submenu with\nsupport for additional levels of nested menus.\n\nTo create the menu bar, first add the component to the page:\n\n```html\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```js\ndocument.querySelector('vaadin-menu-bar').items = [{text: 'File'}, {text: 'Edit'}];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n------------------|----------------\n`container` | The container wrapping menu bar buttons.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------|----------------------------------\n`disabled` | Set when the menu bar is disabled\n`has-single-button` | Set when there is only one button visible\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-menu-bar>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-submenu>` - has the same API as [`<vaadin-context-menu>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-context-menu).\n\nThe `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "disabled",
@@ -30,17 +30,6 @@
30
30
  ]
31
31
  }
32
32
  },
33
- {
34
- "name": "overlay-class",
35
- "description": "A space-delimited list of CSS class names\nto set on each sub-menu overlay element.",
36
- "value": {
37
- "type": [
38
- "string",
39
- "null",
40
- "undefined"
41
- ]
42
- }
43
- },
44
33
  {
45
34
  "name": "open-on-hover",
46
35
  "description": "If true, the submenu will open on hover (mouseover) instead of click.",
@@ -101,7 +90,7 @@
101
90
  },
102
91
  {
103
92
  "name": "i18n",
104
- "description": "The object used to localize this component. To change the default\nlocalization, replace this with an object that provides all properties, or\njust the individual properties you want to change.\n\nThe object has the following JSON structure and default values:\n```\n{\n moreOptions: 'More options'\n}\n```",
93
+ "description": "The object used to localize this component. To change the default\nlocalization, replace this with an object that provides all properties, or\njust the individual properties you want to change.\n\nThe object has the following JSON structure and default values:\n```js\n{\n moreOptions: 'More options'\n}\n```",
105
94
  "value": {
106
95
  "type": [
107
96
  "MenuBarI18n"
@@ -110,24 +99,13 @@
110
99
  },
111
100
  {
112
101
  "name": "items",
113
- "description": "Defines a hierarchical structure, where root level items represent menu bar buttons,\nand `children` property configures a submenu with items to be opened below\nthe button on click, Enter, Space, Up and Down arrow keys.\n\n#### Example\n\n```js\nmenubar.items = [\n {\n text: 'File',\n className: 'file',\n children: [\n {text: 'Open', className: 'file open'}\n {text: 'Auto Save', checked: true},\n ]\n },\n {component: 'hr'},\n {\n text: 'Edit',\n children: [\n {text: 'Undo', disabled: true},\n {text: 'Redo'}\n ]\n },\n {text: 'Help'}\n];\n```\n\n#### Disabled buttons\n\nWhen disabled, menu bar buttons (root-level items) are rendered\nas \"dimmed\" and prevent all user interactions (mouse and keyboard).\n\nSince disabled buttons are not focusable and cannot react to hover\nevents by default, it can cause accessibility issues by making them\nentirely invisible to assistive technologies, and prevents the use\nof Tooltips to explain why the action is not available. This can be\naddressed by enabling the feature flag `accessibleDisabledButtons`,\nwhich makes disabled buttons focusable and hoverable, while still\npreventing them from being triggered:\n\n```\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```\n```\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```",
102
+ "description": "Defines a hierarchical structure, where root level items represent menu bar buttons,\nand `children` property configures a submenu with items to be opened below\nthe button on click, Enter, Space, Up and Down arrow keys.\n\n#### Example\n\n```js\nmenubar.items = [\n {\n text: 'File',\n className: 'file',\n children: [\n {text: 'Open', className: 'file open'}\n {text: 'Auto Save', checked: true},\n ]\n },\n {component: 'hr'},\n {\n text: 'Edit',\n children: [\n {text: 'Undo', disabled: true},\n {text: 'Redo'}\n ]\n },\n {text: 'Help'}\n];\n```\n\n#### Disabled buttons\n\nWhen disabled, menu bar buttons (root-level items) are rendered\nas \"dimmed\" and prevent all user interactions (mouse and keyboard).\n\nSince disabled buttons are not focusable and cannot react to hover\nevents by default, it can cause accessibility issues by making them\nentirely invisible to assistive technologies, and prevents the use\nof Tooltips to explain why the action is not available. This can be\naddressed by enabling the feature flag `accessibleDisabledButtons`,\nwhich makes disabled buttons focusable and hoverable, while still\npreventing them from being triggered:\n\n```js\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```",
114
103
  "value": {
115
104
  "type": [
116
105
  "Array.<MenuBarItem>"
117
106
  ]
118
107
  }
119
108
  },
120
- {
121
- "name": "overlayClass",
122
- "description": "A space-delimited list of CSS class names\nto set on each sub-menu overlay element.",
123
- "value": {
124
- "type": [
125
- "string",
126
- "null",
127
- "undefined"
128
- ]
129
- }
130
- },
131
109
  {
132
110
  "name": "openOnHover",
133
111
  "description": "If true, the submenu will open on hover (mouseover) instead of click.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/menu-bar",
4
- "version": "25.0.0-alpha8",
4
+ "version": "25.0.0-beta1",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,7 +16,7 @@
16
16
  "elements": [
17
17
  {
18
18
  "name": "vaadin-menu-bar",
19
- "description": "`<vaadin-menu-bar>` is a Web Component providing a set of horizontally stacked buttons offering\nthe user quick access to a consistent set of commands. Each button can toggle a submenu with\nsupport for additional levels of nested menus.\n\nTo create the menu bar, first add the component to the page:\n\n```html\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```js\ndocument.querySelector('vaadin-menu-bar').items = [{text: 'File'}, {text: 'Edit'}];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n------------------|----------------\n`container` | The container wrapping menu bar buttons.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------|----------------------------------\n`disabled` | Set when the menu bar is disabled\n`has-single-button` | Set when there is only one button visible\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-menu-bar>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-alpha8/#/elements/vaadin-overlay).\n\nThe `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.\n\nNote: the `theme` attribute value set on `<vaadin-menu-bar>` is\npropagated to the internal components listed above.",
19
+ "description": "`<vaadin-menu-bar>` is a Web Component providing a set of horizontally stacked buttons offering\nthe user quick access to a consistent set of commands. Each button can toggle a submenu with\nsupport for additional levels of nested menus.\n\nTo create the menu bar, first add the component to the page:\n\n```html\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```js\ndocument.querySelector('vaadin-menu-bar').items = [{text: 'File'}, {text: 'Edit'}];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n------------------|----------------\n`container` | The container wrapping menu bar buttons.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------|----------------------------------\n`disabled` | Set when the menu bar is disabled\n`has-single-button` | Set when there is only one button visible\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-menu-bar>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-menu-bar-button>` - has the same API as [`<vaadin-button>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-submenu>` - has the same API as [`<vaadin-context-menu>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-context-menu).\n\nThe `<vaadin-menu-bar-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {
@@ -49,21 +49,14 @@
49
49
  },
50
50
  {
51
51
  "name": ".i18n",
52
- "description": "The object used to localize this component. To change the default\nlocalization, replace this with an object that provides all properties, or\njust the individual properties you want to change.\n\nThe object has the following JSON structure and default values:\n```\n{\n moreOptions: 'More options'\n}\n```",
52
+ "description": "The object used to localize this component. To change the default\nlocalization, replace this with an object that provides all properties, or\njust the individual properties you want to change.\n\nThe object has the following JSON structure and default values:\n```js\n{\n moreOptions: 'More options'\n}\n```",
53
53
  "value": {
54
54
  "kind": "expression"
55
55
  }
56
56
  },
57
57
  {
58
58
  "name": ".items",
59
- "description": "Defines a hierarchical structure, where root level items represent menu bar buttons,\nand `children` property configures a submenu with items to be opened below\nthe button on click, Enter, Space, Up and Down arrow keys.\n\n#### Example\n\n```js\nmenubar.items = [\n {\n text: 'File',\n className: 'file',\n children: [\n {text: 'Open', className: 'file open'}\n {text: 'Auto Save', checked: true},\n ]\n },\n {component: 'hr'},\n {\n text: 'Edit',\n children: [\n {text: 'Undo', disabled: true},\n {text: 'Redo'}\n ]\n },\n {text: 'Help'}\n];\n```\n\n#### Disabled buttons\n\nWhen disabled, menu bar buttons (root-level items) are rendered\nas \"dimmed\" and prevent all user interactions (mouse and keyboard).\n\nSince disabled buttons are not focusable and cannot react to hover\nevents by default, it can cause accessibility issues by making them\nentirely invisible to assistive technologies, and prevents the use\nof Tooltips to explain why the action is not available. This can be\naddressed by enabling the feature flag `accessibleDisabledButtons`,\nwhich makes disabled buttons focusable and hoverable, while still\npreventing them from being triggered:\n\n```\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```\n```\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```",
60
- "value": {
61
- "kind": "expression"
62
- }
63
- },
64
- {
65
- "name": ".overlayClass",
66
- "description": "A space-delimited list of CSS class names\nto set on each sub-menu overlay element.",
59
+ "description": "Defines a hierarchical structure, where root level items represent menu bar buttons,\nand `children` property configures a submenu with items to be opened below\nthe button on click, Enter, Space, Up and Down arrow keys.\n\n#### Example\n\n```js\nmenubar.items = [\n {\n text: 'File',\n className: 'file',\n children: [\n {text: 'Open', className: 'file open'}\n {text: 'Auto Save', checked: true},\n ]\n },\n {component: 'hr'},\n {\n text: 'Edit',\n children: [\n {text: 'Undo', disabled: true},\n {text: 'Redo'}\n ]\n },\n {text: 'Help'}\n];\n```\n\n#### Disabled buttons\n\nWhen disabled, menu bar buttons (root-level items) are rendered\nas \"dimmed\" and prevent all user interactions (mouse and keyboard).\n\nSince disabled buttons are not focusable and cannot react to hover\nevents by default, it can cause accessibility issues by making them\nentirely invisible to assistive technologies, and prevents the use\nof Tooltips to explain why the action is not available. This can be\naddressed by enabling the feature flag `accessibleDisabledButtons`,\nwhich makes disabled buttons focusable and hoverable, while still\npreventing them from being triggered:\n\n```js\n// Set before any menu bar is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledButtons = true;\n```",
67
60
  "value": {
68
61
  "kind": "expression"
69
62
  }
@@ -1,8 +0,0 @@
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 type { CSSResult } from 'lit';
7
-
8
- export const menuBarButtonStyles: CSSResult;
@@ -1,16 +0,0 @@
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 { css } from 'lit';
7
-
8
- export const menuBarButtonStyles = css`
9
- :host {
10
- flex-shrink: 0;
11
- }
12
-
13
- :host([slot='overflow']) {
14
- margin-inline-end: 0;
15
- }
16
- `;
@@ -1,8 +0,0 @@
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 type { CSSResult } from 'lit';
7
-
8
- export const menuBarStyles: CSSResult;
@@ -1,24 +0,0 @@
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 { css } from 'lit';
7
-
8
- export const menuBarStyles = css`
9
- :host {
10
- display: block;
11
- }
12
-
13
- :host([hidden]) {
14
- display: none !important;
15
- }
16
-
17
- [part='container'] {
18
- position: relative;
19
- display: flex;
20
- width: 100%;
21
- flex-wrap: nowrap;
22
- overflow: hidden;
23
- }
24
- `;
@@ -1,8 +0,0 @@
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 type { CSSResult } from 'lit';
7
-
8
- export const menuBarItemStyles: CSSResult;
@@ -1,8 +0,0 @@
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 { itemStyles } from '@vaadin/item/src/styles/vaadin-item-core-styles.js';
7
-
8
- export const menuBarItemStyles = [itemStyles];
@@ -1,8 +0,0 @@
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 type { CSSResult } from 'lit';
7
-
8
- export const menuBarOverlayStyles: CSSResult;
@@ -1,9 +0,0 @@
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 { menuOverlayStyles } from '@vaadin/context-menu/src/styles/vaadin-menu-overlay-core-styles.js';
7
- import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-core-styles.js';
8
-
9
- export const menuBarOverlayStyles = [overlayStyles, menuOverlayStyles];
@@ -1,66 +0,0 @@
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 { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
7
- import { ContextMenuMixin } from '@vaadin/context-menu/src/vaadin-context-menu-mixin.js';
8
-
9
- /**
10
- * @polymerMixin
11
- * @mixes ContextMenuMixin
12
- * @mixes OverlayClassMixin
13
- */
14
- export const SubMenuMixin = (superClass) =>
15
- class SubMenuMixinClass extends ContextMenuMixin(OverlayClassMixin(superClass)) {
16
- constructor() {
17
- super();
18
-
19
- this.openOn = 'opensubmenu';
20
- }
21
-
22
- /**
23
- * Tag name prefix used by overlay, list-box and items.
24
- * @protected
25
- * @return {string}
26
- */
27
- get _tagNamePrefix() {
28
- return 'vaadin-menu-bar';
29
- }
30
-
31
- /**
32
- * Overriding the observer to not add global "contextmenu" listener.
33
- */
34
- _openedChanged(opened) {
35
- this._overlayElement.opened = opened;
36
- }
37
-
38
- /**
39
- * Overriding the public method to reset expanded button state.
40
- */
41
- close() {
42
- super.close();
43
-
44
- // Only handle 1st level submenu
45
- if (this.hasAttribute('is-root')) {
46
- this.parentElement._close();
47
- }
48
- }
49
-
50
- /**
51
- * Override method from `ContextMenuMixin` to prevent closing
52
- * sub-menu on the same click event that was used to open it.
53
- *
54
- * @param {Event} event
55
- * @return {boolean}
56
- * @protected
57
- * @override
58
- */
59
- _shouldCloseOnOutsideClick(event) {
60
- if (this.hasAttribute('is-root') && event.composedPath().includes(this.listenOn)) {
61
- return false;
62
- }
63
-
64
- return super._shouldCloseOnOutsideClick(event);
65
- }
66
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,128 +0,0 @@
1
- import { button } from '@vaadin/button/theme/lumo/vaadin-button-styles.js';
2
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
3
-
4
- const menuBarButton = css`
5
- :host {
6
- margin: calc(var(--lumo-space-xs) / 2);
7
- margin-left: 0;
8
- border-radius: 0;
9
- }
10
-
11
- [part='label'] {
12
- width: 100%;
13
- }
14
-
15
- /* NOTE(web-padawan): avoid using shorthand padding property for IE11 */
16
- [part='label'] ::slotted(vaadin-menu-bar-item) {
17
- justify-content: center;
18
- background-color: transparent;
19
- height: var(--lumo-button-size);
20
- margin: 0 calc((var(--lumo-size-m) / 3 + var(--lumo-border-radius-m) / 2) * -1);
21
- padding-left: calc(var(--lumo-size-m) / 3 + var(--lumo-border-radius-m) / 2);
22
- padding-right: calc(var(--lumo-size-m) / 3 + var(--lumo-border-radius-m) / 2);
23
- }
24
-
25
- :host([theme~='small']) [part='label'] ::slotted(vaadin-menu-bar-item) {
26
- min-height: var(--lumo-size-s);
27
- margin: 0 calc((var(--lumo-size-s) / 3 + var(--lumo-border-radius-m) / 2) * -1);
28
- padding-left: calc(var(--lumo-size-s) / 3 + var(--lumo-border-radius-m) / 2);
29
- padding-right: calc(var(--lumo-size-s) / 3 + var(--lumo-border-radius-m) / 2);
30
- }
31
-
32
- :host([theme~='tertiary']) [part='label'] ::slotted(vaadin-menu-bar-item) {
33
- margin: 0 calc((var(--lumo-button-size) / 6) * -1);
34
- padding-left: calc(var(--lumo-button-size) / 6);
35
- padding-right: calc(var(--lumo-button-size) / 6);
36
- }
37
-
38
- :host([theme~='tertiary-inline']) {
39
- margin-top: calc(var(--lumo-space-xs) / 2);
40
- margin-bottom: calc(var(--lumo-space-xs) / 2);
41
- margin-right: calc(var(--lumo-space-xs) / 2);
42
- }
43
-
44
- :host([theme~='tertiary-inline']) [part='label'] ::slotted(vaadin-menu-bar-item) {
45
- margin: 0;
46
- padding: 0;
47
- }
48
-
49
- :host([first-visible]) {
50
- border-radius: var(--lumo-border-radius-m) 0 0 var(--lumo-border-radius-m);
51
-
52
- /* Needed to retain the focus-ring with border-radius */
53
- margin-left: calc(var(--lumo-space-xs) / 2);
54
- }
55
-
56
- :host([last-visible]),
57
- :host([slot='overflow']) {
58
- border-radius: 0 var(--lumo-border-radius-m) var(--lumo-border-radius-m) 0;
59
- }
60
-
61
- :host([theme~='tertiary']),
62
- :host([theme~='tertiary-inline']) {
63
- border-radius: var(--lumo-border-radius-m);
64
- }
65
-
66
- :host([slot='overflow']) {
67
- min-width: var(--lumo-button-size);
68
- padding-left: calc(var(--lumo-button-size) / 4);
69
- padding-right: calc(var(--lumo-button-size) / 4);
70
- }
71
-
72
- :host([slot='overflow']) ::slotted(*) {
73
- font-size: var(--lumo-font-size-xl);
74
- }
75
-
76
- :host([slot='overflow']) [part='prefix'],
77
- :host([slot='overflow']) [part='suffix'] {
78
- margin-left: 0;
79
- margin-right: 0;
80
- }
81
-
82
- :host([theme~='dropdown-indicators']:not([slot='overflow']):not([theme~='icon'])[aria-haspopup]) [part='suffix'] {
83
- margin-inline-start: 0;
84
- width: 1em;
85
- height: 1em;
86
- line-height: 1;
87
- font-size: var(--lumo-icon-size-s);
88
- position: relative;
89
- inset-inline-start: 0.15em;
90
- }
91
-
92
- /* prettier-ignore */
93
- :host([theme~='dropdown-indicators']:not([slot='overflow']):not([theme~='icon'])[aria-haspopup]) [part='suffix']::after {
94
- font-family: lumo-icons;
95
- content: var(--lumo-icons-dropdown);
96
- }
97
-
98
- /* prettier-ignore */
99
- :host([theme~='dropdown-indicators']:not([slot='overflow']):not([theme~='icon'])[theme~='tertiary'][aria-haspopup]) [part='suffix'] {
100
- inset-inline-start: 0.05em;
101
- }
102
-
103
- /* prettier-ignore */
104
- :host([theme~='dropdown-indicators']:not([slot='overflow']):not([theme~='icon'])[theme~='tertiary-inline'][aria-haspopup]) [part='suffix'] {
105
- inset-inline-start: 0;
106
- }
107
-
108
- /* RTL styles */
109
- :host([dir='rtl']) {
110
- margin-left: calc(var(--lumo-space-xs) / 2);
111
- margin-right: 0;
112
- border-radius: 0;
113
- }
114
-
115
- :host([dir='rtl'][first-visible]) {
116
- border-radius: 0 var(--lumo-border-radius-m) var(--lumo-border-radius-m) 0;
117
- margin-right: calc(var(--lumo-space-xs) / 2);
118
- }
119
-
120
- :host([dir='rtl'][last-visible]),
121
- :host([dir='rtl'][slot='overflow']) {
122
- border-radius: var(--lumo-border-radius-m) 0 0 var(--lumo-border-radius-m);
123
- }
124
- `;
125
-
126
- registerStyles('vaadin-menu-bar-button', [button, menuBarButton], {
127
- moduleId: 'lumo-menu-bar-button',
128
- });
@@ -1,2 +0,0 @@
1
- import './vaadin-menu-bar-button-styles.js';
2
- import '../../src/vaadin-menu-bar-button.js';
@@ -1,2 +0,0 @@
1
- import './vaadin-menu-bar-button-styles.js';
2
- import '../../src/vaadin-menu-bar-button.js';
@@ -1,2 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/sizing.js';
2
- import '@vaadin/vaadin-lumo-styles/spacing.js';
@@ -1,27 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/sizing.js';
2
- import '@vaadin/vaadin-lumo-styles/spacing.js';
3
- import { contextMenuItem } from '@vaadin/context-menu/theme/lumo/vaadin-context-menu-item-styles.js';
4
- import { item } from '@vaadin/item/theme/lumo/vaadin-item-styles.js';
5
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
6
-
7
- const menuBarItem = css`
8
- [part='content'] {
9
- display: flex;
10
- /* tweak to inherit centering from menu bar button */
11
- align-items: inherit;
12
- justify-content: inherit;
13
- }
14
-
15
- [part='content'] ::slotted(vaadin-icon) {
16
- display: inline-block;
17
- width: var(--lumo-icon-size-m);
18
- height: var(--lumo-icon-size-m);
19
- }
20
-
21
- [part='content'] ::slotted(vaadin-icon[icon^='vaadin:']) {
22
- padding: var(--lumo-space-xs);
23
- box-sizing: border-box !important;
24
- }
25
- `;
26
-
27
- registerStyles('vaadin-menu-bar-item', [item, contextMenuItem, menuBarItem], { moduleId: 'lumo-menu-bar-item' });
@@ -1 +0,0 @@
1
- export {};
@@ -1,5 +0,0 @@
1
- import { contextMenuListBox } from '@vaadin/context-menu/theme/lumo/vaadin-context-menu-list-box-styles.js';
2
- import { listBox } from '@vaadin/list-box/theme/lumo/vaadin-list-box-styles.js';
3
- import { registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
4
-
5
- registerStyles('vaadin-menu-bar-list-box', [listBox, contextMenuListBox], { moduleId: 'lumo-menu-bar-list-box' });
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { contextMenuOverlay } from '@vaadin/context-menu/theme/lumo/vaadin-context-menu-overlay-styles.js';
2
- import { menuOverlay } from '@vaadin/vaadin-lumo-styles/mixins/menu-overlay.js';
3
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
4
-
5
- const menuBarOverlay = css`
6
- :host(:first-of-type) {
7
- padding-top: var(--lumo-space-xs);
8
- }
9
- `;
10
-
11
- registerStyles('vaadin-menu-bar-overlay', [menuOverlay, contextMenuOverlay, menuBarOverlay], {
12
- moduleId: 'lumo-menu-bar-overlay',
13
- });
@@ -1 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/style.js';
@@ -1,17 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/style.js';
2
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
3
-
4
- registerStyles(
5
- 'vaadin-menu-bar',
6
- css`
7
- :host([has-single-button]) ::slotted(vaadin-menu-bar-button) {
8
- border-radius: var(--lumo-border-radius-m);
9
- }
10
-
11
- :host([theme~='end-aligned']) ::slotted(vaadin-menu-bar-button[first-visible]),
12
- :host([theme~='end-aligned'][has-single-button]) ::slotted(vaadin-menu-bar-button) {
13
- margin-inline-start: auto;
14
- }
15
- `,
16
- { moduleId: 'lumo-menu-bar' },
17
- );
@@ -1,6 +0,0 @@
1
- import './vaadin-menu-bar-button.js';
2
- import './vaadin-menu-bar-item-styles.js';
3
- import './vaadin-menu-bar-list-box-styles.js';
4
- import './vaadin-menu-bar-overlay-styles.js';
5
- import './vaadin-menu-bar-styles.js';
6
- import '../../src/vaadin-menu-bar.js';
@@ -1,6 +0,0 @@
1
- import './vaadin-menu-bar-button.js';
2
- import './vaadin-menu-bar-item-styles.js';
3
- import './vaadin-menu-bar-list-box-styles.js';
4
- import './vaadin-menu-bar-overlay-styles.js';
5
- import './vaadin-menu-bar-styles.js';
6
- import '../../src/vaadin-menu-bar.js';