@vaadin/combo-box 25.2.0-beta1 → 25.2.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.
@@ -279,6 +279,33 @@
279
279
  }
280
280
  ]
281
281
  },
282
+ {
283
+ "kind": "javascript-module",
284
+ "path": "src/vaadin-combo-box-focus-index-mixin.js",
285
+ "declarations": [
286
+ {
287
+ "kind": "mixin",
288
+ "description": "",
289
+ "name": "ComboBoxFocusIndexMixin",
290
+ "members": [],
291
+ "parameters": [
292
+ {
293
+ "name": "superClass"
294
+ }
295
+ ]
296
+ }
297
+ ],
298
+ "exports": [
299
+ {
300
+ "kind": "js",
301
+ "name": "ComboBoxFocusIndexMixin",
302
+ "declaration": {
303
+ "name": "ComboBoxFocusIndexMixin",
304
+ "module": "src/vaadin-combo-box-focus-index-mixin.js"
305
+ }
306
+ }
307
+ ]
308
+ },
282
309
  {
283
310
  "kind": "javascript-module",
284
311
  "path": "src/vaadin-combo-box-item-mixin.js",
@@ -1459,48 +1486,6 @@
1459
1486
  }
1460
1487
  ]
1461
1488
  },
1462
- {
1463
- "kind": "javascript-module",
1464
- "path": "src/vaadin-combo-box-scroll-to-index-mixin.js",
1465
- "declarations": [
1466
- {
1467
- "kind": "mixin",
1468
- "description": "",
1469
- "name": "ComboBoxScrollToIndexMixin",
1470
- "members": [
1471
- {
1472
- "kind": "method",
1473
- "name": "scrollToIndex",
1474
- "parameters": [
1475
- {
1476
- "name": "index",
1477
- "description": "Index of the item to scroll to",
1478
- "type": {
1479
- "text": "number"
1480
- }
1481
- }
1482
- ],
1483
- "description": "Scrolls the dropdown to the item at the given index and sets it as the\nfocused (highlighted) item. Safe to call before the dropdown is opened\nor while the data provider is loading: the call is queued and executed\nonce the overlay is open and not loading.\n\nBecause this sets the focused item, closing the dropdown without an\nexplicit selection change (e.g. via outside click or blur) will commit\nthe focused item as `selectedItem`. In the typical use case (scroll to\nthe currently selected item) this is a no-op; callers scrolling to a\ndifferent index should be aware of this behavior."
1484
- }
1485
- ],
1486
- "parameters": [
1487
- {
1488
- "name": "superClass"
1489
- }
1490
- ]
1491
- }
1492
- ],
1493
- "exports": [
1494
- {
1495
- "kind": "js",
1496
- "name": "ComboBoxScrollToIndexMixin",
1497
- "declaration": {
1498
- "name": "ComboBoxScrollToIndexMixin",
1499
- "module": "src/vaadin-combo-box-scroll-to-index-mixin.js"
1500
- }
1501
- }
1502
- ]
1503
- },
1504
1489
  {
1505
1490
  "kind": "javascript-module",
1506
1491
  "path": "src/vaadin-combo-box-scroller-mixin.js",
@@ -1614,9 +1599,17 @@
1614
1599
  "type": {
1615
1600
  "text": "number"
1616
1601
  }
1602
+ },
1603
+ {
1604
+ "name": "alignToCenter",
1605
+ "default": "false",
1606
+ "optional": true,
1607
+ "type": {
1608
+ "text": "boolean"
1609
+ }
1617
1610
  }
1618
1611
  ],
1619
- "description": "Scrolls an item at given index into view and adjusts `scrollTop`\nso that the element gets fully visible on Arrow Down key press."
1612
+ "description": "Scrolls an item at given index into view. By default the item is made\nfully visible at the bottom of the viewport (the behavior wanted on\nArrow Down key press). When `alignToCenter` is `true`, the item is placed\nin the middle of the viewport instead, as close to center as the list\nallows near its start and end."
1620
1613
  },
1621
1614
  {
1622
1615
  "kind": "field",
@@ -2246,24 +2239,6 @@
2246
2239
  "package": "@vaadin/field-base/src/validate-mixin.js"
2247
2240
  }
2248
2241
  },
2249
- {
2250
- "kind": "method",
2251
- "name": "scrollToIndex",
2252
- "parameters": [
2253
- {
2254
- "name": "index",
2255
- "description": "Index of the item to scroll to",
2256
- "type": {
2257
- "text": "number"
2258
- }
2259
- }
2260
- ],
2261
- "description": "Scrolls the dropdown to the item at the given index and sets it as the\nfocused (highlighted) item. Safe to call before the dropdown is opened\nor while the data provider is loading: the call is queued and executed\nonce the overlay is open and not loading.\n\nBecause this sets the focused item, closing the dropdown without an\nexplicit selection change (e.g. via outside click or blur) will commit\nthe focused item as `selectedItem`. In the typical use case (scroll to\nthe currently selected item) this is a no-op; callers scrolling to a\ndifferent index should be aware of this behavior.",
2262
- "inheritedFrom": {
2263
- "name": "ComboBoxScrollToIndexMixin",
2264
- "module": "src/vaadin-combo-box-scroll-to-index-mixin.js"
2265
- }
2266
- },
2267
2242
  {
2268
2243
  "kind": "field",
2269
2244
  "name": "selectedItem",
@@ -2811,8 +2786,8 @@
2811
2786
  ],
