@vaadin/menu-bar 25.3.0 → 25.3.1

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": "25.3.0",
3
+ "version": "25.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,23 +35,23 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@open-wc/dedupe-mixin": "^1.3.0",
38
- "@vaadin/a11y-base": "~25.3.0",
39
- "@vaadin/button": "~25.3.0",
40
- "@vaadin/component-base": "~25.3.0",
41
- "@vaadin/context-menu": "~25.3.0",
42
- "@vaadin/item": "~25.3.0",
43
- "@vaadin/list-box": "~25.3.0",
44
- "@vaadin/overlay": "~25.3.0",
45
- "@vaadin/vaadin-themable-mixin": "~25.3.0",
38
+ "@vaadin/a11y-base": "~25.3.1",
39
+ "@vaadin/button": "~25.3.1",
40
+ "@vaadin/component-base": "~25.3.1",
41
+ "@vaadin/context-menu": "~25.3.1",
42
+ "@vaadin/item": "~25.3.1",
43
+ "@vaadin/list-box": "~25.3.1",
44
+ "@vaadin/overlay": "~25.3.1",
45
+ "@vaadin/vaadin-themable-mixin": "~25.3.1",
46
46
  "lit": "^3.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@vaadin/aura": "~25.3.0",
50
- "@vaadin/chai-plugins": "~25.3.0",
51
- "@vaadin/icon": "~25.3.0",
52
- "@vaadin/test-runner-commands": "~25.3.0",
49
+ "@vaadin/aura": "~25.3.1",
50
+ "@vaadin/chai-plugins": "~25.3.1",
51
+ "@vaadin/icon": "~25.3.1",
52
+ "@vaadin/test-runner-commands": "~25.3.1",
53
53
  "@vaadin/testing-helpers": "^2.0.0",
54
- "@vaadin/vaadin-lumo-styles": "~25.3.0",
54
+ "@vaadin/vaadin-lumo-styles": "~25.3.1",
55
55
  "sinon": "^22.0.0"
56
56
  },
57
57
  "customElements": "custom-elements.json",
@@ -59,5 +59,5 @@
59
59
  "web-types.json",
60
60
  "web-types.lit.json"
61
61
  ],
62
- "gitHead": "4133747243c03bf544f6e82cc2ec9f295aaccd65"
62
+ "gitHead": "a652d6071d7ad5add8465772f14f3efe633066a8"
63
63
  }
