Skip to content

Commit

Permalink
refactor!: move menu-bar buttons to light DOM (#4687)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Oct 19, 2022
1 parent 016299b commit ea7c85a
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 94 deletions.
8 changes: 8 additions & 0 deletions packages/menu-bar/src/vaadin-menu-bar-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themab
registerStyles(
'vaadin-menu-bar-button',
css`
:host {
flex-shrink: 0;
}
:host([slot='overflow']) {
margin-inline-end: 0;
}
[part='label'] ::slotted(vaadin-context-menu-item) {
position: relative;
z-index: 1;
Expand Down
3 changes: 2 additions & 1 deletion packages/menu-bar/src/vaadin-menu-bar-buttons-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { Constructor } from '@open-wc/dedupe-mixin';
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
import type { ResizeMixinClass } from '@vaadin/component-base/src/resize-mixin.js';

export declare function ButtonsMixin<T extends Constructor<HTMLElement>>(
base: T,
): Constructor<ButtonsMixinClass> & Constructor<ResizeMixinClass> & T;
): Constructor<ButtonsMixinClass> & Constructor<ControllerMixinClass> & Constructor<ResizeMixinClass> & T;

export declare class ButtonsMixinClass {
protected readonly _buttons: HTMLElement[];
Expand Down
85 changes: 58 additions & 27 deletions packages/menu-bar/src/vaadin-menu-bar-buttons-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
* Copyright (c) 2019 - 2022 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';

/**
* @polymerMixin
* @mixes ResizeMixin
* @mixes ControllerMixin
*/
export const ButtonsMixin = (superClass) =>
class extends ResizeMixin(superClass) {
class extends ResizeMixin(ControllerMixin(superClass)) {
static get properties() {
return {
/**
Expand All @@ -21,11 +24,20 @@ export const ButtonsMixin = (superClass) =>
type: Boolean,
value: false,
},

/** @protected */
_overflow: {
type: Object,
},
};
}

static get observers() {
return ['_menuItemsChanged(items, items.splices)'];
return [
'__hasOverflowChanged(_hasOverflow, _overflow)',
'__i18nChanged(i18n, _overflow)',
'_menuItemsChanged(items, _overflow, items.splices)',
];
}

/**
Expand All @@ -43,21 +55,32 @@ export const ButtonsMixin = (superClass) =>
super.ready();

this.setAttribute('role', 'menubar');
}

/** @protected */
connectedCallback() {
super.connectedCallback();
this._overflowController = new SlotController(
this,
'overflow',
() => document.createElement('vaadin-menu-bar-button'),
(_, btn) => {
btn.setAttribute('hidden', '');

this._initButtonAttrs(this._overflow);
const dots = document.createElement('div');
dots.setAttribute('aria-hidden', 'true');
dots.textContent = '···';
btn.appendChild(dots);

this._overflow = btn;
this._initButtonAttrs(btn);
},
);
this.addController(this._overflowController);
}

/**
* @return {!Array<!HTMLElement>}
* @protected
*/
get _buttons() {
return Array.from(this.shadowRoot.querySelectorAll('[part$="button"]'));
return Array.from(this.querySelectorAll('vaadin-menu-bar-button'));
}

/**
Expand All @@ -68,22 +91,36 @@ export const ButtonsMixin = (superClass) =>
return this.shadowRoot.querySelector('[part="container"]');
}

/**
* @return {!HTMLElement}
* @protected
*/
get _overflow() {
return this.shadowRoot.querySelector('[part="overflow-button"]');
/** @private */
__hasOverflowChanged(hasOverflow, overflow) {
if (overflow) {
overflow.toggleAttribute('hidden', !hasOverflow);
}
}

/** @private */
_menuItemsChanged(items) {
_menuItemsChanged(items, overflow) {
if (!overflow) {
return;
}

if (items !== this._oldItems) {
this._oldItems = items;
this.__renderButtons(items);
}
}

/** @private */
__i18nChanged(i18n, overflow) {
if (overflow && i18n && i18n.moreOptions !== undefined) {
if (i18n.moreOptions) {
overflow.setAttribute('aria-label', i18n.moreOptions);
} else {
overflow.removeAttribute('aria-label');
}
}
}

/** @private */
__getOverflowCount(overflow) {
// We can't use optional chaining due to webpack 4
Expand Down Expand Up @@ -170,17 +207,16 @@ export const ButtonsMixin = (superClass) =>

/** @protected */
_removeButtons() {
const container = this._container;

while (container.children.length > 1) {
container.removeChild(container.firstElementChild);
}
this._buttons.forEach((button) => {
if (button !== this._overflow) {
this.removeChild(button);
}
});
}

/** @protected */
_initButton(item) {
const button = document.createElement('vaadin-menu-bar-button');
button.setAttribute('part', 'menu-bar-button');

const itemCopy = { ...item };
button.item = itemCopy;
Expand Down Expand Up @@ -231,11 +267,6 @@ export const ButtonsMixin = (superClass) =>
}
}

/** @protected */
_appendButton(button) {
this._container.insertBefore(button, this._overflow);
}

/** @private */
__getComponent(item) {
const itemComponent = item.component;
Expand Down Expand Up @@ -268,7 +299,7 @@ export const ButtonsMixin = (superClass) =>

items.forEach((item) => {
const button = this._initButton(item);
this._appendButton(button);
this.insertBefore(button, this._overflow);
this._setButtonDisabled(button, item.disabled);
this._initButtonAttrs(button);
this._setButtonTheme(button, this._theme);
Expand Down
17 changes: 6 additions & 11 deletions packages/menu-bar/src/vaadin-menu-bar-interactions-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
import { isKeyboardActive } from '@vaadin/component-base/src/focus-utils.js';
import { isElementFocused, isKeyboardActive } from '@vaadin/component-base/src/focus-utils.js';
import { KeyboardDirectionMixin } from '@vaadin/component-base/src/keyboard-direction-mixin.js';

/**
Expand Down Expand Up @@ -55,15 +55,15 @@ export const InteractionsMixin = (superClass) =>

/**
* Override getter from `KeyboardDirectionMixin`
* to look for activeElement in shadow root, or
* use the expanded button as a fallback.
* to use expanded button for arrow navigation
* when the sub-menu is opened and has focus.
*
* @return {Element | null}
* @protected
* @override
*/
get focused() {
return this.shadowRoot.activeElement || this._expandedButton;
return (this._getItems() || []).find(isElementFocused) || this._expandedButton;
}

/**
Expand All @@ -89,11 +89,6 @@ export const InteractionsMixin = (superClass) =>
return this._buttons;
}

/** @private */
get __isRTL() {
return this.getAttribute('dir') === 'rtl';
}

/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
Expand Down Expand Up @@ -129,7 +124,7 @@ export const InteractionsMixin = (superClass) =>

/** @protected */
_hideTooltip(immediate) {
const tooltip = this._tooltipController.node;
const tooltip = this._tooltipController && this._tooltipController.node;
if (tooltip) {
tooltip._stateController.close(immediate);
}
Expand Down Expand Up @@ -192,7 +187,7 @@ export const InteractionsMixin = (superClass) =>
*/
_setFocused(focused) {
if (focused) {
const target = this.shadowRoot.querySelector('[part$="button"][tabindex="0"]');
const target = this.querySelector('[tabindex="0"]');
if (target) {
this._buttons.forEach((btn) => {
this._setTabindex(btn, btn === target);
Expand Down
7 changes: 1 addition & 6 deletions packages/menu-bar/src/vaadin-menu-bar.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Copyright (c) 2019 - 2022 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
Expand Down Expand Up @@ -87,8 +86,6 @@ export interface MenuBarEventMap extends HTMLElementEventMap, MenuBarCustomEvent
* Part name | Description
* ------------------|----------------
* `container` | The container wrapping menu bar buttons.
* `menu-bar-button` | The menu bar button.
* `overflow-button` | The "overflow" button appearing when menu bar width is not enough to fit all the buttons.
*
* The following state attributes are available for styling:
*
Expand All @@ -111,9 +108,7 @@ export interface MenuBarEventMap extends HTMLElementEventMap, MenuBarCustomEvent
*
* @fires {CustomEvent} item-selected - Fired when a submenu item or menu bar button without children is clicked.
*/
declare class MenuBar extends ButtonsMixin(
DisabledMixin(InteractionsMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement))))),
) {
declare class MenuBar extends ButtonsMixin(DisabledMixin(InteractionsMixin(ElementMixin(ThemableMixin(HTMLElement))))) {
/**
* Defines a hierarchical structure, where root level items represent menu bar buttons,
* and `children` property configures a submenu with items to be opened below
Expand Down
34 changes: 6 additions & 28 deletions packages/menu-bar/src/vaadin-menu-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import './vaadin-menu-bar-submenu.js';
import './vaadin-menu-bar-button.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
Expand Down Expand Up @@ -38,8 +37,6 @@ import { InteractionsMixin } from './vaadin-menu-bar-interactions-mixin.js';
* Part name | Description
* ------------------|----------------
* `container` | The container wrapping menu bar buttons.
* `menu-bar-button` | The menu bar button.
* `overflow-button` | The "overflow" button appearing when menu bar width is not enough to fit all the buttons.
*
* The following state attributes are available for styling:
*
Expand All @@ -63,16 +60,13 @@ import { InteractionsMixin } from './vaadin-menu-bar-interactions-mixin.js';
* @fires {CustomEvent<boolean>} item-selected - Fired when a submenu item or menu bar button without children is clicked.
*
* @extends HTMLElement
* @mixes ControllerMixin
* @mixes ButtonsMixin
* @mixes InteractionsMixin
* @mixes DisabledMixin
* @mixes ElementMixin
* @mixes ThemableMixin
*/
class MenuBar extends ButtonsMixin(
DisabledMixin(InteractionsMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement))))),
) {
class MenuBar extends ButtonsMixin(DisabledMixin(InteractionsMixin(ElementMixin(ThemableMixin(PolymerElement))))) {
static get template() {
return html`
<style>
Expand All @@ -91,27 +85,11 @@ class MenuBar extends ButtonsMixin(
flex-wrap: nowrap;
overflow: hidden;
}
[part$='button'] {
flex-shrink: 0;
}
[part='overflow-button'] {
margin-right: 0;
}
.dots::before {
display: block;
content: '\\00B7\\00B7\\00B7';
font-size: inherit;
line-height: inherit;
}
</style>
<div part="container">
<vaadin-menu-bar-button part="overflow-button" hidden$="[[!_hasOverflow]]" aria-label$="[[i18n.moreOptions]]">
<div class="dots"></div>
</vaadin-menu-bar-button>
<slot></slot>
<slot name="overflow"></slot>
</div>
<vaadin-menu-bar-submenu is-root=""></vaadin-menu-bar-submenu>
Expand Down Expand Up @@ -219,7 +197,7 @@ class MenuBar extends ButtonsMixin(
}

static get observers() {
return ['_themeChanged(_theme)'];
return ['_themeChanged(_theme, _overflow)'];
}

/** @protected */
Expand Down Expand Up @@ -255,8 +233,8 @@ class MenuBar extends ButtonsMixin(
* @param {string | null} theme
* @protected
*/
_themeChanged(theme) {
if (this.shadowRoot) {
_themeChanged(theme, overflow) {
if (overflow) {
this._buttons.forEach((btn) => this._setButtonTheme(btn, theme));
this.__detectOverflow();
}
Expand Down
Loading

0 comments on commit ea7c85a

Please sign in to comment.