2812
2787
  "mixins": [
2813
2788
  {
2814
- "name": "ComboBoxScrollToIndexMixin",
2815
- "module": "src/vaadin-combo-box-scroll-to-index-mixin.js"
2789
+ "name": "ComboBoxFocusIndexMixin",
2790
+ "module": "src/vaadin-combo-box-focus-index-mixin.js"
2816
2791
  },
2817
2792
  {
2818
2793
  "name": "ComboBoxDataProviderMixin",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/combo-box",
3
- "version": "25.2.0-beta1",
3
+ "version": "25.2.0-rc1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -36,28 +36,28 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@open-wc/dedupe-mixin": "^1.3.0",
39
- "@vaadin/a11y-base": "25.2.0-beta1",
40
- "@vaadin/component-base": "25.2.0-beta1",
41
- "@vaadin/field-base": "25.2.0-beta1",
42
- "@vaadin/input-container": "25.2.0-beta1",
43
- "@vaadin/item": "25.2.0-beta1",
44
- "@vaadin/lit-renderer": "25.2.0-beta1",
45
- "@vaadin/overlay": "25.2.0-beta1",
46
- "@vaadin/vaadin-themable-mixin": "25.2.0-beta1",
39
+ "@vaadin/a11y-base": "25.2.0-rc1",
40
+ "@vaadin/component-base": "25.2.0-rc1",
41
+ "@vaadin/field-base": "25.2.0-rc1",
42
+ "@vaadin/input-container": "25.2.0-rc1",
43
+ "@vaadin/item": "25.2.0-rc1",
44
+ "@vaadin/lit-renderer": "25.2.0-rc1",
45
+ "@vaadin/overlay": "25.2.0-rc1",
46
+ "@vaadin/vaadin-themable-mixin": "25.2.0-rc1",
47
47
  "lit": "^3.0.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@vaadin/aura": "25.2.0-beta1",
51
- "@vaadin/chai-plugins": "25.2.0-beta1",
52
- "@vaadin/test-runner-commands": "25.2.0-beta1",
50
+ "@vaadin/aura": "25.2.0-rc1",
51
+ "@vaadin/chai-plugins": "25.2.0-rc1",
52
+ "@vaadin/test-runner-commands": "25.2.0-rc1",
53
53
  "@vaadin/testing-helpers": "^2.0.0",
54
- "@vaadin/vaadin-lumo-styles": "25.2.0-beta1",
55
- "sinon": "^21.0.2"
54
+ "@vaadin/vaadin-lumo-styles": "25.2.0-rc1",
55
+ "sinon": "^22.0.0"
56
56
  },
57
57
  "customElements": "custom-elements.json",
58
58
  "web-types": [
59
59
  "web-types.json",
60
60
  "web-types.lit.json"
61
61
  ],
62
- "gitHead": "471a23f60d1eb725f98a33f62cb9664d9c0a4163"
62
+ "gitHead": "7c581b396dbb889f75c8073d6b4d25537a65b873"
63
63
  }
@@ -696,11 +696,11 @@ export const ComboBoxBaseMixin = (superClass) =>
696
696
  }
697
697
 
698
698
  /** @protected */
