@vaadin/vaadin-combo-box 21.0.1 → 22.0.0-alpha10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +23 -40
- package/src/vaadin-combo-box-light.d.ts +14 -95
- package/src/vaadin-combo-box-light.js +4 -197
- package/src/vaadin-combo-box.d.ts +14 -244
- package/src/vaadin-combo-box.js +4 -414
- package/theme/lumo/vaadin-combo-box-light.js +1 -3
- package/theme/lumo/vaadin-combo-box.js +1 -4
- package/theme/material/vaadin-combo-box-light.js +1 -3
- package/theme/material/vaadin-combo-box.js +1 -4
- package/vaadin-combo-box.d.ts +0 -1
- package/src/interfaces.d.ts +0 -71
- package/src/vaadin-combo-box-data-provider-mixin.d.ts +0 -44
- package/src/vaadin-combo-box-data-provider-mixin.js +0 -323
- package/src/vaadin-combo-box-dropdown-wrapper.js +0 -498
- package/src/vaadin-combo-box-dropdown.js +0 -289
- package/src/vaadin-combo-box-item.js +0 -170
- package/src/vaadin-combo-box-mixin.d.ts +0 -209
- package/src/vaadin-combo-box-mixin.js +0 -1127
- package/src/vaadin-combo-box-placeholder.js +0 -16
- package/theme/lumo/vaadin-combo-box-dropdown-styles.js +0 -87
- package/theme/lumo/vaadin-combo-box-item-styles.js +0 -47
- package/theme/lumo/vaadin-combo-box-styles.js +0 -18
- package/theme/material/vaadin-combo-box-dropdown-styles.js +0 -124
- package/theme/material/vaadin-combo-box-item-styles.js +0 -35
- package/theme/material/vaadin-combo-box-styles.js +0 -25
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
-
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
-
*/
|
|
6
|
-
import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @polymerMixin
|
|
10
|
-
*/
|
|
11
|
-
export const ComboBoxDataProviderMixin = (superClass) =>
|
|
12
|
-
class DataProviderMixin extends superClass {
|
|
13
|
-
static get properties() {
|
|
14
|
-
return {
|
|
15
|
-
/**
|
|
16
|
-
* Number of items fetched at a time from the dataprovider.
|
|
17
|
-
* @attr {number} page-size
|
|
18
|
-
* @type {number}
|
|
19
|
-
*/
|
|
20
|
-
pageSize: {
|
|
21
|
-
type: Number,
|
|
22
|
-
value: 50,
|
|
23
|
-
observer: '_pageSizeChanged'
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Total number of items.
|
|
28
|
-
* @type {number | undefined}
|
|
29
|
-
*/
|
|
30
|
-
size: {
|
|
31
|
-
type: Number,
|
|
32
|
-
observer: '_sizeChanged'
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Function that provides items lazily. Receives arguments `params`, `callback`
|
|
37
|
-
*
|
|
38
|
-
* `params.page` Requested page index
|
|
39
|
-
*
|
|
40
|
-
* `params.pageSize` Current page size
|
|
41
|
-
*
|
|
42
|
-
* `params.filter` Currently applied filter
|
|
43
|
-
*
|
|
44
|
-
* `callback(items, size)` Callback function with arguments:
|
|
45
|
-
* - `items` Current page of items
|
|
46
|
-
* - `size` Total number of items.
|
|
47
|
-
* @type {ComboBoxDataProvider | undefined}
|
|
48
|
-
*/
|
|
49
|
-
dataProvider: {
|
|
50
|
-
type: Object,
|
|
51
|
-
observer: '_dataProviderChanged'
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
/** @private */
|
|
55
|
-
_pendingRequests: {
|
|
56
|
-
value: () => {
|
|
57
|
-
return {};
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
/** @private */
|
|
62
|
-
__placeHolder: {
|
|
63
|
-
value: new ComboBoxPlaceholder()
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static get observers() {
|
|
69
|
-
return [
|
|
70
|
-
'_dataProviderFilterChanged(filter, dataProvider)',
|
|
71
|
-
'_dataProviderClearFilter(dataProvider, opened, value)',
|
|
72
|
-
'_warnDataProviderValue(dataProvider, value)',
|
|
73
|
-
'_ensureFirstPage(opened)'
|
|
74
|
-
];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** @private */
|
|
78
|
-
_dataProviderClearFilter(dataProvider, opened, value) {
|
|
79
|
-
// Can't depend on filter in this observer as we don't want
|
|
80
|
-
// to clear the filter whenever it's set
|
|
81
|
-
if (dataProvider && !this.loading && this.filter && !(opened && this.autoOpenDisabled && value === this.filter)) {
|
|
82
|
-
this.size = undefined;
|
|
83
|
-
this._pendingRequests = {};
|
|
84
|
-
this.filter = '';
|
|
85
|
-
this.clearCache();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** @protected */
|
|
90
|
-
ready() {
|
|
91
|
-
super.ready();
|
|
92
|
-
this.clearCache();
|
|
93
|
-
this.$.overlay.addEventListener('index-requested', (e) => {
|
|
94
|
-
const index = e.detail.index;
|
|
95
|
-
const currentScrollerPos = e.detail.currentScrollerPos;
|
|
96
|
-
const allowedIndexRange = Math.floor(this.pageSize * 1.5);
|
|
97
|
-
|
|
98
|
-
// Ignores the indexes, which are being re-sent during scrolling reset,
|
|
99
|
-
// if the corresponding page is around the current scroller position.
|
|
100
|
-
// Otherwise, there might be a last pages duplicates, which cause the
|
|
101
|
-
// loading indicator hanging and blank items
|
|
102
|
-
if (this._shouldSkipIndex(index, allowedIndexRange, currentScrollerPos)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (index !== undefined) {
|
|
107
|
-
const page = this._getPageForIndex(index);
|
|
108
|
-
if (this._shouldLoadPage(page)) {
|
|
109
|
-
this._loadPage(page);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** @private */
|
|
116
|
-
_dataProviderFilterChanged() {
|
|
117
|
-
if (!this._shouldFetchData()) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.size = undefined;
|
|
122
|
-
this._pendingRequests = {};
|
|
123
|
-
this.clearCache();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** @private */
|
|
127
|
-
_shouldFetchData() {
|
|
128
|
-
if (!this.dataProvider) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return this.opened || (this.filter && this.filter.length);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** @private */
|
|
136
|
-
_ensureFirstPage(opened) {
|
|
137
|
-
if (opened && this._shouldLoadPage(0)) {
|
|
138
|
-
this._loadPage(0);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** @private */
|
|
143
|
-
_shouldSkipIndex(index, allowedIndexRange, currentScrollerPos) {
|
|
144
|
-
return (
|
|
145
|
-
currentScrollerPos !== 0 &&
|
|
146
|
-
index >= currentScrollerPos - allowedIndexRange &&
|
|
147
|
-
index <= currentScrollerPos + allowedIndexRange
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** @private */
|
|
152
|
-
_shouldLoadPage(page) {
|
|
153
|
-
if (!this.filteredItems || this._forceNextRequest) {
|
|
154
|
-
this._forceNextRequest = false;
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const loadedItem = this.filteredItems[page * this.pageSize];
|
|
159
|
-
if (loadedItem !== undefined) {
|
|
160
|
-
return loadedItem instanceof ComboBoxPlaceholder;
|
|
161
|
-
} else {
|
|
162
|
-
return this.size === undefined;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** @private */
|
|
167
|
-
_loadPage(page) {
|
|
168
|
-
// make sure same page isn't requested multiple times.
|
|
169
|
-
if (!this._pendingRequests[page] && this.dataProvider) {
|
|
170
|
-
this.loading = true;
|
|
171
|
-
|
|
172
|
-
const params = {
|
|
173
|
-
page,
|
|
174
|
-
pageSize: this.pageSize,
|
|
175
|
-
filter: this.filter
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const callback = (items, size) => {
|
|
179
|
-
if (this._pendingRequests[page] === callback) {
|
|
180
|
-
if (!this.filteredItems) {
|
|
181
|
-
const filteredItems = [];
|
|
182
|
-
filteredItems.splice(params.page * params.pageSize, items.length, ...items);
|
|
183
|
-
this.filteredItems = filteredItems;
|
|
184
|
-
} else {
|
|
185
|
-
this.splice('filteredItems', params.page * params.pageSize, items.length, ...items);
|
|
186
|
-
}
|
|
187
|
-
// Update selectedItem from filteredItems if value is set
|
|
188
|
-
if (this._isValidValue(this.value) && this._getItemValue(this.selectedItem) !== this.value) {
|
|
189
|
-
this._selectItemForValue(this.value);
|
|
190
|
-
}
|
|
191
|
-
if (!this.opened && !this.hasAttribute('focused')) {
|
|
192
|
-
this._commitValue();
|
|
193
|
-
}
|
|
194
|
-
this.size = size;
|
|
195
|
-
|
|
196
|
-
delete this._pendingRequests[page];
|
|
197
|
-
|
|
198
|
-
if (Object.keys(this._pendingRequests).length === 0) {
|
|
199
|
-
this.loading = false;
|
|
200
|
-
}
|
|
201
|
-
if (page === 0 && this.__repositionOverlayDebouncer && items.length > (this.__maxRenderedItems || 0)) {
|
|
202
|
-
setTimeout(() => this.__repositionOverlayDebouncer.flush());
|
|
203
|
-
this.__maxRenderedItems = items.length;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
if (!this._pendingRequests[page]) {
|
|
209
|
-
// Don't request page if it's already being requested
|
|
210
|
-
this._pendingRequests[page] = callback;
|
|
211
|
-
this.dataProvider(params, callback);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** @private */
|
|
217
|
-
_getPageForIndex(index) {
|
|
218
|
-
return Math.floor(index / this.pageSize);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Clears the cached pages and reloads data from dataprovider when needed.
|
|
223
|
-
*/
|
|
224
|
-
clearCache() {
|
|
225
|
-
if (!this.dataProvider) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
this._pendingRequests = {};
|
|
229
|
-
const filteredItems = [];
|
|
230
|
-
for (let i = 0; i < (this.size || 0); i++) {
|
|
231
|
-
filteredItems.push(this.__placeHolder);
|
|
232
|
-
}
|
|
233
|
-
this.filteredItems = filteredItems;
|
|
234
|
-
if (this._shouldFetchData()) {
|
|
235
|
-
this._loadPage(0);
|
|
236
|
-
} else {
|
|
237
|
-
this._forceNextRequest = true;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/** @private */
|
|
242
|
-
_sizeChanged(size = 0) {
|
|
243
|
-
const filteredItems = (this.filteredItems || []).slice(0, size);
|
|
244
|
-
for (let i = 0; i < size; i++) {
|
|
245
|
-
filteredItems[i] = filteredItems[i] !== undefined ? filteredItems[i] : this.__placeHolder;
|
|
246
|
-
}
|
|
247
|
-
this.filteredItems = filteredItems;
|
|
248
|
-
|
|
249
|
-
// Cleans up the redundant pending requests for pages > size
|
|
250
|
-
// Refers to https://github.com/vaadin/vaadin-flow-components/issues/229
|
|
251
|
-
this._flushPendingRequests(size);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** @private */
|
|
255
|
-
_pageSizeChanged(pageSize, oldPageSize) {
|
|
256
|
-
if (Math.floor(pageSize) !== pageSize || pageSize < 1) {
|
|
257
|
-
this.pageSize = oldPageSize;
|
|
258
|
-
throw new Error('`pageSize` value must be an integer > 0');
|
|
259
|
-
}
|
|
260
|
-
this.clearCache();
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** @private */
|
|
264
|
-
_dataProviderChanged(dataProvider, oldDataProvider) {
|
|
265
|
-
this._ensureItemsOrDataProvider(() => {
|
|
266
|
-
this.dataProvider = oldDataProvider;
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/** @private */
|
|
271
|
-
_ensureItemsOrDataProvider(restoreOldValueCallback) {
|
|
272
|
-
if (this.items !== undefined && this.dataProvider !== undefined) {
|
|
273
|
-
restoreOldValueCallback();
|
|
274
|
-
throw new Error('Using `items` and `dataProvider` together is not supported');
|
|
275
|
-
} else if (this.dataProvider && !this.filteredItems) {
|
|
276
|
-
this.filteredItems = [];
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/** @private */
|
|
281
|
-
_warnDataProviderValue(dataProvider, value) {
|
|
282
|
-
if (dataProvider && value !== '' && (this.selectedItem === undefined || this.selectedItem === null)) {
|
|
283
|
-
const valueIndex = this._indexOfValue(value, this.filteredItems);
|
|
284
|
-
if (valueIndex < 0 || !this._getItemLabel(this.filteredItems[valueIndex])) {
|
|
285
|
-
/* eslint-disable no-console */
|
|
286
|
-
console.warn(
|
|
287
|
-
'Warning: unable to determine the label for the provided `value`. ' +
|
|
288
|
-
'Nothing to display in the text field. This usually happens when ' +
|
|
289
|
-
'setting an initial `value` before any items are returned from ' +
|
|
290
|
-
'the `dataProvider` callback. Consider setting `selectedItem` ' +
|
|
291
|
-
'instead of `value`'
|
|
292
|
-
);
|
|
293
|
-
/* eslint-enable no-console */
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* This method cleans up the page callbacks which refers to the
|
|
300
|
-
* non-existing pages, i.e. which item indexes are greater than the
|
|
301
|
-
* changed size.
|
|
302
|
-
* This case is basically happens when:
|
|
303
|
-
* 1. Users scroll fast to the bottom and combo box generates the
|
|
304
|
-
* redundant page request/callback
|
|
305
|
-
* 2. Server side uses undefined size lazy loading and suddenly reaches
|
|
306
|
-
* the exact size which is on the range edge
|
|
307
|
-
* (for default page size = 50, it will be 100, 200, 300, ...).
|
|
308
|
-
* @param size the new size of items
|
|
309
|
-
* @private
|
|
310
|
-
*/
|
|
311
|
-
_flushPendingRequests(size) {
|
|
312
|
-
if (this._pendingRequests) {
|
|
313
|
-
const lastPage = Math.ceil(size / this.pageSize);
|
|
314
|
-
const pendingRequestsKeys = Object.keys(this._pendingRequests);
|
|
315
|
-
for (let reqIdx = 0; reqIdx < pendingRequestsKeys.length; reqIdx++) {
|
|
316
|
-
const page = parseInt(pendingRequestsKeys[reqIdx]);
|
|
317
|
-
if (page >= lastPage) {
|
|
318
|
-
this._pendingRequests[page]([], size);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
};
|