@vaadin/menu-bar 24.8.0-alpha14 → 24.8.0-alpha16

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/menu-bar",
3
- "version": "24.8.0-alpha14",
3
+ "version": "24.8.0-alpha16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -37,22 +37,22 @@
37
37
  "dependencies": {
38
38
  "@open-wc/dedupe-mixin": "^1.3.0",
39
39
  "@polymer/polymer": "^3.0.0",
40
- "@vaadin/a11y-base": "24.8.0-alpha14",
41
- "@vaadin/button": "24.8.0-alpha14",
42
- "@vaadin/component-base": "24.8.0-alpha14",
43
- "@vaadin/context-menu": "24.8.0-alpha14",
44
- "@vaadin/item": "24.8.0-alpha14",
45
- "@vaadin/list-box": "24.8.0-alpha14",
46
- "@vaadin/overlay": "24.8.0-alpha14",
47
- "@vaadin/vaadin-lumo-styles": "24.8.0-alpha14",
48
- "@vaadin/vaadin-material-styles": "24.8.0-alpha14",
49
- "@vaadin/vaadin-themable-mixin": "24.8.0-alpha14",
40
+ "@vaadin/a11y-base": "24.8.0-alpha16",
41
+ "@vaadin/button": "24.8.0-alpha16",
42
+ "@vaadin/component-base": "24.8.0-alpha16",
43
+ "@vaadin/context-menu": "24.8.0-alpha16",
44
+ "@vaadin/item": "24.8.0-alpha16",
45
+ "@vaadin/list-box": "24.8.0-alpha16",
46
+ "@vaadin/overlay": "24.8.0-alpha16",
47
+ "@vaadin/vaadin-lumo-styles": "24.8.0-alpha16",
48
+ "@vaadin/vaadin-material-styles": "24.8.0-alpha16",
49
+ "@vaadin/vaadin-themable-mixin": "24.8.0-alpha16",
50
50
  "lit": "^3.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@vaadin/chai-plugins": "24.8.0-alpha14",
54
- "@vaadin/icon": "24.8.0-alpha14",
55
- "@vaadin/test-runner-commands": "24.8.0-alpha14",
53
+ "@vaadin/chai-plugins": "24.8.0-alpha16",
54
+ "@vaadin/icon": "24.8.0-alpha16",
55
+ "@vaadin/test-runner-commands": "24.8.0-alpha16",
56
56
  "@vaadin/testing-helpers": "^1.1.0",
57
57
  "sinon": "^18.0.0"
58
58
  },
@@ -60,5 +60,5 @@
60
60
  "web-types.json",
61
61
  "web-types.lit.json"
62
62
  ],
63
- "gitHead": "c8a33608cf1ab377529afd0650360e349886384c"
63
+ "gitHead": "ac26cb142cd3d6374423ca699677d47557399d47"
64
64
  }
@@ -3,6 +3,9 @@
3
3
  * Copyright (c) 2019 - 2025 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { html, noChange, nothing, render } from 'lit';
7
+ import { Directive, directive } from 'lit/directive.js';
8
+ import { ifDefined } from 'lit/directives/if-defined.js';
6
9
  import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
7
10
  import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
