@vaadin/combo-box 25.3.0-dev.3a3c2d7d2a → 25.3.0
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/custom-elements.json +158 -38
- package/package.json +14 -14
- package/src/vaadin-combo-box-base-mixin.js +65 -13
- package/src/vaadin-combo-box-data-provider-mixin.js +15 -9
- package/src/vaadin-combo-box-focus-index-mixin.js +5 -0
- package/src/vaadin-combo-box-item-mixin.js +1 -1
- package/src/vaadin-combo-box-item.js +4 -3
- package/src/vaadin-combo-box-items-mixin.d.ts +45 -0
- package/src/vaadin-combo-box-items-mixin.js +167 -4
- package/src/vaadin-combo-box-mixin.d.ts +0 -17
- package/src/vaadin-combo-box-mixin.js +7 -112
- package/src/vaadin-combo-box-overlay-mixin.js +10 -13
- package/src/vaadin-combo-box-overlay.js +4 -1
- package/src/vaadin-combo-box-placeholder.d.ts +20 -0
- package/src/vaadin-combo-box-scroller-mixin.d.ts +1 -6
- package/src/vaadin-combo-box-scroller-mixin.js +7 -19
- package/src/vaadin-combo-box-scroller.js +3 -1
- package/src/vaadin-combo-box.d.ts +43 -8
- package/src/vaadin-combo-box.js +43 -8
- package/web-types.json +47 -13
- package/web-types.lit.json +20 -6
|
@@ -120,10 +120,8 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
120
120
|
if (this.__previousDataProviderFilter !== filter) {
|
|
121
121
|
this.__previousDataProviderFilter = filter;
|
|
122
122
|
|
|
123
|
-
this.
|
|
124
|
-
this.size = undefined;
|
|
123
|
+
this.__dataProviderController.rootCache.size = undefined;
|
|
125
124
|
this.clearCache();
|
|
126
|
-
this.__keepOverlayOpened = false;
|
|
127
125
|
}
|
|
128
126
|
}
|
|
129
127
|
|
|
@@ -152,7 +150,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
152
150
|
|
|
153
151
|
/** @private */
|
|
154
152
|
__onDataProviderPageRequested() {
|
|
155
|
-
this.
|
|
153
|
+
this.__synchronizeControllerState();
|
|
156
154
|
}
|
|
157
155
|
|
|
158
156
|
/** @private */
|
|
@@ -179,14 +177,18 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
179
177
|
|
|
180
178
|
this.__dataProviderController.clearCache();
|
|
181
179
|
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
// Request the first page before synchronizing the component state.
|
|
181
|
+
// Otherwise the component would observe an intermediate state where
|
|
182
|
+
// the items are empty but no request is pending, which would close
|
|
183
|
+
// the overlay and re-open it right away.
|
|
184
184
|
if (this._shouldFetchData()) {
|
|
185
185
|
this._forceNextRequest = false;
|
|
186
186
|
this.__dataProviderController.loadFirstPage();
|
|
187
187
|
} else {
|
|
188
188
|
this._forceNextRequest = true;
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
this.__synchronizeControllerState();
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/**
|
|
@@ -240,9 +242,13 @@ export const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
240
242
|
// will be requested in the `ready()` callback.
|
|
241
243
|
if (this.__dataProviderInitialized && this.dataProvider) {
|
|
242
244
|
const { rootCache } = this.__dataProviderController;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.
|
|
245
|
+
// Apply all properties in one update so that observers depending on
|
|
246
|
+
// several of them never see a partially synchronized state.
|
|
247
|
+
this.setProperties({
|
|
248
|
+
size: rootCache.size,
|
|
249
|
+
filteredItems: rootCache.items,
|
|
250
|
+
loading: this.__dataProviderController.isLoading(),
|
|
251
|
+
});
|
|
246
252
|
}
|
|
247
253
|
}
|
|
248
254
|
|
|
@@ -24,6 +24,11 @@ export const ComboBoxFocusIndexMixin = (superClass) =>
|
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Ignore while a filter is active, keep the dropdown at the first match.
|
|
28
|
+
if (this.filter) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
// Defer until the dropdown is open and the items array has been
|
|
28
33
|
// populated. `_onOpened` and `__onDataProviderPageLoaded` re-fire
|
|
29
34
|
// the queued call once those conditions hold.
|
|
@@ -125,10 +125,10 @@ export const ComboBoxItemMixin = (superClass) =>
|
|
|
125
125
|
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
126
126
|
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
127
127
|
delete this._$litPart$;
|
|
128
|
+
this._oldRenderer = renderer;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
if (renderer) {
|
|
131
|
-
this._oldRenderer = renderer;
|
|
132
132
|
this.requestContentUpdate();
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -34,12 +34,11 @@ import { ComboBoxItemMixin } from './vaadin-combo-box-item-mixin.js';
|
|
|
34
34
|
*
|
|
35
35
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
36
36
|
*
|
|
37
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
37
38
|
* @customElement vaadin-combo-box-item
|
|
38
39
|
* @private
|
|
39
40
|
*/
|
|
40
|
-
|
|
41
|
-
ThemableMixin(DirMixin(PolylitMixin(LumoInjectionMixin(LitElement)))),
|
|
42
|
-
) {
|
|
41
|
+
class ComboBoxItem extends ComboBoxItemMixin(ThemableMixin(DirMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
|
|
43
42
|
static get is() {
|
|
44
43
|
return 'vaadin-combo-box-item';
|
|
45
44
|
}
|
|
@@ -60,3 +59,5 @@ export class ComboBoxItem extends ComboBoxItemMixin(
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
defineCustomElement(ComboBoxItem);
|
|
62
|
+
|
|
63
|
+
export { ComboBoxItem };
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
import type { ComboBoxBaseMixinClass } from './vaadin-combo-box-base-mixin.js';
|
|
8
8
|
|
|
9
|
+
export type ComboBoxPartialMatchMode = 'first-match' | 'none' | 'only-match';
|
|
10
|
+
|
|
9
11
|
export declare function ComboBoxItemsMixin<TItem, T extends Constructor<HTMLElement>>(
|
|
10
12
|
base: T,
|
|
11
13
|
): Constructor<ComboBoxBaseMixinClass> & Constructor<ComboBoxItemsMixinClass<TItem>> & T;
|
|
@@ -57,4 +59,47 @@ export declare class ComboBoxItemsMixinClass<TItem> {
|
|
|
57
59
|
* @attr {string} item-value-path
|
|
58
60
|
*/
|
|
59
61
|
itemValuePath: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Path for the id of the item. If `items` is an array of objects,
|
|
65
|
+
* the `itemIdPath` is used to compare and identify the same item
|
|
66
|
+
* in the selection and in `filteredItems` (items given by the
|
|
67
|
+
* `dataProvider` callback).
|
|
68
|
+
* @attr {string} item-id-path
|
|
69
|
+
*/
|
|
70
|
+
itemIdPath: string | null | undefined;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Controls which item is automatically set to be selected, for
|
|
74
|
+
* example on Enter, when the typed filter only partially matches
|
|
75
|
+
* its label. The item that will be selected is highlighted in the
|
|
76
|
+
* dropdown while typing:
|
|
77
|
+
*
|
|
78
|
+
* - `none` (default): an item is automatically set to be selected only when the filter matches its label exactly.
|
|
79
|
+
* - `first-match`: the first item in the filtered results is automatically set to be selected.
|
|
80
|
+
* - `only-match`: the item is automatically set to be selected when filtering narrows the results to a single item.
|
|
81
|
+
*
|
|
82
|
+
* In general, an exact match is always set to be selected and takes
|
|
83
|
+
* precedence over partial matches, regardless of the mode.
|
|
84
|
+
*
|
|
85
|
+
* A partial match is only applied while the dropdown is open. For
|
|
86
|
+
* example, when auto-open is disabled with `autoOpenDisabled`, typing
|
|
87
|
+
* does not highlight a match or set it to be selected until the
|
|
88
|
+
* dropdown is opened.
|
|
89
|
+
*
|
|
90
|
+
* This feature cannot be used together with custom values, because a
|
|
91
|
+
* partial match is also a valid custom value. A partial match is not
|
|
92
|
+
* applied when custom values are allowed with `allowCustomValue`.
|
|
93
|
+
*
|
|
94
|
+
* @attr {none|first-match|only-match} partial-match-mode
|
|
95
|
+
*/
|
|
96
|
+
partialMatchMode: ComboBoxPartialMatchMode;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Requests an update for the content of items.
|
|
100
|
+
* While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
|
|
101
|
+
*
|
|
102
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
103
|
+
*/
|
|
104
|
+
requestContentUpdate(): void;
|
|
60
105
|
}
|
|
@@ -111,6 +111,47 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
111
111
|
value: 'value',
|
|
112
112
|
sync: true,
|
|
113
113
|
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Path for the id of the item. If `items` is an array of objects,
|
|
117
|
+
* the `itemIdPath` is used to compare and identify the same item
|
|
118
|
+
* in the selection and in `filteredItems` (items given by the
|
|
119
|
+
* `dataProvider` callback).
|
|
120
|
+
* @attr {string} item-id-path
|
|
121
|
+
*/
|
|
122
|
+
itemIdPath: {
|
|
123
|
+
type: String,
|
|
124
|
+
sync: true,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Controls which item is automatically set to be selected, for
|
|
129
|
+
* example on Enter, when the typed filter only partially matches
|
|
130
|
+
* its label. The item that will be selected is highlighted in the
|
|
131
|
+
* dropdown while typing:
|
|
132
|
+
*
|
|
133
|
+
* - `none` (default): an item is automatically set to be selected only when the filter matches its label exactly.
|
|
134
|
+
* - `first-match`: the first item in the filtered results is automatically set to be selected.
|
|
135
|
+
* - `only-match`: the item is automatically set to be selected when filtering narrows the results to a single item.
|
|
136
|
+
*
|
|
137
|
+
* In general, an exact match is always set to be selected and takes
|
|
138
|
+
* precedence over partial matches, regardless of the mode.
|
|
139
|
+
*
|
|
140
|
+
* A partial match is only applied while the dropdown is open. For
|
|
141
|
+
* example, when auto-open is disabled with `autoOpenDisabled`, typing
|
|
142
|
+
* does not highlight a match or set it to be selected until the
|
|
143
|
+
* dropdown is opened.
|
|
144
|
+
*
|
|
145
|
+
* This feature cannot be used together with custom values, because a
|
|
146
|
+
* partial match is also a valid custom value. A partial match is not
|
|
147
|
+
* applied when custom values are allowed with `allowCustomValue`.
|
|
148
|
+
*
|
|
149
|
+
* @attr {none|first-match|only-match} partial-match-mode
|
|
150
|
+
*/
|
|
151
|
+
partialMatchMode: {
|
|
152
|
+
type: String,
|
|
153
|
+
value: 'none',
|
|
154
|
+
},
|
|
114
155
|
};
|
|
115
156
|
}
|
|
116
157
|
|
|
@@ -130,6 +171,39 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
130
171
|
}
|
|
131
172
|
}
|
|
132
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Requests an update for the content of items.
|
|
176
|
+
* While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
|
|
177
|
+
*
|
|
178
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
179
|
+
*/
|
|
180
|
+
requestContentUpdate() {
|
|
181
|
+
if (!this._scroller) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this._scroller.requestContentUpdate();
|
|
186
|
+
|
|
187
|
+
this._getItemElements().forEach((item) => {
|
|
188
|
+
item.requestContentUpdate();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Override method from `ComboBoxBaseMixin` to deselect
|
|
194
|
+
* dropdown item by requesting content update on clear.
|
|
195
|
+
* @param {Event} event
|
|
196
|
+
* @protected
|
|
197
|
+
* @override
|
|
198
|
+
*/
|
|
199
|
+
_onClearButtonClick(event) {
|
|
200
|
+
super._onClearButtonClick(event);
|
|
201
|
+
|
|
202
|
+
if (this.opened) {
|
|
203
|
+
this.requestContentUpdate();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
133
207
|
/**
|
|
134
208
|
* Override an event listener from `ComboBoxBaseMixin` to handle
|
|
135
209
|
* batched setting of both `opened` and `filter` properties.
|
|
@@ -161,6 +235,22 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
161
235
|
this.setProperties(props);
|
|
162
236
|
}
|
|
163
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Override method from `ComboBoxBaseMixin` to focus the item matching
|
|
240
|
+
* the filter when the dropdown is opened after typing, which is possible
|
|
241
|
+
* when `autoOpenDisabled` is enabled.
|
|
242
|
+
*
|
|
243
|
+
* @protected
|
|
244
|
+
* @override
|
|
245
|
+
*/
|
|
246
|
+
_onOpened() {
|
|
247
|
+
super._onOpened();
|
|
248
|
+
|
|
249
|
+
if (this.filter && this._focusedIndex === -1) {
|
|
250
|
+
this._focusedIndex = this.__getItemIndexByFilter(this._dropdownItems);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
164
254
|
/**
|
|
165
255
|
* Override method from `ComboBoxBaseMixin` to handle item label path.
|
|
166
256
|
* @protected
|
|
@@ -187,6 +277,26 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
187
277
|
return value;
|
|
188
278
|
}
|
|
189
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Override method from `ComboBoxBaseMixin` to compare object items
|
|
282
|
+
* by the value at `itemIdPath` instead of by identity.
|
|
283
|
+
* @param {unknown} item
|
|
284
|
+
* @param {unknown} other
|
|
285
|
+
* @return {boolean}
|
|
286
|
+
* @protected
|
|
287
|
+
* @override
|
|
288
|
+
*/
|
|
289
|
+
_isSameItem(item, other) {
|
|
290
|
+
if (this.itemIdPath && item && other) {
|
|
291
|
+
const id = get(this.itemIdPath, item);
|
|
292
|
+
if (id !== undefined) {
|
|
293
|
+
return id === get(this.itemIdPath, other);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return super._isSameItem(item, other);
|
|
298
|
+
}
|
|
299
|
+
|
|
190
300
|
/** @private */
|
|
191
301
|
_itemLabelPathChanged(itemLabelPath) {
|
|
192
302
|
if (typeof itemLabelPath !== 'string') {
|
|
@@ -231,12 +341,44 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
231
341
|
}
|
|
232
342
|
|
|
233
343
|
/**
|
|
234
|
-
* Provide items to be rendered in the dropdown.
|
|
235
|
-
*
|
|
344
|
+
* Provide items to be rendered in the dropdown. Override this method
|
|
345
|
+
* to change the items to render, e.g. to show custom items.
|
|
346
|
+
*
|
|
347
|
+
* @param {Array} newItems
|
|
236
348
|
* @protected
|
|
237
349
|
*/
|
|
238
|
-
_setDropdownItems() {
|
|
239
|
-
|
|
350
|
+
_setDropdownItems(newItems) {
|
|
351
|
+
const oldItems = this._dropdownItems;
|
|
352
|
+
this._dropdownItems = newItems;
|
|
353
|
+
|
|
354
|
+
// Store the currently focused item if any. The focused index preserves
|
|
355
|
+
// in the case when more filtered items are loading but it is reset
|
|
356
|
+
// when the user types in a filter query.
|
|
357
|
+
const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
|
|
358
|
+
|
|
359
|
+
// When both the previously-focused entry and the new entry at the
|
|
360
|
+
// same index are placeholders (e.g. the Flow connector mid-scroll
|
|
361
|
+
// re-pushing `_setDropdownItems`), preserve `_focusedIndex` until
|
|
362
|
+
// a follow-up call lands a real item at that position.
|
|
363
|
+
if (
|
|
364
|
+
oldItems &&
|
|
365
|
+
oldItems[this._focusedIndex] instanceof ComboBoxPlaceholder &&
|
|
366
|
+
newItems[this._focusedIndex] instanceof ComboBoxPlaceholder
|
|
367
|
+
) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Try to first set focus on the item that had been focused before `newItems` were updated
|
|
372
|
+
// if it is still present in the `newItems` array. Otherwise, set the focused index
|
|
373
|
+
// depending on the selected item or the filter query.
|
|
374
|
+
const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
|
|
375
|
+
if (focusedItemIndex > -1) {
|
|
376
|
+
this._focusedIndex = focusedItemIndex;
|
|
377
|
+
} else {
|
|
378
|
+
// When the user filled in something that is different from the current value = filtering is enabled,
|
|
379
|
+
// set the focused index to the item that matches the filter query.
|
|
380
|
+
this._focusedIndex = this.__getItemIndexByFilter(newItems);
|
|
381
|
+
}
|
|
240
382
|
}
|
|
241
383
|
|
|
242
384
|
/** @private */
|
|
@@ -282,4 +424,25 @@ export const ComboBoxItemsMixin = (superClass) =>
|
|
|
282
424
|
return this._getItemLabel(item).toString().toLowerCase() === label.toString().toLowerCase();
|
|
283
425
|
});
|
|
284
426
|
}
|
|
427
|
+
|
|
428
|
+
/** @private */
|
|
429
|
+
__getItemIndexByFilter(items) {
|
|
430
|
+
// An item whose label matches the filter exactly takes precedence.
|
|
431
|
+
const exactMatchIndex = this.__getItemIndexByLabel(items, this.filter);
|
|
432
|
+
if (exactMatchIndex > -1) {
|
|
433
|
+
return exactMatchIndex;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!this.opened || !items || items.length === 0 || !this.filter || this.allowCustomValue) {
|
|
437
|
+
return -1;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (this.partialMatchMode === 'first-match' || (this.partialMatchMode === 'only-match' && items.length === 1)) {
|
|
441
|
+
// Skip an item that is not yet loaded. Once the item is loaded,
|
|
442
|
+
// the focused index is updated again.
|
|
443
|
+
return items[0] instanceof ComboBoxPlaceholder ? -1 : 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return -1;
|
|
447
|
+
}
|
|
285
448
|
};
|
|
@@ -78,21 +78,4 @@ export declare class ComboBoxMixinClass<TItem> {
|
|
|
78
78
|
* The selected item from the `items` array.
|
|
79
79
|
*/
|
|
80
80
|
selectedItem: TItem | null | undefined;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Path for the id of the item. If `items` is an array of objects,
|
|
84
|
-
* the `itemIdPath` is used to compare and identify the same item
|
|
85
|
-
* in `selectedItem` and `filteredItems` (items given by the
|
|
86
|
-
* `dataProvider` callback).
|
|
87
|
-
* @attr {string} item-id-path
|
|
88
|
-
*/
|
|
89
|
-
itemIdPath: string | null | undefined;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Requests an update for the content of items.
|
|
93
|
-
* While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
|
|
94
|
-
*
|
|
95
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
96
|
-
*/
|
|
97
|
-
requestContentUpdate(): void;
|
|
98
81
|
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { ValidateMixin } from '@vaadin/field-base/src/validate-mixin.js';
|
|
7
7
|
import { ComboBoxItemsMixin } from './vaadin-combo-box-items-mixin.js';
|
|
8
|
-
import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Checks if the value is supported as an item value in this control.
|
|
@@ -79,33 +78,11 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
79
78
|
itemClassNameGenerator: {
|
|
80
79
|
type: Object,
|
|
81
80
|
},
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Path for the id of the item. If `items` is an array of objects,
|
|
85
|
-
* the `itemIdPath` is used to compare and identify the same item
|
|
86
|
-
* in `selectedItem` and `filteredItems` (items given by the
|
|
87
|
-
* `dataProvider` callback).
|
|
88
|
-
* @attr {string} item-id-path
|
|
89
|
-
*/
|
|
90
|
-
itemIdPath: {
|
|
91
|
-
type: String,
|
|
92
|
-
sync: true,
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
/** @private */
|
|
96
|
-
__keepOverlayOpened: {
|
|
97
|
-
type: Boolean,
|
|
98
|
-
sync: true,
|
|
99
|
-
},
|
|
100
81
|
};
|
|
101
82
|
}
|
|
102
83
|
|
|
103
84
|
static get observers() {
|
|
104
|
-
return [
|
|
105
|
-
'_openedOrItemsChanged(opened, _dropdownItems, loading, __keepOverlayOpened)',
|
|
106
|
-
'_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)',
|
|
107
|
-
'_updateScroller(opened, _dropdownItems, _focusedIndex, _theme)',
|
|
108
|
-
];
|
|
85
|
+
return ['_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)'];
|
|
109
86
|
}
|
|
110
87
|
|
|
111
88
|
/** @protected */
|
|
@@ -121,24 +98,6 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
121
98
|
this._lastCommittedValue = this.value;
|
|
122
99
|
}
|
|
123
100
|
|
|
124
|
-
/**
|
|
125
|
-
* Requests an update for the content of items.
|
|
126
|
-
* While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
|
|
127
|
-
*
|
|
128
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
129
|
-
*/
|
|
130
|
-
requestContentUpdate() {
|
|
131
|
-
if (!this._scroller) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this._scroller.requestContentUpdate();
|
|
136
|
-
|
|
137
|
-
this._getItemElements().forEach((item) => {
|
|
138
|
-
item.requestContentUpdate();
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
101
|
/**
|
|
143
102
|
* @param {Object} props
|
|
144
103
|
* @protected
|
|
@@ -146,49 +105,13 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
146
105
|
updated(props) {
|
|
147
106
|
super.updated(props);
|
|
148
107
|
|
|
149
|
-
['loading', '
|
|
108
|
+
['loading', 'itemClassNameGenerator', 'renderer', 'selectedItem'].forEach((prop) => {
|
|
150
109
|
if (props.has(prop)) {
|
|
151
110
|
this._scroller[prop] = this[prop];
|
|
152
111
|
}
|
|
153
112
|
});
|
|
154
113
|
}
|
|
155
114
|
|
|
156
|
-
/** @private */
|
|
157
|
-
_updateScroller(opened, items, focusedIndex, theme) {
|
|
158
|
-
if (opened) {
|
|
159
|
-
this._scroller.style.maxHeight =
|
|
160
|
-
getComputedStyle(this).getPropertyValue(`--${this._tagNamePrefix}-overlay-max-height`) || '65vh';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
this._scroller.setProperties({
|
|
164
|
-
items: opened ? items : [],
|
|
165
|
-
opened,
|
|
166
|
-
focusedIndex,
|
|
167
|
-
theme,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/** @private */
|
|
172
|
-
_openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
|
|
173
|
-
// Close the overlay if there are no items to display.
|
|
174
|
-
// See https://github.com/vaadin/vaadin-combo-box/pull/964
|
|
175
|
-
this._overlayOpened = opened && (keepOverlayOpened || loading || !!items?.length);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Override method from `ComboBoxBaseMixin` to deselect
|
|
180
|
-
* dropdown item by requesting content update on clear.
|
|
181
|
-
* @param {Event} event
|
|
182
|
-
* @protected
|
|
183
|
-
*/
|
|
184
|
-
_onClearButtonClick(event) {
|
|
185
|
-
super._onClearButtonClick(event);
|
|
186
|
-
|
|
187
|
-
if (this.opened) {
|
|
188
|
-
this.requestContentUpdate();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
115
|
/**
|
|
193
116
|
* Override method inherited from `InputMixin`
|
|
194
117
|
* to revert the input value to value.
|
|
@@ -279,6 +202,8 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
279
202
|
* @override
|
|
280
203
|
*/
|
|
281
204
|
_onOpened() {
|
|
205
|
+
super._onOpened();
|
|
206
|
+
|
|
282
207
|
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-opened', { bubbles: true, composed: true }));
|
|
283
208
|
|
|
284
209
|
// _detectAndDispatchChange() should not consider value changes done before opening
|
|
@@ -495,20 +420,14 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
495
420
|
}
|
|
496
421
|
|
|
497
422
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
423
|
+
* Override method from `ComboBoxItemsMixin` to sync `selectedItem`
|
|
424
|
+
* based on `value` once a new set of items is available.
|
|
500
425
|
*
|
|
501
426
|
* @protected
|
|
502
427
|
* @override
|
|
503
428
|
*/
|
|
504
429
|
_setDropdownItems(newItems) {
|
|
505
|
-
|
|
506
|
-
this._dropdownItems = newItems;
|
|
507
|
-
|
|
508
|
-
// Store the currently focused item if any. The focused index preserves
|
|
509
|
-
// in the case when more filtered items are loading but it is reset
|
|
510
|
-
// when the user types in a filter query.
|
|
511
|
-
const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
|
|
430
|
+
super._setDropdownItems(newItems);
|
|
512
431
|
|
|
513
432
|
// Try to sync `selectedItem` based on `value` once a new set of `filteredItems` is available
|
|
514
433
|
// (as a result of external filtering or when they have been loaded by the data provider).
|
|
@@ -518,30 +437,6 @@ export const ComboBoxMixin = (superClass) =>
|
|
|
518
437
|
if ((this.selectedItem === null || this.selectedItem === undefined) && valueIndex >= 0) {
|
|
519
438
|
this.selectedItem = newItems[valueIndex];
|
|
520
439
|
}
|
|
521
|
-
|
|
522
|
-
// When both the previously-focused entry and the new entry at the
|
|
523
|
-
// same index are placeholders (e.g. the Flow connector mid-scroll
|
|
524
|
-
// re-pushing `_setDropdownItems`), preserve `_focusedIndex` until
|
|
525
|
-
// a follow-up call lands a real item at that position.
|
|
526
|
-
if (
|
|
527
|
-
oldItems &&
|
|
528
|
-
oldItems[this._focusedIndex] instanceof ComboBoxPlaceholder &&
|
|
529
|
-
newItems[this._focusedIndex] instanceof ComboBoxPlaceholder
|
|
530
|
-
) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Try to first set focus on the item that had been focused before `newItems` were updated
|
|
535
|
-
// if it is still present in the `newItems` array. Otherwise, set the focused index
|
|
536
|
-
// depending on the selected item or the filter query.
|
|
537
|
-
const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
|
|
538
|
-
if (focusedItemIndex > -1) {
|
|
539
|
-
this._focusedIndex = focusedItemIndex;
|
|
540
|
-
} else {
|
|
541
|
-
// When the user filled in something that is different from the current value = filtering is enabled,
|
|
542
|
-
// set the focused index to the item that matches the filter query.
|
|
543
|
-
this._focusedIndex = this.__getItemIndexByLabel(newItems, this.filter);
|
|
544
|
-
}
|
|
545
440
|
}
|
|
546
441
|
|
|
547
442
|
/**
|
|
@@ -8,16 +8,22 @@ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin
|
|
|
8
8
|
|
|
9
9
|
export const ComboBoxOverlayMixin = (superClass) =>
|
|
10
10
|
class ComboBoxOverlayMixin extends PositionMixin(superClass) {
|
|
11
|
-
static get observers() {
|
|
12
|
-
return ['_setOverlayWidth(positionTarget, opened)'];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
constructor() {
|
|
16
12
|
super();
|
|
17
13
|
|
|
18
14
|
this.requiredVerticalSpace = 200;
|
|
19
15
|
}
|
|
20
16
|
|
|
17
|
+
/** @protected */
|
|
18
|
+
willUpdate(props) {
|
|
19
|
+
super.willUpdate(props);
|
|
20
|
+
|
|
21
|
+
// Update width here so that `PositionMixin` uses correct width in `updated()`.
|
|
22
|
+
if ((props.has('opened') || props.has('positionTarget')) && this.opened && this.positionTarget) {
|
|
23
|
+
this._updateOverlayWidth();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
/**
|
|
22
28
|
* Override method inherited from `Overlay`
|
|
23
29
|
* to not close on position target click.
|
|
@@ -49,13 +55,4 @@ export const ComboBoxOverlayMixin = (superClass) =>
|
|
|
49
55
|
_updateOverlayWidth() {
|
|
50
56
|
this.style.setProperty(`--_${this.localName}-default-width`, `${this.positionTarget.offsetWidth}px`);
|
|
51
57
|
}
|
|
52
|
-
|
|
53
|
-
/** @private */
|
|
54
|
-
_setOverlayWidth(positionTarget, opened) {
|
|
55
|
-
if (positionTarget && opened) {
|
|
56
|
-
this._updateOverlayWidth();
|
|
57
|
-
|
|
58
|
-
this._updatePosition();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
58
|
};
|
|
@@ -17,11 +17,12 @@ import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';
|
|
|
17
17
|
/**
|
|
18
18
|
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
|
|
19
19
|
*
|
|
20
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
20
21
|
* @customElement vaadin-combo-box-overlay
|
|
21
22
|
* @extends HTMLElement
|
|
22
23
|
* @private
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
class ComboBoxOverlay extends ComboBoxOverlayMixin(
|
|
25
26
|
OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
|
|
26
27
|
) {
|
|
27
28
|
static get is() {
|
|
@@ -44,3 +45,5 @@ export class ComboBoxOverlay extends ComboBoxOverlayMixin(
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
defineCustomElement(ComboBoxOverlay);
|
|
48
|
+
|
|
49
|
+
export { ComboBoxOverlay };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2015 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Placeholder object class representing items being loaded.
|
|
9
|
+
*
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
declare class ComboBoxPlaceholder {
|
|
13
|
+
// Nominal brand: keeps structurally similar values from being assignable
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-private-class-members
|
|
15
|
+
private __comboBoxPlaceholder: true;
|
|
16
|
+
|
|
17
|
+
toString(): string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { ComboBoxPlaceholder };
|
|
@@ -17,11 +17,6 @@ export declare class ComboBoxScrollerMixinClass<TItem, TOwner> {
|
|
|
17
17
|
*/
|
|
18
18
|
focusedIndex: number;
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Path for the id of the item, used to detect whether the item is selected.
|
|
22
|
-
*/
|
|
23
|
-
itemIdPath: string | null | undefined;
|
|
24
|
-
|
|
25
20
|
/**
|
|
26
21
|
* A full set of items to filter the visible options from.
|
|
27
22
|
* Set to an empty array when combo-box is not opened.
|
|
@@ -78,7 +73,7 @@ export declare class ComboBoxScrollerMixinClass<TItem, TOwner> {
|
|
|
78
73
|
*/
|
|
79
74
|
scrollIntoView(index: number, alignToCenter?: boolean): void;
|
|
80
75
|
|
|
81
|
-
protected _isItemSelected(item: TItem, selectedItem: TItem
|
|
76
|
+
protected _isItemSelected(item: TItem, selectedItem: TItem): boolean;
|
|
82
77
|
|
|
83
78
|
protected _updateElement(el: HTMLElement, index: number): void;
|
|
84
79
|
}
|