@vaadin/combo-box 24.4.0-dev.b3e1d14600 → 24.5.0-alpha1

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.
Files changed (49) hide show
  1. package/README.md +0 -1
  2. package/package.json +16 -14
  3. package/src/lit/renderer-directives.d.ts +1 -1
  4. package/src/lit/renderer-directives.js +1 -1
  5. package/src/vaadin-combo-box-data-provider-mixin.d.ts +1 -1
  6. package/src/vaadin-combo-box-data-provider-mixin.js +116 -120
  7. package/src/vaadin-combo-box-item-mixin.d.ts +1 -1
  8. package/src/vaadin-combo-box-item-mixin.js +2 -2
  9. package/src/vaadin-combo-box-item.d.ts +1 -1
  10. package/src/vaadin-combo-box-item.js +1 -1
  11. package/src/vaadin-combo-box-light-mixin.d.ts +26 -0
  12. package/src/vaadin-combo-box-light-mixin.js +140 -0
  13. package/src/vaadin-combo-box-light.d.ts +3 -8
  14. package/src/vaadin-combo-box-light.js +4 -128
  15. package/src/vaadin-combo-box-mixin.d.ts +9 -1
  16. package/src/vaadin-combo-box-mixin.js +112 -33
  17. package/src/vaadin-combo-box-overlay-mixin.d.ts +1 -1
  18. package/src/vaadin-combo-box-overlay-mixin.js +1 -1
  19. package/src/vaadin-combo-box-overlay.d.ts +1 -1
  20. package/src/vaadin-combo-box-overlay.js +1 -1
  21. package/src/vaadin-combo-box-placeholder.js +1 -1
  22. package/src/vaadin-combo-box-scroller-mixin.d.ts +1 -1
  23. package/src/vaadin-combo-box-scroller-mixin.js +65 -16
  24. package/src/vaadin-combo-box-scroller.d.ts +1 -1
  25. package/src/vaadin-combo-box-scroller.js +1 -1
  26. package/src/vaadin-combo-box.d.ts +1 -1
  27. package/src/vaadin-combo-box.js +1 -1
  28. package/src/vaadin-lit-combo-box-item.js +50 -0
  29. package/src/vaadin-lit-combo-box-light.js +58 -0
  30. package/src/vaadin-lit-combo-box-overlay.js +76 -0
  31. package/src/vaadin-lit-combo-box-scroller.js +59 -0
  32. package/src/vaadin-lit-combo-box.js +170 -0
  33. package/theme/lumo/vaadin-combo-box-item-styles.d.ts +5 -0
  34. package/theme/lumo/vaadin-combo-box-item-styles.js +2 -4
  35. package/theme/lumo/vaadin-combo-box-light.d.ts +3 -0
  36. package/theme/lumo/vaadin-combo-box-overlay-styles.d.ts +6 -0
  37. package/theme/lumo/vaadin-combo-box-styles.d.ts +2 -0
  38. package/theme/lumo/vaadin-combo-box.d.ts +4 -0
  39. package/theme/lumo/vaadin-lit-combo-box-light.d.ts +3 -0
  40. package/theme/lumo/vaadin-lit-combo-box-light.js +3 -0
  41. package/theme/lumo/vaadin-lit-combo-box.d.ts +4 -0
  42. package/theme/lumo/vaadin-lit-combo-box.js +4 -0
  43. package/theme/material/vaadin-combo-box-item-styles.d.ts +5 -0
  44. package/theme/material/vaadin-combo-box-light.d.ts +3 -0
  45. package/theme/material/vaadin-combo-box-overlay-styles.d.ts +4 -0
  46. package/theme/material/vaadin-combo-box-styles.d.ts +3 -0
  47. package/theme/material/vaadin-combo-box.d.ts +4 -0
  48. package/web-types.json +1210 -0
  49. package/web-types.lit.json +587 -0
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
  [![npm version](https://badgen.net/npm/v/@vaadin/combo-box)](https://www.npmjs.com/package/@vaadin/combo-box)
8
- [![Discord](https://img.shields.io/discord/732335336448852018?label=discord)](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-dev.b3e1d14600",
3
+ "version": "24.5.0-alpha1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -26,7 +26,9 @@
26
26
  "vaadin-*.d.ts",
27
27
  "vaadin-*.js",
28
28
  "web-types.json",
29
- "web-types.lit.json"
29
+ "web-types.lit.json",
30
+ "!vaadin-lit-*.d.ts",
31
+ "!vaadin-lit-*.js"
30
32
  ],
31
33
  "keywords": [
32
34
  "Vaadin",
@@ -38,21 +40,21 @@
38
40
  "dependencies": {
39
41
  "@open-wc/dedupe-mixin": "^1.3.0",
40
42
  "@polymer/polymer": "^3.0.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"
43
+ "@vaadin/a11y-base": "24.5.0-alpha1",
44
+ "@vaadin/component-base": "24.5.0-alpha1",
45
+ "@vaadin/field-base": "24.5.0-alpha1",
46
+ "@vaadin/input-container": "24.5.0-alpha1",
47
+ "@vaadin/item": "24.5.0-alpha1",
48
+ "@vaadin/lit-renderer": "24.5.0-alpha1",
49
+ "@vaadin/overlay": "24.5.0-alpha1",
50
+ "@vaadin/vaadin-lumo-styles": "24.5.0-alpha1",
51
+ "@vaadin/vaadin-material-styles": "24.5.0-alpha1",
52
+ "@vaadin/vaadin-themable-mixin": "24.5.0-alpha1"
51
53
  },
52
54
  "devDependencies": {
53
55
  "@esm-bundle/chai": "^4.3.4",
54
56
  "@vaadin/testing-helpers": "^0.6.0",
55
- "@vaadin/text-field": "24.4.0-dev.b3e1d14600",
57
+ "@vaadin/text-field": "24.5.0-alpha1",
56
58
  "lit": "^3.0.0",
57
59
  "sinon": "^13.0.2"
58
60
  },
@@ -60,5 +62,5 @@
60
62
  "web-types.json",
61
63
  "web-types.lit.json"
62
64
  ],
63
- "gitHead": "502d4f5b03f770a83d270d98078cde230254dd0e"
65
+ "gitHead": "57806caac5468532a3b4e3dbdda730cd0fca193a"
64
66
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2017 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2017 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { DirectiveResult } from 'lit/directive.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2017 - 2023 Vaadin Ltd.
3
+ * Copyright (c) 2017 - 2024 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { directive } from 'lit/directive.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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';
8
6
  import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
9
7
 
10
8
  /**
@@ -23,6 +21,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
23
21
  type: Number,
24
22
  value: 50,
25
23
  observer: '_pageSizeChanged',
24
+ sync: true,
26
25
  },
27
26
 
28
27
  /**
@@ -32,6 +31,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
32
31
  size: {
33
32
  type: Number,
34
33
  observer: '_sizeChanged',
34
+ sync: true,
35
35
  },
36
36
 
37
37
  /**
@@ -51,6 +51,14 @@ export const ComboBoxDataProviderMixin = (superClass) =>
51
51
  dataProvider: {
52
52
  type: Object,
53
53
  observer: '_dataProviderChanged',
54
+ sync: true,
55
+ },
56
+
57
+ /** @private */
58
+ _pendingRequests: {
59
+ value: () => {
60
+ return {};
61
+ },
54
62
  },
55
63
 
56
64
  /** @private */
@@ -73,47 +81,20 @@ export const ComboBoxDataProviderMixin = (superClass) =>
73
81
  ];
74
82
  }
75
83
 
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
-
90
84
  /** @protected */
91
85
  ready() {
92
86
  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
-
98
87
  this._scroller.addEventListener('index-requested', (e) => {
99
88
  if (!this._shouldFetchData()) {
100
89
  return;
101
90
  }
102
91
 
103
92
  const index = e.detail.index;
104
- const currentScrollerPos = e.detail.currentScrollerPos;
105
- const allowedIndexRange = Math.floor(this.pageSize * 1.5);
106
-
107
- // Ignores the indexes, which are being re-sent during scrolling reset,
108
- // if the corresponding page is around the current scroller position.
109
- // Otherwise, there might be a last pages duplicates, which cause the
110
- // loading indicator hanging and blank items
111
- if (this._shouldSkipIndex(index, allowedIndexRange, currentScrollerPos)) {
112
- return;
113
- }
114
-
115
93
  if (index !== undefined) {
116
- this._dataProviderController.ensureFlatIndexLoaded(index);
94
+ const page = this._getPageForIndex(index);
95
+ if (this._shouldLoadPage(page)) {
96
+ this._loadPage(page);
97
+ }
117
98
  }
118
99
  });
119
100
  }
@@ -128,10 +109,11 @@ export const ComboBoxDataProviderMixin = (superClass) =>
128
109
  if (this.__previousDataProviderFilter !== filter) {
129
110
  this.__previousDataProviderFilter = filter;
130
111
 
131
- this._keepOverlayOpened = true;
112
+ this.__keepOverlayOpened = true;
113
+ this._pendingRequests = {};
132
114
  this.size = undefined;
133
115
  this.clearCache();
134
- this._keepOverlayOpened = false;
116
+ this.__keepOverlayOpened = false;
135
117
  }
136
118
  }