8
11
  import { isElementFocused, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
@@ -12,6 +15,41 @@ import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
12
15
  import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
13
16
  import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
14
17
 
18
+ /**
19
+ * Custom Lit directive for rendering item components
20
+ * inspired by the `flowComponentDirective` logic.
21
+ */
22
+ class ItemComponentDirective extends Directive {
23
+ update(part, [{ component, text }]) {
24
+ const { parentNode, startNode } = part;
25
+
26
+ const newNode = component || (text ? document.createTextNode(text) : null);
27
+ const oldNode = this.getOldNode(part);
28
+
29
+ if (oldNode === newNode) {
30
+ return noChange;
31
+ } else if (oldNode && newNode) {
32
+ parentNode.replaceChild(newNode, oldNode);
33
+ } else if (oldNode) {
34
+ parentNode.removeChild(oldNode);
35
+ } else if (newNode) {
36
+ startNode.after(newNode);
37
+ }
38
+
39
+ return noChange;
40
+ }
41
+
42
+ getOldNode(part) {
43
+ const { startNode, endNode } = part;
44
+ if (startNode.nextSibling === endNode) {
45
+ return null;
46
+ }
47
+ return startNode.nextSibling;
48
+ }
49
+ }
50
+
51
+ const componentDirective = directive(ItemComponentDirective);
52
+
15
53
  const DEFAULT_I18N = {
16
54
  moreOptions: 'More options',
17
55
  };
@@ -112,6 +150,7 @@ export const MenuBarMixin = (superClass) =>
112
150
  */
113
151
  items: {
114
152
  type: Array,
153
+ sync: true,
115
154
  value: () => [],
116
155
  },
117
156
 
@@ -140,6 +179,7 @@ export const MenuBarMixin = (superClass) =>
140
179
  */
141
180
  reverseCollapse: {
142
181
  type: Boolean,
182
+ sync: true,
143
183
  },
144
184
 
145
185
  /**
@@ -149,6 +189,7 @@ export const MenuBarMixin = (superClass) =>
149
189
  */
150
190
  tabNavigation: {
151
191
  type: Boolean,
192
+ sync: true,
152
193
  },
153
194
 
154
195
  /**
@@ -169,6 +210,7 @@ export const MenuBarMixin = (superClass) =>
169
210
  /** @protected */
170
211
  _container: {
171
212
  type: Object,
213
+ sync: true,
172
214
  },
173
215
  };
174
216
  }
@@ -178,7 +220,7 @@ export const MenuBarMixin = (superClass) =>
178
220
  '_themeChanged(_theme, _overflow, _container)',
179
221
  '__hasOverflowChanged(_hasOverflow, _overflow)',
180
222
  '__i18nChanged(__effectiveI18n, _overflow)',
181
- '_menuItemsChanged(items, _overflow, _container)',
223
+ '__updateButtons(items, disabled, _overflow, _container)',
182
224
  '_reverseCollapseChanged(reverseCollapse, _overflow, _container)',
183
225
  '_tabNavigationChanged(tabNavigation, _overflow, _container)',
184
226
  ];
@@ -284,8 +326,11 @@ export const MenuBarMixin = (superClass) =>
284
326
  dots.innerHTML = '·'.repeat(3);
285
327
  btn.appendChild(dots);
286
328
 
329
+ btn.setAttribute('aria-haspopup', 'true');
330
+ btn.setAttribute('aria-expanded', 'false');
331
+ btn.setAttribute('role', this.tabNavigation ? 'button' : 'menuitem');
332
+
287
333
  this._overflow = btn;
288
- this._initButtonAttrs(btn);
289
334
  },
290
335
  });
291
336
  this.addController(this._overflowController);
@@ -340,23 +385,6 @@ export const MenuBarMixin = (superClass) =>
340
385
  this.__detectOverflow();
341
386
  }
342
387
 
343
- /**
344
- * Override method inherited from `DisabledMixin`
345
- * to update the `disabled` property for the buttons
346
- * whenever the property changes on the menu bar.
347
- *
348
- * @param {boolean} newValue the new disabled value
349
- * @param {boolean} oldValue the previous disabled value
350
- * @override
351
- * @protected
352
- */
353
- _disabledChanged(newValue, oldValue) {
354
- super._disabledChanged(newValue, oldValue);
355
- if (oldValue !== newValue) {
356
- this.__updateButtonsDisabled(newValue);
357
- }
358
- }
359
-
360
388
  /**
361
389
  * A callback for the `_theme` property observer.
362
390
  * It propagates the host theme to the buttons and the sub menu.
@@ -366,14 +394,16 @@ export const MenuBarMixin = (superClass) =>
366
394
  */
367
395
  _themeChanged(theme, overflow, container) {
368
396
  if (overflow && container) {
369
- this._buttons.forEach((btn) => this._setButtonTheme(btn, theme));
397
+ this.__renderButtons(this.items);
370
398
  this.__detectOverflow();
371
- }
372
399
 
373
- if (theme) {
374
- this._subMenu.setAttribute('theme', theme);
375
- } else {
376
- this._subMenu.removeAttribute('theme');
400
+ if (theme) {
401
+ overflow.setAttribute('theme', theme);
402
+ this._subMenu.setAttribute('theme', theme);
403
+ } else {
404
+ overflow.removeAttribute('theme');
405
+ this._subMenu.removeAttribute('theme');
406
+ }
377
407
  }
378
408
  }
