@vaadin/combo-box 25.3.0-dev.1fa5a51482 → 25.3.0-rc1

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.
@@ -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
- * Override to provide actual implementation.
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
- // To be implemented
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,51 +105,13 @@ export const ComboBoxMixin = (superClass) =>
146
105
  updated(props) {
147
106
  super.updated(props);
148
107
 
149
- ['loading', 'itemIdPath', 'itemClassNameGenerator', 'renderer', 'selectedItem'].forEach((prop) => {
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
- const isClosing = this.hasAttribute('closing');
164
-
165
- this._scroller.setProperties({
166
- items: opened || isClosing ? items : [],
167
- opened,
168
- focusedIndex,
169
- theme,
170
- });
171
- }
172
-
173
- /** @private */
174
- _openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
175
- // Close the overlay if there are no items to display.
176
- // See https://github.com/vaadin/vaadin-combo-box/pull/964
177
- this._overlayOpened = opened && (keepOverlayOpened || loading || !!items?.length);
178
- }
179
-
180
- /**
181
- * Override method from `ComboBoxBaseMixin` to deselect
182
- * dropdown item by requesting content update on clear.
183
- * @param {Event} event
184
- * @protected
185
- */
186
- _onClearButtonClick(event) {
187
- super._onClearButtonClick(event);
188
-
189
- if (this.opened) {
190
- this.requestContentUpdate();
191
- }
192
- }
193
-
194
115
  /**
195
116
  * Override method inherited from `InputMixin`
196
117
  * to revert the input value to value.
@@ -281,6 +202,8 @@ export const ComboBoxMixin = (superClass) =>
281
202
  * @override
282
203
  */
283
204
  _onOpened() {
205
+ super._onOpened();
206
+
284
207
  this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-opened', { bubbles: true, composed: true }));
285
208
 
286
209
  // _detectAndDispatchChange() should not consider value changes done before opening
@@ -497,20 +420,14 @@ export const ComboBoxMixin = (superClass) =>
497
420
  }
498
421
 
499
422
  /**
500
- * Provide items to be rendered in the dropdown.
501
- * Override this method to show custom items.
423
+ * Override method from `ComboBoxItemsMixin` to sync `selectedItem`
424
+ * based on `value` once a new set of items is available.
502
425
  *
503
426
  * @protected
504
427
  * @override
505
428
  */
506
429
  _setDropdownItems(newItems) {
507
- const oldItems = this._dropdownItems;
508
- this._dropdownItems = newItems;
509
-
510
- // Store the currently focused item if any. The focused index preserves
511
- // in the case when more filtered items are loading but it is reset
512
- // when the user types in a filter query.
513
- const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
430
+ super._setDropdownItems(newItems);
514
431
 
515
432
  // Try to sync `selectedItem` based on `value` once a new set of `filteredItems` is available
516
433
  // (as a result of external filtering or when they have been loaded by the data provider).
@@ -520,30 +437,6 @@ export const ComboBoxMixin = (superClass) =>
520
437
  if ((this.selectedItem === null || this.selectedItem === undefined) && valueIndex >= 0) {
521
438
  this.selectedItem = newItems[valueIndex];
522
439
  }
523
-
524
- // When both the previously-focused entry and the new entry at the
525
- // same index are placeholders (e.g. the Flow connector mid-scroll
526
- // re-pushing `_setDropdownItems`), preserve `_focusedIndex` until
527
- // a follow-up call lands a real item at that position.
528
- if (
529
- oldItems &&
530
- oldItems[this._focusedIndex] instanceof ComboBoxPlaceholder &&
531
- newItems[this._focusedIndex] instanceof ComboBoxPlaceholder
532
- ) {
533
- return;
534
- }
535
-
536
- // Try to first set focus on the item that had been focused before `newItems` were updated
537
- // if it is still present in the `newItems` array. Otherwise, set the focused index
538
- // depending on the selected item or the filter query.
539
- const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
540
- if (focusedItemIndex > -1) {
541
- this._focusedIndex = focusedItemIndex;
542
- } else {
543
- // When the user filled in something that is different from the current value = filtering is enabled,
544
- // set the focused index to the item that matches the filter query.
545
- this._focusedIndex = this.__getItemIndexByLabel(newItems, this.filter);
546
- }
547
440
  }
548
441
 
549
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
  };
