Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: extract shared combo-box overlay logic to mixin #5393

Merged
merged 2 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/combo-box/src/vaadin-combo-box-overlay-mixin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @license
* Copyright (c) 2015 - 2023 Vaadin Ltd.
* 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 { PositionMixinClass } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';

export declare function ComboBoxOverlayMixin<T extends Constructor<HTMLElement>>(
base: T,
): Constructor<PositionMixinClass> & T;
60 changes: 60 additions & 0 deletions packages/combo-box/src/vaadin-combo-box-overlay-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @license
* Copyright (c) 2015 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';

/**
* @polymerMixin
* @mixes PositionMixin
*/
export const ComboBoxOverlayMixin = (superClass) =>
class ComboBoxOverlayMixin extends PositionMixin(superClass) {
static get observers() {
return ['_setOverlayWidth(positionTarget, opened)'];
}

/** @protected */
connectedCallback() {
super.connectedCallback();

const comboBox = this._comboBox;

const hostDir = comboBox && comboBox.getAttribute('dir');
if (hostDir) {
this.setAttribute('dir', hostDir);
}
}

/**
* Override method inherited from `Overlay`
* to not close on position target click.
*
* @param {Event} event
* @return {boolean}
* @protected
*/
_shouldCloseOnOutsideClick(event) {
const eventPath = event.composedPath();
return !eventPath.includes(this.positionTarget) && !eventPath.includes(this);
}

/** @private */
_setOverlayWidth(positionTarget, opened) {
if (positionTarget && opened) {
const propPrefix = this.localName;
this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);

const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);

if (customWidth === '') {
this.style.removeProperty(`--${propPrefix}-width`);
} else {
this.style.setProperty(`--${propPrefix}-width`, customWidth);
}

this._updatePosition();
}
}
};
20 changes: 20 additions & 0 deletions packages/combo-box/src/vaadin-combo-box-overlay.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
* Copyright (c) 2015 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';

/**
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
*/
declare class ComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {}

declare global {
interface HTMLElementTagNameMap {
'vaadin-combo-box-overlay': ComboBoxOverlay;
}
}

export { ComboBoxOverlay };
63 changes: 9 additions & 54 deletions packages/combo-box/src/vaadin-combo-box-overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';