379
409
 
@@ -413,7 +443,7 @@ export const MenuBarMixin = (superClass) =>
413
443
  }
414
444
 
415
445
  /** @private */
416
- _menuItemsChanged(items, overflow, container) {
446
+ __updateButtons(items, disabled, overflow, container) {
417
447
  if (!overflow || !container) {
418
448
  return;
419
449
  }
@@ -421,11 +451,24 @@ export const MenuBarMixin = (superClass) =>
421
451
  if (items !== this._oldItems) {
422
452
  this._oldItems = items;
423
453
  this.__renderButtons(items);
454
+ this.__detectOverflow();
455
+ }
456
+
457
+ if (disabled !== this._oldDisabled) {
458
+ this._oldDisabled = disabled;
459
+ this.__renderButtons(items);
460
+ overflow.toggleAttribute('disabled', disabled);
424
461
  }
425
462
 
426
463
  const subMenu = this._subMenu;
427
464
  if (subMenu && subMenu.opened) {
428
- subMenu.close();
465
+ const button = subMenu._overlayElement.positionTarget;
466
+
467
+ // Close sub-menu if the corresponding button is no longer in the DOM,
468
+ // or if the item on it has been changed to no longer have children.
469
+ if (!button.isConnected || !Array.isArray(button.item.children) || button.item.children.length === 0) {
470
+ subMenu.close();
471
+ }
429
472
  }
430
473
  }
431
474
 
@@ -449,9 +492,9 @@ export const MenuBarMixin = (superClass) =>
449
492
  /** @private */
450
493
  __restoreButtons(buttons) {
451
494
  buttons.forEach((button) => {
452
- button.disabled = (button.item && button.item.disabled) || this.disabled;
453
495
  button.style.visibility = '';
454
496
  button.style.position = '';
497
+ button.style.width = '';
455
498
 
456
499
  // Teleport item component back from "overflow" sub-menu
457
500
  const item = button.item && button.item.component;
@@ -471,14 +514,6 @@ export const MenuBarMixin = (superClass) =>
471
514
  item.removeAttribute('tabindex');
472
515
  }
473
516
 
474
- /** @private */
475
- __updateButtonsDisabled(disabled) {
476
- this._buttons.forEach((btn) => {
477
- // Disable the button if the entire menu-bar is disabled or the item alone is disabled
478
- btn.disabled = disabled || (btn.item && btn.item.disabled);
479
- });
480
- }
481
-
482
517
  /** @private */
483
518
  __updateOverflow(items) {
484
519
  this._overflow.item = { children: items };
@@ -512,7 +547,6 @@ export const MenuBarMixin = (superClass) =>
512
547
 
513
548
  // Save width for buttons with component
514
549
  btn.style.width = getComputedStyle(btn).width;
515
- btn.disabled = true;
516
550
  btn.style.visibility = 'hidden';
517
551
  btn.style.position = 'absolute';
518
552
  }
@@ -561,66 +595,17 @@ export const MenuBarMixin = (superClass) =>
561
595
  });
562
596
  }
563
597
 
