@vaadin/combo-box 24.4.0-alpha9 → 24.4.0-dev.4b20a0c55
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 +0 -1
- package/package.json +13 -13
- package/src/vaadin-combo-box-data-provider-mixin.js +111 -120
- package/src/vaadin-combo-box-light-mixin.d.ts +26 -0
- package/src/vaadin-combo-box-light-mixin.js +140 -0
- package/src/vaadin-combo-box-light.d.ts +2 -7
- package/src/vaadin-combo-box-light.js +3 -127
- package/src/vaadin-combo-box-mixin.js +7 -7
- package/src/vaadin-combo-box-scroller-mixin.js +1 -0
- package/theme/lumo/vaadin-combo-box-item-styles.js +2 -4
- package/theme/lumo/vaadin-combo-box-item-styles.d.ts +0 -5
- package/theme/lumo/vaadin-combo-box-light.d.ts +0 -3
- package/theme/lumo/vaadin-combo-box-overlay-styles.d.ts +0 -6
- package/theme/lumo/vaadin-combo-box-styles.d.ts +0 -2
- package/theme/lumo/vaadin-combo-box.d.ts +0 -4
- package/theme/material/vaadin-combo-box-item-styles.d.ts +0 -5
- package/theme/material/vaadin-combo-box-light.d.ts +0 -3
- package/theme/material/vaadin-combo-box-overlay-styles.d.ts +0 -4
- package/theme/material/vaadin-combo-box-styles.d.ts +0 -3
- package/theme/material/vaadin-combo-box.d.ts +0 -4
- package/web-types.json +0 -1188
- package/web-types.lit.json +0 -573
package/README.md
CHANGED
|
@@ -5,7 +5,6 @@ A web component for choosing a value from a filterable list of options presented
|
|
|
5
5
|
[Documentation + Live Demo ↗](https://vaadin.com/docs/latest/components/combo-box)
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@vaadin/combo-box)
|
|
8
|
-
[](https://discord.gg/PHmkCKC)
|
|
9
8
|
|
|
10
9
|
```html
|
|
11
10
|
<vaadin-combo-box
|
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.4b20a0c55",
|
|
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.4b20a0c55",
|
|
42
|
+
"@vaadin/component-base": "24.4.0-dev.4b20a0c55",
|
|
43
|
+
"@vaadin/field-base": "24.4.0-dev.4b20a0c55",
|
|
44
|
+
"@vaadin/input-container": "24.4.0-dev.4b20a0c55",
|
|
45
|
+
"@vaadin/item": "24.4.0-dev.4b20a0c55",
|
|
46
|
+
"@vaadin/lit-renderer": "24.4.0-dev.4b20a0c55",
|
|
47
|
+
"@vaadin/overlay": "24.4.0-dev.4b20a0c55",
|
|
48
|
+
"@vaadin/vaadin-lumo-styles": "24.4.0-dev.4b20a0c55",
|
|
49
|
+
"@vaadin/vaadin-material-styles": "24.4.0-dev.4b20a0c55",
|
|
50
|
+
"@vaadin/vaadin-themable-mixin": "24.4.0-dev.4b20a0c55"
|
|
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.4b20a0c55",
|
|
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": "b79c81e5f6fd24684b34ee0dc434e94d943ea13e"
|
|
64
64
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2015 - 2024 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(),
|
|
@@ -67,6 +62,12 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
67
62
|
__previousDataProviderFilter: {
|
|
68
63
|
type: String,
|
|
69
64
|
},
|
|
65
|
+
|
|
66
|
+
/** @private */
|
|
67
|
+
_hasData: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
value: false,
|
|
70
|
+
},
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
|
|
@@ -78,10 +79,32 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
78
79
|
];
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
constructor() {
|
|
83
|
+
super();
|
|
84
|
+
|
|
85
|
+
/** @type {DataProviderController} */
|
|
86
|
+
this._dataProviderController = new DataProviderController(this, {
|
|
87
|
+
size: this.size,
|
|
88
|
+
pageSize: this.pageSize,
|
|
89
|
+
getItemId: (item) => get(this.itemIdPath, item),
|
|
90
|
+
placeholder: this.__placeHolder,
|
|
91
|
+
dataProvider: this.dataProvider ? this.dataProvider.bind(this) : null,
|
|
92
|
+
dataProviderParams: () => ({ filter: this.filter }),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
/** @protected */
|
|
82
97
|
ready() {
|
|
83
98
|
super.ready();
|
|
99
|
+
|
|
100
|
+
this._dataProviderController.addEventListener('page-requested', this.__onDataProviderPageRequested.bind(this));
|
|
101
|
+
this._dataProviderController.addEventListener('page-loaded', this.__onDataProviderPageLoaded.bind(this));
|
|
102
|
+
|
|
84
103
|
this._scroller.addEventListener('index-requested', (e) => {
|
|
104
|
+
if (!this._shouldFetchData()) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
85
108
|
const index = e.detail.index;
|
|
86
109
|
const currentScrollerPos = e.detail.currentScrollerPos;
|
|
87
110
|
const allowedIndexRange = Math.floor(this.pageSize * 1.5);
|
|
@@ -95,10 +118,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
95
118
|
}
|
|
96
119
|
|
|
97
120
|
if (index !== undefined) {
|
|
98
|
-
|
|
99
|
-
if (this._shouldLoadPage(page)) {
|
|
100
|
-
this._loadPage(page);
|
|
101
|
-
}
|
|
121
|
+
this._dataProviderController.ensureFlatIndexLoaded(index);
|
|
102
122
|
}
|
|
103
123
|
});
|
|
104
124
|
}
|
|
@@ -113,19 +133,14 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
113
133
|
if (this.__previousDataProviderFilter !== filter) {
|
|
114
134
|
this.__previousDataProviderFilter = filter;
|
|
115
135
|
|
|
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
|
|
136
|
+
this._keepOverlayOpened = true;
|
|
122
137
|
this.size = undefined;
|
|
123
|
-
|
|
124
138
|
this.clearCache();
|
|
139
|
+
this._keepOverlayOpened = false;
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
/** @
|
|
143
|
+
/** @protected */
|
|
129
144
|
_shouldFetchData() {
|
|
130
145
|
if (!this.dataProvider) {
|
|
131
146
|
return false;
|
|
@@ -136,8 +151,12 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
136
151
|
|
|
137
152
|
/** @private */
|
|
138
153
|
_ensureFirstPage(opened) {
|
|
139
|
-
if (
|
|
140
|
-
|
|
154
|
+
if (!this._shouldFetchData()) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (opened && !this._hasData) {
|
|
159
|
+
this._dataProviderController.loadFirstPage();
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
162
|
|
|
@@ -151,104 +170,99 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
/** @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;
|
|
173
|
+
__onDataProviderPageRequested() {
|
|
174
|
+
this.loading = true;
|
|
165
175
|
}
|
|
166
176
|
|
|
167
177
|
/** @private */
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (this._pendingRequests[page] || !this.dataProvider) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
178
|
+
__onDataProviderPageLoaded() {
|
|
179
|
+
this._hasData = true;
|
|
173
180
|
|
|
174
|
-
|
|
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
|
-
}
|
|
181
|
+
this.requestContentUpdate();
|
|
184
182
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
if (!this.opened && !this._isInputFocused()) {
|
|
184
|
+
this._commitValue();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
188
187
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Clears the cached pages and reloads data from dataprovider when needed.
|
|
190
|
+
*/
|
|
191
|
+
clearCache() {
|
|
192
|
+
if (!this.dataProvider) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
192
195
|
|
|
193
|
-
|
|
194
|
-
this.size = size;
|
|
195
|
-
}
|
|
196
|
+
this._dataProviderController.clearCache();
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
this._hasData = false;
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
this.loading = false;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
200
|
+
this.requestContentUpdate();
|
|
203
201
|
|
|
204
|
-
this.
|
|
205
|
-
|
|
206
|
-
|
|
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);
|
|
202
|
+
if (this._shouldFetchData()) {
|
|
203
|
+
this._dataProviderController.loadFirstPage();
|
|
204
|
+
}
|
|
211
205
|
}
|
|
212
206
|
|
|
213
207
|
/** @private */
|
|
214
|
-
|
|
215
|
-
|
|
208
|
+
_sizeChanged(size = 0) {
|
|
209
|
+
const { rootCache } = this._dataProviderController;
|
|
210
|
+
// When the size update originates from the developer,
|
|
211
|
+
// sync the new size with the controller and trigger
|
|
212
|
+
// a content update to re-render the scroller.
|
|
213
|
+
if (rootCache.size !== size) {
|
|
214
|
+
rootCache.size = size;
|
|
215
|
+
this.requestContentUpdate();
|
|
216
|
+
}
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
/**
|
|
219
|
-
*
|
|
220
|
+
* @private
|
|
221
|
+
* @override
|
|
220
222
|
*/
|
|
221
|
-
|
|
223
|
+
_filteredItemsChanged(items) {
|
|
222
224
|
if (!this.dataProvider) {
|
|
223
|
-
return;
|
|
225
|
+
return super._filteredItemsChanged(items);
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (this._shouldFetchData()) {
|
|
234
|
-
this._forceNextRequest = false;
|
|
235
|
-
this._loadPage(0);
|
|
236
|
-
} else {
|
|
237
|
-
this._forceNextRequest = true;
|
|
228
|
+
const { rootCache } = this._dataProviderController;
|
|
229
|
+
// When the items update originates from the developer,
|
|
230
|
+
// sync the new items with the controller and trigger
|
|
231
|
+
// a content update to re-render the scroller.
|
|
232
|
+
if (rootCache.items !== items) {
|
|
233
|
+
rootCache.items = items;
|
|
234
|
+
this.requestContentUpdate();
|
|
238
235
|
}
|
|
239
236
|
}
|
|
240
237
|
|
|
241
|
-
/** @
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
238
|
+
/** @override */
|
|
239
|
+
requestContentUpdate() {
|
|
240
|
+
if (this.dataProvider) {
|
|
241
|
+
const { rootCache } = this._dataProviderController;
|
|
242
|
+
|
|
243
|
+
// Sync the controller's size with the component.
|
|
244
|
+
// They can be out of sync after, for example,
|
|
245
|
+
// the controller received new data.
|
|
246
|
+
if ((this.size || 0) !== rootCache.size) {
|
|
247
|
+
this.size = rootCache.size;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Sync the controller's items with the component.
|
|
251
|
+
// They can be out of sync after, for example,
|
|
252
|
+
// the controller received new data.
|
|
253
|
+
if (this.filteredItems !== rootCache.items) {
|
|
254
|
+
this.filteredItems = rootCache.items;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Sync the controller's loading state with the component.
|
|
258
|
+
this.loading = this._dataProviderController.isLoading();
|
|
259
|
+
|
|
260
|
+
// Set a copy of the controller's items as the dropdown items
|
|
261
|
+
// to trigger an update of the focused index in _setDropdownItems.
|
|
262
|
+
this._setDropdownItems([...this.filteredItems]);
|
|
246
263
|
}
|
|
247
|
-
this.filteredItems = filteredItems;
|
|
248
264
|
|
|
249
|
-
|
|
250
|
-
// Refers to https://github.com/vaadin/vaadin-flow-components/issues/229
|
|
251
|
-
this._flushPendingRequests(size);
|
|
265
|
+
super.requestContentUpdate();
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
/** @private */
|
|
@@ -257,6 +271,8 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
257
271
|
this.pageSize = oldPageSize;
|
|
258
272
|
throw new Error('`pageSize` value must be an integer > 0');
|
|
259
273
|
}
|
|
274
|
+
|
|
275
|
+
this._dataProviderController.setPageSize(pageSize);
|
|
260
276
|
this.clearCache();
|
|
261
277
|
}
|
|
262
278
|
|
|
@@ -266,6 +282,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
266
282
|
this.dataProvider = oldDataProvider;
|
|
267
283
|
});
|
|
268
284
|
|
|
285
|
+
this._dataProviderController.setDataProvider(dataProvider);
|
|
269
286
|
this.clearCache();
|
|
270
287
|
}
|
|
271
288
|
|
|
@@ -274,8 +291,6 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
274
291
|
if (this.items !== undefined && this.dataProvider !== undefined) {
|
|
275
292
|
restoreOldValueCallback();
|
|
276
293
|
throw new Error('Using `items` and `dataProvider` together is not supported');
|
|
277
|
-
} else if (this.dataProvider && !this.filteredItems) {
|
|
278
|
-
this.filteredItems = [];
|
|
279
294
|
}
|
|
280
295
|
}
|
|
281
296
|
|
|
@@ -294,28 +309,4 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
294
309
|
}
|
|
295
310
|
}
|
|
296
311
|
}
|
|
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
312
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2015 - 2024 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
|
|
8
|
+
import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-provider-mixin.js';
|
|
9
|
+
import type { ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
|
|
10
|
+
|
|
11
|
+
export declare function ComboBoxLightMixin<TItem, T extends Constructor<HTMLElement>>(
|
|
12
|
+
base: T,
|
|
13
|
+
): Constructor<ComboBoxDataProviderMixinClass<TItem>> &
|
|
14
|
+
Constructor<ComboBoxLightMixinClass> &
|
|
15
|
+
Constructor<ComboBoxMixinClass<TItem>> &
|
|
16
|
+
Constructor<ValidateMixinClass> &
|
|
17
|
+
T;
|
|
18
|
+
|
|
19
|
+
export declare class ComboBoxLightMixinClass {
|
|
20
|
+
/**
|
|
21
|
+
* Name of the two-way data-bindable property representing the
|
|
22
|
+
* value of the custom input field.
|
|
23
|
+
* @attr {string} attr-for-value
|
|
24
|
+
*/
|
|
25
|
+
attrForValue: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2015 - 2024 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { dashToCamelCase } from '@polymer/polymer/lib/utils/case-map.js';
|
|
7
|
+
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
8
|
+
import { ValidateMixin } from '@vaadin/field-base/src/validate-mixin.js';
|
|
9
|
+
import { ComboBoxDataProviderMixin } from './vaadin-combo-box-data-provider-mixin.js';
|
|
10
|
+
import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @polymerMixin
|
|
14
|
+
* @mixes ComboBoxDataProviderMixin
|
|
15
|
+
* @mixes ComboBoxMixin
|
|
16
|
+
* @mixes ValidateMixin
|
|
17
|
+
*/
|
|
18
|
+
export const ComboBoxLightMixin = (superClass) =>
|
|
19
|
+
class ComboBoxLightMixinClass extends ComboBoxDataProviderMixin(ComboBoxMixin(ValidateMixin(superClass))) {
|
|
20
|
+
static get properties() {
|
|
21
|
+
return {
|
|
22
|
+
/**
|
|
23
|
+
* Name of the two-way data-bindable property representing the
|
|
24
|
+
* value of the custom input field.
|
|
25
|
+
* @attr {string} attr-for-value
|
|
26
|
+
* @type {string}
|
|
27
|
+
*/
|
|
28
|
+
attrForValue: {
|
|
29
|
+
type: String,
|
|
30
|
+
value: 'value',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Used by `InputControlMixin` as a reference to the clear button element.
|
|
37
|
+
* @protected
|
|
38
|
+
* @return {!HTMLElement}
|
|
39
|
+
*/
|
|
40
|
+
get clearElement() {
|
|
41
|
+
return this.querySelector('.clear-button');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Override this getter from `InputMixin` to allow using
|
|
46
|
+
* an arbitrary property name instead of `value`
|
|
47
|
+
* for accessing the input element's value.
|
|
48
|
+
*
|
|
49
|
+
* @protected
|
|
50
|
+
* @override
|
|
51
|
+
* @return {string}
|
|
52
|
+
*/
|
|
53
|
+
get _inputElementValueProperty() {
|
|
54
|
+
return dashToCamelCase(this.attrForValue);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @protected
|
|
59
|
+
* @override
|
|
60
|
+
* @return {HTMLInputElement | undefined}
|
|
61
|
+
*/
|
|
62
|
+
get _nativeInput() {
|
|
63
|
+
const input = this.inputElement;
|
|
64
|
+
|
|
65
|
+
if (input) {
|
|
66
|
+
// Support `<input class="input">`
|
|
67
|
+
if (input instanceof HTMLInputElement) {
|
|
68
|
+
return input;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Support `<input>` in light DOM (e.g. `vaadin-text-field`)
|
|
72
|
+
const slottedInput = input.querySelector('input');
|
|
73
|
+
if (slottedInput) {
|
|
74
|
+
return slottedInput;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (input.shadowRoot) {
|
|
78
|
+
// Support `<input>` in Shadow DOM (e.g. `mwc-textfield`)
|
|
79
|
+
const shadowInput = input.shadowRoot.querySelector('input');
|
|
80
|
+
if (shadowInput) {
|
|
81
|
+
return shadowInput;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @protected */
|
|
90
|
+
ready() {
|
|
91
|
+
super.ready();
|
|
92
|
+
|
|
93
|
+
this._toggleElement = this.querySelector('.toggle-button');
|
|
94
|
+
|
|
95
|
+
// Wait until the slotted input DOM is ready
|
|
96
|
+
afterNextRender(this, () => {
|
|
97
|
+
this._setInputElement(this.querySelector('vaadin-text-field,.input'));
|
|
98
|
+
this._revertInputValue();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns true if the current input value satisfies all constraints (if any).
|
|
104
|
+
* @return {boolean}
|
|
105
|
+
*/
|
|
106
|
+
checkValidity() {
|
|
107
|
+
if (this.inputElement && this.inputElement.validate) {
|
|
108
|
+
return this.inputElement.validate();
|
|
109
|
+
}
|
|
110
|
+
return super.checkValidity();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @protected
|
|
115
|
+
* @override
|
|
116
|
+
*/
|
|
117
|
+
_isClearButton(event) {
|
|
118
|
+
return (
|
|
119
|
+
super._isClearButton(event) ||
|
|
120
|
+
(event.type === 'input' && !event.isTrusted) || // Fake input event dispatched by clear button
|
|
121
|
+
event.composedPath()[0].getAttribute('part') === 'clear-button'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @protected
|
|
127
|
+
* @override
|
|
128
|
+
*/
|
|
129
|
+
_shouldRemoveFocus(event) {
|
|
130
|
+
const isBlurringControlButtons = event.target === this._toggleElement || event.target === this.clearElement;
|
|
131
|
+
const isFocusingInputElement = event.relatedTarget && event.relatedTarget === this._nativeInput;
|
|
132
|
+
|
|
133
|
+
// prevent closing the overlay when moving focus from clear or toggle buttons to the internal input
|
|
134
|
+
if (isBlurringControlButtons && isFocusingInputElement) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return super._shouldRemoveFocus(event);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -11,6 +11,7 @@ import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.j
|
|
|
11
11
|
import type { ThemableMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
12
|
import type { ThemePropertyMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
13
13
|
import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-provider-mixin.js';
|
|
14
|
+
import type { ComboBoxLightMixinClass } from './vaadin-combo-box-light-mixin.js';
|
|
14
15
|
import type { ComboBoxDefaultItem, ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
|
|
15
16
|
export {
|
|
16
17
|
ComboBoxDataProvider,
|
|
@@ -126,13 +127,6 @@ export interface ComboBoxLightEventMap<TItem> extends HTMLElementEventMap {
|
|
|
126
127
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
127
128
|
*/
|
|
128
129
|
declare class ComboBoxLight<TItem = ComboBoxDefaultItem> extends HTMLElement {
|
|
129
|
-
/**
|
|
130
|
-
* Name of the two-way data-bindable property representing the
|
|
131
|
-
* value of the custom input field.
|
|
132
|
-
* @attr {string} attr-for-value
|
|
133
|
-
*/
|
|
134
|
-
attrForValue: string;
|
|
135
|
-
|
|
136
130
|
addEventListener<K extends keyof ComboBoxLightEventMap<TItem>>(
|
|
137
131
|
type: K,
|
|
138
132
|
listener: (this: ComboBoxLight<TItem>, ev: ComboBoxLightEventMap<TItem>[K]) => void,
|
|
@@ -148,6 +142,7 @@ declare class ComboBoxLight<TItem = ComboBoxDefaultItem> extends HTMLElement {
|
|
|
148
142
|
|
|
149
143
|
interface ComboBoxLight<TItem = ComboBoxDefaultItem>
|
|
150
144
|
extends ComboBoxDataProviderMixinClass<TItem>,
|
|
145
|
+
ComboBoxLightMixinClass,
|
|
151
146
|
ComboBoxMixinClass<TItem>,
|
|
152
147
|
KeyboardMixinClass,
|
|
153
148
|
InputMixinClass,
|