@vaadin/combo-box 24.3.2 → 24.4.0-dev.223e39f050
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 +37 -32
- 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.
|
|
3
|
+
"version": "24.4.0-dev.223e39f050",
|
|
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": "
|
|
42
|
-
"@vaadin/component-base": "
|
|
43
|
-
"@vaadin/field-base": "
|
|
44
|
-
"@vaadin/input-container": "
|
|
45
|
-
"@vaadin/item": "
|
|
46
|
-
"@vaadin/lit-renderer": "
|
|
47
|
-
"@vaadin/overlay": "
|
|
48
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
49
|
-
"@vaadin/vaadin-material-styles": "
|
|
50
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
41
|
+
"@vaadin/a11y-base": "24.4.0-dev.223e39f050",
|
|
42
|
+
"@vaadin/component-base": "24.4.0-dev.223e39f050",
|
|
43
|
+
"@vaadin/field-base": "24.4.0-dev.223e39f050",
|
|
44
|
+
"@vaadin/input-container": "24.4.0-dev.223e39f050",
|
|
45
|
+
"@vaadin/item": "24.4.0-dev.223e39f050",
|
|
46
|
+
"@vaadin/lit-renderer": "24.4.0-dev.223e39f050",
|
|
47
|
+
"@vaadin/overlay": "24.4.0-dev.223e39f050",
|
|
48
|
+
"@vaadin/vaadin-lumo-styles": "24.4.0-dev.223e39f050",
|
|
49
|
+
"@vaadin/vaadin-material-styles": "24.4.0-dev.223e39f050",
|
|
50
|
+
"@vaadin/vaadin-themable-mixin": "24.4.0-dev.223e39f050"
|
|
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": "
|
|
55
|
+
"@vaadin/text-field": "24.4.0-dev.223e39f050",
|
|
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": "5e2e3bfc811c95aed9354235fab93fdbf43eb354"
|
|
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) {
|
|
@@ -1122,34 +1126,8 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1122
1126
|
}
|
|
1123
1127
|
|
|
1124
1128
|
/** @private */
|
|
1125
|
-
_filteredItemsChanged(filteredItems
|
|
1129
|
+
_filteredItemsChanged(filteredItems) {
|
|
1126
1130
|
this._setDropdownItems(filteredItems);
|
|
1127
|
-
|
|
1128
|
-
// Store the currently focused item if any. The focused index preserves
|
|
1129
|
-
// in the case when more filtered items are loading but it is reset
|
|
1130
|
-
// when the user types in a filter query.
|
|
1131
|
-
const focusedItem = oldFilteredItems ? oldFilteredItems[this._focusedIndex] : null;
|
|
1132
|
-
|
|
1133
|
-
// Try to sync `selectedItem` based on `value` once a new set of `filteredItems` is available
|
|
1134
|
-
// (as a result of external filtering or when they have been loaded by the data provider).
|
|
1135
|
-
// When `value` is specified but `selectedItem` is not, it means that there was no item
|
|
1136
|
-
// matching `value` at the moment `value` was set, so `selectedItem` has remained unsynced.
|
|
1137
|
-
const valueIndex = this.__getItemIndexByValue(filteredItems, this.value);
|
|
1138
|
-
if ((this.selectedItem === null || this.selectedItem === undefined) && valueIndex >= 0) {
|
|
1139
|
-
this.selectedItem = filteredItems[valueIndex];
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
// Try to first set focus on the item that had been focused before `filteredItems` were updated
|
|
1143
|
-
// if it is still present in the `filteredItems` array. Otherwise, set the focused index
|
|
1144
|
-
// depending on the selected item or the filter query.
|
|
1145
|
-
const focusedItemIndex = this.__getItemIndexByValue(filteredItems, this._getItemValue(focusedItem));
|
|
1146
|
-
if (focusedItemIndex > -1) {
|
|
1147
|
-
this._focusedIndex = focusedItemIndex;
|
|
1148
|
-
} else {
|
|
1149
|
-
// When the user filled in something that is different from the current value = filtering is enabled,
|
|
1150
|
-
// set the focused index to the item that matches the filter query.
|
|
1151
|
-
this._focusedIndex = this.__getItemIndexByLabel(this.filteredItems, this.filter);
|
|
1152
|
-
}
|
|
1153
1131
|
}
|
|
1154
1132
|
|
|
1155
1133
|
/** @private */
|
|
@@ -1191,8 +1169,35 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1191
1169
|
*
|
|
1192
1170
|
* @protected
|
|
1193
1171
|
*/
|
|
1194
|
-
_setDropdownItems(
|
|
1195
|
-
this._dropdownItems
|
|
1172
|
+
_setDropdownItems(newItems) {
|
|
1173
|
+
const oldItems = this._dropdownItems;
|
|
1174
|
+
this._dropdownItems = newItems;
|
|
1175
|
+
|
|
1176
|
+
// Store the currently focused item if any. The focused index preserves
|
|
1177
|
+
// in the case when more filtered items are loading but it is reset
|
|
1178
|
+
// when the user types in a filter query.
|
|
1179
|
+
const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
|
|
1180
|
+
|
|
1181
|
+
// Try to sync `selectedItem` based on `value` once a new set of `filteredItems` is available
|
|
1182
|
+
// (as a result of external filtering or when they have been loaded by the data provider).
|
|
1183
|
+
// When `value` is specified but `selectedItem` is not, it means that there was no item
|
|
1184
|
+
// matching `value` at the moment `value` was set, so `selectedItem` has remained unsynced.
|
|
1185
|
+
const valueIndex = this.__getItemIndexByValue(newItems, this.value);
|
|
1186
|
+
if ((this.selectedItem === null || this.selectedItem === undefined) && valueIndex >= 0) {
|
|
1187
|
+
this.selectedItem = newItems[valueIndex];
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// Try to first set focus on the item that had been focused before `newItems` were updated
|
|
1191
|
+
// if it is still present in the `newItems` array. Otherwise, set the focused index
|
|
1192
|
+
// depending on the selected item or the filter query.
|
|
1193
|
+
const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
|
|
1194
|
+
if (focusedItemIndex > -1) {
|
|
1195
|
+
this._focusedIndex = focusedItemIndex;
|
|
1196
|
+
} else {
|
|
1197
|
+
// When the user filled in something that is different from the current value = filtering is enabled,
|
|
1198
|
+
// set the focused index to the item that matches the filter query.
|
|
1199
|
+
this._focusedIndex = this.__getItemIndexByLabel(newItems, this.filter);
|
|
1200
|
+
}
|
|
1196
1201
|
}
|
|
1197
1202
|
|
|
1198
1203
|
/** @private */
|