699
- _scrollIntoView(index) {
699
+ _scrollIntoView(index, alignToCenter = false) {
700
700
  if (!this._scroller) {
701
701
  return;
702
702
  }
703
- this._scroller.scrollIntoView(index);
703
+ this._scroller.scrollIntoView(index, alignToCenter);
704
704
  }
705
705
 
706
706
  /** @private */
@@ -0,0 +1,99 @@
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
+ export const ComboBoxFocusIndexMixin = (superClass) =>
7
+ class FocusIndexMixin extends superClass {
8
+ static get observers() {
9
+ return ['__clearPendingFocusOnFilter(filter)'];
10
+ }
11
+
12
+ /**
13
+ * Scrolls the dropdown to the item at the given index and sets it as the
14
+ * focused (highlighted) item. Closing the dropdown without an explicit
15
+ * selection change (e.g. via outside click or blur) will commit the
16
+ * focused item as `selectedItem` — callers focusing an index other than
17
+ * the current selection should be aware of this side effect.
18
+ *
19
+ * @private
20
+ * @param {number} index
21
+ */
22
+ __focusIndex(index) {
23
+ if (typeof index !== 'number' || Number.isNaN(index) || index < 0) {
24
+ return;
25
+ }
26
+
27
+ // Defer until the dropdown is open and the items array has been
28
+ // populated. `_onOpened` and `__onDataProviderPageLoaded` re-fire
29
+ // the queued call once those conditions hold.
30
+ if (!this._overlayOpened || !this._dropdownItems || this._dropdownItems.length === 0) {
31
+ this.__pendingFocusIndex = index;
32
+ return;
33
+ }
34
+
35
+ if (index >= this._dropdownItems.length) {
36
+ return;
37
+ }
38
+
39
+ // Set the focused row first: this synchronously runs the scroller's
40
+ // `__focusedIndexChanged` observer, which scrolls the row into view
41
+ // (bottom-aligned). Then scroll again to center it so the final position
42
+ // wins. Rendering rows around `index` also lets placeholder rows fire
43
+ // `index-requested`, loading any missing pages via the data-provider
44
+ // chain.
45
+ this._focusedIndex = index;
46
+ this._scrollIntoView(index, true);
47
+
48
+ // A page-load may have kicked in during the scroll (placeholder
49
+ // rows in the new viewport requested their pages). Re-fire after
50
+ // the page lands so the viewport can settle around real items.
51
+ if (this.loading) {
52
+ this.__pendingFocusIndex = index;
53
+ return;
54
+ }
55
+
56
+ delete this.__pendingFocusIndex;
57
+ requestAnimationFrame(() => {
58
+ if (this.isConnected) {
59
+ this._updateActiveDescendant(index);
60
+ }
61
+ });
62
+ }
63
+
64
+ /** @private */
65
+ __focusPendingIndexIfNeeded() {
66
+ if (this.__pendingFocusIndex !== undefined && !this.loading) {
67
+ this.__focusIndex(this.__pendingFocusIndex);
68
+ }
69
+ }
70
+
71
+ /** @private */
72
+ __clearPendingFocusOnFilter() {
73
+ delete this.__pendingFocusIndex;
74
+ }
75
+
76
+ /**
77
+ * Override method from `ComboBoxBaseMixin` to flush any pending
78
+ * `__focusIndex` call after the overlay opens.
79
+ *
80
+ * @protected
81
+ * @override
82
+ */
83
+ _onOpened() {
84
+ super._onOpened();
85
+ this.__focusPendingIndexIfNeeded();
86
+ }
87
+
88
+ /**
89
+ * Override method from `ComboBoxDataProviderMixin` to flush any pending
90
+ * `__focusIndex` call after a data-provider page lands.
91
+ *
92
+ * @private
93
+ * @override
94
+ */
95
+ __onDataProviderPageLoaded() {
96
+ super.__onDataProviderPageLoaded();
97
+ this.__focusPendingIndexIfNeeded();
98
+ }
99
+ };
@@ -5,6 +5,7 @@
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';
8
9
 
9
10
  /**
10
11
  * Checks if the value is supported as an item value in this control.
@@ -518,6 +519,18 @@ export const ComboBoxMixin = (superClass) =>
518
519
  this.selectedItem = newItems[valueIndex];
519
520
  }
520
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
+
521
534
  // Try to first set focus on the item that had been focused before `newItems` were updated
522
535
  // if it is still present in the `newItems` array. Otherwise, set the focused index
523
536
  // depending on the selected item or the filter query.
@@ -70,10 +70,13 @@ export declare class ComboBoxScrollerMixinClass<TItem, TOwner> {
70
70
  requestContentUpdate(): void;
71
71
 
72
72
  /**
73
- * Scrolls an item at given index into view and adjusts `scrollTop`
74
- * so that the element gets fully visible on Arrow Down key press.
73
+ * Scrolls an item at given index into view. By default the item is made
74
+ * fully visible at the bottom of the viewport (the behavior wanted on
75
+ * Arrow Down key press). When `alignToCenter` is `true`, the item is placed
76
+ * in the middle of the viewport instead, as close to center as the list
77
+ * allows near its start and end.
75
78
  */
76
- scrollIntoView(index: number): void;
79
+ scrollIntoView(index: number, alignToCenter?: boolean): void;
77
80
 
78
81
  protected _isItemSelected(item: TItem, selectedItem: TItem, itemIdPath: string | null | undefined): void;
79
82
 
@@ -166,44 +166,77 @@ export const ComboBoxScrollerMixin = (superClass) =>
166
166
  }
167
167
 
168
168
  /**
169
- * Scrolls an item at given index into view and adjusts `scrollTop`
170
- * so that the element gets fully visible on Arrow Down key press.
169
+ * Scrolls an item at given index into view. By default the item is made
170
+ * fully visible at the bottom of the viewport (the behavior wanted on
171
+ * Arrow Down key press). When `alignToCenter` is `true`, the item is placed
172
+ * in the middle of the viewport instead, as close to center as the list
173
+ * allows near its start and end.
171
174
  * @param {number} index
175
+ * @param {boolean} [alignToCenter=false]
172
176
  */
