@vaadin/menu-bar 24.8.0-alpha9 → 25.0.0-alpha2
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/README.md +0 -23
- package/package.json +16 -19
- package/src/vaadin-menu-bar-button.js +19 -16
- package/src/vaadin-menu-bar-item.js +17 -12
- package/src/vaadin-menu-bar-list-box.d.ts +1 -2
- package/src/vaadin-menu-bar-list-box.js +27 -23
- package/src/vaadin-menu-bar-mixin.d.ts +2 -5
- package/src/vaadin-menu-bar-mixin.js +143 -153
- package/src/vaadin-menu-bar-overlay.js +11 -9
- package/src/vaadin-menu-bar-submenu.js +18 -21
- package/src/vaadin-menu-bar.js +28 -24
- package/web-types.json +2 -2
- package/web-types.lit.json +2 -2
- package/src/vaadin-lit-menu-bar-button.js +0 -67
- package/src/vaadin-lit-menu-bar-item.js +0 -62
- package/src/vaadin-lit-menu-bar-list-box.js +0 -87
- package/src/vaadin-lit-menu-bar-overlay.js +0 -49
- package/src/vaadin-lit-menu-bar-submenu.js +0 -57
- package/src/vaadin-lit-menu-bar.js +0 -83
- package/theme/lumo/vaadin-lit-menu-bar.d.ts +0 -6
- package/theme/lumo/vaadin-lit-menu-bar.js +0 -6
- package/theme/material/vaadin-lit-menu-bar.d.ts +0 -6
- package/theme/material/vaadin-lit-menu-bar.js +0 -6
- package/theme/material/vaadin-menu-bar-button-styles.d.ts +0 -1
- package/theme/material/vaadin-menu-bar-button-styles.js +0 -111
- package/theme/material/vaadin-menu-bar-button.d.ts +0 -2
- package/theme/material/vaadin-menu-bar-button.js +0 -2
- package/theme/material/vaadin-menu-bar-item-styles.d.ts +0 -1
- package/theme/material/vaadin-menu-bar-item-styles.js +0 -23
- package/theme/material/vaadin-menu-bar-list-box-styles.d.ts +0 -1
- package/theme/material/vaadin-menu-bar-list-box-styles.js +0 -5
- package/theme/material/vaadin-menu-bar-overlay-styles.d.ts +0 -1
- package/theme/material/vaadin-menu-bar-overlay-styles.js +0 -13
- package/theme/material/vaadin-menu-bar-styles.d.ts +0 -1
- package/theme/material/vaadin-menu-bar-styles.js +0 -21
- package/theme/material/vaadin-menu-bar.d.ts +0 -6
- package/theme/material/vaadin-menu-bar.js +0 -6
- package/vaadin-lit-menu-bar.d.ts +0 -1
- package/vaadin-lit-menu-bar.js +0 -2
|
@@ -3,22 +3,58 @@
|
|
|
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
|
-
import { isElementFocused,
|
|
11
|
+
import { isElementFocused, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
9
12
|
import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';
|
|
10
|
-
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
11
13
|
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
12
14
|
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
13
15
|
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Custom Lit directive for rendering item components
|
|
19
|
+
* inspired by the `flowComponentDirective` logic.
|
|
20
|
+
*/
|
|
21
|
+
class ItemComponentDirective extends Directive {
|
|
22
|
+
update(part, [{ component, text }]) {
|
|
23
|
+
const { parentNode, startNode } = part;
|
|
24
|
+
|
|
25
|
+
const newNode = component || (text ? document.createTextNode(text) : null);
|
|
26
|
+
const oldNode = this.getOldNode(part);
|
|
27
|
+
|
|
28
|
+
if (oldNode === newNode) {
|
|
29
|
+
return noChange;
|
|
30
|
+
} else if (oldNode && newNode) {
|
|
31
|
+
parentNode.replaceChild(newNode, oldNode);
|
|
32
|
+
} else if (oldNode) {
|
|
33
|
+
parentNode.removeChild(oldNode);
|
|
34
|
+
} else if (newNode) {
|
|
35
|
+
startNode.after(newNode);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return noChange;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getOldNode(part) {
|
|
42
|
+
const { startNode, endNode } = part;
|
|
43
|
+
if (startNode.nextSibling === endNode) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return startNode.nextSibling;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const componentDirective = directive(ItemComponentDirective);
|
|
51
|
+
|
|
15
52
|
const DEFAULT_I18N = {
|
|
16
53
|
moreOptions: 'More options',
|
|
17
54
|
};
|
|
18
55
|
|
|
19
56
|
/**
|
|
20
57
|
* @polymerMixin
|
|
21
|
-
* @mixes ControllerMixin
|
|
22
58
|
* @mixes DisabledMixin
|
|
23
59
|
* @mixes FocusMixin
|
|
24
60
|
* @mixes I18nMixin
|
|
@@ -28,7 +64,7 @@ const DEFAULT_I18N = {
|
|
|
28
64
|
export const MenuBarMixin = (superClass) =>
|
|
29
65
|
class MenuBarMixinClass extends I18nMixin(
|
|
30
66
|
DEFAULT_I18N,
|
|
31
|
-
KeyboardDirectionMixin(ResizeMixin(FocusMixin(DisabledMixin(
|
|
67
|
+
KeyboardDirectionMixin(ResizeMixin(FocusMixin(DisabledMixin(superClass)))),
|
|
32
68
|
) {
|
|
33
69
|
static get properties() {
|
|
34
70
|
return {
|
|
@@ -112,6 +148,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
112
148
|
*/
|
|
113
149
|
items: {
|
|
114
150
|
type: Array,
|
|
151
|
+
sync: true,
|
|
115
152
|
value: () => [],
|
|
116
153
|
},
|
|
117
154
|
|
|
@@ -140,6 +177,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
140
177
|
*/
|
|
141
178
|
reverseCollapse: {
|
|
142
179
|
type: Boolean,
|
|
180
|
+
sync: true,
|
|
143
181
|
},
|
|
144
182
|
|
|
145
183
|
/**
|
|
@@ -149,6 +187,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
149
187
|
*/
|
|
150
188
|
tabNavigation: {
|
|
151
189
|
type: Boolean,
|
|
190
|
+
sync: true,
|
|
152
191
|
},
|
|
153
192
|
|
|
154
193
|
/**
|
|
@@ -169,6 +208,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
169
208
|
/** @protected */
|
|
170
209
|
_container: {
|
|
171
210
|
type: Object,
|
|
211
|
+
sync: true,
|
|
172
212
|
},
|
|
173
213
|
};
|
|
174
214
|
}
|
|
@@ -178,7 +218,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
178
218
|
'_themeChanged(_theme, _overflow, _container)',
|
|
179
219
|
'__hasOverflowChanged(_hasOverflow, _overflow)',
|
|
180
220
|
'__i18nChanged(__effectiveI18n, _overflow)',
|
|
181
|
-
'
|
|
221
|
+
'__updateButtons(items, disabled, _overflow, _container)',
|
|
182
222
|
'_reverseCollapseChanged(reverseCollapse, _overflow, _container)',
|
|
183
223
|
'_tabNavigationChanged(tabNavigation, _overflow, _container)',
|
|
184
224
|
];
|
|
@@ -235,6 +275,17 @@ export const MenuBarMixin = (superClass) =>
|
|
|
235
275
|
return false;
|
|
236
276
|
}
|
|
237
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Override getter from `KeyboardDirectionMixin`.
|
|
280
|
+
*
|
|
281
|
+
* @return {boolean}
|
|
282
|
+
* @protected
|
|
283
|
+
* @override
|
|
284
|
+
*/
|
|
285
|
+
get _tabNavigation() {
|
|
286
|
+
return this.tabNavigation;
|
|
287
|
+
}
|
|
288
|
+
|
|
238
289
|
/**
|
|
239
290
|
* Override getter from `ResizeMixin` to observe parent.
|
|
240
291
|
*
|
|
@@ -273,8 +324,11 @@ export const MenuBarMixin = (superClass) =>
|
|
|
273
324
|
dots.innerHTML = '·'.repeat(3);
|
|
274
325
|
btn.appendChild(dots);
|
|
275
326
|
|
|
327
|
+
btn.setAttribute('aria-haspopup', 'true');
|
|
328
|
+
btn.setAttribute('aria-expanded', 'false');
|
|
329
|
+
btn.setAttribute('role', this.tabNavigation ? 'button' : 'menuitem');
|
|
330
|
+
|
|
276
331
|
this._overflow = btn;
|
|
277
|
-
this._initButtonAttrs(btn);
|
|
278
332
|
},
|
|
279
333
|
});
|
|
280
334
|
this.addController(this._overflowController);
|
|
@@ -329,23 +383,6 @@ export const MenuBarMixin = (superClass) =>
|
|
|
329
383
|
this.__detectOverflow();
|
|
330
384
|
}
|
|
331
385
|
|
|
332
|
-
/**
|
|
333
|
-
* Override method inherited from `DisabledMixin`
|
|
334
|
-
* to update the `disabled` property for the buttons
|
|
335
|
-
* whenever the property changes on the menu bar.
|
|
336
|
-
*
|
|
337
|
-
* @param {boolean} newValue the new disabled value
|
|
338
|
-
* @param {boolean} oldValue the previous disabled value
|
|
339
|
-
* @override
|
|
340
|
-
* @protected
|
|
341
|
-
*/
|
|
342
|
-
_disabledChanged(newValue, oldValue) {
|
|
343
|
-
super._disabledChanged(newValue, oldValue);
|
|
344
|
-
if (oldValue !== newValue) {
|
|
345
|
-
this.__updateButtonsDisabled(newValue);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
386
|
/**
|
|
350
387
|
* A callback for the `_theme` property observer.
|
|
351
388
|
* It propagates the host theme to the buttons and the sub menu.
|
|
@@ -355,14 +392,16 @@ export const MenuBarMixin = (superClass) =>
|
|
|
355
392
|
*/
|
|
356
393
|
_themeChanged(theme, overflow, container) {
|
|
357
394
|
if (overflow && container) {
|
|
358
|
-
this.
|
|
395
|
+
this.__renderButtons(this.items);
|
|
359
396
|
this.__detectOverflow();
|
|
360
|
-
}
|
|
361
397
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
398
|
+
if (theme) {
|
|
399
|
+
overflow.setAttribute('theme', theme);
|
|
400
|
+
this._subMenu.setAttribute('theme', theme);
|
|
401
|
+
} else {
|
|
402
|
+
overflow.removeAttribute('theme');
|
|
403
|
+
this._subMenu.removeAttribute('theme');
|
|
404
|
+
}
|
|
366
405
|
}
|
|
367
406
|
}
|
|
368
407
|
|
|
@@ -402,7 +441,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
402
441
|
}
|
|
403
442
|
|
|
404
443
|
/** @private */
|
|
405
|
-
|
|
444
|
+
__updateButtons(items, disabled, overflow, container) {
|
|
406
445
|
if (!overflow || !container) {
|
|
407
446
|
return;
|
|
408
447
|
}
|
|
@@ -410,11 +449,24 @@ export const MenuBarMixin = (superClass) =>
|
|
|
410
449
|
if (items !== this._oldItems) {
|
|
411
450
|
this._oldItems = items;
|
|
412
451
|
this.__renderButtons(items);
|
|
452
|
+
this.__detectOverflow();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (disabled !== this._oldDisabled) {
|
|
456
|
+
this._oldDisabled = disabled;
|
|
457
|
+
this.__renderButtons(items);
|
|
458
|
+
overflow.toggleAttribute('disabled', disabled);
|
|
413
459
|
}
|
|
414
460
|
|
|
415
461
|
const subMenu = this._subMenu;
|
|
416
462
|
if (subMenu && subMenu.opened) {
|
|
417
|
-
subMenu.
|
|
463
|
+
const button = subMenu._overlayElement.positionTarget;
|
|
464
|
+
|
|
465
|
+
// Close sub-menu if the corresponding button is no longer in the DOM,
|
|
466
|
+
// or if the item on it has been changed to no longer have children.
|
|
467
|
+
if (!button.isConnected || !Array.isArray(button.item.children) || button.item.children.length === 0) {
|
|
468
|
+
subMenu.close();
|
|
469
|
+
}
|
|
418
470
|
}
|
|
419
471
|
}
|
|
420
472
|
|
|
@@ -438,9 +490,9 @@ export const MenuBarMixin = (superClass) =>
|
|
|
438
490
|
/** @private */
|
|
439
491
|
__restoreButtons(buttons) {
|
|
440
492
|
buttons.forEach((button) => {
|
|
441
|
-
button.disabled = (button.item && button.item.disabled) || this.disabled;
|
|
442
493
|
button.style.visibility = '';
|
|
443
494
|
button.style.position = '';
|
|
495
|
+
button.style.width = '';
|
|
444
496
|
|
|
445
497
|
// Teleport item component back from "overflow" sub-menu
|
|
446
498
|
const item = button.item && button.item.component;
|
|
@@ -460,14 +512,6 @@ export const MenuBarMixin = (superClass) =>
|
|
|
460
512
|
item.removeAttribute('tabindex');
|
|
461
513
|
}
|
|
462
514
|
|
|
463
|
-
/** @private */
|
|
464
|
-
__updateButtonsDisabled(disabled) {
|
|
465
|
-
this._buttons.forEach((btn) => {
|
|
466
|
-
// Disable the button if the entire menu-bar is disabled or the item alone is disabled
|
|
467
|
-
btn.disabled = disabled || (btn.item && btn.item.disabled);
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
|
|
471
515
|
/** @private */
|
|
472
516
|
__updateOverflow(items) {
|
|
473
517
|
this._overflow.item = { children: items };
|
|
@@ -501,7 +545,6 @@ export const MenuBarMixin = (superClass) =>
|
|
|
501
545
|
|
|
502
546
|
// Save width for buttons with component
|
|
503
547
|
btn.style.width = getComputedStyle(btn).width;
|
|
504
|
-
btn.disabled = true;
|
|
505
548
|
btn.style.visibility = 'hidden';
|
|
506
549
|
btn.style.position = 'absolute';
|
|
507
550
|
}
|
|
@@ -550,66 +593,17 @@ export const MenuBarMixin = (superClass) =>
|
|
|
550
593
|
});
|
|
551
594
|
}
|
|
552
595
|
|
|
553
|
-
/** @
|
|
554
|
-
|
|
555
|
-
this._buttons.forEach((button) => {
|
|
556
|
-
if (button !== this._overflow) {
|
|
557
|
-
this.removeChild(button);
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/** @protected */
|
|
563
|
-
_initButton(item) {
|
|
564
|
-
const button = document.createElement('vaadin-menu-bar-button');
|
|
565
|
-
|
|
566
|
-
const itemCopy = { ...item };
|
|
567
|
-
button.item = itemCopy;
|
|
568
|
-
|
|
569
|
-
if (item.component) {
|
|
570
|
-
const component = this.__getComponent(itemCopy);
|
|
571
|
-
itemCopy.component = component;
|
|
572
|
-
// Save item for overflow menu
|
|
573
|
-
component.item = itemCopy;
|
|
574
|
-
button.appendChild(component);
|
|
575
|
-
} else if (item.text) {
|
|
576
|
-
button.textContent = item.text;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (item.className) {
|
|
580
|
-
button.className = item.className;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
button.disabled = item.disabled;
|
|
584
|
-
|
|
585
|
-
return button;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/** @protected */
|
|
589
|
-
_initButtonAttrs(button) {
|
|
590
|
-
button.setAttribute('role', this.tabNavigation ? 'button' : 'menuitem');
|
|
591
|
-
|
|
592
|
-
if (button === this._overflow || (button.item && button.item.children)) {
|
|
593
|
-
button.setAttribute('aria-haspopup', 'true');
|
|
594
|
-
button.setAttribute('aria-expanded', 'false');
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/** @protected */
|
|
599
|
-
_setButtonTheme(btn, hostTheme) {
|
|
596
|
+
/** @private */
|
|
597
|
+
__getButtonTheme(item, hostTheme) {
|
|
600
598
|
let theme = hostTheme;
|
|
601
599
|
|
|
602
600
|
// Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
|
|
603
|
-
const itemTheme =
|
|
601
|
+
const itemTheme = item && item.theme;
|
|
604
602
|
if (itemTheme != null) {
|
|
605
603
|
theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
|
|
606
604
|
}
|
|
607
605
|
|
|
608
|
-
|
|
609
|
-
btn.setAttribute('theme', theme);
|
|
610
|
-
} else {
|
|
611
|
-
btn.removeAttribute('theme');
|
|
612
|
-
}
|
|
606
|
+
return theme;
|
|
613
607
|
}
|
|
614
608
|
|
|
615
609
|
/** @private */
|
|
@@ -634,21 +628,47 @@ export const MenuBarMixin = (superClass) =>
|
|
|
634
628
|
|
|
635
629
|
/** @private */
|
|
636
630
|
__renderButtons(items = []) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
631
|
+
render(
|
|
632
|
+
html`
|
|
633
|
+
${items.map((item) => {
|
|
634
|
+
const itemCopy = { ...item };
|
|
635
|
+
const hasChildren = Boolean(item && item.children);
|
|
636
|
+
|
|
637
|
+
if (itemCopy.component) {
|
|
638
|
+
const component = this.__getComponent(itemCopy);
|
|
639
|
+
itemCopy.component = component;
|
|
640
|
+
component.item = itemCopy;
|
|
641
|
+
}
|
|
643
642
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
643
|
+
return html`
|
|
644
|
+
<vaadin-menu-bar-button
|
|
645
|
+
.item="${itemCopy}"
|
|
646
|
+
.disabled="${this.disabled || item.disabled}"
|
|
647
|
+
role="${this.tabNavigation ? 'button' : 'menuitem'}"
|
|
648
|
+
aria-haspopup="${ifDefined(hasChildren ? 'true' : nothing)}"
|
|
649
|
+
aria-expanded="${ifDefined(hasChildren ? 'false' : nothing)}"
|
|
650
|
+
class="${ifDefined(item.className || nothing)}"
|
|
651
|
+
theme="${ifDefined(this.__getButtonTheme(item, this._theme) || nothing)}"
|
|
652
|
+
@click="${this.__onRootButtonClick}"
|
|
653
|
+
>${componentDirective(itemCopy)}</vaadin-menu-bar-button
|
|
654
|
+
>
|
|
655
|
+
`;
|
|
656
|
+
})}
|
|
657
|
+
`,
|
|
658
|
+
this,
|
|
659
|
+
{ renderBefore: this._overflow },
|
|
660
|
+
);
|
|
661
|
+
}
|
|
650
662
|
|
|
651
|
-
|
|
663
|
+
/** @private */
|
|
664
|
+
__onRootButtonClick(event) {
|
|
665
|
+
const button = event.target;
|
|
666
|
+
// Propagate click event from button to the item component if it was outside
|
|
667
|
+
// it e.g. by calling `click()` on the button (used by the Flow counterpart).
|
|
668
|
+
if (button.item && button.item.component && !event.composedPath().includes(button.item.component)) {
|
|
669
|
+
event.stopPropagation();
|
|
670
|
+
button.item.component.click();
|
|
671
|
+
}
|
|
652
672
|
}
|
|
653
673
|
|
|
654
674
|
/**
|
|
@@ -759,12 +779,7 @@ export const MenuBarMixin = (superClass) =>
|
|
|
759
779
|
*/
|
|
760
780
|
_setFocused(focused) {
|
|
761
781
|
if (focused) {
|
|
762
|
-
|
|
763
|
-
if (this.tabNavigation) {
|
|
764
|
-
// Switch submenu on menu button Tab / Shift Tab
|
|
765
|
-
target = this.querySelector('[focused]');
|
|
766
|
-
this.__switchSubMenu(target);
|
|
767
|
-
}
|
|
782
|
+
const target = this.tabNavigation ? this.querySelector('[focused]') : this.querySelector('[tabindex="0"]');
|
|
768
783
|
if (target) {
|
|
769
784
|
this._buttons.forEach((btn) => {
|
|
770
785
|
this._setTabindex(btn, btn === target);
|
|
@@ -883,30 +898,15 @@ export const MenuBarMixin = (superClass) =>
|
|
|
883
898
|
if (e.keyCode === 38 && item === list.items[0]) {
|
|
884
899
|
this._close(true);
|
|
885
900
|
}
|
|
886
|
-
// ArrowLeft, or ArrowRight on non-parent submenu item
|
|
901
|
+
// ArrowLeft, or ArrowRight on non-parent submenu item,
|
|
887
902
|
if (e.keyCode === 37 || (e.keyCode === 39 && !item._item.children)) {
|
|
888
903
|
// Prevent ArrowLeft from being handled in context-menu
|
|
889
904
|
e.stopImmediatePropagation();
|
|
890
905
|
this._onKeyDown(e);
|
|
891
|
-
} else if (e.keyCode === 9 && this.tabNavigation) {
|
|
892
|
-
// Switch opened submenu on submenu item Tab / Shift Tab
|
|
893
|
-
const items = this._getItems() || [];
|
|
894
|
-
const currentIdx = items.indexOf(this.focused);
|
|
895
|
-
const increment = e.shiftKey ? -1 : 1;
|
|
896
|
-
let idx = currentIdx + increment;
|
|
897
|
-
idx = this._getAvailableIndex(items, idx, increment, (item) => !isElementHidden(item));
|
|
898
|
-
this.__switchSubMenu(items[idx]);
|
|
899
906
|
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
907
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
const wasExpanded = this._expandedButton != null && this._expandedButton !== target;
|
|
906
|
-
if (wasExpanded) {
|
|
907
|
-
this._close();
|
|
908
|
-
if (target.item && target.item.children) {
|
|
909
|
-
this.__openSubMenu(target, true, { keepFocus: true });
|
|
908
|
+
if (e.key === 'Tab' && this.tabNavigation) {
|
|
909
|
+
this._onKeyDown(e);
|
|
910
910
|
}
|
|
911
911
|
}
|
|
912
912
|
}
|
|
@@ -951,31 +951,21 @@ export const MenuBarMixin = (superClass) =>
|
|
|
951
951
|
const overlay = subMenu._overlayElement;
|
|
952
952
|
overlay.noVerticalOverlap = true;
|
|
953
953
|
|
|
954
|
-
this.
|
|
955
|
-
|
|
956
|
-
requestAnimationFrame(() => {
|
|
957
|
-
// After changing items, buttons are recreated so the old button is
|
|
958
|
-
// no longer in the DOM. Reset position target to null to prevent
|
|
959
|
-
// overlay from closing due to target width / height equal to 0.
|
|
960
|
-
if (overlay.positionTarget && !overlay.positionTarget.isConnected) {
|
|
961
|
-
overlay.positionTarget = null;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
button.dispatchEvent(
|
|
965
|
-
new CustomEvent('opensubmenu', {
|
|
966
|
-
detail: {
|
|
967
|
-
children: items,
|
|
968
|
-
},
|
|
969
|
-
}),
|
|
970
|
-
);
|
|
971
|
-
this._hideTooltip(true);
|
|
972
|
-
|
|
973
|
-
this._setExpanded(button, true);
|
|
954
|
+
this._hideTooltip(true);
|
|
974
955
|
|
|
975
|
-
|
|
976
|
-
|
|
956
|
+
this._expandedButton = button;
|
|
957
|
+
this._setExpanded(button, true);
|
|
977
958
|
|
|
978
959
|
this.style.pointerEvents = 'auto';
|
|
960
|
+
overlay.positionTarget = button;
|
|
961
|
+
|
|
962
|
+
button.dispatchEvent(
|
|
963
|
+
new CustomEvent('opensubmenu', {
|
|
964
|
+
detail: {
|
|
965
|
+
children: items,
|
|
966
|
+
},
|
|
967
|
+
}),
|
|
968
|
+
);
|
|
979
969
|
|
|
980
970
|
overlay.addEventListener(
|
|
981
971
|
'vaadin-overlay-open',
|
|
@@ -3,18 +3,15 @@
|
|
|
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,
|
|
6
|
+
import { html, LitElement } from 'lit';
|
|
7
7
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
8
8
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
9
10
|
import { MenuOverlayMixin } from '@vaadin/context-menu/src/vaadin-menu-overlay-mixin.js';
|
|
10
11
|
import { styles } from '@vaadin/context-menu/src/vaadin-menu-overlay-styles.js';
|
|
11
12
|
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
|
|
12
13
|
import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
registerStyles('vaadin-menu-bar-overlay', [overlayStyles, styles], {
|
|
16
|
-
moduleId: 'vaadin-menu-bar-overlay-styles',
|
|
17
|
-
});
|
|
14
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
17
|
* An element used internally by `<vaadin-menu-bar>`. Not intended to be used separately.
|
|
@@ -27,14 +24,19 @@ registerStyles('vaadin-menu-bar-overlay', [overlayStyles, styles], {
|
|
|
27
24
|
* @mixes ThemableMixin
|
|
28
25
|
* @protected
|
|
29
26
|
*/
|
|
30
|
-
export class MenuBarOverlay extends MenuOverlayMixin(OverlayMixin(DirMixin(ThemableMixin(
|
|
27
|
+
export class MenuBarOverlay extends MenuOverlayMixin(OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LitElement))))) {
|
|
31
28
|
static get is() {
|
|
32
29
|
return 'vaadin-menu-bar-overlay';
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
static get
|
|
32
|
+
static get styles() {
|
|
33
|
+
return [overlayStyles, styles];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @protected */
|
|
37
|
+
render() {
|
|
36
38
|
return html`
|
|
37
|
-
<div id="backdrop" part="backdrop" hidden
|
|
39
|
+
<div id="backdrop" part="backdrop" ?hidden="${!this.withBackdrop}"></div>
|
|
38
40
|
<div part="overlay" id="overlay" tabindex="0">
|
|
39
41
|
<div part="content" id="content">
|
|
40
42
|
<slot></slot>
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
import './vaadin-menu-bar-item.js';
|
|
7
7
|
import './vaadin-menu-bar-list-box.js';
|
|
8
8
|
import './vaadin-menu-bar-overlay.js';
|
|
9
|
-
import { html,
|
|
10
|
-
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
9
|
+
import { css, html, LitElement } from 'lit';
|
|
11
10
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
11
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
12
12
|
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
13
13
|
import { SubMenuMixin } from './vaadin-menu-bar-submenu-mixin.js';
|
|
14
14
|
|
|
@@ -17,41 +17,38 @@ import { SubMenuMixin } from './vaadin-menu-bar-submenu-mixin.js';
|
|
|
17
17
|
*
|
|
18
18
|
* @customElement
|
|
19
19
|
* @extends HTMLElement
|
|
20
|
-
* @mixes ControllerMixin
|
|
21
20
|
* @mixes SubMenuMixin
|
|
22
21
|
* @mixes ThemePropertyMixin
|
|
23
22
|
* @protected
|
|
24
23
|
*/
|
|
25
|
-
class MenuBarSubmenu extends SubMenuMixin(
|
|
24
|
+
class MenuBarSubmenu extends SubMenuMixin(ThemePropertyMixin(PolylitMixin(LitElement))) {
|
|
26
25
|
static get is() {
|
|
27
26
|
return 'vaadin-menu-bar-submenu';
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
static get
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
:
|
|
34
|
-
|
|
35
|
-
}
|
|
29
|
+
static get styles() {
|
|
30
|
+
return css`
|
|
31
|
+
:host {
|
|
32
|
+
display: block;
|
|
33
|
+
}
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</style>
|
|
41
|
-
|
|
42
|
-
<slot id="slot"></slot>
|
|
35
|
+
:host([hidden]) {
|
|
36
|
+
display: none !important;
|
|
37
|
+
}
|
|
43
38
|
`;
|
|
44
39
|
}
|
|
45
40
|
|
|
41
|
+
/** @protected */
|
|
42
|
+
render() {
|
|
43
|
+
return html`<slot id="slot"></slot>`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
46
|
/**
|
|
47
|
-
* @param {DocumentFragment} dom
|
|
48
|
-
* @return {ShadowRoot}
|
|
49
47
|
* @protected
|
|
50
48
|
* @override
|
|
51
49
|
*/
|
|
52
|
-
|
|
53
|
-
const root =
|
|
54
|
-
root.appendChild(dom);
|
|
50
|
+
createRenderRoot() {
|
|
51
|
+
const root = super.createRenderRoot();
|
|
55
52
|
root.appendChild(this._overlayElement);
|
|
56
53
|
return root;
|
|
57
54
|
}
|
package/src/vaadin-menu-bar.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import './vaadin-menu-bar-submenu.js';
|
|
7
7
|
import './vaadin-menu-bar-button.js';
|
|
8
|
-
import { html,
|
|
8
|
+
import { css, html, LitElement } from 'lit';
|
|
9
9
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
10
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
11
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
11
12
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
12
13
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
13
14
|
import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
|
|
@@ -74,41 +75,44 @@ import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
|
|
|
74
75
|
* @mixes MenuBarMixin
|
|
75
76
|
* @mixes ThemableMixin
|
|
76
77
|
*/
|
|
77
|
-
class MenuBar extends MenuBarMixin(ElementMixin(ThemableMixin(
|
|
78
|
-
static get
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
:host {
|
|
82
|
-
display: block;
|
|
83
|
-
}
|
|
78
|
+
class MenuBar extends MenuBarMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
|
|
79
|
+
static get is() {
|
|
80
|
+
return 'vaadin-menu-bar';
|
|
81
|
+
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
static get styles() {
|
|
84
|
+
return css`
|
|
85
|
+
:host {
|
|
86
|
+
display: block;
|
|
87
|
+
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
:host([hidden]) {
|
|
90
|
+
display: none !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[part='container'] {
|
|
94
|
+
position: relative;
|
|
95
|
+
display: flex;
|
|
96
|
+
width: 100%;
|
|
97
|
+
flex-wrap: nowrap;
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
97
102
|
|
|
103
|
+
/** @protected */
|
|
104
|
+
render() {
|
|
105
|
+
return html`
|
|
98
106
|
<div part="container">
|
|
99
107
|
<slot></slot>
|
|
100
108
|
<slot name="overflow"></slot>
|
|
101
109
|
</div>
|
|
102
|
-
<vaadin-menu-bar-submenu is-root
|
|
110
|
+
<vaadin-menu-bar-submenu is-root .overlayClass="${this.overlayClass}"></vaadin-menu-bar-submenu>
|
|
103
111
|
|
|
104
112
|
<slot name="tooltip"></slot>
|
|
105
113
|
`;
|
|
106
114
|
}
|
|
107
115
|
|
|
108
|
-
static get is() {
|
|
109
|
-
return 'vaadin-menu-bar';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
116
|
/** @protected */
|
|
113
117
|
ready() {
|
|
114
118
|
super.ready();
|