137
119
 
@@ -150,39 +132,74 @@ export const ComboBoxDataProviderMixin = (superClass) =>
150
132
  return;
151
133
  }
152
134
 
153
- if (opened && !this._dataProviderController.hasData) {
154
- this._dataProviderController.loadFirstPage();
135
+ if (opened && this._shouldLoadPage(0)) {
136
+ this._loadPage(0);
155
137
  }
156
138
  }
157
139
 
158
140
  /** @private */
159
- _shouldSkipIndex(index, allowedIndexRange, currentScrollerPos) {
160
- return (
161
- currentScrollerPos !== 0 &&
162
- index >= currentScrollerPos - allowedIndexRange &&
163
- index <= currentScrollerPos + allowedIndexRange
164
- );
165
- }
141
+ _shouldLoadPage(page) {
142
+ if (this._forceNextRequest) {
143
+ this._forceNextRequest = false;
144
+ return true;
145
+ }
166
146
 
167
- /** @private */
168
- __onDataProviderPageRequested() {
169
- this.loading = true;
147
+ const loadedItem = this.filteredItems[page * this.pageSize];
148
+ if (loadedItem !== undefined) {
149
+ return loadedItem instanceof ComboBoxPlaceholder;
150
+ }
151
+ return this.size === undefined;
170
152
  }
171
153
 
172
154
  /** @private */
173
- __onDataProviderPageReceived() {
174
- this.requestContentUpdate();
155
+ _loadPage(page) {
156
+ // Make sure same page isn't requested multiple times.
157
+ if (this._pendingRequests[page] || !this.dataProvider) {
158
+ return;
159
+ }
160
+
161
+ const params = {
162
+ page,
163
+ pageSize: this.pageSize,
164
+ filter: this.filter,
165
+ };
166
+
167
+ const callback = (items, size) => {
168
+ if (this._pendingRequests[page] !== callback) {
169
+ return;
170
+ }
171
+
172
+ const filteredItems = this.filteredItems ? [...this.filteredItems] : [];
173
+ filteredItems.splice(params.page * params.pageSize, items.length, ...items);
174
+ this.filteredItems = filteredItems;
175
+
176
+ if (!this.opened && !this._isInputFocused()) {
177
+ this._commitValue();
178
+ }
179
+
180
+ if (size !== undefined) {
181
+ this.size = size;
182
+ }
183
+
184
+ delete this._pendingRequests[page];
185
+
186
+ if (Object.keys(this._pendingRequests).length === 0) {
187
+ this.loading = false;
188
+ }
189
+ };
190
+
191
+ this._pendingRequests[page] = callback;
192
+ // Set the `loading` flag only after marking the request as pending
193
+ // to prevent the same page from getting requested multiple times
194
+ // as a result of `__loadingChanged` in the scroller which requests
195
+ // a virtualizer update which in turn may trigger a data provider page request.
196
+ this.loading = true;
197
+ this.dataProvider(params, callback);
175
198
  }
176
199
 
177
200
  /** @private */
178
- __onDataProviderPageLoaded() {
179
- if (!this.opened && !this._isInputFocused()) {
180
- this._commitValue();
181
- }
182
-
183
- if (!this._dataProviderController.isLoading()) {
184
- this.loading = false;
185
- }
201
+ _getPageForIndex(index) {
202
+ return Math.floor(index / this.pageSize);
186
203
  }
187
204
 
188
205
  /**
@@ -193,74 +210,32 @@ export const ComboBoxDataProviderMixin = (superClass) =>
193
210
  return;
194
211
  }
195
212
 
196
- this._dataProviderController.clearCache();
197
-
198
- this.requestContentUpdate();
213
+ this._pendingRequests = {};
214
+ const filteredItems = [];
215
+ for (let i = 0; i < (this.size || 0); i++) {
216
+ filteredItems.push(this.__placeHolder);
217
+ }
218
+ this.filteredItems = filteredItems;
199
219
 
200
220
  if (this._shouldFetchData()) {
201
- this._dataProviderController.loadFirstPage();
221
+ this._forceNextRequest = false;
222
+ this._loadPage(0);
223
+ } else {
224
+ this._forceNextRequest = true;
202
225
  }
203
226
  }
204
227
 
205
228
  /** @private */
206
229
  _sizeChanged(size = 0) {
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();
230
+ const filteredItems = (this.filteredItems || []).slice(0, size);
231
+ for (let i = 0; i < size; i++) {
232
+ filteredItems[i] = filteredItems[i] !== undefined ? filteredItems[i] : this.__placeHolder;
214
233
  }
215
- }
234
+ this.filteredItems = filteredItems;
216
235
 
217
- /**
218
- * @private
219
- * @override
220
- */
221
- _filteredItemsChanged(items) {
222
- if (!this.dataProvider) {
223
- return super._filteredItemsChanged(items);
224
- }
225
-
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();
236
+ // Cleans up the redundant pending requests for pages > size
237
+ // Refers to https://github.com/vaadin/vaadin-flow-components/issues/229
238
+ this._flushPendingRequests(size);
264
239
  }
265
240
 
266
241
  /** @private */
@@ -269,8 +244,6 @@ export const ComboBoxDataProviderMixin = (superClass) =>
269
244
  this.pageSize = oldPageSize;
270
245
  throw new Error('`pageSize` value must be an integer > 0');
271
246
  }
272
-
273
- this._dataProviderController.setPageSize(pageSize);
274
247
  this.clearCache();
275
248
  }
276
249
 
@@ -280,7 +253,6 @@ export const ComboBoxDataProviderMixin = (superClass) =>
280
253
  this.dataProvider = oldDataProvider;
281
254
  });