registerStyles(
'vaadin-combo-box-overlay',
Expand All @@ -29,73 +29,28 @@ let memoizedTemplate;
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
*
* @extends Overlay
* @mixes ComboBoxOverlayMixin
* @private
*/
export class ComboBoxOverlay extends PositionMixin(Overlay) {
export class ComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {
static get is() {
return 'vaadin-combo-box-overlay';
}

static get template() {
if (!memoizedTemplate) {
memoizedTemplate = super.template.cloneNode(true);
memoizedTemplate.content.querySelector('[part~="overlay"]').removeAttribute('tabindex');
}

return memoizedTemplate;
}

static get observers() {
return ['_setOverlayWidth(positionTarget, opened)'];
}

connectedCallback() {
super.connectedCallback();
const overlay = memoizedTemplate.content.querySelector('[part~="overlay"]');
overlay.removeAttribute('tabindex');

const comboBox = this._comboBox;
const loader = document.createElement('div');
loader.setAttribute('part', 'loader');

const hostDir = comboBox && comboBox.getAttribute('dir');
if (hostDir) {
this.setAttribute('dir', hostDir);
overlay.insertBefore(loader, overlay.firstElementChild);
}
}

ready() {
super.ready();
const loader = document.createElement('div');
loader.setAttribute('part', 'loader');
const content = this.shadowRoot.querySelector('[part~="content"]');
content.parentNode.insertBefore(loader, content);
}

/**
* Override method inherited from `Overlay`
* to not close on position target click.
*
* @param {Event} _event
* @return {boolean}
* @protected
*/
_shouldCloseOnOutsideClick(event) {
const eventPath = event.composedPath();
return !eventPath.includes(this.positionTarget) && !eventPath.includes(this);
}

_setOverlayWidth(positionTarget, opened) {
if (positionTarget && opened) {
const propPrefix = this.localName;
this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);

const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);

if (customWidth === '') {
this.style.removeProperty(`--${propPrefix}-width`);
} else {
this.style.setProperty(`--${propPrefix}-width`, customWidth);
}

this._updatePosition();
}
return memoizedTemplate;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/combo-box/theme/lumo/vaadin-combo-box-light.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import './vaadin-combo-box-dropdown-styles.js';
import './vaadin-combo-box-item-styles.js';
import './vaadin-combo-box-overlay-styles.js';
import '../../src/vaadin-combo-box-light.js';
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ const comboBoxOverlay = css`
--_vaadin-combo-box-items-container-border-color: transparent;
}

/* Loading state */

/* When items are empty, the spinner needs some room */
:host(:not([closing])) [part~='content'] {
min-height: calc(2 * var(--lumo-space-s) + var(--lumo-icon-size-s));
Expand All @@ -36,7 +34,9 @@ const comboBoxOverlay = css`
:host([bottom-aligned]) [part~='overlay'] {
margin-bottom: var(--lumo-space-xs);
}
`;

const comboBoxLoader = css`
[part~='loader'] {
position: absolute;
z-index: 1;
Expand All @@ -48,8 +48,6 @@ const comboBoxOverlay = css`
margin-inline-end: 0;
}

/* RTL specific styles */

:host([dir='rtl']) [part~='loader'] {
left: auto;
margin-left: 0;
Expand All @@ -59,6 +57,8 @@ const comboBoxOverlay = css`
}
`;

registerStyles('vaadin-combo-box-overlay', [overlay, menuOverlayCore, comboBoxOverlay, loader], {
registerStyles('vaadin-combo-box-overlay', [overlay, menuOverlayCore, comboBoxOverlay, loader, comboBoxLoader], {
moduleId: 'lumo-combo-box-overlay',
});

export { comboBoxLoader, comboBoxOverlay };
2 changes: 1 addition & 1 deletion packages/combo-box/theme/lumo/vaadin-combo-box.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '@vaadin/input-container/theme/lumo/vaadin-input-container.js';
import './vaadin-combo-box-dropdown-styles.js';
import './vaadin-combo-box-item-styles.js';
import './vaadin-combo-box-overlay-styles.js';
import './vaadin-combo-box-styles.js';
import '../../src/vaadin-combo-box.js';
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import './vaadin-combo-box-dropdown-styles.js';
import './vaadin-combo-box-item-styles.js';
import './vaadin-combo-box-overlay-styles.js';
import '../../src/vaadin-combo-box-light.js';
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const comboBoxOverlay = css`
[part='content'] {
padding: 0;
}
`;

const comboBoxLoader = css`
[part~='loader'] {
position: absolute;
z-index: 1;
Expand All @@ -31,6 +33,8 @@ const comboBoxOverlay = css`
}
`;

registerStyles('vaadin-combo-box-overlay', [menuOverlay, comboBoxOverlay, loader], {
registerStyles('vaadin-combo-box-overlay', [menuOverlay, comboBoxOverlay, loader, comboBoxLoader], {
moduleId: 'material-combo-box-overlay',
});

export { comboBoxLoader, comboBoxOverlay };
2 changes: 1 addition & 1 deletion packages/combo-box/theme/material/vaadin-combo-box.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '@vaadin/input-container/theme/material/vaadin-input-container.js';
import './vaadin-combo-box-dropdown-styles.js';
import './vaadin-combo-box-item-styles.js';
import './vaadin-combo-box-overlay-styles.js';
import './vaadin-combo-box-styles.js';
import '../../src/vaadin-combo-box.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
* Copyright (c) 2021 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ComboBoxOverlayMixin } from '@vaadin/combo-box/src/vaadin-combo-box-overlay-mixin.js';
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';

/**
* An element used internally by `<vaadin-multi-select-combo-box>`. Not intended to be used separately.
*/
declare class MultiSelectComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {}

declare global {
interface HTMLElementTagNameMap {
'vaadin-multi-select-combo-box-overlay': MultiSelectComboBoxOverlay;
}
}

export { MultiSelectComboBoxOverlay };
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* Copyright (c) 2021 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ComboBoxOverlay } from '@vaadin/combo-box/src/vaadin-combo-box-overlay.js';
import { ComboBoxOverlayMixin } from '@vaadin/combo-box/src/vaadin-combo-box-overlay-mixin.js';
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

registerStyles(
Expand All @@ -15,20 +16,44 @@ registerStyles(
var(--_vaadin-multi-select-combo-box-overlay-default-width, auto)
);
}

[part='content'] {
display: flex;
flex-direction: column;
height: 100%;
}
`,
{ moduleId: 'vaadin-multi-select-combo-box-overlay-styles' },
);

let memoizedTemplate;

/**
* An element used internally by `<vaadin-multi-select-combo-box>`. Not intended to be used separately.
*
* @extends ComboBoxOverlay
* @private
*/
class MultiSelectComboBoxOverlay extends ComboBoxOverlay {
class MultiSelectComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {
static get is() {
return 'vaadin-multi-select-combo-box-overlay';
}

static get template() {
if (!memoizedTemplate) {
memoizedTemplate = super.template.cloneNode(true);

const overlay = memoizedTemplate.content.querySelector('[part~="overlay"]');
overlay.removeAttribute('tabindex');

const loader = document.createElement('div');
loader.setAttribute('part', 'loader');

overlay.insertBefore(loader, overlay.firstElementChild);
}

return memoizedTemplate;
}
}

customElements.define(MultiSelectComboBoxOverlay.is, MultiSelectComboBoxOverlay);
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import '@vaadin/vaadin-lumo-styles/font-icons.js';
import '@vaadin/vaadin-lumo-styles/style.js';
import '@vaadin/vaadin-lumo-styles/typography.js';
import { comboBoxItem } from '@vaadin/combo-box/theme/lumo/vaadin-combo-box-item-styles.js';
import { comboBoxLoader, comboBoxOverlay } from '@vaadin/combo-box/theme/lumo/vaadin-combo-box-overlay-styles.js';
import { item } from '@vaadin/item/theme/lumo/vaadin-item-styles.js';
import { inputFieldShared } from '@vaadin/vaadin-lumo-styles/mixins/input-field-shared.js';
import { loader } from '@vaadin/vaadin-lumo-styles/mixins/loader.js';
import { menuOverlayCore } from '@vaadin/vaadin-lumo-styles/mixins/menu-overlay.js';
import { overlay } from '@vaadin/vaadin-lumo-styles/mixins/overlay.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

const multiSelectComboBoxItem = css`
Expand All @@ -25,6 +29,14 @@ registerStyles('vaadin-multi-select-combo-box-item', [item, comboBoxItem, multiS
moduleId: 'lumo-multi-select-combo-box-item',
});

registerStyles(
'vaadin-multi-select-combo-box-overlay',
[overlay, menuOverlayCore, comboBoxOverlay, loader, comboBoxLoader],
{
moduleId: 'lumo-multi-select-combo-box-overlay',
},
);

const multiSelectComboBox = css`
:host([has-value]) {
padding-inline-start: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Copyright (c) 2021 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/combo-box/theme/lumo/vaadin-combo-box-dropdown-styles.js';
import '@vaadin/input-container/theme/lumo/vaadin-input-container.js';
import '@vaadin/overlay/theme/lumo/vaadin-overlay.js';
import './vaadin-multi-select-combo-box-chip-styles.js';
import './vaadin-multi-select-combo-box-styles.js';
import '../../src/vaadin-multi-select-combo-box.js';
Loading