173
- scrollIntoView(index) {
177
+ scrollIntoView(index, alignToCenter = false) {
174
178
  if (!this.__virtualizer || !(this.opened && index >= 0)) {
175
179
  return;
176
180
  }
177
181
 
178
- const visibleItemsCount = this._visibleItemsCount();
182
+ // If the target row is already fully visible, leave the scroll untouched.
183
+ // Re-running scrollToIndex would snap an unaligned scrollTop to a row
184
+ // boundary — a visible jump when only the focus ring should move (arrow
185
+ // navigation, clicks). Centering always repositions, so it skips this.
186
+ const renderedTarget = [...this.children].find((el) => !el.hidden && el.index === index);
187
+ if (!alignToCenter && renderedTarget) {
188
+ const targetRect = renderedTarget.getBoundingClientRect();
189
+ const scrollerRect = this.getBoundingClientRect();
190
+ if (
191
+ targetRect.top >= scrollerRect.top &&
192
+ targetRect.bottom + this._viewportTotalPaddingBottom <= scrollerRect.bottom
193
+ ) {
194
+ return;
195
+ }
196
+ }
179
197
 
198
+ // When centering, scrolling straight to the target index renders it (the
199
+ // native `scrollIntoView({ block: 'center' })` below then centers it), so
200
+ // no index recalculation is needed.
180
201
  let targetIndex = index;
181
202
 
182
- if (index > this.__virtualizer.lastVisibleIndex - 1) {
183
- // Index is below the bottom, scrolling down. Make the item appear at the bottom.
184
- // First scroll to target (will be at the top of the scroller) to make sure it's rendered.
185
- this.__virtualizer.scrollToIndex(index);
186
- // Then calculate the index for the following scroll (to get the target to bottom of the scroller).
187
- targetIndex = index - visibleItemsCount + 1;
188
- } else if (index > this.__virtualizer.firstVisibleIndex) {
189
- // The item is already visible, scrolling is unnecessary per se. But we need to trigger iron-list to set
190
- // the correct scrollTop on the scrollTarget. Scrolling to firstVisibleIndex.
191
- targetIndex = this.__virtualizer.firstVisibleIndex;
203
+ if (!alignToCenter) {
204
+ const visibleItemsCount = this._visibleItemsCount();
205
+ if (index > this.__virtualizer.lastVisibleIndex - 1) {
206
+ // Index is below the bottom, scrolling down. Make the item appear at the bottom.
207
+ // First scroll to target (will be at the top of the scroller) to make sure it's rendered.
208
+ this.__virtualizer.scrollToIndex(index);
209
+ // Then calculate the index for the following scroll (to get the target to bottom of the scroller).
210
+ targetIndex = index - visibleItemsCount + 1;
211
+ } else if (index > this.__virtualizer.firstVisibleIndex) {
212
+ // The item is already visible, scrolling is unnecessary per se. But we need to trigger iron-list to set
213
+ // the correct scrollTop on the scrollTarget. Scrolling to firstVisibleIndex.
214
+ targetIndex = this.__virtualizer.firstVisibleIndex;
215
+ }
192
216
  }
193
217
  this.__virtualizer.scrollToIndex(Math.max(0, targetIndex));
194
218
 
195
- // Sometimes the item is partly below the bottom edge, detect and adjust.
196
- const lastPhysicalItem = [...this.children].find(
197
- (el) => !el.hidden && el.index === this.__virtualizer.lastVisibleIndex,
198
- );
199
- if (!lastPhysicalItem || index !== lastPhysicalItem.index) {
219
+ // Flush so the rect-based correction below runs on settled positions.
220
+ this.__virtualizer.flush();
221
+
222
+ const target = [...this.children].find((el) => !el.hidden && el.index === index);
223
+ if (!target) {
224
+ return;
225
+ }
226
+ if (alignToCenter) {
227
+ // Center the now-rendered row in the viewport. The browser clamps near
228
+ // the list start and end, placing the row as close to center as the
229
+ // list allows.
230
+ target.scrollIntoView({ block: 'center' });
200
231
  return;
201
232
  }
202
- const lastPhysicalItemRect = lastPhysicalItem.getBoundingClientRect();
233
+ const targetRect = target.getBoundingClientRect();
203
234
  const scrollerRect = this.getBoundingClientRect();
204
- const scrollTopAdjust = lastPhysicalItemRect.bottom - scrollerRect.bottom + this._viewportTotalPaddingBottom;
205
- if (scrollTopAdjust > 0) {
206
- this.scrollTop += scrollTopAdjust;
235
+ const targetBottom = targetRect.bottom + this._viewportTotalPaddingBottom;
236
+ if (targetBottom > scrollerRect.bottom) {
237
+ this.scrollTop += targetBottom - scrollerRect.bottom;
238
+ } else if (targetRect.top < scrollerRect.top) {
239
+ this.scrollTop -= scrollerRect.top - targetRect.top;
207
240
  }
208
241
  }
209
242
 
@@ -24,7 +24,6 @@ import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-pro
24
24
  import type { ComboBoxItemsMixinClass } from './vaadin-combo-box-items-mixin.js';
25
25
  import type { ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
26
26
  import type { ComboBoxDefaultItem } from './vaadin-combo-box-mixin.js';
27
- import type { ComboBoxScrollToIndexMixinClass } from './vaadin-combo-box-scroll-to-index-mixin.js';
28
27
 
29
28
  export {
30
29
  ComboBoxDataProvider,
@@ -248,7 +247,6 @@ interface ComboBox<TItem = ComboBoxDefaultItem>
248
247
  ComboBoxDataProviderMixinClass<TItem>,
249
248
  ComboBoxItemsMixinClass<TItem>,
250
249
  ComboBoxMixinClass<TItem>,
251
- ComboBoxScrollToIndexMixinClass,
252
250
  ComboBoxBaseMixinClass,
253
251
  ValidateMixinClass,
254
252
  PatternMixinClass,
@@ -22,8 +22,8 @@ import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection
22
22
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
23
23
  import { comboBoxStyles } from './styles/vaadin-combo-box-base-styles.js';
24
24
  import { ComboBoxDataProviderMixin } from './vaadin-combo-box-data-provider-mixin.js';
25
+ import { ComboBoxFocusIndexMixin } from './vaadin-combo-box-focus-index-mixin.js';
25
26
  import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
26
- import { ComboBoxScrollToIndexMixin } from './vaadin-combo-box-scroll-to-index-mixin.js';
27
27
 
28
28
  /**
29
29
  * `<vaadin-combo-box>` is a web component for choosing a value from a filterable list of options
@@ -164,7 +164,7 @@ import { ComboBoxScrollToIndexMixin } from './vaadin-combo-box-scroll-to-index-m
164
164
  * @customElement vaadin-combo-box
165
165
  * @extends HTMLElement
166
166
  */
167
- class ComboBox extends ComboBoxScrollToIndexMixin(
167
+ class ComboBox extends ComboBoxFocusIndexMixin(
168
168
  ComboBoxDataProviderMixin(
169
169
  ComboBoxMixin(
170
170
  PatternMixin(InputControlMixin(ThemableMixin(ElementMixin(PolylitMixin(LumoInjectionMixin(LitElement)))))),
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/combo-box",
4
- "version": "25.2.0-beta1",
4
+ "version": "25.2.0-rc1",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "elements": [
9
9
  {
10
10
  "name": "vaadin-combo-box",
11
- "description": "`<vaadin-combo-box>` is a web component for choosing a value from a filterable list of options\npresented in a dropdown overlay. The options can be provided as a list of strings or objects\nby setting [`items`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-items) property on the element.\n\n```html\n<vaadin-combo-box id=\"combo-box\"></vaadin-combo-box>\n```\n```js\ndocument.querySelector('#combo-box').items = ['apple', 'orange', 'banana'];\n```\n\nWhen the selected `value` is changed, a `value-changed` event is triggered.\n\n### Item rendering\n\nTo customize the content of the `<vaadin-combo-box-item>` elements placed in the dropdown, use\n[`renderer`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-renderer) property which accepts a function.\nThe renderer function is called with `root`, `comboBox`, and `model` as arguments.\n\nGenerate DOM content by using `model` object properties if needed, and append it to the `root`\nelement. The `comboBox` reference is provided to access the combo-box element state. Do not\nset combo-box properties in a `renderer` function.\n\n```js\nconst comboBox = document.querySelector('#combo-box');\ncomboBox.items = [{'label': 'Hydrogen', 'value': 'H'}];\ncomboBox.renderer = (root, comboBox, model) => {\n const item = model.item;\n root.innerHTML = `${model.index}: ${item.label} <b>${item.value}</b>`;\n};\n```\n\nRenderer is called on the opening of the combo-box and each time the related model is updated.\nBefore creating new content, it is recommended to check if there is already an existing DOM\nelement in `root` from a previous renderer call for reusing it. Even though combo-box uses\ninfinite scrolling, reducing DOM operations might improve performance.\n\nThe following properties are available in the `model` argument:\n\nProperty | Type | Description\n-----------|------------------|-------------\n`index` | Number | Index of the item in the `items` array\n`item` | String or Object | The item reference\n`selected` | Boolean | True when item is selected\n`focused` | Boolean | True when item is focused\n\n### Lazy Loading with Function Data Provider\n\nIn addition to assigning an array to the items property, you can alternatively use the\n[`dataProvider`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-dataProvider) function property.\nThe `<vaadin-combo-box>` calls this function lazily, only when it needs more data\nto be displayed.\n\n__Note that when using function data providers, the total number of items\nneeds to be set manually. The total number of items can be returned\nin the second argument of the data provider callback:__\n\n```js\ncomboBox.dataProvider = async (params, callback) => {\n const API = 'https://demo.vaadin.com/demo-data/1.0/filtered-countries';\n const { filter, page, pageSize } = params;\n const index = page * pageSize;\n\n const res = await fetch(`${API}?index=${index}&count=${pageSize}&filter=${filter}`);\n if (res.ok) {\n const { result, size } = await res.json();\n callback(result, size);\n }\n};\n```\n\n### Styling\n\nThe following custom properties are available for styling:\n\nCustom property | Description | Default\n----------------------------------------|----------------------------|---------\n`--vaadin-field-default-width` | Default width of the field | `12em`\n`--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`\n`--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------------|----------------\n`label` | The label element\n`input-field` | The element that wraps prefix, value and buttons\n`field-button` | Set on both clear and toggle buttons\n`clear-button` | The clear button\n`error-message` | The error message element\n`helper-text` | The helper text element wrapper\n`required-indicator` | The `required` state indicator element\n`toggle-button` | The toggle button\n`overlay` | The overlay container\n`content` | The overlay content\n`loader` | The loading indicator shown while loading items\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------------|---------------------------------\n`disabled` | Set when the element is disabled\n`has-value` | Set when the element has a value\n`has-label` | Set when the element has a label\n`has-helper` | Set when the element has helper text or slot\n`has-error-message` | Set when the element has an error message\n`has-tooltip` | Set when the element has a slotted tooltip\n`invalid` | Set when the element is invalid\n`focused` | Set when the element is focused\n`focus-ring` | Set when the element is keyboard focused\n`readonly` | Set when the element is readonly\n`opened` | Set when the overlay is opened\n`loading` | Set when loading items from the data provider\n\n### Internal components\n\nIn addition to `<vaadin-combo-box>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-combo-box-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-item).\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
11
+ "description": "`<vaadin-combo-box>` is a web component for choosing a value from a filterable list of options\npresented in a dropdown overlay. The options can be provided as a list of strings or objects\nby setting [`items`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-items) property on the element.\n\n```html\n<vaadin-combo-box id=\"combo-box\"></vaadin-combo-box>\n```\n```js\ndocument.querySelector('#combo-box').items = ['apple', 'orange', 'banana'];\n```\n\nWhen the selected `value` is changed, a `value-changed` event is triggered.\n\n### Item rendering\n\nTo customize the content of the `<vaadin-combo-box-item>` elements placed in the dropdown, use\n[`renderer`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-renderer) property which accepts a function.\nThe renderer function is called with `root`, `comboBox`, and `model` as arguments.\n\nGenerate DOM content by using `model` object properties if needed, and append it to the `root`\nelement. The `comboBox` reference is provided to access the combo-box element state. Do not\nset combo-box properties in a `renderer` function.\n\n```js\nconst comboBox = document.querySelector('#combo-box');\ncomboBox.items = [{'label': 'Hydrogen', 'value': 'H'}];\ncomboBox.renderer = (root, comboBox, model) => {\n const item = model.item;\n root.innerHTML = `${model.index}: ${item.label} <b>${item.value}</b>`;\n};\n```\n\nRenderer is called on the opening of the combo-box and each time the related model is updated.\nBefore creating new content, it is recommended to check if there is already an existing DOM\nelement in `root` from a previous renderer call for reusing it. Even though combo-box uses\ninfinite scrolling, reducing DOM operations might improve performance.\n\nThe following properties are available in the `model` argument:\n\nProperty | Type | Description\n-----------|------------------|-------------\n`index` | Number | Index of the item in the `items` array\n`item` | String or Object | The item reference\n`selected` | Boolean | True when item is selected\n`focused` | Boolean | True when item is focused\n\n### Lazy Loading with Function Data Provider\n\nIn addition to assigning an array to the items property, you can alternatively use the\n[`dataProvider`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-dataProvider) function property.\nThe `<vaadin-combo-box>` calls this function lazily, only when it needs more data\nto be displayed.\n\n__Note that when using function data providers, the total number of items\nneeds to be set manually. The total number of items can be returned\nin the second argument of the data provider callback:__\n\n```js\ncomboBox.dataProvider = async (params, callback) => {\n const API = 'https://demo.vaadin.com/demo-data/1.0/filtered-countries';\n const { filter, page, pageSize } = params;\n const index = page * pageSize;\n\n const res = await fetch(`${API}?index=${index}&count=${pageSize}&filter=${filter}`);\n if (res.ok) {\n const { result, size } = await res.json();\n callback(result, size);\n }\n};\n```\n\n### Styling\n\nThe following custom properties are available for styling:\n\nCustom property | Description | Default\n----------------------------------------|----------------------------|---------\n`--vaadin-field-default-width` | Default width of the field | `12em`\n`--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`\n`--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------------|----------------\n`label` | The label element\n`input-field` | The element that wraps prefix, value and buttons\n`field-button` | Set on both clear and toggle buttons\n`clear-button` | The clear button\n`error-message` | The error message element\n`helper-text` | The helper text element wrapper\n`required-indicator` | The `required` state indicator element\n`toggle-button` | The toggle button\n`overlay` | The overlay container\n`content` | The overlay content\n`loader` | The loading indicator shown while loading items\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------------|---------------------------------\n`disabled` | Set when the element is disabled\n`has-value` | Set when the element has a value\n`has-label` | Set when the element has a label\n`has-helper` | Set when the element has helper text or slot\n`has-error-message` | Set when the element has an error message\n`has-tooltip` | Set when the element has a slotted tooltip\n`invalid` | Set when the element is invalid\n`focused` | Set when the element is focused\n`focus-ring` | Set when the element is keyboard focused\n`readonly` | Set when the element is readonly\n`opened` | Set when the overlay is opened\n`loading` | Set when loading items from the data provider\n\n### Internal components\n\nIn addition to `<vaadin-combo-box>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-combo-box-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-item).\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "accessible-name",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/combo-box",
4
- "version": "25.2.0-beta1",
4
+ "version": "25.2.0-rc1",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,7 +16,7 @@
16
16
  "elements": [
17
17
  {
18
18
  "name": "vaadin-combo-box",
19
- "description": "`<vaadin-combo-box>` is a web component for choosing a value from a filterable list of options\npresented in a dropdown overlay. The options can be provided as a list of strings or objects\nby setting [`items`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-items) property on the element.\n\n```html\n<vaadin-combo-box id=\"combo-box\"></vaadin-combo-box>\n```\n```js\ndocument.querySelector('#combo-box').items = ['apple', 'orange', 'banana'];\n```\n\nWhen the selected `value` is changed, a `value-changed` event is triggered.\n\n### Item rendering\n\nTo customize the content of the `<vaadin-combo-box-item>` elements placed in the dropdown, use\n[`renderer`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-renderer) property which accepts a function.\nThe renderer function is called with `root`, `comboBox`, and `model` as arguments.\n\nGenerate DOM content by using `model` object properties if needed, and append it to the `root`\nelement. The `comboBox` reference is provided to access the combo-box element state. Do not\nset combo-box properties in a `renderer` function.\n\n```js\nconst comboBox = document.querySelector('#combo-box');\ncomboBox.items = [{'label': 'Hydrogen', 'value': 'H'}];\ncomboBox.renderer = (root, comboBox, model) => {\n const item = model.item;\n root.innerHTML = `${model.index}: ${item.label} <b>${item.value}</b>`;\n};\n```\n\nRenderer is called on the opening of the combo-box and each time the related model is updated.\nBefore creating new content, it is recommended to check if there is already an existing DOM\nelement in `root` from a previous renderer call for reusing it. Even though combo-box uses\ninfinite scrolling, reducing DOM operations might improve performance.\n\nThe following properties are available in the `model` argument:\n\nProperty | Type | Description\n-----------|------------------|-------------\n`index` | Number | Index of the item in the `items` array\n`item` | String or Object | The item reference\n`selected` | Boolean | True when item is selected\n`focused` | Boolean | True when item is focused\n\n### Lazy Loading with Function Data Provider\n\nIn addition to assigning an array to the items property, you can alternatively use the\n[`dataProvider`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-combo-box#property-dataProvider) function property.\nThe `<vaadin-combo-box>` calls this function lazily, only when it needs more data\nto be displayed.\n\n__Note that when using function data providers, the total number of items\nneeds to be set manually. The total number of items can be returned\nin the second argument of the data provider callback:__\n\n```js\ncomboBox.dataProvider = async (params, callback) => {\n const API = 'https://demo.vaadin.com/demo-data/1.0/filtered-countries';\n const { filter, page, pageSize } = params;\n const index = page * pageSize;\n\n const res = await fetch(`${API}?index=${index}&count=${pageSize}&filter=${filter}`);\n if (res.ok) {\n const { result, size } = await res.json();\n callback(result, size);\n }\n};\n```\n\n### Styling\n\nThe following custom properties are available for styling:\n\nCustom property | Description | Default\n----------------------------------------|----------------------------|---------\n`--vaadin-field-default-width` | Default width of the field | `12em`\n`--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`\n`--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------------|----------------\n`label` | The label element\n`input-field` | The element that wraps prefix, value and buttons\n`field-button` | Set on both clear and toggle buttons\n`clear-button` | The clear button\n`error-message` | The error message element\n`helper-text` | The helper text element wrapper\n`required-indicator` | The `required` state indicator element\n`toggle-button` | The toggle button\n`overlay` | The overlay container\n`content` | The overlay content\n`loader` | The loading indicator shown while loading items\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------------|---------------------------------\n`disabled` | Set when the element is disabled\n`has-value` | Set when the element has a value\n`has-label` | Set when the element has a label\n`has-helper` | Set when the element has helper text or slot\n`has-error-message` | Set when the element has an error message\n`has-tooltip` | Set when the element has a slotted tooltip\n`invalid` | Set when the element is invalid\n`focused` | Set when the element is focused\n`focus-ring` | Set when the element is keyboard focused\n`readonly` | Set when the element is readonly\n`opened` | Set when the overlay is opened\n`loading` | Set when loading items from the data provider\n\n### Internal components\n\nIn addition to `<vaadin-combo-box>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-combo-box-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-beta1/#/elements/vaadin-item).\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
19
+ "description": "`<vaadin-combo-box>` is a web component for choosing a value from a filterable list of options\npresented in a dropdown overlay. The options can be provided as a list of strings or objects\nby setting [`items`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-items) property on the element.\n\n```html\n<vaadin-combo-box id=\"combo-box\"></vaadin-combo-box>\n```\n```js\ndocument.querySelector('#combo-box').items = ['apple', 'orange', 'banana'];\n```\n\nWhen the selected `value` is changed, a `value-changed` event is triggered.\n\n### Item rendering\n\nTo customize the content of the `<vaadin-combo-box-item>` elements placed in the dropdown, use\n[`renderer`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-renderer) property which accepts a function.\nThe renderer function is called with `root`, `comboBox`, and `model` as arguments.\n\nGenerate DOM content by using `model` object properties if needed, and append it to the `root`\nelement. The `comboBox` reference is provided to access the combo-box element state. Do not\nset combo-box properties in a `renderer` function.\n\n```js\nconst comboBox = document.querySelector('#combo-box');\ncomboBox.items = [{'label': 'Hydrogen', 'value': 'H'}];\ncomboBox.renderer = (root, comboBox, model) => {\n const item = model.item;\n root.innerHTML = `${model.index}: ${item.label} <b>${item.value}</b>`;\n};\n```\n\nRenderer is called on the opening of the combo-box and each time the related model is updated.\nBefore creating new content, it is recommended to check if there is already an existing DOM\nelement in `root` from a previous renderer call for reusing it. Even though combo-box uses\ninfinite scrolling, reducing DOM operations might improve performance.\n\nThe following properties are available in the `model` argument:\n\nProperty | Type | Description\n-----------|------------------|-------------\n`index` | Number | Index of the item in the `items` array\n`item` | String or Object | The item reference\n`selected` | Boolean | True when item is selected\n`focused` | Boolean | True when item is focused\n\n### Lazy Loading with Function Data Provider\n\nIn addition to assigning an array to the items property, you can alternatively use the\n[`dataProvider`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-combo-box#property-dataProvider) function property.\nThe `<vaadin-combo-box>` calls this function lazily, only when it needs more data\nto be displayed.\n\n__Note that when using function data providers, the total number of items\nneeds to be set manually. The total number of items can be returned\nin the second argument of the data provider callback:__\n\n```js\ncomboBox.dataProvider = async (params, callback) => {\n const API = 'https://demo.vaadin.com/demo-data/1.0/filtered-countries';\n const { filter, page, pageSize } = params;\n const index = page * pageSize;\n\n const res = await fetch(`${API}?index=${index}&count=${pageSize}&filter=${filter}`);\n if (res.ok) {\n const { result, size } = await res.json();\n callback(result, size);\n }\n};\n```\n\n### Styling\n\nThe following custom properties are available for styling:\n\nCustom property | Description | Default\n----------------------------------------|----------------------------|---------\n`--vaadin-field-default-width` | Default width of the field | `12em`\n`--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`\n`--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------------|----------------\n`label` | The label element\n`input-field` | The element that wraps prefix, value and buttons\n`field-button` | Set on both clear and toggle buttons\n`clear-button` | The clear button\n`error-message` | The error message element\n`helper-text` | The helper text element wrapper\n`required-indicator` | The `required` state indicator element\n`toggle-button` | The toggle button\n`overlay` | The overlay container\n`content` | The overlay content\n`loader` | The loading indicator shown while loading items\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------------|---------------------------------\n`disabled` | Set when the element is disabled\n`has-value` | Set when the element has a value\n`has-label` | Set when the element has a label\n`has-helper` | Set when the element has helper text or slot\n`has-error-message` | Set when the element has an error message\n`has-tooltip` | Set when the element has a slotted tooltip\n`invalid` | Set when the element is invalid\n`focused` | Set when the element is focused\n`focus-ring` | Set when the element is keyboard focused\n`readonly` | Set when the element is readonly\n`opened` | Set when the overlay is opened\n`loading` | Set when loading items from the data provider\n\n### Internal components\n\nIn addition to `<vaadin-combo-box>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-combo-box-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.2.0-rc1/#/elements/vaadin-item).\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {
@@ -1,28 +0,0 @@
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
- import type { Constructor } from '@open-wc/dedupe-mixin';
7
-
8
- export declare function ComboBoxScrollToIndexMixin<T extends Constructor<HTMLElement>>(
9
- base: T,
10
- ): Constructor<ComboBoxScrollToIndexMixinClass> & T;
11
-
12
- export declare class ComboBoxScrollToIndexMixinClass {
13
- /**
14
- * Scrolls the dropdown to the item at the given index and sets it as the
15
- * focused (highlighted) item. Safe to call before the dropdown is opened
16
- * or while the data provider is loading: the call is queued and executed
17
- * once the overlay is open and not loading.
18
- *
19
- * Because this sets the focused item, closing the dropdown without an
20
- * explicit selection change (e.g. via outside click or blur) will commit
21
- * the focused item as `selectedItem`. In the typical use case (scroll to
22
- * the currently selected item) this is a no-op; callers scrolling to a
23
- * different index should be aware of this behavior.
24
- *
25
- * @param index Index of the item to scroll to
26
- */
27
- scrollToIndex(index: number): void;
28
- }
@@ -1,100 +0,0 @@
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
- import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
7
-
8
- export const ComboBoxScrollToIndexMixin = (superClass) =>
9
- class ScrollToIndexMixin extends superClass {
10
- static get observers() {
11
- return ['__clearPendingScrollOnFilter(filter)'];
12
- }
13
-
14
- /**
15
- * Scrolls the dropdown to the item at the given index and sets it as the
16
- * focused (highlighted) item. Safe to call before the dropdown is opened
17
- * or while the data provider is loading: the call is queued and executed
18
- * once the overlay is open and not loading.
19
- *
20
- * Because this sets the focused item, closing the dropdown without an
21
- * explicit selection change (e.g. via outside click or blur) will commit
22
- * the focused item as `selectedItem`. In the typical use case (scroll to
23
- * the currently selected item) this is a no-op; callers scrolling to a
24
- * different index should be aware of this behavior.
25
- *
26
- * @param {number} index Index of the item to scroll to
27
- */
28
- scrollToIndex(index) {
29
- if (typeof index !== 'number' || Number.isNaN(index) || index < 0) {
30
- return;
31
- }
32
-
33
- if (!this._overlayOpened || this.loading) {
34
- this.__scrollToPendingIndex = index;
35
- return;
36
- }
37
-
38
- if (!this._dropdownItems || index >= this._dropdownItems.length) {
39
- return;
40
- }
41
-
42
- if (this._dropdownItems[index] instanceof ComboBoxPlaceholder) {
43
- // The target item is on a page that has not been loaded yet. Request
44
- // the page directly and queue the focus-index update for after the
45
- // page loads (see `__onDataProviderPageLoaded` →
46
- // `__scrollToPendingIndexIfNeeded`). Relying on `_scrollIntoView` to
47
- // trigger the page load via the visible-placeholder `index-requested`
48
- // chain is unreliable on reopen with cached data: the virtualizer
49
- // has just been torn down by closing the overlay and its scroll API
50
- // is a no-op while it rebuilds physical items.
51
- this.__scrollToPendingIndex = index;
52
- this.__dataProviderController.ensureFlatIndexLoaded(index);
53
- return;
54
- }
55
-
56
- delete this.__scrollToPendingIndex;
57
- this._focusedIndex = index;
58
- requestAnimationFrame(() => {
59
- if (this.isConnected) {
60
- this._updateActiveDescendant(index);
61
- }
62
- });
63
- }
64
-
65
- /** @private */
66
- __scrollToPendingIndexIfNeeded() {
67
- if (this.__scrollToPendingIndex !== undefined && !this.loading) {
68
- this.scrollToIndex(this.__scrollToPendingIndex);
69
- }
70
- }
71
-
72
- /** @private */
73
- __clearPendingScrollOnFilter() {
74
- delete this.__scrollToPendingIndex;
75
- }
76
-
77
- /**
78
- * Override method from `ComboBoxBaseMixin` to flush any pending
79
- * `scrollToIndex` call after the overlay opens.
80
- *
81
- * @protected
82
- * @override
83
- */
84
- _onOpened() {
85
- super._onOpened();
86
- this.__scrollToPendingIndexIfNeeded();
87
- }
88
-
89
- /**
90
- * Override method from `ComboBoxDataProviderMixin` to flush any pending
91
- * `scrollToIndex` call after a data-provider page lands.
92
- *
93
- * @private
94
- * @override
95
- */
96
- __onDataProviderPageLoaded() {
97
- super.__onDataProviderPageLoaded();
98
- this.__scrollToPendingIndexIfNeeded();
99
- }
100
- };