@vaadin/vaadin-combo-box 5.4.5 → 5.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,21 +1,18 @@
1
- [![npm latest version](https://badgen.net/npm/v/@vaadin/vaadin-combo-box/latest)](https://www.npmjs.com/package/@vaadin/vaadin-combo-box)
2
- [![npm next version](https://badgen.net/npm/v/@vaadin/vaadin-combo-box/next)](https://www.npmjs.com/package/@vaadin/vaadin-combo-box)
3
- [![Bower version](https://badgen.net/github/release/vaadin/vaadin-combo-box)](https://github.com/vaadin/vaadin-combo-box/releases)
4
- [![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/vaadin/vaadin-combo-box)
5
- [![Build Status](https://travis-ci.org/vaadin/vaadin-combo-box.svg?branch=master)](https://travis-ci.org/vaadin/vaadin-combo-box)
6
- [![Coverage Status](https://coveralls.io/repos/github/vaadin/vaadin-combo-box/badge.svg?branch=master)](https://coveralls.io/github/vaadin/vaadin-combo-box?branch=master)
7
- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/vaadin/web-components?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
1
+ # <vaadin-combo-box>
8
2
 
9
- [![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/vaadinvaadin-combo-box)
10
- [![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/vaadinvaadin-combo-box.svg)](https://vaadin.com/directory/component/vaadinvaadin-combo-box)
3
+ > ⚠️ Starting from Vaadin 20, the source code and issues for this component are migrated to the [`vaadin/web-components`](https://github.com/vaadin/web-components/tree/master/packages/vaadin-combo-box) monorepository.
4
+ > This repository contains the source code and releases of `<vaadin-combo-box>` for the Vaadin versions 10 to 19.
11
5
 
12
- # &lt;vaadin-combo-box&gt;
6
+ [&lt;vaadin-combo-box&gt;](https://vaadin.com/components/vaadin-combo-box) is a Web Component combining a dropdown list with an input field for filtering the list of items, part of the [Vaadin components](https://vaadin.com/components).
13
7
 
14
8
  [Live Demo ↗](https://vaadin.com/components/vaadin-combo-box/html-examples)
15
9
  |
16
10
  [API documentation ↗](https://vaadin.com/components/vaadin-combo-box/html-api)
17
11
 
18
- [&lt;vaadin-combo-box&gt;](https://vaadin.com/components/vaadin-combo-box) is a Web Component combining a dropdown list with an input field for filtering the list of items, part of the [Vaadin components](https://vaadin.com/components).
12
+ [![npm latest version](https://badgen.net/npm/v/@vaadin/vaadin-combo-box/latest)](https://www.npmjs.com/package/@vaadin/vaadin-combo-box)
13
+ [![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/vaadin/vaadin-combo-box)
14
+ [![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/vaadinvaadin-combo-box)
15
+ [![Discord](https://img.shields.io/discord/732335336448852018?label=discord)](https://discord.gg/PHmkCKC)
19
16
 
20
17
  <!--
21
18
  ```
@@ -95,18 +92,18 @@ To use the Material theme, import the correspondent file from the `theme/materia
95
92
 
96
93
  - The components with the Lumo theme:
97
94
 
98
- `theme/lumo/vaadin-combo-box.html`
95
+ `theme/lumo/vaadin-combo-box.html`
99
96
  `theme/lumo/vaadin-combo-box-light.html`
100
97
 
101
98
  - The components with the Material theme:
102
99
 
103
- `theme/material/vaadin-combo-box.html`
100
+ `theme/material/vaadin-combo-box.html`
104
101
  `theme/material/vaadin-combo-box-light.html`
105
102
 
106
- - Alias for `theme/lumo/vaadin-combo-box.html`
103
+ - Alias for `theme/lumo/vaadin-combo-box.html`
107
104
  `theme/lumo/vaadin-combo-box-light.html`
108
105
 
109
- `vaadin-combo-box.html`
106
+ `vaadin-combo-box.html`
110
107
  `vaadin-combo-box-light.html`
111
108
 
112
109
 
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "repository": "vaadin/vaadin-combo-box",
11
11
  "homepage": "https://vaadin.com/components",
12
12
  "name": "@vaadin/vaadin-combo-box",
13
- "version": "5.4.5",
13
+ "version": "5.4.9",
14
14
  "main": "vaadin-combo-box.js",
15
15
  "author": "Vaadin Ltd",
16
16
  "license": "Apache-2.0",
@@ -48,9 +48,9 @@
48
48
  "@vaadin/vaadin-element-mixin": "^2.4.1"
49
49
  },
50
50
  "scripts": {
51
- "generate-typings": "gen-typescript-declarations --outDir . --verify"
52
- },
53
- "devDependencies": {
51
+ "generate-typings": "gen-typescript-declarations --outDir . --verify"
52
+ },
53
+ "devDependencies": {
54
54
  "@polymer/iron-form": "^3.0.0",
55
55
  "@polymer/iron-component-page": "^4.0.0",
56
56
  "@polymer/iron-test-helpers": "^3.0.0",
@@ -94,6 +94,17 @@ export const ComboBoxDataProviderMixin = superClass => class DataProviderMixin e
94
94
  this.clearCache();
95
95
  this.$.overlay.addEventListener('index-requested', e => {
96
96
  const index = e.detail.index;
97
+ const currentScrollerPos = e.detail.currentScrollerPos;
98
+ const allowedIndexRange = Math.floor(this.pageSize * 1.5);
99
+
100
+ // Ignores the indexes, which are being re-sent during scrolling reset,
101
+ // if the corresponding page is around the current scroller position.
102
+ // Otherwise, there might be a last pages duplicates, which cause the
103
+ // loading indicator hanging and blank items
104
+ if (this._shouldSkipIndex(index, allowedIndexRange, currentScrollerPos)) {
105
+ return;
106
+ }
107
+
97
108
  if (index !== undefined) {
98
109
  const page = this._getPageForIndex(index);
99
110
  if (this._shouldLoadPage(page)) {
@@ -132,6 +143,13 @@ export const ComboBoxDataProviderMixin = superClass => class DataProviderMixin e
132
143
  }
133
144
  }
134
145
 
146
+ /** @private */
147
+ _shouldSkipIndex(index, allowedIndexRange, currentScrollerPos) {
148
+ return currentScrollerPos !== 0 &&
149
+ index >= currentScrollerPos - allowedIndexRange &&
150
+ index <= currentScrollerPos + allowedIndexRange;
151
+ }
152
+
135
153
  /** @private */
136
154
  _shouldLoadPage(page) {
137
155
  if (!this.filteredItems || this._forceNextRequest) {
@@ -188,8 +206,13 @@ export const ComboBoxDataProviderMixin = superClass => class DataProviderMixin e
188
206
  }
189
207
  }
190
208
  };
191
- this._pendingRequests[page] = callback;
192
- this.dataProvider(params, callback);
209
+
210
+ if (!this._pendingRequests[page]) {
211
+ // Don't request page if it's already being requested
212
+ this._pendingRequests[page] = callback;
213
+ this.dataProvider(params, callback);
214
+ }
215
+
193
216
  }
194
217
  }
195
218
 
@@ -48,7 +48,7 @@ class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
48
48
  <div id="scroller" on-click="_stopPropagation">
49
49
  <iron-list id="selector" role="listbox" items="[[_getItems(opened, _items)]]" scroll-target="[[_scroller]]">
50
50
  <template>
51
- <vaadin-combo-box-item on-click="_onItemClick" index="[[__requestItemByIndex(item, index)]]" 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]]">
51
+ <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]]">
52
52
  </vaadin-combo-box-item>
53
53
  </template>
54
54
  </iron-list>
@@ -102,21 +102,21 @@ class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
102
102
  theme: String,
103
103
 
104
104
  /**
105
- * Used to recognize scroller reset after new items have been set
106
- * to iron-list and to ignore unwanted pages load. If 'true', then
107
- * skip loading of the pages until it becomes 'false'.
105
+ * Used to recognize if the filter changed, so to skip the
106
+ * scrolling restore. If true, then scroll to 0 position. Restore
107
+ * the previous position otherwise.
108
108
  */
109
- resetScrolling: {
109
+ filterChanged: {
110
110
  type: Boolean,
111
111
  value: false
112
112
  },
113
113
 
114
114
  /**
115
- * Used to recognize if the filter changed, so to skip the
116
- * scrolling restore. If true, then scroll to 0 position. Restore
117
- * the previous position otherwise.
115
+ * Used to recognize scroller reset after new items have been set
116
+ * to iron-list and to ignore unwanted pages load. If 'true', then
117
+ * skip loading of the pages until it becomes 'false'.
118
118
  */
119
- filterChanged: {
119
+ _resetScrolling: {
120
120
  type: Boolean,
121
121
  value: false
122
122
  },
@@ -188,7 +188,7 @@ class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
188
188
  const currentScrollerPosition = this._selector.firstVisibleIndex;
189
189
  if (currentScrollerPosition !== 0) {
190
190
  this._oldScrollerPosition = currentScrollerPosition;
191
- this.resetScrolling = true;
191
+ this._resetScrolling = true;
192
192
  }
193
193
  }
194
194
  // Let the position to be restored in the future calls unless it's not
@@ -203,7 +203,7 @@ class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
203
203
  if (this._isNotEmpty(items) && this._selector && this._oldScrollerPosition !== 0) {
204
204
  // new items size might be less than old scrolling position
205
205
  this._scrollIntoView(Math.min(items.length - 1, this._oldScrollerPosition));
206
- this.resetScrolling = false;
206
+ this._resetScrolling = false;
207
207
  // reset position to 0 again in order to properly handle the filter
208
208
  // cases (scroll to 0 after typing the filter)
209
209
  this._oldScrollerPosition = 0;
@@ -358,10 +358,11 @@ class ComboBoxDropdownWrapperElement extends (class extends PolymerElement {}) {
358
358
  *
359
359
  * @return {number}
360
360
  */
361
- __requestItemByIndex(item, index) {
361
+ __requestItemByIndex(item, index, resetScrolling) {
362
362
  if ((item instanceof ComboBoxPlaceholder) && index !==
363
- undefined && !this.resetScrolling) {
364
- this.dispatchEvent(new CustomEvent('index-requested', {detail: {index}}));
363
+ undefined && !resetScrolling) {
364
+ this.dispatchEvent(new CustomEvent('index-requested', {detail:
365
+ {index: index, currentScrollerPos: this._oldScrollerPosition}}));
365
366
  }
366
367
 
367
368
  return index;
@@ -23,6 +23,9 @@ $_documentContainer.innerHTML = `<dom-module id="vaadin-combo-box-overlay-styles
23
23
  </dom-module>`;
24
24
 
25
25
  document.head.appendChild($_documentContainer.content);
26
+ let memoizedTemplate;
27
+
28
+
26
29
  /**
27
30
  * The overlay element.
28
31
  *
@@ -41,6 +44,15 @@ class ComboBoxOverlayElement extends OverlayElement {
41
44
  return 'vaadin-combo-box-overlay';
42
45
  }
43
46
 
47
+ static get template() {
48
+ if (!memoizedTemplate) {
49
+ memoizedTemplate = super.template.cloneNode(true);
50
+ memoizedTemplate.content.querySelector('[part~="overlay"]').removeAttribute('tabindex');
51
+ }
52
+
53
+ return memoizedTemplate;
54
+ }
55
+
44
56
  connectedCallback() {
45
57
  super.connectedCallback();
46
58
 
@@ -260,7 +260,7 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
260
260
  '_filteredItemsChanged(filteredItems.*, itemValuePath, itemLabelPath)',
261
261
  '_templateOrRendererChanged(_itemTemplate, renderer)',
262
262
  '_loadingChanged(loading)',
263
- '_selectedItemChanged(selectedItem, itemLabelPath)',
263
+ '_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)',
264
264
  '_toggleElementChanged(_toggleElement)'
265
265
  ];
266
266
  }
@@ -683,8 +683,9 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
683
683
  this.value = '';
684
684
  }
685
685
  } else {
686
+ const toLowerCase = (item) => item && item.toLowerCase && item.toLowerCase();
686
687
  const itemsMatchedByLabel = this.filteredItems
687
- && this.filteredItems.filter(item => this._getItemLabel(item) === this._inputElementValue)
688
+ && this.filteredItems.filter(item => toLowerCase(this._getItemLabel(item)) === toLowerCase(this._inputElementValue))
688
689
  || [];
689
690
  if (this.allowCustomValue
690
691
  // to prevent a repetitive input value being saved after pressing ESC and Tab.
@@ -697,7 +698,7 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
697
698
  this._selectItemForValue(customValue);
698
699
  this.value = customValue;
699
700
  }
700
- } else if (!this.allowCustomValue && !this.opened && itemsMatchedByLabel.length == 1) {
701
+ } else if (!this.allowCustomValue && !this.opened && itemsMatchedByLabel.length > 0) {
701
702
  this.value = this._getItemValue(itemsMatchedByLabel[0]);
702
703
  } else {
703
704
  this._inputElementValue = this.selectedItem ? this._getItemLabel(this.selectedItem) : (this.value || '');
@@ -897,11 +898,13 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
897
898
 
898
899
  /** @private */
899
900
  _itemsOrPathsChanged(e, itemValuePath, itemLabelPath) {
900
- if (e.value === undefined) {
901
- return;
902
- }
903
901
  if (e.path === 'items' || e.path === 'items.splices') {
904
- this.filteredItems = this.items ? this.items.slice(0) : this.items;
902
+ if (this.items) {
903
+ this.filteredItems = this.items.slice(0);
904
+ } else if (this.__previousItems) {
905
+ // Only clear filteredItems if the component had items previously but got cleared
906
+ this.filteredItems = null;
907
+ }
905
908
 
906
909
  const valueIndex = this._indexOfValue(this.value, this.items);
907
910
  this._focusedIndex = valueIndex;
@@ -911,13 +914,11 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
911
914
  this.selectedItem = item;
912
915
  }
913
916
  }
917
+ this.__previousItems = e.value;
914
918
  }
915
919
 
916
920
  /** @private */
917
921
  _filteredItemsChanged(e, itemValuePath, itemLabelPath) {
918
- if (e.value === undefined) {
919
- return;
920
- }
921
922
  if (e.path === 'filteredItems' || e.path === 'filteredItems.splices') {
922
923
  this._setOverlayItems(this.filteredItems);
923
924
 
@@ -943,7 +944,7 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
943
944
  return this._getItemLabel(item).toString().toLowerCase().indexOf(filter) > -1;
944
945
  });
945
946
 
946
- return !filteredItems.length && this.autoOpenDisabled ? arr : filteredItems;
947
+ return filteredItems;
947
948
  }
948
949
 
949
950
  /** @private */
@@ -1045,7 +1046,7 @@ export const ComboBoxMixin = subclass => class VaadinComboBoxMixinElement extend
1045
1046
  event.composedPath()[0].focus();
1046
1047
  return;
1047
1048
  }
1048
- if (!this._closeOnBlurIsPrevented) {
1049
+ if (!this.readonly && !this._closeOnBlurIsPrevented) {
1049
1050
  this._closeOrCommit();
1050
1051
  }
1051
1052
  }
@@ -229,7 +229,7 @@ class ComboBoxElement extends
229
229
  }
230
230
 
231
231
  static get version() {
232
- return '5.4.5';
232
+ return '5.4.9';
233
233
  }
234
234
 
235
235
  static get properties() {