@vaadin/combo-box 24.4.0-alpha1 → 24.4.0-dev.b3e1d14600
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 +13 -13
- package/src/vaadin-combo-box-data-provider-mixin.js +109 -120
- package/src/vaadin-combo-box-mixin.js +7 -3
- package/web-types.json +0 -1188
- package/web-types.lit.json +0 -573
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/combo-box",
|
|
3
|
-
"version": "24.4.0-
|
|
3
|
+
"version": "24.4.0-dev.b3e1d14600",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -38,21 +38,21 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
40
40
|
"@polymer/polymer": "^3.0.0",
|
|
41
|
-
"@vaadin/a11y-base": "24.4.0-
|
|
42
|
-
"@vaadin/component-base": "24.4.0-
|
|
43
|
-
"@vaadin/field-base": "24.4.0-
|
|
44
|
-
"@vaadin/input-container": "24.4.0-
|
|
45
|
-
"@vaadin/item": "24.4.0-
|
|
46
|
-
"@vaadin/lit-renderer": "24.4.0-
|
|
47
|
-
"@vaadin/overlay": "24.4.0-
|
|
48
|
-
"@vaadin/vaadin-lumo-styles": "24.4.0-
|
|
49
|
-
"@vaadin/vaadin-material-styles": "24.4.0-
|
|
50
|
-
"@vaadin/vaadin-themable-mixin": "24.4.0-
|
|
41
|
+
"@vaadin/a11y-base": "24.4.0-dev.b3e1d14600",
|
|
42
|
+
"@vaadin/component-base": "24.4.0-dev.b3e1d14600",
|
|
43
|
+
"@vaadin/field-base": "24.4.0-dev.b3e1d14600",
|
|
44
|
+
"@vaadin/input-container": "24.4.0-dev.b3e1d14600",
|
|
45
|
+
"@vaadin/item": "24.4.0-dev.b3e1d14600",
|
|
46
|
+
"@vaadin/lit-renderer": "24.4.0-dev.b3e1d14600",
|
|
47
|
+
"@vaadin/overlay": "24.4.0-dev.b3e1d14600",
|
|
48
|
+
"@vaadin/vaadin-lumo-styles": "24.4.0-dev.b3e1d14600",
|
|
49
|
+
"@vaadin/vaadin-material-styles": "24.4.0-dev.b3e1d14600",
|
|
50
|
+
"@vaadin/vaadin-themable-mixin": "24.4.0-dev.b3e1d14600"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@esm-bundle/chai": "^4.3.4",
|
|
54
54
|
"@vaadin/testing-helpers": "^0.6.0",
|
|
55
|
-
"@vaadin/text-field": "24.4.0-
|
|
55
|
+
"@vaadin/text-field": "24.4.0-dev.b3e1d14600",
|
|
56
56
|
"lit": "^3.0.0",
|
|
57
57
|
"sinon": "^13.0.2"
|
|
58
58
|
},
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"web-types.json",
|
|
61
61
|
"web-types.lit.json"
|
|
62
62
|
],
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "502d4f5b03f770a83d270d98078cde230254dd0e"
|
|
64
64
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
+
import { DataProviderController } from '@vaadin/component-base/src/data-provider-controller/data-provider-controller.js';
|
|
7
|
+
import { get } from '@vaadin/component-base/src/path-utils.js';
|
|
6
8
|
import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -51,13 +53,6 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
51
53
|
observer: '_dataProviderChanged',
|
|
52
54
|
},
|
|
53
55
|
|
|
54
|
-
/** @private */
|
|
55
|
-
_pendingRequests: {
|
|
56
|
-
value: () => {
|
|
57
|
-
return {};
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
|
|
61
56
|
/** @private */
|
|
62
57
|
__placeHolder: {
|
|
63
58
|
value: new ComboBoxPlaceholder(),
|
|
@@ -78,10 +73,33 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
78
73
|
];
|
|
79
74
|
}
|
|
80
75
|
|
|
76
|
+
constructor() {
|
|
77
|
+
super();
|
|
78
|
+
|
|
79
|
+
/** @type {DataProviderController} */
|
|
80
|
+
this._dataProviderController = new DataProviderController(this, {
|
|
81
|
+
size: this.size,
|
|
82
|
+
pageSize: this.pageSize,
|
|
83
|
+
getItemId: (item) => get(this.itemIdPath, item),
|
|
84
|
+
placeholder: this.__placeHolder,
|
|
85
|
+
dataProvider: this.dataProvider ? this.dataProvider.bind(this) : null,
|
|
86
|
+
dataProviderParams: () => ({ filter: this.filter }),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
/** @protected */
|
|
82
91
|
ready() {
|
|
83
92
|
super.ready();
|
|
93
|
+
|
|
94
|
+
this._dataProviderController.addEventListener('page-requested', this.__onDataProviderPageRequested.bind(this));
|
|
95
|
+
this._dataProviderController.addEventListener('page-received', this.__onDataProviderPageReceived.bind(this));
|
|
96
|
+
this._dataProviderController.addEventListener('page-loaded', this.__onDataProviderPageLoaded.bind(this));
|
|
97
|
+
|
|
84
98
|
this._scroller.addEventListener('index-requested', (e) => {
|
|
99
|
+
if (!this._shouldFetchData()) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
85
103
|
const index = e.detail.index;
|
|
86
104
|
const currentScrollerPos = e.detail.currentScrollerPos;
|
|
87
105
|
const allowedIndexRange = Math.floor(this.pageSize * 1.5);
|
|
@@ -95,10 +113,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
if (index !== undefined) {
|
|
98
|
-
|
|
99
|
-
if (this._shouldLoadPage(page)) {
|
|
100
|
-
this._loadPage(page);
|
|
101
|
-
}
|
|
116
|
+
this._dataProviderController.ensureFlatIndexLoaded(index);
|
|
102
117
|
}
|
|
103
118
|
});
|
|
104
119
|
}
|
|
@@ -113,19 +128,14 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
113
128
|
if (this.__previousDataProviderFilter !== filter) {
|
|
114
129
|
this.__previousDataProviderFilter = filter;
|
|
115
130
|
|
|
116
|
-
this.
|
|
117
|
-
// Immediately mark as loading if this refresh leads to re-fetching pages
|
|
118
|
-
// This prevents some issues with the properties below triggering
|
|
119
|
-
// observers that also rely on the loading state
|
|
120
|
-
this.loading = this._shouldFetchData();
|
|
121
|
-
// Reset size and internal loading state
|
|
131
|
+
this._keepOverlayOpened = true;
|
|
122
132
|
this.size = undefined;
|
|
123
|
-
|
|
124
133
|
this.clearCache();
|
|
134
|
+
this._keepOverlayOpened = false;
|
|
125
135
|
}
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
/** @
|
|
138
|
+
/** @protected */
|
|
129
139
|
_shouldFetchData() {
|
|
130
140
|
if (!this.dataProvider) {
|
|
131
141
|
return false;
|
|
@@ -136,8 +146,12 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
136
146
|
|
|
137
147
|
/** @private */
|
|
138
148
|
_ensureFirstPage(opened) {
|
|
139
|
-
if (
|
|
140
|
-
|
|
149
|
+
if (!this._shouldFetchData()) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (opened && !this._dataProviderController.hasData) {
|
|
154
|
+
this._dataProviderController.loadFirstPage();
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
157
|
|
|
@@ -151,68 +165,24 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
151
165
|
}
|
|
152
166
|
|
|
153
167
|
/** @private */
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
this._forceNextRequest = false;
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const loadedItem = this.filteredItems[page * this.pageSize];
|
|
161
|
-
if (loadedItem !== undefined) {
|
|
162
|
-
return loadedItem instanceof ComboBoxPlaceholder;
|
|
163
|
-
}
|
|
164
|
-
return this.size === undefined;
|
|
168
|
+
__onDataProviderPageRequested() {
|
|
169
|
+
this.loading = true;
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
/** @private */
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (this._pendingRequests[page] || !this.dataProvider) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const params = {
|
|
175
|
-
page,
|
|
176
|
-
pageSize: this.pageSize,
|
|
177
|
-
filter: this.filter,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const callback = (items, size) => {
|
|
181
|
-
if (this._pendingRequests[page] !== callback) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const filteredItems = this.filteredItems ? [...this.filteredItems] : [];
|
|
186
|
-
filteredItems.splice(params.page * params.pageSize, items.length, ...items);
|
|
187
|
-
this.filteredItems = filteredItems;
|
|
188
|
-
|
|
189
|
-
if (!this.opened && !this._isInputFocused()) {
|
|
190
|
-
this._commitValue();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (size !== undefined) {
|
|
194
|
-
this.size = size;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
delete this._pendingRequests[page];
|
|
198
|
-
|
|
199
|
-
if (Object.keys(this._pendingRequests).length === 0) {
|
|
200
|
-
this.loading = false;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
this._pendingRequests[page] = callback;
|
|
205
|
-
// Set the `loading` flag only after marking the request as pending
|
|
206
|
-
// to prevent the same page from getting requested multiple times
|
|
207
|
-
// as a result of `__loadingChanged` in the scroller which requests
|
|
208
|
-
// a virtualizer update which in turn may trigger a data provider page request.
|
|
209
|
-
this.loading = true;
|
|
210
|
-
this.dataProvider(params, callback);
|
|
173
|
+
__onDataProviderPageReceived() {
|
|
174
|
+
this.requestContentUpdate();
|
|
211
175
|
}
|
|
212
176
|
|
|
213
177
|
/** @private */
|
|
214
|
-
|
|
215
|
-
|
|
178
|
+
__onDataProviderPageLoaded() {
|
|
179
|
+
if (!this.opened && !this._isInputFocused()) {
|
|
180
|
+
this._commitValue();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!this._dataProviderController.isLoading()) {
|
|
184
|
+
this.loading = false;
|
|
185
|
+
}
|
|
216
186
|
}
|
|
217
187
|
|
|
218
188
|
/**
|
|
@@ -223,32 +193,74 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
223
193
|
return;
|
|
224
194
|
}
|
|
225
195
|
|
|
226
|
-
this.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
filteredItems.push(this.__placeHolder);
|
|
230
|
-
}
|
|
231
|
-
this.filteredItems = filteredItems;
|
|
196
|
+
this._dataProviderController.clearCache();
|
|
197
|
+
|
|
198
|
+
this.requestContentUpdate();
|
|
232
199
|
|
|
233
200
|
if (this._shouldFetchData()) {
|
|
234
|
-
this.
|
|
235
|
-
this._loadPage(0);
|
|
236
|
-
} else {
|
|
237
|
-
this._forceNextRequest = true;
|
|
201
|
+
this._dataProviderController.loadFirstPage();
|
|
238
202
|
}
|
|
239
203
|
}
|
|
240
204
|
|
|
241
205
|
/** @private */
|
|
242
206
|
_sizeChanged(size = 0) {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
207
|
+
const { rootCache } = this._dataProviderController;
|
|
208
|
+
// When the size update originates from the developer,
|
|
209
|
+
// sync the new size with the controller and trigger
|
|
210
|
+
// a content update to re-render the scroller.
|
|
211
|
+
if (rootCache.size !== size) {
|
|
212
|
+
rootCache.size = size;
|
|
213
|
+
this.requestContentUpdate();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @private
|
|
219
|
+
* @override
|
|
220
|
+
*/
|
|
221
|
+
_filteredItemsChanged(items) {
|
|
222
|
+
if (!this.dataProvider) {
|
|
223
|
+
return super._filteredItemsChanged(items);
|
|
246
224
|
}
|
|
247
|
-
this.filteredItems = filteredItems;
|
|
248
225
|
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
|
|
226
|
+
const { rootCache } = this._dataProviderController;
|
|
227
|
+
// When the items update originates from the developer,
|
|
228
|
+
// sync the new items with the controller and trigger
|
|
229
|
+
// a content update to re-render the scroller.
|
|
230
|
+
if (rootCache.items !== items) {
|
|
231
|
+
rootCache.items = items;
|
|
232
|
+
this.requestContentUpdate();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** @override */
|
|
237
|
+
requestContentUpdate() {
|
|
238
|
+
if (this.dataProvider) {
|
|
239
|
+
const { rootCache } = this._dataProviderController;
|
|
240
|
+
|
|
241
|
+
// Sync the controller's size with the component.
|
|
242
|
+
// They can be out of sync after, for example,
|
|
243
|
+
// the controller received new data.
|
|
244
|
+
if ((this.size || 0) !== rootCache.size) {
|
|
245
|
+
this.size = rootCache.size;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Sync the controller's items with the component.
|
|
249
|
+
// They can be out of sync after, for example,
|
|
250
|
+
// the controller received new data.
|
|
251
|
+
if (this.filteredItems !== rootCache.items) {
|
|
252
|
+
this.filteredItems = rootCache.items;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Sync the controller's loading state with the component.
|
|
256
|
+
this.loading = this._dataProviderController.isLoading();
|
|
257
|
+
|
|
258
|
+
// Set a copy of the controller's items as the dropdown items
|
|
259
|
+
// to trigger an update of the focused index in _setDropdownItems.
|
|
260
|
+
this._setDropdownItems([...this.filteredItems]);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
super.requestContentUpdate();
|
|
252
264
|
}
|
|
253
265
|
|
|
254
266
|
/** @private */
|
|
@@ -257,6 +269,8 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
257
269
|
this.pageSize = oldPageSize;
|
|
258
270
|
throw new Error('`pageSize` value must be an integer > 0');
|
|
259
271
|
}
|
|
272
|
+
|
|
273
|
+
this._dataProviderController.setPageSize(pageSize);
|
|
260
274
|
this.clearCache();
|
|
261
275
|
}
|
|
262
276
|
|
|
@@ -266,6 +280,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
266
280
|
this.dataProvider = oldDataProvider;
|
|
267
281
|
});
|
|
268
282
|
|
|
283
|
+
this._dataProviderController.setDataProvider(dataProvider);
|
|
269
284
|
this.clearCache();
|
|
270
285
|
}
|
|
271
286
|
|
|
@@ -274,8 +289,6 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
274
289
|
if (this.items !== undefined && this.dataProvider !== undefined) {
|
|
275
290
|
restoreOldValueCallback();
|
|
276
291
|
throw new Error('Using `items` and `dataProvider` together is not supported');
|
|
277
|
-
} else if (this.dataProvider && !this.filteredItems) {
|
|
278
|
-
this.filteredItems = [];
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
294
|
|
|
@@ -294,28 +307,4 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
294
307
|
}
|
|
295
308
|
}
|
|
296
309
|
}
|
|
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
|
-
Object.entries(this._pendingRequests).forEach(([page, callback]) => {
|
|
315
|
-
if (parseInt(page) >= lastPage) {
|
|
316
|
-
callback([], size);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
310
|
};
|
|
@@ -259,7 +259,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
259
259
|
static get observers() {
|
|
260
260
|
return [
|
|
261
261
|
'_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)',
|
|
262
|
-
'_openedOrItemsChanged(opened, _dropdownItems, loading)',
|
|
262
|
+
'_openedOrItemsChanged(opened, _dropdownItems, loading, _keepOverlayOpened)',
|
|
263
263
|
'_updateScroller(_scroller, _dropdownItems, opened, loading, selectedItem, itemIdPath, _focusedIndex, renderer, theme)',
|
|
264
264
|
];
|
|
265
265
|
}
|
|
@@ -487,10 +487,10 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
/** @private */
|
|
490
|
-
_openedOrItemsChanged(opened, items, loading) {
|
|
490
|
+
_openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
|
|
491
491
|
// Close the overlay if there are no items to display.
|
|
492
492
|
// See https://github.com/vaadin/vaadin-combo-box/pull/964
|
|
493
|
-
this._overlayOpened = !!(opened && (loading || (items && items.length)));
|
|
493
|
+
this._overlayOpened = !!(opened && (keepOverlayOpened || loading || (items && items.length)));
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
/** @private */
|
|
@@ -1113,6 +1113,10 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1113
1113
|
this.items = oldItems;
|
|
1114
1114
|
});
|
|
1115
1115
|
|
|
1116
|
+
if (this.dataProvider) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1116
1120
|
if (items) {
|
|
1117
1121
|
this.filteredItems = items.slice(0);
|
|
1118
1122
|
} else if (oldItems) {
|