564
- /** @protected */
565
- _removeButtons() {
566
- this._buttons.forEach((button) => {
567
- if (button !== this._overflow) {
568
- this.removeChild(button);
569
- }
570
- });
571
- }
572
-
573
- /** @protected */
574
- _initButton(item) {
575
- const button = document.createElement('vaadin-menu-bar-button');
576
-
577
- const itemCopy = { ...item };
578
- button.item = itemCopy;
579
-
580
- if (item.component) {
581
- const component = this.__getComponent(itemCopy);
582
- itemCopy.component = component;
583
- // Save item for overflow menu
584
- component.item = itemCopy;
585
- button.appendChild(component);
586
- } else if (item.text) {
587
- button.textContent = item.text;
588
- }
589
-
590
- if (item.className) {
591
- button.className = item.className;
592
- }
593
-
594
- button.disabled = item.disabled;
595
-
596
- return button;
597
- }
598
-
599
- /** @protected */
600
- _initButtonAttrs(button) {
601
- button.setAttribute('role', this.tabNavigation ? 'button' : 'menuitem');
602
-
603
- if (button === this._overflow || (button.item && button.item.children)) {
604
- button.setAttribute('aria-haspopup', 'true');
605
- button.setAttribute('aria-expanded', 'false');
606
- }
607
- }
608
-
609
- /** @protected */
610
- _setButtonTheme(btn, hostTheme) {
598
+ /** @private */
599
+ __getButtonTheme(item, hostTheme) {
611
600
  let theme = hostTheme;
612
601
 
613
602
  // Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
614
- const itemTheme = btn.item && btn.item.theme;
603
+ const itemTheme = item && item.theme;
615
604
  if (itemTheme != null) {
616
605
  theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
617
606
  }
618
607
 
619
- if (theme) {
620
- btn.setAttribute('theme', theme);
621
- } else {
622
- btn.removeAttribute('theme');
623
- }
608
+ return theme;
624
609
  }
625
610
 
626
611
  /** @private */
@@ -645,21 +630,47 @@ export const MenuBarMixin = (superClass) =>
645
630
 
646
631
  /** @private */
647
632
  __renderButtons(items = []) {
648
- this._removeButtons();
649
-
650
- /* Empty array, do nothing */
651
- if (items.length === 0) {
652
- return;
653
- }
633
+ render(
634
+ html`
635
+ ${items.map((item) => {
636
+ const itemCopy = { ...item };
637
+ const hasChildren = Boolean(item && item.children);
638
+
639
+ if (itemCopy.component) {
640
+ const component = this.__getComponent(itemCopy);
641
+ itemCopy.component = component;
642
+ component.item = itemCopy;
643
+ }
654
644
 
655
- items.forEach((item) => {
656
- const button = this._initButton(item);
657
- this.insertBefore(button, this._overflow);
658
- this._initButtonAttrs(button);
659
- this._setButtonTheme(button, this._theme);
660
- });
645
+ return html`
646
+ <vaadin-menu-bar-button
647
+ .item="${itemCopy}"
648
+ .disabled="${this.disabled || item.disabled}"
649
+ role="${this.tabNavigation ? 'button' : 'menuitem'}"
650
+ aria-haspopup="${ifDefined(hasChildren ? 'true' : nothing)}"
651
+ aria-expanded="${ifDefined(hasChildren ? 'false' : nothing)}"
652
+ class="${ifDefined(item.className || nothing)}"
653
+ theme="${ifDefined(this.__getButtonTheme(item, this._theme) || nothing)}"
654
+ @click="${this.__onRootButtonClick}"
655
+ >${componentDirective(itemCopy)}</vaadin-menu-bar-button
656
+ >
657
+ `;
658
+ })}
659
+ `,
660
+ this,
661
+ { renderBefore: this._overflow },
662
+ );
663
+ }
661
664
 
662
- this.__detectOverflow();
665
+ /** @private */
666
+ __onRootButtonClick(event) {
667
+ const button = event.target;
668
+ // Propagate click event from button to the item component if it was outside
669
+ // it e.g. by calling `click()` on the button (used by the Flow counterpart).
670
+ if (button.item && button.item.component && !event.composedPath().includes(button.item.component)) {
671
+ event.stopPropagation();
672
+ button.item.component.click();
673
+ }
663
674
  }
664
675
 
665
676
  /**
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": "24.8.0-alpha14",
4
+ "version": "24.8.0-alpha16",
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```\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```\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/24.8.0-alpha14/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/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```\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```\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/24.8.0-alpha16/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/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.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "disabled",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/menu-bar",
4
- "version": "24.8.0-alpha14",
4
+ "version": "24.8.0-alpha16",
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```\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```\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/24.8.0-alpha14/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha14/#/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```\n<vaadin-menu-bar></vaadin-menu-bar>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-menu-bar#property-items) property to initialize the structure:\n\n```\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/24.8.0-alpha16/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-item).\n- `<vaadin-menu-bar-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/elements/vaadin-list-box).\n- `<vaadin-menu-bar-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.8.0-alpha16/#/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.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {