@vaadin/menu-bar 23.3.3 → 24.0.0-alpha10

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": "23.3.3",
3
+ "version": "24.0.0-alpha10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -37,16 +37,16 @@
37
37
  "dependencies": {
38
38
  "@open-wc/dedupe-mixin": "^1.3.0",
39
39
  "@polymer/polymer": "^3.0.0",
40
- "@vaadin/button": "~23.3.3",
41
- "@vaadin/component-base": "~23.3.3",
42
- "@vaadin/context-menu": "~23.3.3",
43
- "@vaadin/vaadin-lumo-styles": "~23.3.3",
44
- "@vaadin/vaadin-material-styles": "~23.3.3",
45
- "@vaadin/vaadin-themable-mixin": "~23.3.3"
40
+ "@vaadin/button": "24.0.0-alpha10",
41
+ "@vaadin/component-base": "24.0.0-alpha10",
42
+ "@vaadin/context-menu": "24.0.0-alpha10",
43
+ "@vaadin/vaadin-lumo-styles": "24.0.0-alpha10",
44
+ "@vaadin/vaadin-material-styles": "24.0.0-alpha10",
45
+ "@vaadin/vaadin-themable-mixin": "24.0.0-alpha10"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@esm-bundle/chai": "^4.3.4",
49
- "@vaadin/icon": "~23.3.3",
49
+ "@vaadin/icon": "24.0.0-alpha10",
50
50
  "@vaadin/testing-helpers": "^0.3.2",
51
51
  "sinon": "^13.0.2"
52
52
  },
@@ -54,5 +54,5 @@
54
54
  "web-types.json",
55
55
  "web-types.lit.json"
56
56
  ],
