@vaadin/menu-bar 25.3.0-rc1 → 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 +15 -15
- package/src/styles/vaadin-menu-bar-base-styles.js +6 -0
- package/src/vaadin-menu-bar-mixin.js +165 -41
- package/web-types.json +2 -2
- package/web-types.lit.json +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/menu-bar",
|
|
3
|
-
"version": "25.3.
|
|
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.
|
|
39
|
-
"@vaadin/button": "25.3.
|
|
40
|
-
"@vaadin/component-base": "25.3.
|
|
41
|
-
"@vaadin/context-menu": "25.3.
|
|
42
|
-
"@vaadin/item": "25.3.
|
|
43
|
-
"@vaadin/list-box": "25.3.
|
|
44
|
-
"@vaadin/overlay": "25.3.
|
|
45
|
-
"@vaadin/vaadin-themable-mixin": "25.3.
|
|
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.
|
|
50
|
-
"@vaadin/chai-plugins": "25.3.
|
|
51
|
-
"@vaadin/icon": "25.3.
|
|
52
|
-
"@vaadin/test-runner-commands": "25.3.
|
|
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.
|
|
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": "
|
|
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
|
-
/**
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
this._hasOverflow = true;
|
|
500
|
-
|
|
501
|
-
const isRTL = this.__isRTL;
|
|
502
|
-
const containerLeft = container.offsetLeft;
|
|
503
|
-
|
|
504
|
-
const remaining = [...buttons];
|
|
505
|
-
while (remaining.length) {
|
|
506
|
-
const lastButton = remaining[remaining.length - 1];
|
|
507
|
-
const btnLeft = lastButton.offsetLeft - containerLeft;
|
|
508
|
-
|
|
509
|
-
// If this button isn't overflowing, then the rest aren't either
|
|
510
|
-
if (
|
|
511
|
-
(!isRTL && btnLeft + lastButton.offsetWidth < container.offsetWidth - overflow.offsetWidth) ||
|
|
512
|
-
(isRTL && btnLeft >= overflow.offsetWidth)
|
|
513
|
-
) {
|
|
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
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|
package/web-types.lit.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
{
|