@@ -15,6 +15,12 @@ export const menuBarStyles = css`
15
15
  display: none !important;
16
16
  }
17
17
 
18
+ /* Keep the width measured with all buttons in flow while the host is frozen */
19
+ :host([overflow-frozen]) {
20
+ contain: inline-size !important;
21
+ contain-intrinsic-inline-size: var(--_vaadin-menu-bar-content-width);
22
+ }
23
+
18
24
  [part='container'] {
19
25
  display: flex;
20
26
  flex-wrap: nowrap;
@@ -57,6 +57,9 @@ const DEFAULT_I18N = {
57
57
  moreOptions: 'More options',
58
58
  };
59
59
 
60
+ // Two client rects of the same edge can differ by float noise, a few 1e-5 px under browser zoom
61
+ const OVERFLOW_TOLERANCE = 0.1;
62
+
60
63
  export const MenuBarMixin = (superClass) =>
61
64
  class MenuBarMixinClass extends I18nMixin(
62
65
  KeyboardDirectionMixin(ResizeMixin(FocusMixin(DisabledMixin(superClass)))),
@@ -459,7 +462,6 @@ export const MenuBarMixin = (superClass) =>
459
462
  buttons.forEach((button) => {
460
463
  button.style.visibility = '';
461
464
  button.style.position = '';
462
- button.style.width = '';
463
465
 
464
466
  // Teleport item component back from "overflow" sub-menu
465
467
  const item = button.item?.component;
@@ -469,6 +471,20 @@ export const MenuBarMixin = (superClass) =>
469
471
  });
470
472
  }
471
473
 
474
+ /**
475
+ * Restores all buttons, the overflow button and the state attributes, so that
476
+ * the measurement does not use the button margins of the previous state.
477
+ *
478
+ * @param {!Array<!HTMLElement>} buttons
479
+ * @private
480
+ */
481
+ __restoreState(buttons) {
482
+ this.__restoreButtons(buttons);
483
+ this.__updateOverflow([]);
484
+ this.toggleAttribute('has-single-button', buttons.length === 1);
485
+ this.__updateVisibleAttributes(buttons);
486
+ }
487
+
472
488
  /** @private */
473
489
  __restoreItem(button, item) {
474
490
  button.appendChild(item);
@@ -484,49 +500,154 @@ export const MenuBarMixin = (superClass) =>
484
500
  this._hasOverflow = items.length > 0;
485
501
  }
486
502
 
487
- /** @private */
488
- __setOverflowItems(buttons, overflow) {
489
- const container = this._container;
490
-
491
- // Prevent the container from shrinking while buttons are being hidden.
492
- // The host has min-width: 0 so it can shrink inside flex/grid layouts.
493
- // Without this lock, hiding a button reduces the host width, which
494
- // shrinks the container (width: 100%), shifting all button positions
495
- // and causing a cascading collapse where every button appears to overflow.
496
- container.style.minWidth = `${container.offsetWidth}px`;
497
-
498
- if (container.offsetWidth < container.scrollWidth) {
499
- this._hasOverflow = true;
500
-
501
- const isRTL = this.__isRTL;
502
- const containerLeft = container.offsetLeft;
503
- const containerWidth = container.offsetWidth;
504
-
505
- const remaining = [...buttons];
506
- while (remaining.length) {
507
- // The overflow button follows the last remaining one, so its far edge is what has
508
- // to fit. Auto margins move the whole row but cannot push that edge past the end.
509
- const overflowEnd = isRTL
510
- ? containerWidth - (overflow.offsetLeft - containerLeft)
511
- : overflow.offsetLeft + overflow.offsetWidth - containerLeft;
512
-
513
- if (overflowEnd <= containerWidth) {
514
- break;
515
- }
503
+ /**
504
+ * Inline end edge of the element, mirrored in RTL. Unlike `offsetWidth` and `scrollWidth`,
505
+ * the rect keeps the fraction that browser zoom leaves on the width.
506
+ *
507
+ * @param {!HTMLElement} el
508
+ * @return {number}
509
+ * @private
510
+ */
511
+ __getInlineEnd(el) {
512
+ const { left, right } = el.getBoundingClientRect();
513
+ return this.__isRTL ? -left : right;
514
+ }
516
515
 
517
- const btn = this.reverseCollapse ? remaining.shift() : remaining.pop();
516
+ /**
517
+ * Whether the element ends past the container, with the tolerance for fractional widths.
518
+ *
519
+ * @param {!HTMLElement} el
520
+ * @return {boolean}
521
+ * @private
522
+ */
523
+ __isPastContainerEnd(el) {
524
+ return this.__getInlineEnd(el) > this.__getInlineEnd(this._container) + OVERFLOW_TOLERANCE;
525
+ }
518
526
 
519
- // Save width for buttons with component
520
- btn.style.width = getComputedStyle(btn).width;
521
- btn.style.visibility = 'hidden';
522
- btn.style.position = 'absolute';
527
+ /**
528
+ * Layout of the buttons, the overflow button and the container, read in one batch
529
+ * before any button is hidden. Positions are mirrored in RTL so that a larger value
530
+ * is always further along the inline axis.
531
+ *
532
+ * @typedef {object} MenuBarOverflowLayout
533
+ * @property {number[]} starts Inline start of each button
534
+ * @property {number[]} ends Inline end of each button
535
+ * @property {number[]} margins Inline start margin of each button
536
+ * @property {number} overflowExtent Space the overflow button adds after the last button
537
+ * @property {number} containerWidth Width of the container
538
+ * @property {number} contentWidth Width of all buttons and the overflow button in flow
539
+ */
540
+
541
+ /**
542
+ * @param {!Array<!HTMLElement>} buttons
543
+ * @param {!HTMLElement} overflow
544
+ * @return {!MenuBarOverflowLayout}
545
+ * @private
546
+ */
547
+ __getOverflowLayout(buttons, overflow) {
548
+ const isRTL = this.__isRTL;
549
+ const rects = buttons.map((btn) => btn.getBoundingClientRect());
550
+ const ends = rects.map(({ left, right }) => (isRTL ? -left : right));
551
+ const overflowEnd = this.__getInlineEnd(overflow);
552
+ const containerRect = this._container.getBoundingClientRect();
553
+ const containerStart = isRTL ? -containerRect.right : containerRect.left;
554
+
555
+ return {
556
+ starts: rects.map(({ left, right }) => (isRTL ? -right : left)),
557
+ ends,
558
+ // `auto` resolves to 0 while the content overflows.
559
+ margins: buttons.map((btn) => parseFloat(getComputedStyle(btn).marginInlineStart) || 0),
560
+ overflowExtent: overflowEnd - ends.at(-1),
561
+ containerWidth: containerRect.width,
562
+ contentWidth: overflowEnd - containerStart,
563
+ };
564
+ }
565
+
566
+ /**
567
+ * Picks the buttons to collapse so that the rest plus the overflow button end inside
568
+ * the container. Hiding a button only shifts the buttons after it, so the end of any
569
+ * kept range follows from one measurement.
570
+ *
571
+ * @param {!Array<!HTMLElement>} buttons
572
+ * @param {!MenuBarOverflowLayout} layout
573
+ * @return {!Array<!HTMLElement>} buttons to collapse, in DOM order
574
+ * @private
575
+ */
576
+ __getButtonsToCollapse(buttons, { starts, ends, margins, overflowExtent, containerWidth }) {
577
+ let lo = 0;
578
+ let hi = buttons.length - 1;
579
+
580
+ // The first kept button keeps its own start margin in front of the range.
581
+ while (lo <= hi && margins[lo] + ends[hi] - starts[lo] + overflowExtent > containerWidth + OVERFLOW_TOLERANCE) {
582
+ if (this.reverseCollapse) {
583
+ lo += 1;
584
+ } else {
585
+ hi -= 1;
523
586
  }
587
+ }
588
+
589
+ return buttons.filter((_, i) => i < lo || i > hi);
590
+ }
524
591
 
525
- const items = buttons.filter((b) => !remaining.includes(b)).map((b) => b.item);
526
- this.__updateOverflow(items);
592
+ /**
593
+ * Keeps the intrinsic width of the host at the given value. A parent sized by content,
594
+ * such as a split layout pane, shrinks the host once the buttons are out of flow. The
595
+ * width the host had with all buttons in flow keeps the collapse that was decided for it.
596
+ * The `overflow-frozen` attribute marks the frozen state for the styles and for the next
597
+ * detection. It is internal and not documented.
598
+ *
599
+ * @param {number} width
600
+ * @private
601
+ */
602
+ __freezeWidth(width) {
603
+ this.style.setProperty('--_vaadin-menu-bar-content-width', `${width}px`);
604
+ this.setAttribute('overflow-frozen', '');
605
+ }
606
+
607
+ /** @private */
608
+ __unfreezeWidth() {
609
+ this.style.removeProperty('--_vaadin-menu-bar-content-width');
610
+ this.removeAttribute('overflow-frozen');
611
+ }
612
+
613
+ /**
614
+ * Shows the overflow button and collapses buttons into it when the last button
615
+ * does not fit in the container. Freezes the host width when the parent shrinks
616
+ * the host after the collapse. A frozen host stays frozen until all buttons and the
617
+ * overflow button fit, so that its flex basis does not change between detections
618
+ * when another element in the same row depends on it.
619
+ *
620
+ * @param {!Array<!HTMLElement>} buttons
621
+ * @param {!HTMLElement} overflow
622
+ * @private
623
+ */
624
+ __setOverflowItems(buttons, overflow) {
625
+ const lastButton = buttons.at(-1);
626
+ // A frozen host is as wide as all buttons plus the overflow button, so the quick
627
+ // check would report a fit one overflow button too early. Measure instead.
628
+ const wasFrozen = this.hasAttribute('overflow-frozen');
629
+ if (!lastButton || (!wasFrozen && !this.__isPastContainerEnd(lastButton))) {
630
+ this.__unfreezeWidth();
631
+ return;
527
632
  }
633
+ this._hasOverflow = true;
634
+
635
+ // Read the layout once the overflow button is in flow
636
+ const layout = this.__getOverflowLayout(buttons, overflow);
637
+ const collapsed = this.__getButtonsToCollapse(buttons, layout);
528
638
 
529
- container.style.minWidth = '';
639
+ // Write the DOM state
640
+ collapsed.forEach((btn) => {
641
+ btn.style.visibility = 'hidden';
642
+ btn.style.position = 'absolute';
643
+ });
644
+ this.__updateOverflow(collapsed.map((btn) => btn.item));
645
+
646
+ if (collapsed.length === 0) {
647
+ this.__unfreezeWidth();
648
+ } else if (wasFrozen || this.__isPastContainerEnd(overflow)) {
649
+ this.__freezeWidth(layout.contentWidth);
650
+ }
530
651
  }
531
652
 
532
653
  /** @private */
@@ -542,9 +663,7 @@ export const MenuBarMixin = (superClass) =>
542
663
  const buttons = this._buttons.filter((btn) => btn !== overflow);
543
664
  const oldOverflowCount = this.__getOverflowCount(overflow);
544
665
 
545
- // Reset all buttons in the menu bar and the overflow button
546
- this.__restoreButtons(buttons);
547
- this.__updateOverflow([]);
666
+ this.__restoreState(buttons);
548
667
 
549
668
  // Hide any overflowing buttons and put them in the 'overflow' button
550
669
  this.__setOverflowItems(buttons, overflow);
@@ -573,6 +692,11 @@ export const MenuBarMixin = (superClass) =>
573
692
  this._setTabindex(visibleButtons[visibleButtons.length - 1], true);
574
693
  }
575
694
 
695
+ this.__updateVisibleAttributes(visibleButtons);
696
+ }
697
+
698
+ /** @private */
699
+ __updateVisibleAttributes(visibleButtons) {
576
700
  visibleButtons.forEach((btn, index) => {
577
701
  btn.toggleAttribute('first-visible', index === 0);
578
702
  btn.toggleAttribute('last-visible', !this._hasOverflow && index === visibleButtons.length - 1);
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.3.0",
4
+ "version": "25.3.1",
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.3.0/#/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\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:---------------------------|\n| `--vaadin-menu-bar-gap` |\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.3.0/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.3.0/#/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.3.0/#/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.3.0/#/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.",
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.3.1/#/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\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:---------------------------|\n| `--vaadin-menu-bar-gap` |\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.3.1/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.3.1/#/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.3.1/#/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.3.1/#/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",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/menu-bar",
4
- "version": "25.3.0",
4
+ "version": "25.3.1",
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.3.0/#/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\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:---------------------------|\n| `--vaadin-menu-bar-gap` |\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.3.0/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.3.0/#/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.3.0/#/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.3.0/#/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.",
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.3.1/#/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\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:---------------------------|\n| `--vaadin-menu-bar-gap` |\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.3.1/#/elements/vaadin-button).\n- `<vaadin-menu-bar-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.3.1/#/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.3.1/#/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.3.1/#/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
  {