57
- "gitHead": "1529ed623e053d28a3c1c66af55ebe402743ddd0"
57
+ "gitHead": "2e04534d8b47bcd216f89b5f849bafef1a73b174"
58
58
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2019 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { Button } from '@vaadin/button/src/vaadin-button.js';
@@ -9,6 +9,14 @@ import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themab
9
9
  registerStyles(
10
10
  'vaadin-menu-bar-button',
11
11
  css`
12
+ :host {
13
+ flex-shrink: 0;
14
+ }
15
+
16
+ :host([slot='overflow']) {
17
+ margin-inline-end: 0;
18
+ }
19
+
12
20
  [part='label'] ::slotted(vaadin-context-menu-item) {
13
21
  position: relative;
14
22
  z-index: 1;
@@ -1,31 +1,37 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2019 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
7
+ import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
7
8
  import type { FocusMixinClass } from '@vaadin/component-base/src/focus-mixin.js';
8
9
  import type { KeyboardDirectionMixinClass } from '@vaadin/component-base/src/keyboard-direction-mixin.js';
9
10
  import type { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mixin.js';
11
+ import type { ResizeMixinClass } from '@vaadin/component-base/src/resize-mixin.js';
10
12
 
11
- export declare function InteractionsMixin<T extends Constructor<HTMLElement>>(
13
+ export declare function MenuBarMixin<T extends Constructor<HTMLElement>>(
12
14
  base: T,
13
- ): Constructor<FocusMixinClass> &
14
- Constructor<InteractionsMixinClass> &
15
+ ): Constructor<ControllerMixinClass> &
16
+ Constructor<FocusMixinClass> &
15
17
  Constructor<KeyboardDirectionMixinClass> &
16
18
  Constructor<KeyboardMixinClass> &
19
+ Constructor<MenuBarMixinClass> &
20
+ Constructor<ResizeMixinClass> &
17
21
  T;
18
22
 
19
- export declare class InteractionsMixinClass {
23
+ export declare class MenuBarMixinClass {
20
24
  /**
21
25
  * If true, the submenu will open on hover (mouseover) instead of click.
22
26
  * @attr {boolean} open-on-hover
23
27
  */
24
28
  openOnHover: boolean | null | undefined;
25
29
 
26
- /**
27
- * @deprecated Since Vaadin 23, `notifyResize()` is deprecated. The component uses a
28
- * ResizeObserver internally and doesn't need to be explicitly notified of resizes.
29
- */
30
- notifyResize(): void;
30
+ protected readonly _buttons: HTMLElement[];
31
+
32
+ protected readonly _container: HTMLElement;
33
+
34
+ protected readonly _overflow: HTMLElement;
35
+
36
+ protected _hasOverflow: boolean;
31
37
  }
@@ -1,19 +1,24 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2019 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
6
7
  import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
7
- import { isKeyboardActive } from '@vaadin/component-base/src/focus-utils.js';
8
+ import { isElementFocused, isKeyboardActive } from '@vaadin/component-base/src/focus-utils.js';
8
9
  import { KeyboardDirectionMixin } from '@vaadin/component-base/src/keyboard-direction-mixin.js';
10
+ import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
11
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
9
12
 
10
13
  /**
11
14
  * @polymerMixin
15
+ * @mixes ControllerMixin
12
16
  * @mixes FocusMixin
13
- * @mixes KeyboardDirectionMixinClass
17
+ * @mixes KeyboardDirectionMixin
18
+ * @mixes ResizeMixin
14
19
  */
15
- export const InteractionsMixin = (superClass) =>
16
- class InteractionsMixin extends KeyboardDirectionMixin(FocusMixin(superClass)) {
20
+ export const MenuBarMixin = (superClass) =>
21
+ class MenuBarMixinClass extends KeyboardDirectionMixin(ResizeMixin(FocusMixin(ControllerMixin(superClass)))) {
17
22
  static get properties() {
18
23
  return {
19
24
  /**
@@ -23,22 +28,110 @@ export const InteractionsMixin = (superClass) =>
23
28
  openOnHover: {
24
29
  type: Boolean,
25
30
  },
31
+
32
+ /**
33
+ * @type {boolean}
34
+ * @protected
35
+ */
36
+ _hasOverflow: {
37
+ type: Boolean,
38
+ value: false,
39
+ },
40
+
41
+ /** @protected */
42
+ _overflow: {
43
+ type: Object,
44
+ },
45
+
46
+ /** @protected */
47
+ _container: {
48
+ type: Object,
49
+ },
26
50
  };
27
51
  }
28
52
 
53
+ static get observers() {
54
+ return [
55
+ '_itemsChanged(items, items.splices)',
56
+ '__hasOverflowChanged(_hasOverflow, _overflow)',
57
+ '__i18nChanged(i18n, _overflow)',
58
+ '_menuItemsChanged(items, _overflow, _container, items.splices)',
59
+ ];
60
+ }
61
+
29
62
  constructor() {
30
63
  super();
31
64
  this.__boundOnContextMenuKeydown = this.__onContextMenuKeydown.bind(this);
32
65
  }
33
66
 
34
- static get observers() {
35
- return ['_itemsChanged(items, items.splices)'];
67
+ /**
68
+ * Override getter from `KeyboardDirectionMixin`
69
+ * to use expanded button for arrow navigation
70
+ * when the sub-menu is opened and has focus.
71
+ *
72
+ * @return {Element | null}
73
+ * @protected
74
+ * @override
75
+ */
76
+ get focused() {
77
+ return (this._getItems() || []).find(isElementFocused) || this._expandedButton;
78
+ }
79
+
80
+ /**
81
+ * Override getter from `KeyboardDirectionMixin`.
82
+ *
83
+ * @return {boolean}
84
+ * @protected
85
+ * @override
86
+ */
87
+ get _vertical() {
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ * Override getter from `ResizeMixin` to observe parent.
93
+ *
94
+ * @protected
95
+ * @override
96
+ */
97
+ get _observeParent() {
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * @return {!Array<!HTMLElement>}
103
+ * @protected
104
+ */
105
+ get _buttons() {
106
+ return Array.from(this.querySelectorAll('vaadin-menu-bar-button'));
107
+ }
108
+
109
+ /** @private */
110
+ get _subMenu() {
111
+ return this.shadowRoot.querySelector('vaadin-menu-bar-submenu');
36
112
  }
37
113
 
38
114
  /** @protected */
39
115
  ready() {
40
116
  super.ready();
41
117
 
118
+ this.setAttribute('role', 'menubar');
119
+
120
+ this._overflowController = new SlotController(this, 'overflow', 'vaadin-menu-bar-button', {
121
+ initializer: (btn) => {
122
+ btn.setAttribute('hidden', '');
123
+
124
+ const dots = document.createElement('div');
125
+ dots.setAttribute('aria-hidden', 'true');
126
+ dots.textContent = '···';
127
+ btn.appendChild(dots);
128
+
129
+ this._overflow = btn;
130
+ this._initButtonAttrs(btn);
131
+ },
132
+ });
133
+ this.addController(this._overflowController);
134
+
42
135
  this.addEventListener('mousedown', () => this._hideTooltip());
43
136
  this.addEventListener('mouseleave', () => this._hideTooltip());
44
137
 
@@ -48,56 +141,257 @@ export const InteractionsMixin = (superClass) =>
48
141
  const overlay = this._subMenu.$.overlay;
49
142
  overlay.addEventListener('keydown', this.__boundOnContextMenuKeydown);
50
143
 
51
- const container = this._container;
144
+ const container = this.shadowRoot.querySelector('[part="container"]');
52
145
  container.addEventListener('click', this.__onButtonClick.bind(this));
53
146
  container.addEventListener('mouseover', (e) => this._onMouseOver(e));
147
+
148
+ this._container = container;
54
149
  }
55
150
 
56
151
  /**
57
- * Override getter from `KeyboardDirectionMixin`
58
- * to look for activeElement in shadow root, or
59
- * use the expanded button as a fallback.
152
+ * Override method inherited from `KeyboardDirectionMixin`
153
+ * to use the list of menu-bar buttons as items.
60
154
  *
61
- * @return {Element | null}
155
+ * @return {Element[]}
62
156
  * @protected
63
157
  * @override
64
158
  */
65
- get focused() {
66
- return this.shadowRoot.activeElement || this._expandedButton;
159
+ _getItems() {
160
+ return this._buttons;
67
161
  }
68
162
 
69
- /**
70
- * Override getter from `KeyboardDirectionMixin`.
71
- *
72
- * @return {boolean}
73
- * @protected
74
- * @override
75
- */
76
- get _vertical() {
77
- return false;
163
+ /** @protected */
164
+ disconnectedCallback() {
165
+ super.disconnectedCallback();
166
+ this._hideTooltip(true);
78
167
  }
79
168
 
80
169
  /**
81
- * Override method inherited from `KeyboardDirectionMixin`
82
- * to use the list of menu-bar buttons as items.
170
+ * Implement callback from `ResizeMixin` to update buttons
171
+ * and detect whether to show or hide the overflow button.
83
172
  *
84
- * @return {Element[]}
85
173
  * @protected
86
174
  * @override
87
175
  */
88
- _getItems() {
89
- return this._buttons;
176
+ _onResize() {
177
+ this.__detectOverflow();
178
+ }
179
+
180
+ /** @private */
181
+ __hasOverflowChanged(hasOverflow, overflow) {
182
+ if (overflow) {
183
+ overflow.toggleAttribute('hidden', !hasOverflow);
184
+ }
185
+ }
186
+
187
+ /** @private */
188
+ _menuItemsChanged(items, overflow, container) {
189
+ if (!overflow || !container) {
190
+ return;
191
+ }
192
+
193
+ if (items !== this._oldItems) {
194
+ this._oldItems = items;
195
+ this.__renderButtons(items);
196
+ }
197
+ }
198
+
199
+ /** @private */
200
+ __i18nChanged(i18n, overflow) {
201
+ if (overflow && i18n && i18n.moreOptions !== undefined) {
202
+ if (i18n.moreOptions) {
203
+ overflow.setAttribute('aria-label', i18n.moreOptions);
204
+ } else {
205
+ overflow.removeAttribute('aria-label');
206
+ }
207
+ }
208
+ }
209
+
210
+ /** @private */
211
+ __getOverflowCount(overflow) {
212
+ // We can't use optional chaining due to webpack 4
213
+ return (overflow.item && overflow.item.children && overflow.item.children.length) || 0;
214
+ }
215
+
216
+ /** @private */
217
+ __restoreButtons(buttons) {
218
+ for (let i = 0; i < buttons.length; i++) {
219
+ const btn = buttons[i];
220
+ btn.disabled = (btn.item && btn.item.disabled) || this.disabled;
221
+ btn.style.visibility = '';
222
+ btn.style.position = '';
223
+
224
+ // Teleport item component back from "overflow" sub-menu
225
+ const item = btn.item && btn.item.component;
226
+ if (item instanceof HTMLElement && item.getAttribute('role') === 'menuitem') {
227
+ btn.appendChild(item);
228
+ item.removeAttribute('role');
229
+ }
230
+ }
231
+ this.__updateOverflow([]);
232
+ }
233
+
234
+ /** @private */
235
+ __updateOverflow(items) {
236
+ this._overflow.item = { children: items };
237
+ this._hasOverflow = items.length > 0;
238
+ }
239
+
240
+ /** @private */
241
+ __setOverflowItems(buttons, overflow) {
242
+ const container = this._container;
243
+
244
+ if (container.offsetWidth < container.scrollWidth) {
245
+ this._hasOverflow = true;
246
+
247
+ const isRTL = this.__isRTL;
248
+
249
+ let i;
250
+ for (i = buttons.length; i > 0; i--) {
251
+ const btn = buttons[i - 1];
252
+ const btnStyle = getComputedStyle(btn);
253
+
254
+ // If this button isn't overflowing, then the rest aren't either
255
+ if (
256
+ (!isRTL && btn.offsetLeft + btn.offsetWidth < container.offsetWidth - overflow.offsetWidth) ||
257
+ (isRTL && btn.offsetLeft >= overflow.offsetWidth)
258
+ ) {
259
+ break;
260
+ }
261
+
262
+ btn.disabled = true;
263
+ btn.style.visibility = 'hidden';
264
+ btn.style.position = 'absolute';
265
+ // Save width for buttons with component
266
+ btn.style.width = btnStyle.width;
267
+ }
268
+ const items = buttons.filter((_, idx) => idx >= i).map((b) => b.item);
269
+ this.__updateOverflow(items);
270
+ }
90
271
  }
91
272
 
92
273
  /** @private */
93
- get __isRTL() {
94
- return this.getAttribute('dir') === 'rtl';
274
+ __detectOverflow() {
275
+ const overflow = this._overflow;
276
+ const buttons = this._buttons.filter((btn) => btn !== overflow);
277
+ const oldOverflowCount = this.__getOverflowCount(overflow);
278
+
279
+ // Reset all buttons in the menu bar and the overflow button
280
+ this.__restoreButtons(buttons);
281
+
282
+ // Hide any overflowing buttons and put them in the 'overflow' button
283
+ this.__setOverflowItems(buttons, overflow);
284
+
285
+ const newOverflowCount = this.__getOverflowCount(overflow);
286
+ if (oldOverflowCount !== newOverflowCount && this._subMenu.opened) {
287
+ this._subMenu.close();
288
+ }
289
+
290
+ const isSingleButton = newOverflowCount === buttons.length || (newOverflowCount === 0 && buttons.length === 1);
291
+ this.toggleAttribute('has-single-button', isSingleButton);
95
292
  }
96
293
 
97
294
  /** @protected */
98
- disconnectedCallback() {
99
- super.disconnectedCallback();
100
- this._hideTooltip(true);
295
+ _removeButtons() {
296
+ this._buttons.forEach((button) => {
297
+ if (button !== this._overflow) {
298
+ this.removeChild(button);
299
+ }
300
+ });
301
+ }
302
+
303
+ /** @protected */
304
+ _initButton(item) {
305
+ const button = document.createElement('vaadin-menu-bar-button');
306
+
307
+ const itemCopy = { ...item };
308
+ button.item = itemCopy;
309
+
310
+ if (item.component) {
311
+ const component = this.__getComponent(itemCopy);
312
+ itemCopy.component = component;
313
+ // Save item for overflow menu
314
+ component.item = itemCopy;
315
+ button.appendChild(component);
316
+ } else if (item.text) {
317
+ button.textContent = item.text;
318
+ }
319
+
320
+ return button;
321
+ }
322
+
323
+ /** @protected */
324
+ _initButtonAttrs(button) {
325
+ button.setAttribute('role', 'menuitem');
326
+
327
+ if (button === this._overflow || (button.item && button.item.children)) {
328
+ button.setAttribute('aria-haspopup', 'true');
329
+ button.setAttribute('aria-expanded', 'false');
330
+ }
331
+ }
332
+
333
+ /** @protected */
334
+ _setButtonDisabled(button, disabled) {
335
+ button.disabled = disabled;
336
+ button.setAttribute('tabindex', disabled ? '-1' : '0');
337
+ }
338
+
339
+ /** @protected */
340
+ _setButtonTheme(btn, hostTheme) {
341
+ let theme = hostTheme;
342
+
343
+ // Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
344
+ const itemTheme = btn.item && btn.item.theme;
345
+ if (itemTheme != null) {
346
+ theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
347
+ }
348
+
349
+ if (theme) {
350
+ btn.setAttribute('theme', theme);
351
+ } else {
352
+ btn.removeAttribute('theme');
353
+ }
354
+ }
355
+
356
+ /** @private */
357
+ __getComponent(item) {
358
+ const itemComponent = item.component;
359
+ let component;
360
+
361
+ const isElement = itemComponent instanceof HTMLElement;
362
+ // Use existing item component, if any
363
+ if (isElement && itemComponent.localName === 'vaadin-context-menu-item') {
364
+ component = itemComponent;
365
+ } else {
366
+ component = document.createElement('vaadin-context-menu-item');
367
+ component.appendChild(isElement ? itemComponent : document.createElement(itemComponent));
368
+ }
369
+ if (item.text) {
370
+ const node = component.firstChild || component;
371
+ node.textContent = item.text;
372
+ }
373
+ component.setAttribute('theme', 'menu-bar-item');
374
+ return component;
375
+ }
376
+
377
+ /** @private */
378
+ __renderButtons(items = []) {
379
+ this._removeButtons();
380
+
381
+ /* Empty array, do nothing */
382
+ if (items.length === 0) {
383
+ return;
384
+ }
385
+
386
+ items.forEach((item) => {
387
+ const button = this._initButton(item);
388
+ this.insertBefore(button, this._overflow);
389
+ this._setButtonDisabled(button, item.disabled);
390
+ this._initButtonAttrs(button);
391
+ this._setButtonTheme(button, this._theme);
392
+ });
393
+
394
+ this.__detectOverflow();
101
395
  }
102
396
 
103
397
  /**
@@ -129,7 +423,7 @@ export const InteractionsMixin = (superClass) =>
129
423
 
130
424
  /** @protected */
131
425
  _hideTooltip(immediate) {
132
- const tooltip = this._tooltipController.node;
426
+ const tooltip = this._tooltipController && this._tooltipController.node;
133
427
  if (tooltip) {
134
428
  tooltip._stateController.close(immediate);
135
429
  }
@@ -192,7 +486,7 @@ export const InteractionsMixin = (superClass) =>
192
486
  */
193
487
  _setFocused(focused) {
194
488
  if (focused) {
195
- const target = this.shadowRoot.querySelector('[part$="button"][tabindex="0"]');
489
+ const target = this.querySelector('[tabindex="0"]');
196
490
  if (target) {
197
491
  this._buttons.forEach((btn) => {
198
492
  this._setTabindex(btn, btn === target);
@@ -278,11 +572,6 @@ export const InteractionsMixin = (superClass) =>
278
572
  }
279
573
  }
280
574
 
281
- /** @private */
282
- get _subMenu() {
283
- return this.shadowRoot.querySelector('vaadin-menu-bar-submenu');
284
- }
285
-
286
575
  /** @private */
287
576
  _itemsChanged() {
288
577
  const subMenu = this._subMenu;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2019 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { ContextMenu } from '@vaadin/context-menu/src/vaadin-context-menu.js';
@@ -1,14 +1,12 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2019 - 2022 Vaadin Ltd.
3
+ * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
7
6
  import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
8
7
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
9
8
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
- import { ButtonsMixin } from './vaadin-menu-bar-buttons-mixin.js';
11
- import { InteractionsMixin } from './vaadin-menu-bar-interactions-mixin.js';
9
+ import { MenuBarMixin } from './vaadin-menu-bar-mixin.js';
12
10
 
13
11
  export interface MenuBarItem {
14
12
  /**
@@ -87,8 +85,6 @@ export interface MenuBarEventMap extends HTMLElementEventMap, MenuBarCustomEvent
87
85
  * Part name | Description
88
86
  * ------------------|----------------
89
87
  * `container` | The container wrapping menu bar buttons.
90
- * `menu-bar-button` | The menu bar button.
91
- * `overflow-button` | The "overflow" button appearing when menu bar width is not enough to fit all the buttons.
92
88
  *
93
89
  * The following state attributes are available for styling:
94
90
  *
@@ -111,9 +107,7 @@ export interface MenuBarEventMap extends HTMLElementEventMap, MenuBarCustomEvent
111
107
  *
112
108
  * @fires {CustomEvent} item-selected - Fired when a submenu item or menu bar button without children is clicked.
113
109
  */
114
- declare class MenuBar extends ButtonsMixin(
115
- DisabledMixin(InteractionsMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement))))),
116
- ) {
110
+ declare class MenuBar extends MenuBarMixin(DisabledMixin(ElementMixin(ThemableMixin(HTMLElement)))) {
117
111
  /**
118
112
  * Defines a hierarchical structure, where root level items represent menu bar buttons,
119
113
  * and `children` property configures a submenu with items to be opened below
@@ -166,6 +160,14 @@ declare class MenuBar extends ButtonsMixin(
166
160
  */
167
161
  i18n: MenuBarI18n;
168
162
 
163
+ /**
164
+ * A space-delimited list of CSS class names
165
+ * to set on each sub-menu overlay element.
166
+ *
167
+ * @attr {string} overlay-class
168
+ */
169
+ overlayClass: string;
170
+
169
171
  addEventListener<K extends keyof MenuBarEventMap>(
170
172
  type: K,
171
173
  listener: (this: MenuBar, ev: MenuBarEventMap[K]) => void,