@@ -22,7 +22,7 @@ import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';
22
22
  * @extends HTMLElement
23
23
  * @private
24
24
  */
25
- export class ComboBoxOverlay extends ComboBoxOverlayMixin(
25
+ class ComboBoxOverlay extends ComboBoxOverlayMixin(
26
26
  OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
27
27
  ) {
28
28
  static get is() {
@@ -45,3 +45,5 @@ export class ComboBoxOverlay extends ComboBoxOverlayMixin(
45
45
  }
46
46
 
47
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, itemIdPath: string | null | undefined): void;
76
+ protected _isItemSelected(item: TItem, selectedItem: TItem): boolean;
82
77
 
83
78
  protected _updateElement(el: HTMLElement, index: number): void;
84
79
  }
@@ -3,7 +3,7 @@
3
3
  * Copyright (c) 2015 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { get } from '@vaadin/component-base/src/path-utils.js';
6
+ import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
7
7
  import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
8
8
  import { Virtualizer } from '@vaadin/component-base/src/virtualizer.js';
9
9
  import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
@@ -71,13 +71,6 @@ export const ComboBoxScrollerMixin = (superClass) =>
71
71
  observer: '__itemClassNameGeneratorChanged',
72
72
  },
73
73
 
74
- /**
75
- * Path for the id of the item, used to detect whether the item is selected.
76
- */
77
- itemIdPath: {
78
- type: String,
79
- },
80
-
81
74
  /**
82
75
  * Reference to the owner (combo-box owner), used by the item elements.
83
76
  */
@@ -243,16 +236,15 @@ export const ComboBoxScrollerMixin = (superClass) =>
243
236
  /**
244
237
  * @param {string | object} item
245
238
  * @param {string | object} selectedItem
246
- * @param {string} itemIdPath
239
+ * @return {boolean}
247
240
  * @protected
248
241
  */
249
- _isItemSelected(item, selectedItem, itemIdPath) {
242
+ _isItemSelected(item, selectedItem) {
250
243
  if (item instanceof ComboBoxPlaceholder) {
251
244
  return false;
252
- } else if (itemIdPath && item !== undefined && selectedItem !== undefined) {
253
- return get(itemIdPath, item) === get(itemIdPath, selectedItem);
254
245
  }
255
- return item === selectedItem;
246
+
247
+ return this.owner._isSameItem(item, selectedItem);
256
248
  }
257
249
 
258
250
  /** @private */
@@ -366,7 +358,7 @@ export const ComboBoxScrollerMixin = (superClass) =>
366
358
  _updateElement(el, index) {
367
359
  const item = this.items[index];
368
360
  const focusedIndex = this.focusedIndex;
369
- const selected = this._isItemSelected(item, this.selectedItem, this.itemIdPath);
361
+ const selected = this._isItemSelected(item, this.selectedItem);
370
362
 
371
363
  el.setProperties({
372
364
  item,
@@ -389,11 +381,7 @@ export const ComboBoxScrollerMixin = (superClass) =>
389
381
  el.setAttribute('aria-posinset', index + 1);
390
382
  el.setAttribute('aria-setsize', this.items.length);
391
383
 
392
- if (this.theme) {
393
- el.setAttribute('theme', this.theme);
394
- } else {
395
- el.removeAttribute('theme');
396
- }
384
+ setOrRemoveAttribute(el, 'theme', this.theme);
397
385
 
398
386
  if (item instanceof ComboBoxPlaceholder) {
399
387
  this.__requestItemByIndex(index);
@@ -16,7 +16,7 @@ import { ComboBoxScrollerMixin } from './vaadin-combo-box-scroller-mixin.js';
16
16
  * @extends HTMLElement
17
17
  * @private
18
18
  */
19
- export class ComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElement)) {
19
+ class ComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElement)) {
20
20
  static get is() {
21
21
  return 'vaadin-combo-box-scroller';
22
22
  }
@@ -36,3 +36,5 @@ export class ComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElem
36
36
  }
37
37
 
38
38
  defineCustomElement(ComboBoxScroller);
39
+
40
+ export { ComboBoxScroller };