282
255
 
283
- this._dataProviderController.setDataProvider(dataProvider);
284
256
  this.clearCache();
285
257
  }
286
258
 
@@ -307,4 +279,28 @@ export const ComboBoxDataProviderMixin = (superClass) =>
307
279
  }
308
280
  }
309
281
  }
282
+
283
+ /**
284
+ * This method cleans up the page callbacks which refers to the
285
+ * non-existing pages, i.e. which item indexes are greater than the
286
+ * changed size.
287
+ * This case is basically happens when:
288
+ * 1. Users scroll fast to the bottom and combo box generates the
289
+ * redundant page request/callback
290
+ * 2. Server side uses undefined size lazy loading and suddenly reaches
291
+ * the exact size which is on the range edge
292
+ * (for default page size = 50, it will be 100, 200, 300, ...).
293
+ * @param size the new size of items
294
+ * @private
295
+ */
296
+ _flushPendingRequests(size) {
297
+ if (this._pendingRequests) {
298
+ const lastPage = Math.ceil(size / this.pageSize);
299
+ Object.entries(this._pendingRequests).forEach(([page, callback]) => {
300
+ if (parseInt(page) >= lastPage) {
301
+ callback([], size);
302
+ }
303
+ });
304
+ }
305
+ }
310
306
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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
6
 
@@ -60,7 +60,7 @@ export const ComboBoxItemMixin = (superClass) =>
60
60
  }
61
61
 
62
62
  static get observers() {
63
- return ['__rendererOrItemChanged(renderer, index, item.*, selected, focused)', '__updateLabel(label, renderer)'];
63
+ return ['__rendererOrItemChanged(renderer, index, item, selected, focused)', '__updateLabel(label, renderer)'];
64
64
  }
65
65
 
66
66
  static get observedAttributes() {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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
6
  import type { DirMixinClass } from '@vaadin/component-base/src/dir-mixin.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2015 - 2023 Vaadin Ltd.
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
6
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
@@ -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
+ };