New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@vaadin/vaadin-combo-box

Package Overview
Dependencies
Maintainers
16
Versions
304
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin/vaadin-combo-box - npm Package Compare versions

Comparing version 5.4.7 to 6.0.0-alpha1

src/interfaces.d.ts

92

package.json
{
"name": "@vaadin/vaadin-combo-box",
"version": "6.0.0-alpha1",
"description": "Web Component for displaying a list of items with filtering",
"main": "vaadin-combo-box.js",
"repository": "vaadin/vaadin-combo-box",
"keywords": [

@@ -10,7 +14,2 @@ "Vaadin",

],
"repository": "vaadin/vaadin-combo-box",
"homepage": "https://vaadin.com/components",
"name": "@vaadin/vaadin-combo-box",
"version": "5.4.7",
"main": "vaadin-combo-box.js",
"author": "Vaadin Ltd",

@@ -21,19 +20,39 @@ "license": "Apache-2.0",

},
"homepage": "https://vaadin.com/components",
"files": [
"vaadin-*.d.ts",
"vaadin-*.js",
"@types",
"src",
"theme"
],
"resolutions": {
"es-abstract": "1.17.6",
"@types/doctrine": "0.0.3",
"inherits": "2.0.3",
"samsam": "1.1.3",
"supports-color": "3.1.2",
"type-detect": "1.0.0"
"scripts": {
"analyze": "polymer analyze vaadin-* > analysis.json",
"check-version": "magi check-version",
"debug": "web-test-runner test/*.test.js --watch",
"dist": "rimraf dist && npm run analyze && rollup -c rollup.config.js && cp analysis.json dist",
"lint": "npm run lint:js && npm run lint:css && npm run lint:types",
"lint:js": "eslint src theme test",
"lint:css": "stylelint src/*.js theme/**/*-styles.js",
"lint:types": "tsc",
"prestart": "npm run analyze",
"preversion": "magi update-version",
"screenshots": "hermione test/visual/test.js --update-refs",
"serve:dist": "web-dev-server --app-index dist/index.html --open",
"start": "web-dev-server --node-resolve --open",
"test": "web-test-runner test/*.test.js --coverage",
"test:sauce": "TEST_ENV=sauce npm test",
"test:visual": "hermione test/visual/test.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier --write"
]
},
"dependencies": {
"@polymer/iron-a11y-keys-behavior": "^3.0.0",
"@polymer/iron-a11y-announcer": "^3.0.0",

@@ -45,22 +64,41 @@ "@polymer/iron-list": "^3.0.0",

"@vaadin/vaadin-overlay": "^3.5.0",
"@vaadin/vaadin-text-field": "^2.8.0",
"@vaadin/vaadin-themable-mixin": "^1.6.1",
"@vaadin/vaadin-lumo-styles": "^1.1.1",
"@vaadin/vaadin-material-styles": "^1.1.2",
"@vaadin/vaadin-item": "^2.3.0",
"@vaadin/vaadin-text-field": "^3.0.0-alpha1",
"@vaadin/vaadin-themable-mixin": "^1.6.2",
"@vaadin/vaadin-lumo-styles": "^1.6.1",
"@vaadin/vaadin-material-styles": "^1.3.2",
"@vaadin/vaadin-item": "^3.0.0-alpha1",
"@vaadin/vaadin-element-mixin": "^2.4.1"
},
"scripts": {
"generate-typings": "gen-typescript-declarations --outDir . --verify"
},
"devDependencies": {
"@polymer/iron-form": "^3.0.0",
"@esm-bundle/chai": "^4.1.5",
"@open-wc/rollup-plugin-html": "^1.2.5",
"@open-wc/testing-helpers": "^1.8.0",
"@polymer/iron-component-page": "^4.0.0",
"@polymer/iron-input": "^3.0.1",
"@polymer/iron-test-helpers": "^3.0.0",
"@polymer/paper-input": "^3.0.0",
"@vaadin/vaadin-button": "^2.4.0",
"wct-browser-legacy": "^1.0.1",
"@vaadin/vaadin-demo-helpers": "^3.1.0",
"@vaadin/vaadin-dialog": "^2.5.0"
"@rollup/plugin-node-resolve": "^11.0.0",
"@vaadin/vaadin-dialog": "^2.5.0",
"@web/dev-server": "~0.0.25",
"@web/test-runner": "^0.9.13",
"@web/test-runner-saucelabs": "0.1.1",
"eslint": "^7.13.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"hermione": "^3.9.0",
"hermione-esm": "^0.4.0",
"hermione-sauce": "^0.1.0",
"husky": "^4.3.0",
"lint-staged": "^10.5.1",
"magi-cli": "^0.28.0",
"prettier": "^2.1.2",
"rimraf": "^3.0.2",
"rollup": "^2.34.1",
"rollup-plugin-terser": "^7.0.2",
"sinon": "^9.2.0",
"stylelint": "^13.7.2",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-vaadin": "^0.2.6",
"typescript": "^4.1.2"
}
}
[![npm latest version](https://badgen.net/npm/v/@vaadin/vaadin-combo-box/latest)](https://www.npmjs.com/package/@vaadin/vaadin-combo-box)
[![npm next version](https://badgen.net/npm/v/@vaadin/vaadin-combo-box/next)](https://www.npmjs.com/package/@vaadin/vaadin-combo-box)
[![Bower version](https://badgen.net/github/release/vaadin/vaadin-combo-box)](https://github.com/vaadin/vaadin-combo-box/releases)
[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/vaadin/vaadin-combo-box)

@@ -20,20 +18,2 @@ [![Build Status](https://travis-ci.org/vaadin/vaadin-combo-box.svg?branch=master)](https://travis-ci.org/vaadin/vaadin-combo-box)

<!--
```
<custom-element-demo height="300">
<template>
<script src="../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="vaadin-combo-box.html">
<custom-style>
<style>
vaadin-combo-box {
width: 300px;
}
</style>
</custom-style>
<next-code-block></next-code-block>
</template>
</custom-element-demo>
```
-->
```html

@@ -55,27 +35,5 @@ <vaadin-combo-box label="User" placeholder="Please select" item-value-path="email" item-label-path="email"></vaadin-combo-box>

The Vaadin components are distributed as Bower and npm packages.
Please note that the version range is the same, as the API has not changed.
You should not mix Bower and npm versions in the same application, though.
Unlike the official Polymer Elements, the converted Polymer 3 compatible Vaadin components
are only published on npm, not pushed to GitHub repositories.
### Polymer 2 and HTML Imports Compatible Version
Install `vaadin-combo-box`:
```sh
bower i vaadin/vaadin-combo-box --save
```
Once installed, import it in your application:
```html
<link rel="import" href="bower_components/vaadin-combo-box/vaadin-combo-box.html">
```
### Polymer 3 and ES Modules Compatible Version
Install `vaadin-combo-box`:
```sh
npm i @vaadin/vaadin-combo-box --save

@@ -100,31 +58,32 @@ ```

`theme/lumo/vaadin-combo-box.html`
`theme/lumo/vaadin-combo-box-light.html`
`theme/lumo/vaadin-combo-box.js`
`theme/lumo/vaadin-combo-box-light.js`
- The components with the Material theme:
`theme/material/vaadin-combo-box.html`
`theme/material/vaadin-combo-box-light.html`
`theme/material/vaadin-combo-box.js`
`theme/material/vaadin-combo-box-light.js`
- Alias for `theme/lumo/vaadin-combo-box.html`
`theme/lumo/vaadin-combo-box-light.html`
- Alias for `theme/lumo/vaadin-combo-box.js`
`theme/lumo/vaadin-combo-box-light.js`
`vaadin-combo-box.html`
`vaadin-combo-box-light.html`
`vaadin-combo-box.js`
`vaadin-combo-box-light.js`
## Running demos and tests in a browser
## Running API docs and tests in a browser
1. Fork the `vaadin-combo-box` repository and clone it locally.
1. Make sure you have [npm](https://www.npmjs.com/) and [Bower](https://bower.io) installed.
1. Make sure you have [node.js](https://nodejs.org/) 12.x installed.
1. When in the `vaadin-combo-box` directory, run `npm install` and then `bower install` to install dependencies.
1. Make sure you have [npm](https://www.npmjs.com/) installed.
1. When in the `vaadin-combo-box` directory, run `npm install` to install dependencies.
1. Run `npm start`, browser will automatically open the component API documentation.
1. You can also open demo or in-browser tests by adding **demo** or **test** to the URL, for example:
1. You can also open visual tests, for example:
- http://127.0.0.1:3000/components/vaadin-combo-box/demo
- http://127.0.0.1:3000/components/vaadin-combo-box/test
- http://127.0.0.1:3000/test/visual/default.html

@@ -134,8 +93,11 @@

1. When in the `vaadin-combo-box` directory, run `polymer test`
1. When in the `vaadin-combo-box` directory, run `npm test`
## Debugging tests in the browser
1. Run `npm run debug`, then choose manual mode (M) and open the link in browser.
## Following the coding style
We are using [ESLint](http://eslint.org/) for linting JavaScript code. You can check if your code is following our standards by running `npm run lint`, which will automatically lint all `.js` files as well as JavaScript snippets inside `.html` files.
We are using [ESLint](http://eslint.org/) for linting JavaScript code. You can check if your code is following our standards by running `npm run lint`, which will automatically lint all `.js` files.

@@ -142,0 +104,0 @@

@@ -1,27 +0,12 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* src/vaadin-combo-box-data-provider-mixin.js
*/
import { ComboBoxDataProvider } from './interfaces';
declare function ComboBoxDataProviderMixin<T extends new (...args: any[]) => {}>(
base: T
): T & ComboBoxDataProviderMixinConstructor;
// tslint:disable:variable-name Describing an API that's defined elsewhere.
// tslint:disable:no-any describes the API as best we are able today
export {ComboBoxDataProviderMixin};
declare function ComboBoxDataProviderMixin<T extends new (...args: any[]) => {}>(base: T): T & ComboBoxDataProviderMixinConstructor;
interface ComboBoxDataProviderMixinConstructor {
new(...args: any[]): ComboBoxDataProviderMixin;
new (...args: any[]): ComboBoxDataProviderMixin;
}
export {ComboBoxDataProviderMixinConstructor};
interface ComboBoxDataProviderMixin {
/**

@@ -36,3 +21,3 @@ * Number of items fetched at a time from the dataprovider.

*/
size: number|undefined;
size: number | undefined;

@@ -52,4 +37,3 @@ /**

*/
dataProvider: ComboBoxDataProvider|null|undefined;
ready(): void;
dataProvider: ComboBoxDataProvider | null | undefined;

@@ -62,2 +46,2 @@ /**

import {ComboBoxDataProvider} from '../@types/interfaces';
export { ComboBoxDataProviderMixin, ComboBoxDataProviderMixinConstructor };
/**
@license
Copyright (c) 2018 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';

@@ -11,318 +11,314 @@

*/
export const ComboBoxDataProviderMixin = superClass => class DataProviderMixin extends superClass {
export const ComboBoxDataProviderMixin = (superClass) =>
class DataProviderMixin extends superClass {
static get properties() {
return {
/**
* Number of items fetched at a time from the dataprovider.
* @attr {number} page-size
* @type {number}
*/
pageSize: {
type: Number,
value: 50,
observer: '_pageSizeChanged'
},
static get properties() {
return {
/**
* Total number of items.
* @type {number | undefined}
*/
size: {
type: Number,
observer: '_sizeChanged'
},
/**
* Number of items fetched at a time from the dataprovider.
* @attr {number} page-size
* @type {number}
*/
pageSize: {
type: Number,
value: 50,
observer: '_pageSizeChanged'
},
/**
* Function that provides items lazily. Receives arguments `params`, `callback`
*
* `params.page` Requested page index
*
* `params.pageSize` Current page size
*
* `params.filter` Currently applied filter
*
* `callback(items, size)` Callback function with arguments:
* - `items` Current page of items
* - `size` Total number of items.
* @type {ComboBoxDataProvider | undefined}
*/
dataProvider: {
type: Object,
observer: '_dataProviderChanged'
},
/**
* Total number of items.
* @type {number | undefined}
*/
size: {
type: Number,
observer: '_sizeChanged'
},
/** @private */
_pendingRequests: {
value: () => {
return {};
}
},
/**
* Function that provides items lazily. Receives arguments `params`, `callback`
*
* `params.page` Requested page index
*
* `params.pageSize` Current page size
*
* `params.filter` Currently applied filter
*
* `callback(items, size)` Callback function with arguments:
* - `items` Current page of items
* - `size` Total number of items.
* @type {ComboBoxDataProvider | undefined}
*/
dataProvider: {
type: Object,
observer: '_dataProviderChanged'
},
/** @private */
_pendingRequests: {
value: () => {
return {};
/** @private */
__placeHolder: {
value: new ComboBoxPlaceholder()
}
},
};
}
/** @private */
__placeHolder: {
value: new ComboBoxPlaceholder()
static get observers() {
return [
'_dataProviderFilterChanged(filter, dataProvider)',
'_dataProviderClearFilter(dataProvider, opened, value)',
'_warnDataProviderValue(dataProvider, value)',
'_ensureFirstPage(opened)'
];
}
/** @private */
_dataProviderClearFilter(dataProvider) {
// Can't depend on filter in this observer as we don't want
// to clear the filter whenever it's set
if (dataProvider && !this.loading && this.filter) {
this.size = undefined;
this._pendingRequests = {};
this.filter = '';
this.clearCache();
}
}
};
}
/** @protected */
ready() {
super.ready();
this.clearCache();
this.$.overlay.addEventListener('index-requested', (e) => {
const index = e.detail.index;
const currentScrollerPos = e.detail.currentScrollerPos;
const allowedIndexRange = Math.floor(this.pageSize * 1.5);
static get observers() {
return [
'_dataProviderFilterChanged(filter, dataProvider)',
'_dataProviderClearFilter(dataProvider, opened, value)',
'_warnDataProviderValue(dataProvider, value)',
'_ensureFirstPage(opened)',
];
}
// Ignores the indexes, which are being re-sent during scrolling reset,
// if the corresponding page is around the current scroller position.
// Otherwise, there might be a last pages duplicates, which cause the
// loading indicator hanging and blank items
if (this._shouldSkipIndex(index, allowedIndexRange, currentScrollerPos)) {
return;
}
/** @private */
_dataProviderClearFilter(dataProvider, opened, value) {
// Can't depend on filter in this observer as we don't want
// to clear the filter whenever it's set
if (dataProvider && !this.loading && this.filter) {
this.size = undefined;
this._pendingRequests = {};
this.filter = '';
this.clearCache();
if (index !== undefined) {
const page = this._getPageForIndex(index);
if (this._shouldLoadPage(page)) {
this._loadPage(page);
}
}
});
}
}
/** @protected */
ready() {
super.ready();
this.clearCache();
this.$.overlay.addEventListener('index-requested', e => {
const index = e.detail.index;
const currentScrollerPos = e.detail.currentScrollerPos;
const allowedIndexRange = Math.floor(this.pageSize * 1.5);
// Ignores the indexes, which are being re-sent during scrolling reset,
// if the corresponding page is around the current scroller position.
// Otherwise, there might be a last pages duplicates, which cause the
// loading indicator hanging and blank items
if (this._shouldSkipIndex(index, allowedIndexRange, currentScrollerPos)) {
/** @private */
_dataProviderFilterChanged() {
if (!this._shouldFetchData()) {
return;
}
if (index !== undefined) {
const page = this._getPageForIndex(index);
if (this._shouldLoadPage(page)) {
this._loadPage(page);
}
}
});
}
/** @private */
_dataProviderFilterChanged() {
if (!this._shouldFetchData()) {
return;
this.size = undefined;
this._pendingRequests = {};
this.clearCache();
}
this.size = undefined;
this._pendingRequests = {};
this.clearCache();
}
/** @private */
_shouldFetchData() {
if (!this.dataProvider) {
return false;
}
/** @private */
_shouldFetchData() {
if (!this.dataProvider) {
return false;
return this.opened || (this.filter && this.filter.length);
}
return this.opened ||
(this.filter && this.filter.length);
}
/** @private */
_ensureFirstPage(opened) {
if (opened && this._shouldLoadPage(0)) {
this._loadPage(0);
/** @private */
_ensureFirstPage(opened) {
if (opened && this._shouldLoadPage(0)) {
this._loadPage(0);
}
}
}
/** @private */
_shouldSkipIndex(index, allowedIndexRange, currentScrollerPos) {
return currentScrollerPos !== 0 &&
/** @private */
_shouldSkipIndex(index, allowedIndexRange, currentScrollerPos) {
return (
currentScrollerPos !== 0 &&
index >= currentScrollerPos - allowedIndexRange &&
index <= currentScrollerPos + allowedIndexRange;
}
/** @private */
_shouldLoadPage(page) {
if (!this.filteredItems || this._forceNextRequest) {
this._forceNextRequest = false;
return true;
index <= currentScrollerPos + allowedIndexRange
);
}
const loadedItem = this.filteredItems[page * this.pageSize];
if (loadedItem !== undefined) {
return loadedItem instanceof ComboBoxPlaceholder;
} else {
return this.size === undefined;
/** @private */
_shouldLoadPage(page) {
if (!this.filteredItems || this._forceNextRequest) {
this._forceNextRequest = false;
return true;
}
const loadedItem = this.filteredItems[page * this.pageSize];
if (loadedItem !== undefined) {
return loadedItem instanceof ComboBoxPlaceholder;
} else {
return this.size === undefined;
}
}
}
/** @private */
_loadPage(page) {
// make sure same page isn't requested multiple times.
if (!this._pendingRequests[page] && this.dataProvider) {
this.loading = true;
/** @private */
_loadPage(page) {
// make sure same page isn't requested multiple times.
if (!this._pendingRequests[page] && this.dataProvider) {
this.loading = true;
const params = {
page,
pageSize: this.pageSize,
filter: this.filter
};
const params = {
page,
pageSize: this.pageSize,
filter: this.filter
};
const callback = (items, size) => {
if (this._pendingRequests[page] === callback) {
if (!this.filteredItems) {
const filteredItems = [];
filteredItems.splice(params.page * params.pageSize, items.length, ...items);
this.filteredItems = filteredItems;
} else {
this.splice('filteredItems', params.page * params.pageSize, items.length, ...items);
}
// Update selectedItem from filteredItems if value is set
if (this._isValidValue(this.value) && this._getItemValue(this.selectedItem) !== this.value) {
this._selectItemForValue(this.value);
}
if (!this.opened && !this.hasAttribute('focused')) {
this._commitValue();
}
this.size = size;
const callback = (items, size) => {
if (this._pendingRequests[page] === callback) {
if (!this.filteredItems) {
const filteredItems = [];
filteredItems.splice(params.page * params.pageSize, items.length, ...items);
this.filteredItems = filteredItems;
} else {
this.splice('filteredItems', params.page * params.pageSize, items.length, ...items);
}
// Update selectedItem from filteredItems if value is set
if (this._isValidValue(this.value) && this._getItemValue(this.selectedItem) !== this.value) {
this._selectItemForValue(this.value);
}
if (!this.opened && !this.hasAttribute('focused')) {
this._commitValue();
}
this.size = size;
delete this._pendingRequests[page];
delete this._pendingRequests[page];
if (Object.keys(this._pendingRequests).length === 0) {
this.loading = false;
if (Object.keys(this._pendingRequests).length === 0) {
this.loading = false;
}
if (page === 0 && this.__repositionOverlayDebouncer && items.length > (this.__maxRenderedItems || 0)) {
setTimeout(() => this.__repositionOverlayDebouncer.flush());
this.__maxRenderedItems = items.length;
}
}
if (page === 0 && this.__repositionOverlayDebouncer && items.length > (this.__maxRenderedItems || 0)) {
setTimeout(() => this.__repositionOverlayDebouncer.flush());
this.__maxRenderedItems = items.length;
}
};
if (!this._pendingRequests[page]) {
// Don't request page if it's already being requested
this._pendingRequests[page] = callback;
this.dataProvider(params, callback);
}
};
if (!this._pendingRequests[page]) {
// Don't request page if it's already being requested
this._pendingRequests[page] = callback;
this.dataProvider(params, callback);
}
}
}
/** @private */
_getPageForIndex(index) {
return Math.floor(index / this.pageSize);
}
/**
* Clears the cached pages and reloads data from dataprovider when needed.
*/
clearCache() {
if (!this.dataProvider) {
return;
/** @private */
_getPageForIndex(index) {
return Math.floor(index / this.pageSize);
}
this._pendingRequests = {};
const filteredItems = [];
for (let i = 0; i < (this.size || 0); i++) {
filteredItems.push(this.__placeHolder);
}
this.filteredItems = filteredItems;
if (this._shouldFetchData()) {
this._loadPage(0);
} else {
this._forceNextRequest = true;
}
}
/** @private */
_sizeChanged(size = 0) {
const filteredItems = (this.filteredItems || []).slice(0, size);
for (let i = 0; i < size; i++) {
filteredItems[i] = filteredItems[i] !== undefined ? filteredItems[i] : this.__placeHolder;
/**
* Clears the cached pages and reloads data from dataprovider when needed.
*/
clearCache() {
if (!this.dataProvider) {
return;
}
this._pendingRequests = {};
const filteredItems = [];
for (let i = 0; i < (this.size || 0); i++) {
filteredItems.push(this.__placeHolder);
}
this.filteredItems = filteredItems;
if (this._shouldFetchData()) {
this._loadPage(0);
} else {
this._forceNextRequest = true;
}
}
this.filteredItems = filteredItems;
// Cleans up the redundant pending requests for pages > size
// Refers to https://github.com/vaadin/vaadin-flow-components/issues/229
this._flushPendingRequests(size);
}
/** @private */
_sizeChanged(size = 0) {
const filteredItems = (this.filteredItems || []).slice(0, size);
for (let i = 0; i < size; i++) {
filteredItems[i] = filteredItems[i] !== undefined ? filteredItems[i] : this.__placeHolder;
}
this.filteredItems = filteredItems;
/** @private */
_pageSizeChanged(pageSize, oldPageSize) {
if (Math.floor(pageSize) !== pageSize || pageSize < 1) {
this.pageSize = oldPageSize;
throw new Error('`pageSize` value must be an integer > 0');
// Cleans up the redundant pending requests for pages > size
// Refers to https://github.com/vaadin/vaadin-flow-components/issues/229
this._flushPendingRequests(size);
}
this.clearCache();
}
/** @private */
_dataProviderChanged(dataProvider, oldDataProvider) {
this._ensureItemsOrDataProvider(() => {
this.dataProvider = oldDataProvider;
});
}
/** @private */
_pageSizeChanged(pageSize, oldPageSize) {
if (Math.floor(pageSize) !== pageSize || pageSize < 1) {
this.pageSize = oldPageSize;
throw new Error('`pageSize` value must be an integer > 0');
}
this.clearCache();
}
/** @private */
_ensureItemsOrDataProvider(restoreOldValueCallback) {
if (this.items !== undefined && this.dataProvider !== undefined) {
restoreOldValueCallback();
throw new Error('Using `items` and `dataProvider` together is not supported');
} else if (this.dataProvider && !this.filteredItems) {
this.filteredItems = [];
/** @private */
_dataProviderChanged(dataProvider, oldDataProvider) {
this._ensureItemsOrDataProvider(() => {
this.dataProvider = oldDataProvider;
});
}
}
/** @private */
_warnDataProviderValue(dataProvider, value) {
if (dataProvider && value !== '' && (this.selectedItem === undefined || this.selectedItem === null)) {
const valueIndex = this._indexOfValue(value, this.filteredItems);
if (valueIndex < 0 || !this._getItemLabel(this.filteredItems[valueIndex])) {
/* eslint-disable no-console */
console.warn(
'Warning: unable to determine the label for the provided `value`. ' +
'Nothing to display in the text field. This usually happens when ' +
'setting an initial `value` before any items are returned from ' +
'the `dataProvider` callback. Consider setting `selectedItem` ' +
'instead of `value`'
);
/* eslint-enable no-console */
/** @private */
_ensureItemsOrDataProvider(restoreOldValueCallback) {
if (this.items !== undefined && this.dataProvider !== undefined) {
restoreOldValueCallback();
throw new Error('Using `items` and `dataProvider` together is not supported');
} else if (this.dataProvider && !this.filteredItems) {
this.filteredItems = [];
}
}
}
/**
* This method cleans up the page callbacks which refers to the
* non-existing pages, i.e. which item indexes are greater than the
* changed size.
* This case is basically happens when:
* 1. Users scroll fast to the bottom and combo box generates the
* redundant page request/callback
* 2. Server side uses undefined size lazy loading and suddenly reaches
* the exact size which is on the range edge
* (for default page size = 50, it will be 100, 200, 300, ...).
* @param size the new size of items
* @private
*/
_flushPendingRequests(size) {
if (this._pendingRequests) {
const lastPage = Math.ceil(size / this.pageSize);
const pendingRequestsKeys = Object.keys(this._pendingRequests);
for (let reqIdx = 0; reqIdx < pendingRequestsKeys.length; reqIdx++) {
const page = parseInt(pendingRequestsKeys[reqIdx]);
if (page >= lastPage) {
this._pendingRequests[page]([], size);
/** @private */
_warnDataProviderValue(dataProvider, value) {
if (dataProvider && value !== '' && (this.selectedItem === undefined || this.selectedItem === null)) {
const valueIndex = this._indexOfValue(value, this.filteredItems);
if (valueIndex < 0 || !this._getItemLabel(this.filteredItems[valueIndex])) {
/* eslint-disable no-console */
console.warn(
'Warning: unable to determine the label for the provided `value`. ' +
'Nothing to display in the text field. This usually happens when ' +
'setting an initial `value` before any items are returned from ' +
'the `dataProvider` callback. Consider setting `selectedItem` ' +
'instead of `value`'
);
/* eslint-enable no-console */
}
}
}
}
};
/**
* This method cleans up the page callbacks which refers to the
* non-existing pages, i.e. which item indexes are greater than the
* changed size.
* This case is basically happens when:
* 1. Users scroll fast to the bottom and combo box generates the
* redundant page request/callback
* 2. Server side uses undefined size lazy loading and suddenly reaches
* the exact size which is on the range edge
* (for default page size = 50, it will be 100, 200, 300, ...).
* @param size the new size of items
* @private
*/
_flushPendingRequests(size) {
if (this._pendingRequests) {
const lastPage = Math.ceil(size / this.pageSize);
const pendingRequestsKeys = Object.keys(this._pendingRequests);
for (let reqIdx = 0; reqIdx < pendingRequestsKeys.length; reqIdx++) {
const page = parseInt(pendingRequestsKeys[reqIdx]);
if (page >= lastPage) {
this._pendingRequests[page]([], size);
}
}
}
}
};
/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-list/iron-list.js';

@@ -12,3 +11,2 @@ import './vaadin-combo-box-item.js';

import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';

@@ -27,35 +25,54 @@ const TOUCH_DEVICE = (() => {

*
* @extends PolymerElement
* @extends HTMLElement
* @private
*/
class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
class ComboBoxDropdownWrapperElement extends PolymerElement {
static get template() {
return html`
<vaadin-combo-box-dropdown id="dropdown" hidden="[[_hidden(_items.*, loading)]]" position-target="[[positionTarget]]" on-template-changed="_templateChanged" on-position-changed="_setOverlayHeight" disable-upgrade="" theme="[[theme]]">
<template>
<style>
#scroller {
overflow: auto;
<vaadin-combo-box-dropdown
id="dropdown"
hidden="[[_hidden(_items.*, loading)]]"
position-target="[[positionTarget]]"
on-template-changed="_templateChanged"
on-position-changed="_setOverlayHeight"
disable-upgrade=""
theme="[[theme]]"
>
<template>
<style>
#scroller {
overflow: auto;
/* Fixes item background from getting on top of scrollbars on Safari */
transform: translate3d(0, 0, 0);
/* Fixes item background from getting on top of scrollbars on Safari */
transform: translate3d(0, 0, 0);
/* Enable momentum scrolling on iOS (iron-list v1.2+ no longer does it for us) */
-webkit-overflow-scrolling: touch;
/* Enable momentum scrolling on iOS (iron-list v1.2+ no longer does it for us) */
-webkit-overflow-scrolling: touch;
/* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */
box-shadow: 0 0 0 white;
}
</style>
<div id="scroller" on-click="_stopPropagation">
<iron-list id="selector" role="listbox" items="[[_getItems(opened, _items)]]" scroll-target="[[_scroller]]">
<template>
<vaadin-combo-box-item on-click="_onItemClick" index="[[__requestItemByIndex(item, index, _resetScrolling)]]" item="[[item]]" label="[[getItemLabel(item, _itemLabelPath)]]" selected="[[_isItemSelected(item, _selectedItem, _itemIdPath)]]" renderer="[[renderer]]" role\$="[[_getAriaRole(index)]]" aria-selected\$="[[_getAriaSelected(_focusedIndex,index)]]" focused="[[_isItemFocused(_focusedIndex,index)]]" tabindex="-1" theme\$="[[theme]]">
</vaadin-combo-box-item>
</template>
</iron-list>
</div>
</template>
</vaadin-combo-box-dropdown>
`;
/* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */
box-shadow: 0 0 0 white;
}
</style>
<div id="scroller" on-click="_stopPropagation">
<iron-list id="selector" role="listbox" items="[[_getItems(opened, _items)]]" scroll-target="[[_scroller]]">
<template>
<vaadin-combo-box-item
on-click="_onItemClick"
index="[[__requestItemByIndex(item, index, _resetScrolling)]]"
item="[[item]]"
label="[[getItemLabel(item, _itemLabelPath)]]"
selected="[[_isItemSelected(item, _selectedItem, _itemIdPath)]]"
renderer="[[renderer]]"
role$="[[_getAriaRole(index)]]"
aria-selected$="[[_getAriaSelected(_focusedIndex,index)]]"
focused="[[_isItemFocused(_focusedIndex,index)]]"
tabindex="-1"
theme$="[[theme]]"
></vaadin-combo-box-item>
</template>
</iron-list>
</div>
</template>
</vaadin-combo-box-dropdown>
`;
}

@@ -171,11 +188,16 @@

static get observers() {
return ['_selectorChanged(_selector)', '_loadingChanged(loading)',
return [
'_selectorChanged(_selector)',
'_loadingChanged(loading)',
'_openedChanged(opened, _items, loading)',
'_restoreScrollerPosition(_items)'];
'_restoreScrollerPosition(_items)'
];
}
_fireTouchAction(sourceEvent) {
this.dispatchEvent(new CustomEvent('vaadin-overlay-touch-action', {
detail: {sourceEvent: sourceEvent}
}));
this.dispatchEvent(
new CustomEvent('vaadin-overlay-touch-action', {
detail: { sourceEvent: sourceEvent }
})
);
}

@@ -246,17 +268,10 @@

this.$.dropdown.$.overlay.addEventListener('touchend', e => this._fireTouchAction(e));
this.$.dropdown.$.overlay.addEventListener('touchmove', e => this._fireTouchAction(e));
this.$.dropdown.$.overlay.addEventListener('touchend', (e) => this._fireTouchAction(e));
this.$.dropdown.$.overlay.addEventListener('touchmove', (e) => this._fireTouchAction(e));
// Prevent blurring the input when clicking inside the overlay.
this.$.dropdown.$.overlay.addEventListener('mousedown', e => e.preventDefault());
// IE11: when scrolling with mouse, the focus goes to the scroller.
// This causes the overlay closing due to defocusing the input field.
// Prevent focusing the scroller by setting `unselectable="on"`.
if (/Trident/.test(navigator.userAgent)) {
this._scroller.setAttribute('unselectable', 'on');
}
this.$.dropdown.$.overlay.addEventListener('mousedown', (e) => e.preventDefault());
}
_templateChanged(e) {
_templateChanged() {
if (this.$.dropdown.hasAttribute('disable-upgrade')) {

@@ -282,3 +297,3 @@ return;

_selectorChanged(selector) {
_selectorChanged() {
this._patchWheelOverScrolling();

@@ -294,5 +309,4 @@ }

this._scroller.style.maxHeight = (window.ShadyCSS ?
window.ShadyCSS.getComputedStyleValue(this, '--vaadin-combo-box-overlay-max-height') :
getComputedStyle(this).getPropertyValue('--vaadin-combo-box-overlay-max-height')) || '65vh';
this._scroller.style.maxHeight =
getComputedStyle(this).getPropertyValue('--vaadin-combo-box-overlay-max-height') || '65vh';

@@ -341,3 +355,3 @@ const maxHeight = this._maxOverlayHeight(targetRect);

this.dispatchEvent(new CustomEvent('selection-changed', {detail: {item: e.model.item}}));
this.dispatchEvent(new CustomEvent('selection-changed', { detail: { item: e.model.item } }));
}

@@ -352,4 +366,3 @@

for (let i = 0; i < this._items.length; i++) {
if (this.getItemLabel(this._items[i]).toString().toLowerCase() ===
label.toString().toLowerCase()) {
if (this.getItemLabel(this._items[i]).toString().toLowerCase() === label.toString().toLowerCase()) {
return i;

@@ -370,6 +383,6 @@ }

__requestItemByIndex(item, index, resetScrolling) {
if ((item instanceof ComboBoxPlaceholder) && index !==
undefined && !resetScrolling) {
this.dispatchEvent(new CustomEvent('index-requested', {detail:
{index: index, currentScrollerPos: this._oldScrollerPosition}}));
if (item instanceof ComboBoxPlaceholder && index !== undefined && !resetScrolling) {
this.dispatchEvent(
new CustomEvent('index-requested', { detail: { index, currentScrollerPos: this._oldScrollerPosition } })
);
}

@@ -467,6 +480,6 @@

const selector = this._selector;
selector.addEventListener('wheel', e => {
selector.addEventListener('wheel', (e) => {
const scroller = selector._scroller || selector.scrollTarget;
const scrolledToTop = scroller.scrollTop === 0;
const scrolledToBottom = (scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight) <= 1;
const scrolledToBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight <= 1;

@@ -489,10 +502,9 @@ if (scrolledToTop && e.deltaY < 0) {

const itemsStyle = window.getComputedStyle(this._selector.$.items);
this._cachedViewportTotalPaddingBottom = [
itemsStyle.paddingBottom,
itemsStyle.borderBottomWidth
].map(v => {
return parseInt(v, 10);
}).reduce((sum, v) => {
return sum + v;
});
this._cachedViewportTotalPaddingBottom = [itemsStyle.paddingBottom, itemsStyle.borderBottomWidth]
.map((v) => {
return parseInt(v, 10);
})
.reduce((sum, v) => {
return sum + v;
});
}

@@ -517,15 +529,2 @@

_selectItem(item) {
item = (typeof item === 'number') ? this._items[item] : item;
if (this._selector.selectedItem !== item) {
this._selector.selectItem(item);
}
}
_preventDefault(e) {
if (e.cancelable) {
e.preventDefault();
}
}
_stopPropagation(e) {

@@ -535,4 +534,4 @@ e.stopPropagation();

_hidden(itemsChange) {
return !this.loading && (this._isEmpty(this._items));
_hidden() {
return !this.loading && this._isEmpty(this._items);
}

@@ -539,0 +538,0 @@ }

/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
import { DisableUpgradeMixin } from '@polymer/polymer/lib/mixins/disable-upgrade-mixin.js';
import { OverlayElement } from '@vaadin/vaadin-overlay/src/vaadin-overlay.js';
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
const $_documentContainer = document.createElement('template');
import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
$_documentContainer.innerHTML = `<dom-module id="vaadin-combo-box-overlay-styles" theme-for="vaadin-combo-box-overlay">
<template>
<style>
:host {
width: var(--vaadin-combo-box-overlay-width, var(--_vaadin-combo-box-overlay-default-width, auto));
}
</style>
</template>
</dom-module>`;
registerStyles(
'vaadin-combo-box-overlay',
css`
:host {
width: var(--vaadin-combo-box-overlay-width, var(--_vaadin-combo-box-overlay-default-width, auto));
}
`,
{ moduleId: 'vaadin-combo-box-overlay-styles' }
);
document.head.appendChild($_documentContainer.content);
/**

@@ -36,3 +33,2 @@ * The overlay element.

*
* @extends PolymerElement
* @private

@@ -71,22 +67,28 @@ */

*
* @extends PolymerElement
* @extends HTMLElement
* @private
*/
class ComboBoxDropdownElement extends DisableUpgradeMixin(
mixinBehaviors(IronResizableBehavior, PolymerElement)) {
class ComboBoxDropdownElement extends DisableUpgradeMixin(mixinBehaviors(IronResizableBehavior, PolymerElement)) {
static get template() {
return html`
<style>
:host {
display: block;
}
<style>
:host {
display: block;
}
:host > #overlay {
display: none;
}
</style>
<vaadin-combo-box-overlay id="overlay" hidden\$="[[hidden]]" opened="[[opened]]" template="{{template}}" style="align-items: stretch; margin: 0;" theme\$="[[theme]]">
<slot></slot>
</vaadin-combo-box-overlay>
`;
:host > #overlay {
display: none;
}
</style>
<vaadin-combo-box-overlay
id="overlay"
hidden$="[[hidden]]"
opened="[[opened]]"
template="{{template}}"
style="align-items: stretch; margin: 0;"
theme$="[[theme]]"
>
<slot></slot>
</vaadin-combo-box-overlay>
`;
}

@@ -147,3 +149,3 @@

// Preventing the default modal behaviour of the overlay on input clicking
this.$.overlay.addEventListener('vaadin-overlay-outside-click', e => {
this.$.overlay.addEventListener('vaadin-overlay-outside-click', (e) => {
e.preventDefault();

@@ -193,11 +195,10 @@ });

document.addEventListener('click', this._boundOutsideClickListener, true);
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-opened', {bubbles: true, composed: true}));
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-opened', { bubbles: true, composed: true }));
} else if (!this.__emptyItems) {
window.removeEventListener('scroll', this._boundSetPosition, true);
document.removeEventListener('click', this._boundOutsideClickListener, true);
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-closed', {bubbles: true, composed: true}));
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-closed', { bubbles: true, composed: true }));
}
}
// We need to listen on 'click' event and capture it and close the overlay before

@@ -216,4 +217,5 @@ // propagating the event to the listener in the button. Otherwise, if the clicked button would call

return window.getComputedStyle(element).position === 'fixed' ||
(offsetParent && this._isPositionFixed(offsetParent));
return (
window.getComputedStyle(element).position === 'fixed' || (offsetParent && this._isPositionFixed(offsetParent))
);
}

@@ -240,34 +242,18 @@

_shouldAlignAbove(targetRect) {
const spaceBelow = (
window.innerHeight -
targetRect.bottom -
Math.min(document.body.scrollTop, 0)
) / window.innerHeight;
const spaceBelow =
(window.innerHeight - targetRect.bottom - Math.min(document.body.scrollTop, 0)) / window.innerHeight;
return spaceBelow < 0.30;
return spaceBelow < 0.3;
}
_getCustomWidth() {
return window.ShadyCSS ?
window.ShadyCSS.getComputedStyleValue(this, '--vaadin-combo-box-overlay-width') :
getComputedStyle(this).getPropertyValue('--vaadin-combo-box-overlay-width');
}
_setOverlayWidth() {
const inputWidth = this.positionTarget.clientWidth + 'px';
const customWidth = this._getCustomWidth();
const customWidth = getComputedStyle(this).getPropertyValue('--vaadin-combo-box-overlay-width');
if (window.ShadyCSS && !window.ShadyCSS.nativeCss) {
window.ShadyCSS.styleSubtree(this.$.overlay, {
'--vaadin-combo-box-overlay-width': customWidth,
'--_vaadin-combo-box-overlay-default-width': inputWidth
});
this.$.overlay.style.setProperty('--_vaadin-combo-box-overlay-default-width', inputWidth);
if (customWidth === '') {
this.$.overlay.style.removeProperty('--vaadin-combo-box-overlay-width');
} else {
this.$.overlay.style.setProperty('--_vaadin-combo-box-overlay-default-width', inputWidth);
if (customWidth === '') {
this.$.overlay.style.removeProperty('--vaadin-combo-box-overlay-width');
} else {
this.$.overlay.style.setProperty('--vaadin-combo-box-overlay-width', customWidth);
}
this.$.overlay.style.setProperty('--vaadin-combo-box-overlay-width', customWidth);
}

@@ -293,4 +279,4 @@ }

this._translateX = targetRect.left - overlayRect.left + (this._translateX || 0);
this._translateY = targetRect.top - overlayRect.top + (this._translateY || 0) +
this._verticalOffset(overlayRect, targetRect);
this._translateY =
targetRect.top - overlayRect.top + (this._translateY || 0) + this._verticalOffset(overlayRect, targetRect);

@@ -297,0 +283,0 @@ const _devicePixelRatio = window.devicePixelRatio || 1;

/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { DirMixin } from '@vaadin/vaadin-element-mixin/vaadin-dir-mixin.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
/**

@@ -31,3 +30,2 @@ * The default element used for items in the vaadin-combo-box.

*
* @extends PolymerElement
* @mixes ThemableMixin

@@ -39,13 +37,13 @@ * @private

return html`
<style>
:host {
display: block;
}
<style>
:host {
display: block;
}
:host([hidden]) {
display: none;
}
</style>
<div part="content" id="content"></div>
`;
:host([hidden]) {
display: none;
}
</style>
<div part="content" id="content"></div>
`;
}

@@ -52,0 +50,0 @@

@@ -1,26 +0,9 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* src/vaadin-combo-box-light.js
*/
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
// tslint:disable:variable-name Describing an API that's defined elsewhere.
import { ComboBoxDataProviderMixin } from './vaadin-combo-box-data-provider-mixin.js';
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import { ComboBoxEventMap } from './interfaces';
import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import {ComboBoxMixin} from './vaadin-combo-box-mixin.js';
import {ComboBoxDataProviderMixin} from './vaadin-combo-box-data-provider-mixin.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {dashToCamelCase} from '@polymer/polymer/lib/utils/case-map.js';
/**

@@ -68,10 +51,14 @@ * `<vaadin-combo-box-light>` is a customizable version of the `<vaadin-combo-box>` providing

* ```
*
* @fires {CustomEvent<string>} filter-changed
* @fires {CustomEvent<boolean>} invalid-changed
* @fires {CustomEvent<boolean>} opened-change
* @fires {CustomEvent<unknown>} selected-item-changed
* @fires {CustomEvent<string>} value-changed
*/
declare class ComboBoxLightElement extends
ComboBoxDataProviderMixin(
ComboBoxMixin(
ThemableMixin(
PolymerElement))) {
declare class ComboBoxLightElement extends ComboBoxDataProviderMixin(ComboBoxMixin(ThemableMixin(HTMLElement))) {
readonly _propertyForValue: string;
_inputElementValue: string;
readonly focused: boolean;

@@ -85,15 +72,24 @@

attrForValue: string;
readonly inputElement: Element|undefined;
ready(): void;
connectedCallback(): void;
disconnectedCallback(): void;
readonly inputElement: Element | undefined;
addEventListener<K extends keyof ComboBoxEventMap>(
type: K,
listener: (this: ComboBoxLightElement, ev: ComboBoxEventMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof ComboBoxEventMap>(
type: K,
listener: (this: ComboBoxLightElement, ev: ComboBoxEventMap[K]) => void,
options?: boolean | EventListenerOptions
): void;
}
declare global {
interface HTMLElementTagNameMap {
"vaadin-combo-box-light": ComboBoxLightElement;
'vaadin-combo-box-light': ComboBoxLightElement;
}
}
export {ComboBoxLightElement};
export { ComboBoxLightElement };
/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { dashToCamelCase } from '@polymer/polymer/lib/utils/case-map.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

@@ -12,4 +12,3 @@ import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';

import './vaadin-combo-box-dropdown-wrapper.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { dashToCamelCase } from '@polymer/polymer/lib/utils/case-map.js';
/**

@@ -57,3 +56,10 @@ * `<vaadin-combo-box-light>` is a customizable version of the `<vaadin-combo-box>` providing

* ```
* @extends PolymerElement
*
* @fires {CustomEvent<string>} filter-changed
* @fires {CustomEvent<boolean>} invalid-changed
* @fires {CustomEvent<boolean>} opened-change
* @fires {CustomEvent<unknown>} selected-item-changed
* @fires {CustomEvent<string>} value-changed
*
* @extends HTMLElement
* @mixes ComboBoxDataProviderMixin

@@ -63,19 +69,25 @@ * @mixes ComboBoxMixin

*/
class ComboBoxLightElement extends
ThemableMixin(
ComboBoxDataProviderMixin(
ComboBoxMixin(PolymerElement))) {
class ComboBoxLightElement extends ThemableMixin(ComboBoxDataProviderMixin(ComboBoxMixin(PolymerElement))) {
static get template() {
return html`
<style>
:host([opened]) {
pointer-events: auto;
}
</style>
<style>
:host([opened]) {
pointer-events: auto;
}
</style>
<slot></slot>
<slot></slot>
<vaadin-combo-box-dropdown-wrapper id="overlay" opened="[[opened]]" position-target="[[inputElement]]" renderer="[[renderer]]" _focused-index="[[_focusedIndex]]" _item-id-path="[[itemIdPath]]" _item-label-path="[[itemLabelPath]]" loading="[[loading]]" theme="[[theme]]">
</vaadin-combo-box-dropdown-wrapper>
`;
<vaadin-combo-box-dropdown-wrapper
id="overlay"
opened="[[opened]]"
position-target="[[inputElement]]"
renderer="[[renderer]]"
_focused-index="[[_focusedIndex]]"
_item-id-path="[[itemIdPath]]"
_item-label-path="[[itemLabelPath]]"
loading="[[loading]]"
theme="[[theme]]"
></vaadin-combo-box-dropdown-wrapper>
`;
}

@@ -123,3 +135,3 @@

if (this._clearElement) {
this._clearElement.addEventListener('mousedown', e => {
this._clearElement.addEventListener('mousedown', (e) => {
e.preventDefault(); // Prevent native focus changes

@@ -126,0 +138,0 @@ // _focusableElement is needed for paper-input

@@ -1,39 +0,9 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* src/vaadin-combo-box-mixin.js
*/
import { ComboBoxItem, ComboBoxRenderer } from './interfaces';
// tslint:disable:variable-name Describing an API that's defined elsewhere.
// tslint:disable:no-any describes the API as best we are able today
import {timeOut} from '@polymer/polymer/lib/utils/async.js';
import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';
import {flush} from '@polymer/polymer/lib/utils/flush.js';
import {templatize} from '@polymer/polymer/lib/utils/templatize.js';
import {IronA11yAnnouncer} from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js';
import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
export {ComboBoxMixin};
declare function ComboBoxMixin<T extends new (...args: any[]) => {}>(base: T): T & ComboBoxMixinConstructor;
interface ComboBoxMixinConstructor {
new(...args: any[]): ComboBoxMixin;
new (...args: any[]): ComboBoxMixin;
}
export {ComboBoxMixinConstructor};
interface ComboBoxMixin {

@@ -51,3 +21,3 @@ readonly _propertyForValue: string;

*/
autoOpenDisabled: boolean|null|undefined;
autoOpenDisabled: boolean | null | undefined;

@@ -75,3 +45,3 @@ /**

*/
renderer: ComboBoxRenderer|null|undefined;
renderer: ComboBoxRenderer | null | undefined;

@@ -82,3 +52,3 @@ /**

*/
items: Array<ComboBoxItem|string>|undefined;
items: Array<ComboBoxItem | string> | undefined;

@@ -99,7 +69,6 @@ /**

*/
filteredItems: Array<ComboBoxItem|string>|undefined;
filteredItems: Array<ComboBoxItem | string> | undefined;
/**
* The `String` value for the selected item of the combo box. Provides
* the value for `iron-form`.
* The `String` value for the selected item of the combo box.
*

@@ -117,2 +86,3 @@ * When there’s no item selected, the value is an empty string.

loading: boolean;
_focusedIndex: number;

@@ -128,3 +98,3 @@

*/
selectedItem: ComboBoxItem|string|null|undefined;
selectedItem: ComboBoxItem | string | null | undefined;

@@ -163,3 +133,3 @@ /**

*/
itemIdPath: string|null|undefined;
itemIdPath: string | null | undefined;

@@ -169,3 +139,3 @@ /**

*/
name: string|null|undefined;
name: string | null | undefined;

@@ -176,7 +146,9 @@ /**

invalid: boolean;
_toggleElement: HTMLElement|undefined;
_clearElement: HTMLElement|undefined;
_inputElementValue: string|null|undefined;
ready(): void;
_toggleElement: HTMLElement | undefined;
_clearElement: HTMLElement | undefined;
_inputElementValue: string | null | undefined;
/**

@@ -196,3 +168,3 @@ * Manually invoke existing renderer.

close(): void;
_isEventKey(event: KeyboardEvent, key: string): boolean;
_onEscape(e: KeyboardEvent): void;

@@ -214,2 +186,3 @@

_inputValueChanged(e: Event): void;
_revertInputValue(): void;

@@ -229,11 +202,13 @@

*/
checkValidity(): boolean|undefined;
checkValidity(): boolean | undefined;
_ensureTemplatized(): void;
_preventInputBlur(): void;
_restoreInputBlur(): void;
_stopPropagation(e: Event): void;
}
import {ComboBoxRenderer} from '../@types/interfaces';
import {ComboBoxItem} from '../@types/interfaces';
export { ComboBoxMixin, ComboBoxMixinConstructor };
/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { timeOut } from '@polymer/polymer/lib/utils/async.js';
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';

@@ -12,3 +11,2 @@ import { flush } from '@polymer/polymer/lib/utils/flush.js';

import { IronA11yAnnouncer } from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js';
import { IronA11yKeysBehavior } from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';

@@ -20,1163 +18,1163 @@ import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';

*/
export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extends subclass {
export const ComboBoxMixin = (subclass) =>
class VaadinComboBoxMixinElement extends subclass {
static get properties() {
return {
/**
* True if the dropdown is open, false otherwise.
* @type {boolean}
*/
opened: {
type: Boolean,
notify: true,
value: false,
reflectToAttribute: true,
observer: '_openedChanged'
},
static get properties() {
return {
/**
* True if the dropdown is open, false otherwise.
* @type {boolean}
*/
opened: {
type: Boolean,
notify: true,
value: false,
reflectToAttribute: true,
observer: '_openedChanged'
},
/**
* Set true to prevent the overlay from opening automatically.
* @attr {boolean} auto-open-disabled
*/
autoOpenDisabled: Boolean,
/**
* Set true to prevent the overlay from opening automatically.
* @attr {boolean} auto-open-disabled
*/
autoOpenDisabled: Boolean,
/**
* Set to true to disable this element.
* @type {boolean}
*/
disabled: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Set to true to disable this element.
* @type {boolean}
*/
disabled: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* When present, it specifies that the element field is read-only.
* @type {boolean}
*/
readonly: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* When present, it specifies that the element field is read-only.
* @type {boolean}
*/
readonly: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Custom function for rendering the content of every item.
* Receives three arguments:
*
* - `root` The `<vaadin-combo-box-item>` internal container DOM element.
* - `comboBox` The reference to the `<vaadin-combo-box>` element.
* - `model` The object with the properties related with the rendered
* item, contains:
* - `model.index` The index of the rendered item.
* - `model.item` The item.
* @type {ComboBoxRenderer | undefined}
*/
renderer: Function,
/**
* Custom function for rendering the content of every item.
* Receives three arguments:
*
* - `root` The `<vaadin-combo-box-item>` internal container DOM element.
* - `comboBox` The reference to the `<vaadin-combo-box>` element.
* - `model` The object with the properties related with the rendered
* item, contains:
* - `model.index` The index of the rendered item.
* - `model.item` The item.
* @type {ComboBoxRenderer | undefined}
*/
renderer: Function,
/**
* A full set of items to filter the visible options from.
* The items can be of either `String` or `Object` type.
* @type {!Array<!ComboBoxItem | string> | undefined}
*/
items: {
type: Array,
observer: '_itemsChanged'
},
/**
* A full set of items to filter the visible options from.
* The items can be of either `String` or `Object` type.
* @type {!Array<!ComboBoxItem | string> | undefined}
*/
items: {
type: Array,
observer: '_itemsChanged'
},
/**
* If `true`, the user can input a value that is not present in the items list.
* `value` property will be set to the input value in this case.
* Also, when `value` is set programmatically, the input value will be set
* to reflect that value.
* @attr {boolean} allow-custom-value
* @type {boolean}
*/
allowCustomValue: {
type: Boolean,
value: false
},
/**
* If `true`, the user can input a value that is not present in the items list.
* `value` property will be set to the input value in this case.
* Also, when `value` is set programmatically, the input value will be set
* to reflect that value.
* @attr {boolean} allow-custom-value
* @type {boolean}
*/
allowCustomValue: {
type: Boolean,
value: false
},
/**
* A subset of items, filtered based on the user input. Filtered items
* can be assigned directly to omit the internal filtering functionality.
* The items can be of either `String` or `Object` type.
* @type {!Array<!ComboBoxItem | string> | undefined}
*/
filteredItems: {
type: Array
},
/**
* A subset of items, filtered based on the user input. Filtered items
* can be assigned directly to omit the internal filtering functionality.
* The items can be of either `String` or `Object` type.
* @type {!Array<!ComboBoxItem | string> | undefined}
*/
filteredItems: {
type: Array
},
/**
* The `String` value for the selected item of the combo box.
*
* When there’s no item selected, the value is an empty string.
*
* Use `selectedItem` property to get the raw selected item from
* the `items` array.
* @type {string}
*/
value: {
type: String,
observer: '_valueChanged',
notify: true,
value: ''
},
/**
* The `String` value for the selected item of the combo box. Provides
* the value for `iron-form`.
*
* When there’s no item selected, the value is an empty string.
*
* Use `selectedItem` property to get the raw selected item from
* the `items` array.
* @type {string}
*/
value: {
type: String,
observer: '_valueChanged',
notify: true,
value: ''
},
/**
* Used to detect user value changes and fire `change` events.
* @private
*/
_lastCommittedValue: String,
/**
* Used to detect user value changes and fire `change` events.
* @private
*/
_lastCommittedValue: String,
/**
* When set to `true`, "loading" attribute is added to host and the overlay element.
* @type {boolean}
*/
loading: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* When set to `true`, "loading" attribute is added to host and the overlay element.
* @type {boolean}
*/
loading: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* @type {number}
* @protected
*/
_focusedIndex: {
type: Number,
value: -1
},
/**
* @type {number}
* @protected
*/
_focusedIndex: {
type: Number,
value: -1
},
/**
* Filtering string the user has typed into the input field.
* @type {string}
*/
filter: {
type: String,
value: '',
notify: true
},
/**
* Filtering string the user has typed into the input field.
* @type {string}
*/
filter: {
type: String,
value: '',
notify: true
},
/**
* The selected item from the `items` array.
* @type {ComboBoxItem | string | undefined}
*/
selectedItem: {
type: Object,
notify: true
},
/**
* The selected item from the `items` array.
* @type {ComboBoxItem | string | undefined}
*/
selectedItem: {
type: Object,
notify: true
},
/**
* Path for label of the item. If `items` is an array of objects, the
* `itemLabelPath` is used to fetch the displayed string label for each
* item.
*
* The item label is also used for matching items when processing user
* input, i.e., for filtering and selecting items.
*
* When using item templates, the property is still needed because it is used
* for filtering, and for displaying the selected item value in the input box.
* @attr {string} item-label-path
* @type {string}
*/
itemLabelPath: {
type: String,
value: 'label',
observer: '_itemLabelPathChanged'
},
/**
* Path for label of the item. If `items` is an array of objects, the
* `itemLabelPath` is used to fetch the displayed string label for each
* item.
*
* The item label is also used for matching items when processing user
* input, i.e., for filtering and selecting items.
*
* When using item templates, the property is still needed because it is used
* for filtering, and for displaying the selected item value in the input box.
* @attr {string} item-label-path
* @type {string}
*/
itemLabelPath: {
type: String,
value: 'label',
observer: '_itemLabelPathChanged'
},
/**
* Path for the value of the item. If `items` is an array of objects, the
* `itemValuePath:` is used to fetch the string value for the selected
* item.
*
* The item value is used in the `value` property of the combo box,
* to provide the form value.
* @attr {string} item-value-path
* @type {string}
*/
itemValuePath: {
type: String,
value: 'value'
},
/**
* Path for the value of the item. If `items` is an array of objects, the
* `itemValuePath:` is used to fetch the string value for the selected
* item.
*
* The item value is used in the `value` property of the combo box,
* to provide the form value.
* @attr {string} item-value-path
* @type {string}
*/
itemValuePath: {
type: String,
value: 'value'
},
/**
* Path for the id of the item. If `items` is an array of objects,
* the `itemIdPath` is used to compare and identify the same item
* in `selectedItem` and `filteredItems` (items given by the
* `dataProvider` callback).
* @attr {string} item-id-path
*/
itemIdPath: String,
/**
* Path for the id of the item. If `items` is an array of objects,
* the `itemIdPath` is used to compare and identify the same item
* in `selectedItem` and `filteredItems` (items given by the
* `dataProvider` callback).
* @attr {string} item-id-path
*/
itemIdPath: String,
/**
* The name of this element.
*/
name: {
type: String
},
/**
* The name of this element.
*/
name: {
type: String
},
/**
* Set to true if the value is invalid.
* @type {boolean}
*/
invalid: {
type: Boolean,
reflectToAttribute: true,
notify: true,
value: false
},
/**
* Set to true if the value is invalid.
* @type {boolean}
*/
invalid: {
type: Boolean,
reflectToAttribute: true,
notify: true,
value: false
},
/**
* @type {!HTMLElement | undefined}
* @protected
*/
_toggleElement: Object,
/**
* @type {!HTMLElement | undefined}
* @protected
*/
_toggleElement: Object,
/**
* @type {!HTMLElement | undefined}
* @protected
*/
_clearElement: Object,
/**
* @type {!HTMLElement | undefined}
* @protected
*/
_clearElement: Object,
/** @protected */
_inputElementValue: String,
/** @protected */
_inputElementValue: String,
/** @private */
_closeOnBlurIsPrevented: Boolean,
/** @private */
_closeOnBlurIsPrevented: Boolean,
/** @private */
_previousDocumentPointerEvents: String,
/** @private */
_previousDocumentPointerEvents: String,
/** @private */
_itemTemplate: Object
};
}
/** @private */
_itemTemplate: Object
};
}
static get observers() {
return [
'_filterChanged(filter, itemValuePath, itemLabelPath)',
'_itemsOrPathsChanged(items.*, itemValuePath, itemLabelPath)',
'_filteredItemsChanged(filteredItems.*, itemValuePath, itemLabelPath)',
'_templateOrRendererChanged(_itemTemplate, renderer)',
'_loadingChanged(loading)',
'_selectedItemChanged(selectedItem, itemLabelPath)',
'_toggleElementChanged(_toggleElement)'
];
}
static get observers() {
return [
'_filterChanged(filter, itemValuePath, itemLabelPath)',
'_itemsOrPathsChanged(items.*, itemValuePath, itemLabelPath)',
'_filteredItemsChanged(filteredItems.*, itemValuePath, itemLabelPath)',
'_templateOrRendererChanged(_itemTemplate, renderer)',
'_loadingChanged(loading)',
'_selectedItemChanged(selectedItem, itemLabelPath)',
'_toggleElementChanged(_toggleElement)'
];
}
constructor() {
super();
this._boundOnFocusout = this._onFocusout.bind(this);
this._boundOverlaySelectedItemChanged = this._overlaySelectedItemChanged.bind(this);
this._boundClose = this.close.bind(this);
this._boundOnOpened = this._onOpened.bind(this);
this._boundOnKeyDown = this._onKeyDown.bind(this);
this._boundOnClick = this._onClick.bind(this);
this._boundOnOverlayTouchAction = this._onOverlayTouchAction.bind(this);
this._boundOnTouchend = this._onTouchend.bind(this);
}
constructor() {
super();
this._boundOnFocusout = this._onFocusout.bind(this);
this._boundOverlaySelectedItemChanged = this._overlaySelectedItemChanged.bind(this);
this._boundClose = this.close.bind(this);
this._boundOnOpened = this._onOpened.bind(this);
this._boundOnKeyDown = this._onKeyDown.bind(this);
this._boundOnClick = this._onClick.bind(this);
this._boundOnOverlayTouchAction = this._onOverlayTouchAction.bind(this);
this._boundOnTouchend = this._onTouchend.bind(this);
}
/** @protected */
ready() {
super.ready();
/** @protected */
ready() {
super.ready();
this.addEventListener('focusout', this._boundOnFocusout);
this.addEventListener('focusout', this._boundOnFocusout);
this._lastCommittedValue = this.value;
IronA11yAnnouncer.requestAvailability();
this._lastCommittedValue = this.value;
IronA11yAnnouncer.requestAvailability();
// 2.0 does not support 'overlay.selection-changed' syntax in listeners
this.$.overlay.addEventListener('selection-changed', this._boundOverlaySelectedItemChanged);
// 2.0 does not support 'overlay.selection-changed' syntax in listeners
this.$.overlay.addEventListener('selection-changed', this._boundOverlaySelectedItemChanged);
this.addEventListener('vaadin-combo-box-dropdown-closed', this._boundClose);
this.addEventListener('vaadin-combo-box-dropdown-opened', this._boundOnOpened);
this.addEventListener('keydown', this._boundOnKeyDown);
this.addEventListener('click', this._boundOnClick);
this.addEventListener('vaadin-combo-box-dropdown-closed', this._boundClose);
this.addEventListener('vaadin-combo-box-dropdown-opened', this._boundOnOpened);
this.addEventListener('keydown', this._boundOnKeyDown);
this.addEventListener('click', this._boundOnClick);
this.$.overlay.addEventListener('vaadin-overlay-touch-action', this._boundOnOverlayTouchAction);
this.$.overlay.addEventListener('vaadin-overlay-touch-action', this._boundOnOverlayTouchAction);
this.addEventListener('touchend', this._boundOnTouchend);
this.addEventListener('touchend', this._boundOnTouchend);
this._observer = new FlattenedNodesObserver(this, (info) => {
this._setTemplateFromNodes(info.addedNodes);
});
this._observer = new FlattenedNodesObserver(this, info => {
this._setTemplateFromNodes(info.addedNodes);
});
const bringToFrontListener = () => {
const overlay = this.$.overlay;
const dropdown = overlay && overlay.$.dropdown;
// Check dropdown.$ because overlay is lazily instantiated
if (dropdown && dropdown.$) {
requestAnimationFrame(() => {
dropdown.$.overlay.bringToFront();
});
}
};
const bringToFrontListener = (e) => {
const overlay = this.$.overlay;
const dropdown = overlay && overlay.$.dropdown;
// Check dropdown.$ because overlay is lazily instantiated
if (dropdown && dropdown.$ && this.$.overlay.$.dropdown.$.overlay.bringToFront) {
requestAnimationFrame(() => {
dropdown.$.overlay.bringToFront();
});
}
};
this.addEventListener('mousedown', bringToFrontListener);
this.addEventListener('touchstart', bringToFrontListener);
}
/**
* Manually invoke existing renderer.
*/
render() {
if (this.$.overlay._selector) {
this.$.overlay._selector.querySelectorAll('vaadin-combo-box-item').forEach(item => item._render());
this.addEventListener('mousedown', bringToFrontListener);
this.addEventListener('touchstart', bringToFrontListener);
}
}
/** @private */
_setTemplateFromNodes(nodes) {
this._itemTemplate = nodes.filter(node => node.localName && node.localName === 'template')[0] || this._itemTemplate;
}
/** @private */
_removeNewRendererOrTemplate(template, oldTemplate, renderer, oldRenderer) {
if (template !== oldTemplate) {
this._itemTemplate = undefined;
} else if (renderer !== oldRenderer) {
this.renderer = undefined;
/**
* Manually invoke existing renderer.
*/
render() {
if (this.$.overlay._selector) {
this.$.overlay._selector.querySelectorAll('vaadin-combo-box-item').forEach((item) => item._render());
}
}
}
/** @private */
_templateOrRendererChanged(template, renderer) {
if (template && renderer) {
this._removeNewRendererOrTemplate(template, this._oldTemplate, renderer, this._oldRenderer);
throw new Error('You should only use either a renderer or a template for combo box items');
/** @private */
_setTemplateFromNodes(nodes) {
this._itemTemplate =
nodes.filter((node) => node.localName && node.localName === 'template')[0] || this._itemTemplate;
}
this._oldTemplate = template;
this._oldRenderer = renderer;
}
/**
* Opens the dropdown list.
*/
open() {
// Prevent _open() being called when input is disabled or read-only
if (!this.disabled && !this.readonly) {
this.opened = true;
/** @private */
_removeNewRendererOrTemplate(template, oldTemplate, renderer, oldRenderer) {
if (template !== oldTemplate) {
this._itemTemplate = undefined;
} else if (renderer !== oldRenderer) {
this.renderer = undefined;
}
}
}
/**
* Closes the dropdown list.
*/
close() {
this.opened = false;
}
/** @private */
_templateOrRendererChanged(template, renderer) {
if (template && renderer) {
this._removeNewRendererOrTemplate(template, this._oldTemplate, renderer, this._oldRenderer);
throw new Error('You should only use either a renderer or a template for combo box items');
}
/** @private */
_openedChanged(value, old) {
// Prevent _close() being called when opened is set to its default value (false).
if (old === undefined) {
return;
this._oldTemplate = template;
this._oldRenderer = renderer;
}
if (this.opened) {
this._openedWithFocusRing = this.hasAttribute('focus-ring') || (this.focusElement && this.focusElement.hasAttribute('focus-ring'));
// For touch devices, we don't want to popup virtual keyboard unless input is explicitly focused by the user.
if (!this.hasAttribute('focused') && !this.$.overlay.touchDevice) {
this.focus();
/**
* Opens the dropdown list.
*/
open() {
// Prevent _open() being called when input is disabled or read-only
if (!this.disabled && !this.readonly) {
this.opened = true;
}
} else {
this._onClosed();
if (this._openedWithFocusRing && this.hasAttribute('focused')) {
this.focusElement.setAttribute('focus-ring', '');
}
}
}
/** @private */
_onOverlayTouchAction(event) {
// On touch devices, blur the input on touch start inside the overlay, in order to hide
// the virtual keyboard. But don't close the overlay on this blur.
this._closeOnBlurIsPrevented = true;
this.inputElement.blur();
this._closeOnBlurIsPrevented = false;
}
/**
* Closes the dropdown list.
*/
close() {
this.opened = false;
}
/** @private */
_onClick(e) {
this._closeOnBlurIsPrevented = true;
/** @private */
_openedChanged(value, old) {
// Prevent _close() being called when opened is set to its default value (false).
if (old === undefined) {
return;
}
const path = e.composedPath();
const isClearElement = (path.indexOf(this._clearElement) !== -1) || (path[0].getAttribute('part') === 'clear-button');
if (isClearElement) {
this._clear();
this.focus();
} else if (path.indexOf(this.inputElement) !== -1) {
if (path.indexOf(this._toggleElement) > -1 && this.opened) {
this.close();
} else if (path.indexOf(this._toggleElement) > -1 || !this.autoOpenDisabled) {
this.open();
if (this.opened) {
this._openedWithFocusRing =
this.hasAttribute('focus-ring') || (this.focusElement && this.focusElement.hasAttribute('focus-ring'));
// For touch devices, we don't want to popup virtual keyboard unless input is explicitly focused by the user.
if (!this.hasAttribute('focused') && !this.$.overlay.touchDevice) {
this.focus();
}
} else {
this._onClosed();
if (this._openedWithFocusRing && this.hasAttribute('focused')) {
this.focusElement.setAttribute('focus-ring', '');
}
}
}
this._closeOnBlurIsPrevented = false;
}
/**
* Keyboard navigation
* @private
*/
_onKeyDown(e) {
if (this._isEventKey(e, 'down')) {
/** @private */
_onOverlayTouchAction() {
// On touch devices, blur the input on touch start inside the overlay, in order to hide
// the virtual keyboard. But don't close the overlay on this blur.
this._closeOnBlurIsPrevented = true;
this._onArrowDown();
this.inputElement.blur();
this._closeOnBlurIsPrevented = false;
}
// prevent caret from moving
e.preventDefault();
} else if (this._isEventKey(e, 'up')) {
/** @private */
_onClick(e) {
this._closeOnBlurIsPrevented = true;
this._onArrowUp();
const path = e.composedPath();
const isClearElement = path.indexOf(this._clearElement) !== -1 || path[0].getAttribute('part') === 'clear-button';
if (isClearElement) {
this._clear();
this.focus();
} else if (path.indexOf(this.inputElement) !== -1) {
if (path.indexOf(this._toggleElement) > -1 && this.opened) {
this.close();
} else if (path.indexOf(this._toggleElement) > -1 || !this.autoOpenDisabled) {
this.open();
}
}
this._closeOnBlurIsPrevented = false;
// prevent caret from moving
e.preventDefault();
} else if (this._isEventKey(e, 'enter')) {
this._onEnter(e);
} else if (this._isEventKey(e, 'esc')) {
this._onEscape(e);
}
}
/**
* @param {!KeyboardEvent} event
* @param {string} key
* @return {boolean}
* @protected
*/
_isEventKey(event, key) {
return IronA11yKeysBehavior.keyboardEventMatchesKeys(event, key);
}
/**
* Keyboard navigation
* @private
*/
_onKeyDown(e) {
if (e.keyCode === 40) {
this._closeOnBlurIsPrevented = true;
this._onArrowDown();
this._closeOnBlurIsPrevented = false;
/** @private */
_getItemLabel(item) {
return this.$.overlay.getItemLabel(item);
}
// prevent caret from moving
e.preventDefault();
} else if (e.keyCode === 38) {
this._closeOnBlurIsPrevented = true;
this._onArrowUp();
this._closeOnBlurIsPrevented = false;
/** @private */
_getItemValue(item) {
let value = item && this.itemValuePath ? this.get(this.itemValuePath, item) : undefined;
if (value === undefined) {
value = item ? item.toString() : '';
// prevent caret from moving
e.preventDefault();
} else if (e.keyCode === 13) {
this._onEnter(e);
} else if (e.keyCode === 27) {
this._onEscape(e);
}
}
return value;
}
/** @private */
_onArrowDown() {
if (this.opened) {
if (this.$.overlay._items) {
this._focusedIndex = Math.min(this.$.overlay._items.length - 1, this._focusedIndex + 1);
this._prefillFocusedItemLabel();
/** @private */
_getItemLabel(item) {
return this.$.overlay.getItemLabel(item);
}
/** @private */
_getItemValue(item) {
let value = item && this.itemValuePath ? this.get(this.itemValuePath, item) : undefined;
if (value === undefined) {
value = item ? item.toString() : '';
}
} else {
this.open();
return value;
}
}
/** @private */
_onArrowUp() {
if (this.opened) {
if (this._focusedIndex > -1) {
this._focusedIndex = Math.max(0, this._focusedIndex - 1);
} else {
/** @private */
_onArrowDown() {
if (this.opened) {
if (this.$.overlay._items) {
this._focusedIndex = this.$.overlay._items.length - 1;
this._focusedIndex = Math.min(this.$.overlay._items.length - 1, this._focusedIndex + 1);
this._prefillFocusedItemLabel();
}
} else {
this.open();
}
}
this._prefillFocusedItemLabel();
} else {
this.open();
/** @private */
_onArrowUp() {
if (this.opened) {
if (this._focusedIndex > -1) {
this._focusedIndex = Math.max(0, this._focusedIndex - 1);
} else {
if (this.$.overlay._items) {
this._focusedIndex = this.$.overlay._items.length - 1;
}
}
this._prefillFocusedItemLabel();
} else {
this.open();
}
}
}
/** @private */
_prefillFocusedItemLabel() {
if (this._focusedIndex > -1) {
// Reset the input value asyncronously to prevent partial value changes
// announce. Makes OSX VoiceOver to announce the complete value instead.
this._inputElementValue = '';
// 1ms delay needed for OSX VoiceOver to realise input value was reset
setTimeout(() => {
this._inputElementValue = this._getItemLabel(this.$.overlay._focusedItem);
this._markAllSelectionRange();
}, 1);
/** @private */
_prefillFocusedItemLabel() {
if (this._focusedIndex > -1) {
// Reset the input value asyncronously to prevent partial value changes
// announce. Makes OSX VoiceOver to announce the complete value instead.
this._inputElementValue = '';
// 1ms delay needed for OSX VoiceOver to realise input value was reset
setTimeout(() => {
this._inputElementValue = this._getItemLabel(this.$.overlay._focusedItem);
this._markAllSelectionRange();
}, 1);
}
}
}
/** @private */
_setSelectionRange(start, end) {
// vaadin-text-field does not implement setSelectionRange, hence we need the native input
const input = this._nativeInput || this.inputElement;
/** @private */
_setSelectionRange(start, end) {
// vaadin-text-field does not implement setSelectionRange, hence we need the native input
const input = this._nativeInput || this.inputElement;
// Setting selection range focuses and/or moves the caret in some browsers,
// and there's no need to modify the selection range if the input isn't focused anyway.
// This affects Safari. When the overlay is open, and then hiting tab, browser should focus
// the next focusable element instead of the combo-box itself.
// Checking the focused property here is enough instead of checking the activeElement.
if (this.hasAttribute('focused') && input && input.setSelectionRange) {
try {
// Setting selection range focuses and/or moves the caret in some browsers,
// and there's no need to modify the selection range if the input isn't focused anyway.
// This affects Safari. When the overlay is open, and then hiting tab, browser should focus
// the next focusable element instead of the combo-box itself.
// Checking the focused property here is enough instead of checking the activeElement.
if (this.hasAttribute('focused') && input && input.setSelectionRange) {
input.setSelectionRange(start, end);
} catch (ignore) {
// IE11 randomly fails when running tests in Sauce.
}
}
}
/** @private */
_markAllSelectionRange() {
if (this._inputElementValue !== undefined) {
this._setSelectionRange(0, this._inputElementValue.length);
/** @private */
_markAllSelectionRange() {
if (this._inputElementValue !== undefined) {
this._setSelectionRange(0, this._inputElementValue.length);
}
}
}
/** @private */
_clearSelectionRange() {
if (this._inputElementValue !== undefined) {
const pos = this._inputElementValue ? this._inputElementValue.length : 0;
this._setSelectionRange(pos, pos);
/** @private */
_clearSelectionRange() {
if (this._inputElementValue !== undefined) {
const pos = this._inputElementValue ? this._inputElementValue.length : 0;
this._setSelectionRange(pos, pos);
}
}
}
/** @private */
_closeOrCommit() {
if (!this.opened && !this.loading) {
this._commitValue();
} else {
this.close();
/** @private */
_closeOrCommit() {
if (!this.opened && !this.loading) {
this._commitValue();
} else {
this.close();
}
}
}
/** @private */
_onEnter(e) {
// should close on enter when custom values are allowed, input field is cleared, or when an existing
// item is focused with keyboard. If auto open is disabled, under the same conditions, commit value.
if ((this.opened || this.autoOpenDisabled) && (this.allowCustomValue || this._inputElementValue === '' || this._focusedIndex > -1)) {
this._closeOrCommit();
/** @private */
_onEnter(e) {
// should close on enter when custom values are allowed, input field is cleared, or when an existing
// item is focused with keyboard. If auto open is disabled, under the same conditions, commit value.
if (
(this.opened || this.autoOpenDisabled) &&
(this.allowCustomValue || this._inputElementValue === '' || this._focusedIndex > -1)
) {
this._closeOrCommit();
// Do not submit the surrounding form.
e.preventDefault();
// Do not submit the surrounding form.
e.preventDefault();
// Do not trigger global listeners
e.stopPropagation();
// Do not trigger global listeners
e.stopPropagation();
}
}
}
/**
* @param {!KeyboardEvent} e
* @protected
*/
_onEscape(e) {
if (this.autoOpenDisabled) {
this._focusedIndex = -1;
this.cancel();
} else if (this.opened) {
this._stopPropagation(e);
if (this._focusedIndex > -1) {
/**
* @param {!KeyboardEvent} e
* @protected
*/
_onEscape(e) {
if (this.autoOpenDisabled) {
this._focusedIndex = -1;
this._revertInputValue();
} else {
this.cancel();
}
}
}
} else if (this.opened) {
this._stopPropagation(e);
/** @private */
_toggleElementChanged(toggleElement) {
if (toggleElement) {
// Don't blur the input on toggle mousedown
toggleElement.addEventListener('mousedown', e => e.preventDefault());
// Unfocus previously focused element if focus is not inside combo box (on touch devices)
toggleElement.addEventListener('click', e => {
if (this.$.overlay.touchDevice && !this.hasAttribute('focused')) {
document.activeElement.blur();
if (this._focusedIndex > -1) {
this._focusedIndex = -1;
this._revertInputValue();
} else {
this.cancel();
}
});
}
}
}
/**
* Clears the current value.
* @protected
*/
_clear() {
this.selectedItem = null;
if (this.allowCustomValue) {
this.value = '';
/** @private */
_toggleElementChanged(toggleElement) {
if (toggleElement) {
// Don't blur the input on toggle mousedown
toggleElement.addEventListener('mousedown', (e) => e.preventDefault());
// Unfocus previously focused element if focus is not inside combo box (on touch devices)
toggleElement.addEventListener('click', () => {
if (this.$.overlay.touchDevice && !this.hasAttribute('focused')) {
document.activeElement.blur();
}
});
}
}
this._detectAndDispatchChange();
}
/**
* Clears the current value.
* @protected
*/
_clear() {
this.selectedItem = null;
/**
* Reverts back to original value.
*/
cancel() {
this._revertInputValueToValue();
// In the next _detectAndDispatchChange() call, the change detection should not pass
this._lastCommittedValue = this.value;
this._closeOrCommit();
}
if (this.allowCustomValue) {
this.value = '';
}
/** @private */
_onOpened() {
// Pre P2 iron-list used a debouncer to render. Now that we synchronously render items,
// we need to flush the DOM to make sure it doesn't get flushed in the middle of _render call
// because that will cause problems to say the least.
flush();
this._detectAndDispatchChange();
}
// With iron-list v1.3.9, calling `notifyResize()` no longer renders
// the items synchronously. It is required to have the items rendered
// before we update the overlay and the list positions and sizes.
this.$.overlay.ensureItemsRendered();
this.$.overlay._selector.toggleScrollListener(true);
/**
* Reverts back to original value.
*/
cancel() {
this._revertInputValueToValue();
// In the next _detectAndDispatchChange() call, the change detection should not pass
this._lastCommittedValue = this.value;
this._closeOrCommit();
}
// Ensure metrics are up-to-date
this.$.overlay.updateViewportBoundaries();
// Force iron-list to create reusable nodes. Otherwise it will only start
// doing that in scroll listener, which is especially slow in Edge.
this.$.overlay._selector._increasePoolIfNeeded();
setTimeout(() => this._resizeDropdown(), 1);
// Defer scroll position adjustment to prevent freeze in Edge
window.requestAnimationFrame(() => this.$.overlay.adjustScrollPosition());
/** @private */
_onOpened() {
// Pre P2 iron-list used a debouncer to render. Now that we synchronously render items,
// we need to flush the DOM to make sure it doesn't get flushed in the middle of _render call
// because that will cause problems to say the least.
flush();
// With iron-list v1.3.9, calling `notifyResize()` no longer renders
// the items synchronously. It is required to have the items rendered
// before we update the overlay and the list positions and sizes.
this.$.overlay.ensureItemsRendered();
this.$.overlay._selector.toggleScrollListener(true);
// _detectAndDispatchChange() should not consider value changes done before opening
this._lastCommittedValue = this.value;
}
// Ensure metrics are up-to-date
this.$.overlay.updateViewportBoundaries();
// Force iron-list to create reusable nodes. Otherwise it will only start
// doing that in scroll listener, which might affect performance.
// See https://github.com/vaadin/vaadin-combo-box/pull/776
this.$.overlay._selector._increasePoolIfNeeded();
setTimeout(() => this._resizeDropdown(), 1);
// Defer scroll position adjustment to improve performance.
window.requestAnimationFrame(() => this.$.overlay.adjustScrollPosition());
/** @private */
_onClosed() {
// Happens when the overlay is closed by clicking outside
if (this.opened) {
this.close();
// _detectAndDispatchChange() should not consider value changes done before opening
this._lastCommittedValue = this.value;
}
if (!this.loading || this.allowCustomValue) {
this._commitValue();
}
}
/** @private */
_commitValue() {
if (this.$.overlay._items && this._focusedIndex > -1) {
const focusedItem = this.$.overlay._items[this._focusedIndex];
if (this.selectedItem !== focusedItem) {
this.selectedItem = focusedItem;
/** @private */
_onClosed() {
// Happens when the overlay is closed by clicking outside
if (this.opened) {
this.close();
}
// make sure input field is updated in case value doesn't change (i.e. FOO -> foo)
this._inputElementValue = this._getItemLabel(this.selectedItem);
} else if (this._inputElementValue === '' || this._inputElementValue === undefined) {
this.selectedItem = null;
if (this.allowCustomValue) {
this.value = '';
if (!this.loading || this.allowCustomValue) {
this._commitValue();
}
} else {
const itemsMatchedByLabel = this.filteredItems
&& this.filteredItems.filter(item => this._getItemLabel(item) === this._inputElementValue)
|| [];
if (this.allowCustomValue
// to prevent a repetitive input value being saved after pressing ESC and Tab.
&& !itemsMatchedByLabel.length) {
}
const e = new CustomEvent('custom-value-set', {detail: this._inputElementValue, composed: true, cancelable: true, bubbles: true});
this.dispatchEvent(e);
if (!e.defaultPrevented) {
const customValue = this._inputElementValue;
this._selectItemForValue(customValue);
this.value = customValue;
/** @private */
_commitValue() {
if (this.$.overlay._items && this._focusedIndex > -1) {
const focusedItem = this.$.overlay._items[this._focusedIndex];
if (this.selectedItem !== focusedItem) {
this.selectedItem = focusedItem;
}
} else if (!this.allowCustomValue && !this.opened && itemsMatchedByLabel.length == 1) {
this.value = this._getItemValue(itemsMatchedByLabel[0]);
// make sure input field is updated in case value doesn't change (i.e. FOO -> foo)
this._inputElementValue = this._getItemLabel(this.selectedItem);
} else if (this._inputElementValue === '' || this._inputElementValue === undefined) {
this.selectedItem = null;
if (this.allowCustomValue) {
this.value = '';
}
} else {
this._inputElementValue = this.selectedItem ? this._getItemLabel(this.selectedItem) : (this.value || '');
const itemsMatchedByLabel =
(this.filteredItems &&
this.filteredItems.filter((item) => this._getItemLabel(item) === this._inputElementValue)) ||
[];
if (
this.allowCustomValue &&
// to prevent a repetitive input value being saved after pressing ESC and Tab.
!itemsMatchedByLabel.length
) {
const e = new CustomEvent('custom-value-set', {
detail: this._inputElementValue,
composed: true,
cancelable: true,
bubbles: true
});
this.dispatchEvent(e);
if (!e.defaultPrevented) {
const customValue = this._inputElementValue;
this._selectItemForValue(customValue);
this.value = customValue;
}
} else if (!this.allowCustomValue && !this.opened && itemsMatchedByLabel.length == 1) {
this.value = this._getItemValue(itemsMatchedByLabel[0]);
} else {
this._inputElementValue = this.selectedItem ? this._getItemLabel(this.selectedItem) : this.value || '';
}
}
}
this._detectAndDispatchChange();
this._detectAndDispatchChange();
this._clearSelectionRange();
this._clearSelectionRange();
if (!this.dataProvider) {
this.filter = '';
if (!this.dataProvider) {
this.filter = '';
}
}
}
/**
* @return {string}
* @protected
*/
get _propertyForValue() {
return 'value';
}
/**
* Filtering and items handling
* @param {!Event} e
* @protected
*/
_inputValueChanged(e) {
// Handle only input events from our inputElement.
if (e.composedPath().indexOf(this.inputElement) !== -1) {
this._inputElementValue = this.inputElement[this._propertyForValue];
this._filterFromInput(e);
/**
* @return {string}
* @protected
*/
get _propertyForValue() {
return 'value';
}
}
/** @private */
_filterFromInput(e) {
if (!this.opened && !e.__fromClearButton && !this.autoOpenDisabled) {
this.open();
/**
* Filtering and items handling
* @param {!Event} e
* @protected
*/
_inputValueChanged(e) {
// Handle only input events from our inputElement.
if (e.composedPath().indexOf(this.inputElement) !== -1) {
this._inputElementValue = this.inputElement[this._propertyForValue];
this._filterFromInput(e);
}
}
if (this.filter === this._inputElementValue) {
// Filter and input value might get out of sync, while keyboard navigating for example.
// Afterwards, input value might be changed to the same value as used in filtering.
// In situation like these, we need to make sure all the filter changes handlers are run.
this._filterChanged(this.filter, this.itemValuePath, this.itemLabelPath);
} else {
this.filter = this._inputElementValue;
}
}
/** @private */
_filterFromInput(e) {
if (!this.opened && !e.__fromClearButton && !this.autoOpenDisabled) {
this.open();
}
/** @private */
_itemLabelPathChanged(itemLabelPath, oldItemLabelPath) {
if (typeof itemLabelPath !== 'string') {
console.error('You should set itemLabelPath to a valid string');
if (this.filter === this._inputElementValue) {
// Filter and input value might get out of sync, while keyboard navigating for example.
// Afterwards, input value might be changed to the same value as used in filtering.
// In situation like these, we need to make sure all the filter changes handlers are run.
this._filterChanged(this.filter, this.itemValuePath, this.itemLabelPath);
} else {
this.filter = this._inputElementValue;
}
}
}
/** @private */
_filterChanged(filter, itemValuePath, itemLabelPath) {
if (filter === undefined) {
return;
/** @private */
_itemLabelPathChanged(itemLabelPath) {
if (typeof itemLabelPath !== 'string') {
console.error('You should set itemLabelPath to a valid string');
}
}
// Notify the dropdown about filter changing, so to let it skip the
// scrolling restore
this.$.overlay.filterChanged = true;
/** @private */
_filterChanged(filter, itemValuePath, itemLabelPath) {
if (filter === undefined) {
return;
}
if (this.items) {
this.filteredItems = this._filterItems(this.items, filter);
} else {
// With certain use cases (e. g., external filtering), `items` are
// undefined. Filtering is unnecessary per se, but the filteredItems
// observer should still be invoked to update focused item.
this._filteredItemsChanged({path: 'filteredItems', value: this.filteredItems}, itemValuePath, itemLabelPath);
// Notify the dropdown about filter changing, so to let it skip the
// scrolling restore
this.$.overlay.filterChanged = true;
if (this.items) {
this.filteredItems = this._filterItems(this.items, filter);
} else {
// With certain use cases (e. g., external filtering), `items` are
// undefined. Filtering is unnecessary per se, but the filteredItems
// observer should still be invoked to update focused item.
this._filteredItemsChanged({ path: 'filteredItems', value: this.filteredItems }, itemValuePath, itemLabelPath);
}
}
}
/** @private */
_loadingChanged(loading) {
if (loading) {
this._focusedIndex = -1;
/** @private */
_loadingChanged(loading) {
if (loading) {
this._focusedIndex = -1;
}
}
}
/** @protected */
_revertInputValue() {
if (this.filter !== '') {
this._inputElementValue = this.filter;
} else {
this._revertInputValueToValue();
/** @protected */
_revertInputValue() {
if (this.filter !== '') {
this._inputElementValue = this.filter;
} else {
this._revertInputValueToValue();
}
this._clearSelectionRange();
}
this._clearSelectionRange();
}
/** @private */
_revertInputValueToValue() {
if (this.allowCustomValue && !this.selectedItem) {
this._inputElementValue = this.value;
} else {
this._inputElementValue = this._getItemLabel(this.selectedItem);
/** @private */
_revertInputValueToValue() {
if (this.allowCustomValue && !this.selectedItem) {
this._inputElementValue = this.value;
} else {
this._inputElementValue = this._getItemLabel(this.selectedItem);
}
}
}
/** @private */
_resizeDropdown() {
this.$.overlay.$.dropdown.notifyResize();
}
/** @private */
_resizeDropdown() {
this.$.overlay.$.dropdown.notifyResize();
}
/** @private */
_updateHasValue(hasValue) {
if (hasValue) {
this.setAttribute('has-value', '');
} else {
this.removeAttribute('has-value');
/** @private */
_updateHasValue(hasValue) {
if (hasValue) {
this.setAttribute('has-value', '');
} else {
this.removeAttribute('has-value');
}
}
}
/** @private */
_selectedItemChanged(selectedItem, itemLabelPath) {
if (selectedItem === null || selectedItem === undefined) {
if (this.filteredItems) {
if (!this.allowCustomValue) {
this.value = '';
/** @private */
_selectedItemChanged(selectedItem) {
if (selectedItem === null || selectedItem === undefined) {
if (this.filteredItems) {
if (!this.allowCustomValue) {
this.value = '';
}
this._updateHasValue(this.value !== '');
this._inputElementValue = this.value;
}
this._updateHasValue(this.value !== '');
this._inputElementValue = this.value;
}
} else {
const value = this._getItemValue(selectedItem);
if (this.value !== value) {
this.value = value;
} else {
const value = this._getItemValue(selectedItem);
if (this.value !== value) {
// The value was changed to something else in value-changed listener,
// so prevent from resetting it to the previous value.
return;
this.value = value;
if (this.value !== value) {
// The value was changed to something else in value-changed listener,
// so prevent from resetting it to the previous value.
return;
}
}
}
this._updateHasValue(true);
this._inputElementValue = this._getItemLabel(selectedItem);
this._updateHasValue(true);
this._inputElementValue = this._getItemLabel(selectedItem);
// Could not be defined in 1.x because ready is called after all prop-setters
if (this.inputElement) {
this.inputElement[this._propertyForValue] = this._inputElementValue;
// Could not be defined in 1.x because ready is called after all prop-setters
if (this.inputElement) {
this.inputElement[this._propertyForValue] = this._inputElementValue;
}
}
}
this.$.overlay._selectedItem = selectedItem;
if (this.filteredItems && this.$.overlay._items) {
this._focusedIndex = this.filteredItems.indexOf(selectedItem);
this.$.overlay._selectedItem = selectedItem;
if (this.filteredItems && this.$.overlay._items) {
this._focusedIndex = this.filteredItems.indexOf(selectedItem);
}
}
}
/** @private */
_valueChanged(value, oldVal) {
if (value === '' && oldVal === undefined) { // initializing, no need to do anything (#554)
return;
}
/** @private */
_valueChanged(value, oldVal) {
if (value === '' && oldVal === undefined) {
// initializing, no need to do anything (#554)
return;
}
if (this._isValidValue(value)) {
let item;
if (this._getItemValue(this.selectedItem) !== value) {
this._selectItemForValue(value);
if (this._isValidValue(value)) {
let item;
if (this._getItemValue(this.selectedItem) !== value) {
this._selectItemForValue(value);
} else {
item = this.selectedItem;
}
if (!item && this.allowCustomValue) {
this._inputElementValue = value;
}
this._updateHasValue(this.value !== '');
} else {
item = this.selectedItem;
this.selectedItem = null;
}
// In the next _detectAndDispatchChange() call, the change detection should pass
this._lastCommittedValue = undefined;
}
if (!item && this.allowCustomValue) {
this._inputElementValue = value;
/** @private */
_detectAndDispatchChange() {
if (this.value !== this._lastCommittedValue) {
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
this._lastCommittedValue = this.value;
}
this._updateHasValue(this.value !== '');
} else {
this.selectedItem = null;
}
// In the next _detectAndDispatchChange() call, the change detection should pass
this._lastCommittedValue = undefined;
}
/** @private */
_detectAndDispatchChange() {
if (this.value !== this._lastCommittedValue) {
this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
this._lastCommittedValue = this.value;
/** @private */
_itemsChanged(items, oldItems) {
this._ensureItemsOrDataProvider(() => {
this.items = oldItems;
});
}
}
/** @private */
_itemsChanged(items, oldItems) {
this._ensureItemsOrDataProvider(() => {
this.items = oldItems;
});
}
/** @private */
_itemsOrPathsChanged(e) {
if (e.path === 'items' || e.path === 'items.splices') {
if (this.items) {
this.filteredItems = this.items.slice(0);
} else if (this.__previousItems) {
// Only clear filteredItems if the component had items previously but got cleared
this.filteredItems = null;
}
/** @private */
_itemsOrPathsChanged(e, itemValuePath, itemLabelPath) {
if (e.path === 'items' || e.path === 'items.splices') {
if (this.items) {
this.filteredItems = this.items.slice(0);
} else if (this.__previousItems) {
// Only clear filteredItems if the component had items previously but got cleared
this.filteredItems = null;
}
const valueIndex = this._indexOfValue(this.value, this.items);
this._focusedIndex = valueIndex;
const valueIndex = this._indexOfValue(this.value, this.items);
this._focusedIndex = valueIndex;
const item = valueIndex > -1 && this.items[valueIndex];
if (item) {
this.selectedItem = item;
const item = valueIndex > -1 && this.items[valueIndex];
if (item) {
this.selectedItem = item;
}
}
this.__previousItems = e.value;
}
this.__previousItems = e.value;
}
/** @private */
_filteredItemsChanged(e, itemValuePath, itemLabelPath) {
if (e.path === 'filteredItems' || e.path === 'filteredItems.splices') {
this._setOverlayItems(this.filteredItems);
/** @private */
_filteredItemsChanged(e) {
if (e.path === 'filteredItems' || e.path === 'filteredItems.splices') {
this._setOverlayItems(this.filteredItems);
this._focusedIndex = this.opened || this.autoOpenDisabled ?
this.$.overlay.indexOfLabel(this.filter) :
this._indexOfValue(this.value, this.filteredItems);
this._focusedIndex =
this.opened || this.autoOpenDisabled
? this.$.overlay.indexOfLabel(this.filter)
: this._indexOfValue(this.value, this.filteredItems);
if (this.opened) {
this._repositionOverlay();
if (this.opened) {
this._repositionOverlay();
}
}
}
}
/** @private */
_filterItems(arr, filter) {
if (!arr) {
return arr;
}
/** @private */
_filterItems(arr, filter) {
if (!arr) {
return arr;
}
const filteredItems = arr.filter(item => {
filter = filter ? filter.toString().toLowerCase() : '';
// Check if item contains input value.
return this._getItemLabel(item).toString().toLowerCase().indexOf(filter) > -1;
});
const filteredItems = arr.filter((item) => {
filter = filter ? filter.toString().toLowerCase() : '';
// Check if item contains input value.
return this._getItemLabel(item).toString().toLowerCase().indexOf(filter) > -1;
});
return filteredItems;
}
return filteredItems;
}
/** @private */
_selectItemForValue(value) {
const valueIndex = this._indexOfValue(value, this.filteredItems);
const previouslySelectedItem = this.selectedItem;
/** @private */
_selectItemForValue(value) {
const valueIndex = this._indexOfValue(value, this.filteredItems);
const previouslySelectedItem = this.selectedItem;
this.selectedItem = valueIndex >= 0
? this.filteredItems[valueIndex]
: (this.dataProvider && this.selectedItem === undefined)
? undefined
: null;
this.selectedItem =
valueIndex >= 0
? this.filteredItems[valueIndex]
: this.dataProvider && this.selectedItem === undefined
? undefined
: null;
if (this.selectedItem === null && previouslySelectedItem === null) {
this._selectedItemChanged(this.selectedItem);
if (this.selectedItem === null && previouslySelectedItem === null) {
this._selectedItemChanged(this.selectedItem);
}
}
}
/** @private */
_setOverlayItems(items) {
this.$.overlay.set('_items', items);
}
/** @private */
_setOverlayItems(items) {
this.$.overlay.set('_items', items);
}
/** @private */
_repositionOverlay() {
// async needed to reposition correctly after filtering
// (especially when aligned on top of input)
this.__repositionOverlayDebouncer = Debouncer.debounce(
this.__repositionOverlayDebouncer,
// Long debounce: sizing updates invoke multiple styling rounds,
// which is very slow in Edge
timeOut.after(500),
() => {
const selector = this.$.overlay._selector;
if (!selector._isClientFull()) {
// Due to the mismatch of the Y position of the item rendered
// at the top of the scrolling list with some specific scroll
// position values (2324, 3486, 6972, 60972, 95757 etc.)
// iron-list loops the increasing of the pool and adds
// too many items to the DOM.
// Adjusting scroll position to equal the current scrollTop value
// to avoid looping.
selector._resetScrollPosition(selector._physicalTop);
/** @private */
_repositionOverlay() {
// async needed to reposition correctly after filtering
// (especially when aligned on top of input)
this.__repositionOverlayDebouncer = Debouncer.debounce(
this.__repositionOverlayDebouncer,
// Long debounce: sizing updates invoke multiple styling rounds,
// which might affect performance, especially in old browsers.
// See https://github.com/vaadin/vaadin-combo-box/pull/800
timeOut.after(500),
() => {
const selector = this.$.overlay._selector;
if (!selector._isClientFull()) {
// Due to the mismatch of the Y position of the item rendered
// at the top of the scrolling list with some specific scroll
// position values (2324, 3486, 6972, 60972, 95757 etc.)
// iron-list loops the increasing of the pool and adds
// too many items to the DOM.
// Adjusting scroll position to equal the current scrollTop value
// to avoid looping.
selector._resetScrollPosition(selector._physicalTop);
}
this._resizeDropdown();
this.$.overlay.updateViewportBoundaries();
this.$.overlay.ensureItemsRendered();
selector.notifyResize();
flush();
}
this._resizeDropdown();
this.$.overlay.updateViewportBoundaries();
this.$.overlay.ensureItemsRendered();
selector.notifyResize();
flush();
}
);
}
);
}
/** @private */
_indexOfValue(value, items) {
if (items && this._isValidValue(value)) {
for (let i = 0; i < items.length; i++) {
if (this._getItemValue(items[i]) === value) {
return i;
/** @private */
_indexOfValue(value, items) {
if (items && this._isValidValue(value)) {
for (let i = 0; i < items.length; i++) {
if (this._getItemValue(items[i]) === value) {
return i;
}
}
}
return -1;
}
return -1;
}
/**
* Checks if the value is supported as an item value in this control.
* @private
*/
_isValidValue(value) {
return value !== undefined && value !== null;
}
/**
* Checks if the value is supported as an item value in this control.
* @private
*/
_isValidValue(value) {
return value !== undefined && value !== null;
}
/** @private */
_overlaySelectedItemChanged(e) {
// stop this private event from leaking outside.
e.stopPropagation();
/** @private */
_overlaySelectedItemChanged(e) {
// stop this private event from leaking outside.
e.stopPropagation();
if (e.detail.item instanceof ComboBoxPlaceholder) {
// Placeholder items should not be selectable.
return;
}
if (e.detail.item instanceof ComboBoxPlaceholder) {
// Placeholder items should not be selectable.
return;
if (this.opened) {
this._focusedIndex = this.filteredItems.indexOf(e.detail.item);
this.close();
} else if (this.selectedItem !== e.detail.item) {
this.selectedItem = e.detail.item;
this._detectAndDispatchChange();
}
}
if (this.opened) {
this._focusedIndex = this.filteredItems.indexOf(e.detail.item);
this.close();
} else if (this.selectedItem !== e.detail.item) {
this.selectedItem = e.detail.item;
this._detectAndDispatchChange();
/** @private */
_onFocusout(event) {
// Fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
const dropdown = this.$.overlay.$.dropdown;
if (dropdown && dropdown.$ && event.relatedTarget === dropdown.$.overlay) {
event.composedPath()[0].focus();
return;
}
if (!this._closeOnBlurIsPrevented) {
this._closeOrCommit();
}
}
}
/** @private */
_onFocusout(event) {
// Fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
const dropdown = this.$.overlay.$.dropdown;
if (dropdown && dropdown.$ && event.relatedTarget === dropdown.$.overlay) {
event.composedPath()[0].focus();
return;
/** @private */
_onTouchend(event) {
if (!this._clearElement || event.composedPath()[0] !== this._clearElement) {
return;
}
event.preventDefault();
this._clear();
}
if (!this._closeOnBlurIsPrevented) {
this._closeOrCommit();
/**
* Returns true if `value` is valid, and sets the `invalid` flag appropriately.
*
* @return {boolean} True if the value is valid and sets the `invalid` flag appropriately
*/
validate() {
return !(this.invalid = !this.checkValidity());
}
}
/** @private */
_onTouchend(event) {
if (!this._clearElement || event.composedPath()[0] !== this._clearElement) {
return;
/**
* Returns true if the current input value satisfies all constraints (if any)
*
* You can override the `checkValidity` method for custom validations.
* @return {boolean | undefined}
*/
checkValidity() {
if (this.inputElement.validate) {
return this.inputElement.validate();
}
}
event.preventDefault();
this._clear();
}
/** @private */
get _instanceProps() {
return {
item: true,
index: true,
selected: true,
focused: true
};
}
/**
* Returns true if `value` is valid, and sets the `invalid` flag appropriately.
*
* @return {boolean} True if the value is valid and sets the `invalid` flag appropriately
*/
validate() {
return !(this.invalid = !this.checkValidity());
}
/** @protected */
_ensureTemplatized() {
if (!this._TemplateClass) {
const tpl = this._itemTemplate || this._getRootTemplate();
if (tpl) {
this._TemplateClass = templatize(tpl, this, {
instanceProps: this._instanceProps,
forwardHostProp: function (prop, value) {
const items = this.$.overlay._selector.querySelectorAll('vaadin-combo-box-item');
Array.prototype.forEach.call(items, (item) => {
if (item._itemTemplateInstance) {
item._itemTemplateInstance.set(prop, value);
item._itemTemplateInstance.notifyPath(prop, value, true);
}
});
}
});
}
}
}
/**
* Returns true if the current input value satisfies all constraints (if any)
*
* You can override the `checkValidity` method for custom validations.
* @return {boolean | undefined}
*/
checkValidity() {
if (this.inputElement.validate) {
return this.inputElement.validate();
/** @private */
_getRootTemplate() {
return Array.prototype.filter.call(this.children, (elem) => elem.tagName === 'TEMPLATE')[0];
}
}
/** @private */
get _instanceProps() {
return {
item: true,
index: true,
selected: true,
focused: true
};
}
/** @protected */
_preventInputBlur() {
if (this._toggleElement) {
this._toggleElement.addEventListener('click', this._preventDefault);
}
if (this._clearElement) {
this._clearElement.addEventListener('click', this._preventDefault);
}
}
/** @protected */
_ensureTemplatized() {
if (!this._TemplateClass) {
const tpl = this._itemTemplate || this._getRootTemplate();
if (tpl) {
this._TemplateClass = templatize(tpl, this, {
instanceProps: this._instanceProps,
forwardHostProp: function(prop, value) {
const items = this.$.overlay._selector.querySelectorAll('vaadin-combo-box-item');
Array.prototype.forEach.call(items, item => {
if (item._itemTemplateInstance) {
item._itemTemplateInstance.set(prop, value);
item._itemTemplateInstance.notifyPath(prop, value, true);
}
});
}
});
/** @protected */
_restoreInputBlur() {
if (this._toggleElement) {
this._toggleElement.removeEventListener('click', this._preventDefault);
}
if (this._clearElement) {
this._clearElement.removeEventListener('click', this._preventDefault);
}
}
}
/** @private */
_getRootTemplate() {
return Array.prototype.filter.call(this.children, elem => elem.tagName === 'TEMPLATE')[0];
}
/** @protected */
_preventInputBlur() {
if (this._toggleElement) {
this._toggleElement.addEventListener('click', this._preventDefault);
/** @private */
_preventDefault(e) {
e.preventDefault();
}
if (this._clearElement) {
this._clearElement.addEventListener('click', this._preventDefault);
}
}
/** @protected */
_restoreInputBlur() {
if (this._toggleElement) {
this._toggleElement.removeEventListener('click', this._preventDefault);
/**
* @param {!Event} e
* @protected
*/
_stopPropagation(e) {
e.stopPropagation();
}
if (this._clearElement) {
this._clearElement.removeEventListener('click', this._preventDefault);
}
}
/** @private */
_preventDefault(e) {
e.preventDefault();
}
/**
* Fired when the value changes.
*
* @event value-changed
* @param {Object} detail
* @param {String} detail.value the combobox value
*/
/**
* @param {!Event} e
* @protected
*/
_stopPropagation(e) {
e.stopPropagation();
}
/**
* Fired when selected item changes.
*
* @event selected-item-changed
* @param {Object} detail
* @param {Object|String} detail.value the selected item. Type is the same as the type of `items`.
*/
/**
* Fired when the value changes.
*
* @event value-changed
* @param {Object} detail
* @param {String} detail.value the combobox value
*/
/**
* Fired when the user sets a custom value.
* @event custom-value-set
* @param {String} detail the custom value
*/
/**
* Fired when selected item changes.
*
* @event selected-item-changed
* @param {Object} detail
* @param {Object|String} detail.value the selected item. Type is the same as the type of `items`.
*/
/**
* Fired when the user sets a custom value.
* @event custom-value-set
* @param {String} detail the custom value
*/
/**
* Fired when value changes.
* To comply with https://developer.mozilla.org/en-US/docs/Web/Events/change
* @event change
*/
};
/**
* Fired when value changes.
* To comply with https://developer.mozilla.org/en-US/docs/Web/Events/change
* @event change
*/
};
/**
@license
Copyright (c) 2018 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
/*

@@ -7,0 +8,0 @@ * Placeholder object class representing items being loaded.

@@ -1,28 +0,15 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* src/vaadin-combo-box.js
*/
import { TextFieldElement } from '@vaadin/vaadin-text-field/vaadin-text-field';
import { ControlStateMixin } from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
// tslint:disable:variable-name Describing an API that's defined elsewhere.
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
import {ControlStateMixin} from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
import { ComboBoxDataProviderMixin } from './vaadin-combo-box-data-provider-mixin.js';
import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import {ComboBoxMixin} from './vaadin-combo-box-mixin.js';
import { ComboBoxEventMap } from './interfaces';
import {ComboBoxDataProviderMixin} from './vaadin-combo-box-data-provider-mixin.js';
import {ElementMixin} from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
/**

@@ -51,4 +38,2 @@ * `<vaadin-combo-box>` is a combo box element combining a dropdown list with an

*
* This element can be used within an `iron-form`.
*
* ### Item rendering

@@ -183,11 +168,12 @@ *

* See [ThemableMixin – how to apply styles for shadow parts](https://github.com/vaadin/vaadin-themable-mixin/wiki)
*
* @fires {CustomEvent<string>} filter-changed
* @fires {CustomEvent<boolean>} invalid-changed
* @fires {CustomEvent<boolean>} opened-change
* @fires {CustomEvent<unknown>} selected-item-changed
* @fires {CustomEvent<string>} value-changed
*/
declare class ComboBoxElement extends
ElementMixin(
ControlStateMixin(
ComboBoxDataProviderMixin(
ComboBoxMixin(
ThemableMixin(
PolymerElement))))) {
declare class ComboBoxElement extends ElementMixin(
ControlStateMixin(ComboBoxDataProviderMixin(ComboBoxMixin(ThemableMixin(HTMLElement))))
) {
/**

@@ -197,2 +183,3 @@ * Focusable element used by vaadin-control-state-mixin

readonly focusElement: HTMLElement;
autofocus: boolean;

@@ -204,9 +191,14 @@

disabled: boolean;
/**
* Set to true to prevent the user from picking a value or typing in the input.
*/
readonly: boolean;
readonly inputElement: TextFieldElement|undefined;
readonly inputElement: TextFieldElement | undefined;
/**
* The label for this element.
*/
label: string|null|undefined;
label: string | null | undefined;

@@ -222,3 +214,3 @@ /**

*/
preventInvalidInput: boolean|null|undefined;
preventInvalidInput: boolean | null | undefined;

@@ -228,3 +220,3 @@ /**

*/
pattern: string|null|undefined;
pattern: string | null | undefined;

@@ -235,3 +227,3 @@ /**

*/
errorMessage: string|null|undefined;
errorMessage: string | null | undefined;

@@ -247,3 +239,3 @@ /**

*/
helperText: string|null|undefined;
helperText: string | null | undefined;

@@ -255,17 +247,22 @@ /**

clearButtonVisible: boolean;
ready(): void;
attributeChangedCallback(name: string, oldValue: string|null, newValue: string|null): void;
connectedCallback(): void;
disconnectedCallback(): void;
addEventListener<K extends keyof ComboBoxEventMap>(
type: K,
listener: (this: ComboBoxElement, ev: ComboBoxEventMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof ComboBoxEventMap>(
type: K,
listener: (this: ComboBoxElement, ev: ComboBoxEventMap[K]) => void,
options?: boolean | EventListenerOptions
): void;
}
declare global {
interface HTMLElementTagNameMap {
"vaadin-combo-box": ComboBoxElement;
'vaadin-combo-box': ComboBoxElement;
}
}
export {ComboBoxElement};
import {TextFieldElement} from '@vaadin/vaadin-text-field/vaadin-text-field';
export { ComboBoxElement };
/**
@license
Copyright (c) 2017 Vaadin Ltd.
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '@vaadin/vaadin-text-field/src/vaadin-text-field.js';
* @license
* Copyright (c) 2020 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { ControlStateMixin } from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
import '@vaadin/vaadin-text-field/src/vaadin-text-field.js';
import './vaadin-combo-box-dropdown-wrapper.js';
import { ComboBoxDataProviderMixin } from './vaadin-combo-box-data-provider-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
/**

@@ -39,4 +38,2 @@ * `<vaadin-combo-box>` is a combo box element combining a dropdown list with an

*
* This element can be used within an `iron-form`.
*
* ### Item rendering

@@ -172,3 +169,9 @@ *

*
* @extends PolymerElement
* @fires {CustomEvent<string>} filter-changed
* @fires {CustomEvent<boolean>} invalid-changed
* @fires {CustomEvent<boolean>} opened-change
* @fires {CustomEvent<unknown>} selected-item-changed
* @fires {CustomEvent<string>} value-changed
*
* @extends HTMLElement
* @mixes ElementMixin

@@ -179,42 +182,68 @@ * @mixes ControlStateMixin

* @mixes ThemableMixin
* @demo demo/index.html
*/
class ComboBoxElement extends
ElementMixin(
ControlStateMixin(
ThemableMixin(
ComboBoxDataProviderMixin(
ComboBoxMixin(PolymerElement))))) {
class ComboBoxElement extends ElementMixin(
ControlStateMixin(ThemableMixin(ComboBoxDataProviderMixin(ComboBoxMixin(PolymerElement))))
) {
static get template() {
return html`
<style>
:host {
display: inline-block;
}
<style>
:host {
display: inline-block;
}
:host([hidden]) {
display: none !important;
}
:host([hidden]) {
display: none !important;
}
:host([opened]) {
pointer-events: auto;
}
:host([opened]) {
pointer-events: auto;
}
[part="text-field"] {
width: 100%;
min-width: 0;
}
</style>
[part='text-field'] {
width: 100%;
min-width: 0;
}
</style>
<vaadin-text-field part="text-field" id="input" pattern="[[pattern]]" prevent-invalid-input="[[preventInvalidInput]]" value="{{_inputElementValue}}" autocomplete="off" invalid="[[invalid]]" label="[[label]]" name="[[name]]" placeholder="[[placeholder]]" required="[[required]]" disabled="[[disabled]]" readonly="[[readonly]]" helper-text="[[helperText]]" error-message="[[errorMessage]]" autocapitalize="none" autofocus="[[autofocus]]" on-change="_stopPropagation" on-input="_inputValueChanged" clear-button-visible="[[clearButtonVisible]]" theme\$="[[theme]]">
<slot name="prefix" slot="prefix"></slot>
<slot name="helper" slot="helper">[[helperText]]</slot>
<vaadin-text-field
part="text-field"
id="input"
pattern="[[pattern]]"
prevent-invalid-input="[[preventInvalidInput]]"
value="{{_inputElementValue}}"
autocomplete="off"
invalid="[[invalid]]"
label="[[label]]"
name="[[name]]"
placeholder="[[placeholder]]"
required="[[required]]"
disabled="[[disabled]]"
readonly="[[readonly]]"
helper-text="[[helperText]]"
error-message="[[errorMessage]]"
autocapitalize="none"
autofocus="[[autofocus]]"
on-change="_stopPropagation"
on-input="_inputValueChanged"
clear-button-visible="[[clearButtonVisible]]"
theme$="[[theme]]"
>
<slot name="prefix" slot="prefix"></slot>
<slot name="helper" slot="helper">[[helperText]]</slot>
<div part="toggle-button" id="toggleButton" slot="suffix" role="button" aria-label="Toggle"></div>
<div part="toggle-button" id="toggleButton" slot="suffix" role="button" aria-label="Toggle"></div>
</vaadin-text-field>
</vaadin-text-field>
<vaadin-combo-box-dropdown-wrapper id="overlay" opened="[[opened]]" renderer="[[renderer]]" position-target="[[_getPositionTarget()]]" _focused-index="[[_focusedIndex]]" _item-id-path="[[itemIdPath]]" _item-label-path="[[itemLabelPath]]" loading="[[loading]]" theme="[[theme]]">
</vaadin-combo-box-dropdown-wrapper>
`;
<vaadin-combo-box-dropdown-wrapper
id="overlay"
opened="[[opened]]"
renderer="[[renderer]]"
position-target="[[_getPositionTarget()]]"
_focused-index="[[_focusedIndex]]"
_item-id-path="[[itemIdPath]]"
_item-label-path="[[itemLabelPath]]"
loading="[[loading]]"
theme="[[theme]]"
></vaadin-combo-box-dropdown-wrapper>
`;
}

@@ -235,3 +264,3 @@

static get version() {
return '5.4.7';
return '6.0.0-alpha1';
}

@@ -313,6 +342,10 @@

/** @type {boolean} */
/**
* Set to true to prevent the user from picking a value or typing in the input.
* @type {boolean}
*/
readonly: {
type: Boolean,
value: false
value: false,
reflectToAttribute: true
},

@@ -336,21 +369,2 @@

/**
* @param {string} name
* @param {?string} oldValue
* @param {?string} newValue
* @protected
*/
attributeChangedCallback(name, oldValue, newValue) {
super.attributeChangedCallback(name, oldValue, newValue);
// Safari has an issue with repainting shadow root element styles when a host attribute changes.
// Need this workaround (toggle any inline css property on and off) until the issue gets fixed.
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari && this.root) {
Array.prototype.forEach.call(this.root.querySelectorAll('*'), el => {
el.style['-webkit-backface-visibility'] = 'visible';
el.style['-webkit-backface-visibility'] = '';
});
}
}
/** @protected */

@@ -370,10 +384,14 @@ ready() {

// breaks a bit on Safari and some related tests fail.
this.inputElement.addEventListener('keydown', e => {
if (this._isEventKey(e, 'esc')) {
this._stopPropagation(e);
// Trigger _onEscape method of vaadin-combo-box-mixin because
// bubbling phase is not reached.
this._onEscape(e);
}
}, true);
this.inputElement.addEventListener(
'keydown',
(e) => {
if (e.keyCode === 27) {
this._stopPropagation(e);
// Trigger _onEscape method of vaadin-combo-box-mixin because
// bubbling phase is not reached.
this._onEscape(e);
}
},
true
);

@@ -380,0 +398,0 @@ this._nativeInput.setAttribute('role', 'combobox');

@@ -0,1 +1,2 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-lumo-styles/color.js';

@@ -6,97 +7,82 @@ import '@vaadin/vaadin-lumo-styles/spacing.js';

import '@vaadin/vaadin-lumo-styles/mixins/menu-overlay.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="lumo-combo-box-overlay" theme-for="vaadin-combo-box-overlay">
<template>
<style include="lumo-overlay lumo-menu-overlay-core">
[part="content"] {
padding: 0;
}
registerStyles(
'vaadin-combo-box-overlay',
css`
[part='content'] {
padding: 0;
}
:host {
/* TODO: using a legacy mixin (unsupported) */
--iron-list-items-container: {
border-width: var(--lumo-space-xs);
border-style: solid;
border-color: transparent;
};
:host {
/* TODO: using a legacy mixin (unsupported) */
--iron-list-items-container: {
border-width: var(--lumo-space-xs);
border-style: solid;
border-color: transparent;
}
}
/* TODO: workaround ShadyCSS issue when using inside of the dom-if */
:host([opened]) {
--iron-list-items-container_-_border-width: var(--lumo-space-xs);
--iron-list-items-container_-_border-style: solid;
--iron-list-items-container_-_border-color: transparent;
}
/* Loading state */
/* 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));
}
/* When items are empty, the sinner needs some room */
:host(:not([closing])) [part~="content"] {
min-height: calc(2 * var(--lumo-space-s) + var(--lumo-icon-size-s));
}
[part~='overlay'] {
position: relative;
}
[part~="overlay"] {
position: relative;
}
:host([loading]) [part~='loader'] {
box-sizing: border-box;
width: var(--lumo-icon-size-s);
height: var(--lumo-icon-size-s);
position: absolute;
z-index: 1;
left: var(--lumo-space-s);
right: var(--lumo-space-s);
top: var(--lumo-space-s);
margin-left: auto;
margin-inline-start: auto;
margin-inline-end: 0;
border: 2px solid transparent;
border-color: var(--lumo-primary-color-50pct) var(--lumo-primary-color-50pct) var(--lumo-primary-color)
var(--lumo-primary-color);
border-radius: calc(0.5 * var(--lumo-icon-size-s));
opacity: 0;
animation: 1s linear infinite lumo-combo-box-loader-rotate, 0.3s 0.1s lumo-combo-box-loader-fade-in both;
pointer-events: none;
}
:host([loading]) [part~="loader"] {
box-sizing: border-box;
width: var(--lumo-icon-size-s);
height: var(--lumo-icon-size-s);
position: absolute;
z-index: 1;
left: var(--lumo-space-s);
right: var(--lumo-space-s);
top: var(--lumo-space-s);
margin-left: auto;
margin-inline-start: auto;
margin-inline-end: 0;
border: 2px solid transparent;
border-color:
var(--lumo-primary-color-50pct)
var(--lumo-primary-color-50pct)
var(--lumo-primary-color)
var(--lumo-primary-color);
border-radius: calc(0.5 * var(--lumo-icon-size-s));
@keyframes lumo-combo-box-loader-fade-in {
0% {
opacity: 0;
animation:
1s linear infinite lumo-combo-box-loader-rotate,
.3s .1s lumo-combo-box-loader-fade-in both;
pointer-events: none;
}
@keyframes lumo-combo-box-loader-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
100% {
opacity: 1;
}
@keyframes lumo-combo-box-loader-rotate {
0% {
transform: rotate(0deg);
}
@keyframes lumo-combo-box-loader-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
100% {
transform: rotate(360deg);
}
}
/* RTL specific styles */
/* RTL specific styles */
:host([loading][dir="rtl"]) [part~="loader"] {
left: auto;
margin-left: 0;
margin-right: auto;
margin-inline-start: 0;
margin-inline-end: auto;
}
</style>
</template>
</dom-module>`;
document.head.appendChild($_documentContainer.content);
:host([loading][dir='rtl']) [part~='loader'] {
left: auto;
margin-left: 0;
margin-right: auto;
margin-inline-start: 0;
margin-inline-end: auto;
}
`,
{ moduleId: 'lumo-combo-box-overlay', include: ['lumo-overlay', 'lumo-menu-overlay-core'] }
);

@@ -0,1 +1,2 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-lumo-styles/color.js';

@@ -5,52 +6,48 @@ import '@vaadin/vaadin-lumo-styles/spacing.js';

import '@vaadin/vaadin-item/theme/lumo/vaadin-item.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="lumo-combo-box-item" theme-for="vaadin-combo-box-item">
<template>
<style include="lumo-item">
/* TODO partly duplicated from vaadin-list-box styles. Should find a way to make it DRY */
/* TODO partly duplicated from vaadin-list-box styles. Should find a way to make it DRY */
registerStyles(
'vaadin-combo-box-item',
css`
:host {
cursor: default;
-webkit-tap-highlight-color: var(--lumo-primary-color-10pct);
padding-left: calc(var(--lumo-border-radius) / 4);
padding-right: calc(var(--lumo-space-l) + var(--lumo-border-radius) / 4);
transition: background-color 100ms;
border-radius: var(--lumo-border-radius);
overflow: hidden;
--_lumo-item-selected-icon-display: block;
}
:host {
cursor: default;
-webkit-tap-highlight-color: var(--lumo-primary-color-10pct);
padding-left: calc(var(--lumo-border-radius) / 4);
padding-right: calc(var(--lumo-space-l) + var(--lumo-border-radius) / 4);
transition: background-color 100ms;
border-radius: var(--lumo-border-radius);
overflow: hidden;
--_lumo-item-selected-icon-display: block;
}
/* ShadyCSS workaround (show the selected item checkmark) */
:host::before {
display: block;
}
/* ShadyCSS workaround (show the selected item checkmark) */
:host::before {
display: block;
}
:host(:hover) {
background-color: var(--lumo-primary-color-10pct);
}
:host([focused]:not([disabled])) {
box-shadow: inset 0 0 0 2px var(--lumo-primary-color-50pct);
}
@media (pointer: coarse) {
:host(:hover) {
background-color: var(--lumo-primary-color-10pct);
background-color: transparent;
}
:host([focused]:not([disabled])) {
box-shadow: inset 0 0 0 2px var(--lumo-primary-color-50pct);
box-shadow: none;
}
}
@media (pointer: coarse) {
:host(:hover) {
background-color: transparent;
}
:host([focused]:not([disabled])) {
box-shadow: none;
}
}
/* RTL specific styles */
:host([dir="rtl"]) {
padding-right: calc(var(--lumo-border-radius) / 4);
padding-left: calc(var(--lumo-space-l) + var(--lumo-border-radius) / 4);
}
</style>
</template>
</dom-module>`;
document.head.appendChild($_documentContainer.content);
/* RTL specific styles */
:host([dir='rtl']) {
padding-right: calc(var(--lumo-border-radius) / 4);
padding-left: calc(var(--lumo-space-l) + var(--lumo-border-radius) / 4);
}
`,
{ moduleId: 'lumo-combo-box-item', include: ['lumo-item'] }
);

@@ -0,20 +1,18 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-lumo-styles/font-icons.js';
import '@vaadin/vaadin-lumo-styles/mixins/field-button.js';
import '@vaadin/vaadin-text-field/theme/lumo/vaadin-text-field.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="lumo-combo-box" theme-for="vaadin-combo-box">
<template>
<style include="lumo-field-button">
:host {
outline: none;
}
registerStyles(
'vaadin-combo-box',
css`
:host {
outline: none;
}
[part="toggle-button"]::before {
content: var(--lumo-icons-dropdown);
}
</style>
</template>
</dom-module>`;
document.head.appendChild($_documentContainer.content);
[part='toggle-button']::before {
content: var(--lumo-icons-dropdown);
}
`,
{ moduleId: 'lumo-combo-box', include: ['lumo-field-button'] }
);

@@ -0,128 +1,124 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-material-styles/color.js';
import '@vaadin/vaadin-material-styles/mixins/menu-overlay.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="material-combo-box-overlay" theme-for="vaadin-combo-box-overlay">
<template>
<style include="material-menu-overlay">
:host {
/* TODO using a legacy mixin (unsupported) */
--iron-list-items-container: {
border-width: 8px 0;
border-style: solid;
border-color: transparent;
};
registerStyles(
'vaadin-combo-box-overlay',
css`
:host {
/* TODO using a legacy mixin (unsupported) */
--iron-list-items-container: {
border-width: 8px 0;
border-style: solid;
border-color: transparent;
}
}
[part="overlay"] {
position: relative;
overflow: visible;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
[part='overlay'] {
position: relative;
overflow: visible;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
[part="content"] {
padding: 0;
}
[part='content'] {
padding: 0;
}
:host([loading]) [part="loader"] {
height: 2px;
position: absolute;
z-index: 1;
top: -2px;
left: 0;
right: 0;
background:
var(--material-background-color) linear-gradient(
90deg,
transparent 0%,
transparent 20%,
var(--material-primary-color) 20%,
var(--material-primary-color) 40%,
transparent 40%,
transparent 60%,
var(--material-primary-color) 60%,
var(--material-primary-color) 80%,
transparent 80%,
transparent 100%
) 0 0 / 400% 100% repeat-x;
:host([loading]) [part='loader'] {
height: 2px;
position: absolute;
z-index: 1;
top: -2px;
left: 0;
right: 0;
background: var(--material-background-color)
linear-gradient(
90deg,
transparent 0%,
transparent 20%,
var(--material-primary-color) 20%,
var(--material-primary-color) 40%,
transparent 40%,
transparent 60%,
var(--material-primary-color) 60%,
var(--material-primary-color) 80%,
transparent 80%,
transparent 100%
)
0 0 / 400% 100% repeat-x;
opacity: 0;
animation: 3s linear infinite material-combo-box-loader-progress, 0.3s 0.1s both material-combo-box-loader-fade-in;
}
[part='loader']::before {
content: '';
display: block;
height: 100%;
opacity: 0.16;
background: var(--material-primary-color);
}
@keyframes material-combo-box-loader-fade-in {
0% {
opacity: 0;
animation:
3s linear infinite material-combo-box-loader-progress,
.3s .1s both material-combo-box-loader-fade-in;
}
[part="loader"]::before {
content: '';
display: block;
height: 100%;
opacity: 0.16;
background: var(--material-primary-color);
100% {
opacity: 1;
}
}
@keyframes material-combo-box-loader-fade-in {
0% {
opacity: 0;
}
@keyframes material-combo-box-loader-progress {
0% {
background-position: 0 0;
background-size: 300% 100%;
}
100% {
opacity: 1;
}
33% {
background-position: -100% 0;
background-size: 400% 100%;
}
@keyframes material-combo-box-loader-progress {
0% {
background-position: 0 0;
background-size: 300% 100%;
}
67% {
background-position: -200% 0;
background-size: 250% 100%;
}
33% {
background-position: -100% 0;
background-size: 400% 100%;
}
100% {
background-position: -300% 0;
background-size: 300% 100%;
}
}
67% {
background-position: -200% 0;
background-size: 250% 100%;
}
/* RTL specific styles */
100% {
background-position: -300% 0;
background-size: 300% 100%;
}
@keyframes material-combo-box-loader-progress-rtl {
0% {
background-position: 100% 0;
background-size: 300% 100%;
}
/* RTL specific styles */
33% {
background-position: 200% 0;
background-size: 400% 100%;
}
@keyframes material-combo-box-loader-progress-rtl {
0% {
background-position: 100% 0;
background-size: 300% 100%;
}
33% {
background-position: 200% 0;
background-size: 400% 100%;
}
67% {
background-position: 300% 0;
background-size: 250% 100%;
}
100% {
background-position: 400% 0;
background-size: 300% 100%;
}
67% {
background-position: 300% 0;
background-size: 250% 100%;
}
:host([loading][dir="rtl"]) [part="loader"] {
animation:
3s linear infinite material-combo-box-loader-progress-rtl,
.3s .1s both material-combo-box-loader-fade-in;
100% {
background-position: 400% 0;
background-size: 300% 100%;
}
</style>
</template>
</dom-module>`;
}
document.head.appendChild($_documentContainer.content);
:host([loading][dir='rtl']) [part='loader'] {
animation: 3s linear infinite material-combo-box-loader-progress-rtl,
0.3s 0.1s both material-combo-box-loader-fade-in;
}
`,
{ moduleId: 'material-combo-box-overlay', include: ['material-menu-overlay'] }
);

@@ -0,1 +1,2 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-material-styles/color.js';

@@ -5,39 +6,36 @@ import '@vaadin/vaadin-material-styles/font-icons.js';

import '@vaadin/vaadin-item/theme/material/vaadin-item.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="material-combo-box-item" theme-for="vaadin-combo-box-item">
<template>
<style include="material-item">
:host {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
padding: 4px 10px;
min-height: 36px;
font-size: var(--material-small-font-size);
--_material-item-selected-icon-display: block;
}
registerStyles(
'vaadin-combo-box-item',
css`
:host {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
padding: 4px 10px;
min-height: 36px;
font-size: var(--material-small-font-size);
--_material-item-selected-icon-display: block;
}
/* ShadyCSS workaround */
:host::before {
display: block;
}
/* ShadyCSS workaround */
:host::before {
display: block;
}
:host(:hover) {
background-color: var(--material-secondary-background-color);
}
:host(:hover) {
background-color: var(--material-secondary-background-color);
}
:host([focused]) {
background-color: var(--material-divider-color);
}
@media (pointer: coarse) {
:host(:hover),
:host([focused]) {
background-color: var(--material-divider-color);
background-color: transparent;
}
@media (pointer: coarse) {
:host(:hover),
:host([focused]) {
background-color: transparent;
}
}
</style>
</template>
</dom-module>`;
document.head.appendChild($_documentContainer.content);
}
`,
{ moduleId: 'material-combo-box-item', include: ['material-item'] }
);

@@ -0,1 +1,2 @@

import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-text-field/theme/material/vaadin-text-field.js';

@@ -5,24 +6,21 @@ import '@vaadin/vaadin-material-styles/color.js';

import '@vaadin/vaadin-material-styles/mixins/field-button.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
const $_documentContainer = html`<dom-module id="material-combo-box" theme-for="vaadin-combo-box">
<template>
<style include="material-field-button">
:host {
display: inline-flex;
outline: none;
-webkit-tap-highlight-color: transparent;
}
registerStyles(
'vaadin-combo-box',
css`
:host {
display: inline-flex;
outline: none;
-webkit-tap-highlight-color: transparent;
}
[part="toggle-button"]::before {
content: var(--material-icons-dropdown);
}
[part='toggle-button']::before {
content: var(--material-icons-dropdown);
}
:host([opened]) [part="toggle-button"] {
transform: rotate(180deg);
}
</style>
</template>
</dom-module>`;
document.head.appendChild($_documentContainer.content);
:host([opened]) [part='toggle-button'] {
transform: rotate(180deg);
}
`,
{ moduleId: 'material-combo-box', include: ['material-field-button'] }
);

@@ -1,14 +0,1 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* vaadin-combo-box-light.js
*/
// tslint:disable:variable-name Describing an API that's defined elsewhere.
export * from './src/vaadin-combo-box-light.js';

@@ -1,15 +0,2 @@

/**
* DO NOT EDIT
*
* This file was automatically generated by
* https://github.com/Polymer/tools/tree/master/packages/gen-typescript-declarations
*
* To modify these typings, edit the source file(s):
* vaadin-combo-box.js
*/
// tslint:disable:variable-name Describing an API that's defined elsewhere.
export * from './src/vaadin-combo-box.js';
export * from './@types/interfaces';
export * from './src/interfaces';
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc