@vaadin/combo-box 25.2.0-alpha9 → 25.2.0-beta2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/combo-box",
3
- "version": "25.2.0-alpha9",
3
+ "version": "25.2.0-beta2",
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-alpha9",
40
- "@vaadin/component-base": "25.2.0-alpha9",
41
- "@vaadin/field-base": "25.2.0-alpha9",
42
- "@vaadin/input-container": "25.2.0-alpha9",
43
- "@vaadin/item": "25.2.0-alpha9",
44
- "@vaadin/lit-renderer": "25.2.0-alpha9",
45
- "@vaadin/overlay": "25.2.0-alpha9",
46
- "@vaadin/vaadin-themable-mixin": "25.2.0-alpha9",
39
+ "@vaadin/a11y-base": "25.2.0-beta2",
40
+ "@vaadin/component-base": "25.2.0-beta2",
41
+ "@vaadin/field-base": "25.2.0-beta2",
42
+ "@vaadin/input-container": "25.2.0-beta2",
43
+ "@vaadin/item": "25.2.0-beta2",
44
+ "@vaadin/lit-renderer": "25.2.0-beta2",
45
+ "@vaadin/overlay": "25.2.0-beta2",
46
+ "@vaadin/vaadin-themable-mixin": "25.2.0-beta2",
47
47
  "lit": "^3.0.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@vaadin/aura": "25.2.0-alpha9",
51
- "@vaadin/chai-plugins": "25.2.0-alpha9",
52
- "@vaadin/test-runner-commands": "25.2.0-alpha9",
50
+ "@vaadin/aura": "25.2.0-beta2",
51
+ "@vaadin/chai-plugins": "25.2.0-beta2",
52
+ "@vaadin/test-runner-commands": "25.2.0-beta2",
53
53
  "@vaadin/testing-helpers": "^2.0.0",
54
- "@vaadin/vaadin-lumo-styles": "25.2.0-alpha9",
55
- "sinon": "^21.0.2"
54
+ "@vaadin/vaadin-lumo-styles": "25.2.0-beta2",
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": "a38a03e8a8be45821f39c14054c63634dafe08d0"
62
+ "gitHead": "9e18feb8057baf278b72fec4e42657b19e48f499"
63
63
  }
@@ -12,14 +12,6 @@ import { InputMixin } from '@vaadin/field-base/src/input-mixin.js';
12
12
  import { VirtualKeyboardController } from '@vaadin/field-base/src/virtual-keyboard-controller.js';
13
13
  import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
14
14
 
15
- /**
16
- * @polymerMixin
17
- * @mixes DisabledMixin
18
- * @mixes FocusMixin
19
- * @mixes InputMixin
20
- * @mixes KeyboardMixin
21
- * @param {function(new:HTMLElement)} superClass
22
- */
23
15
  export const ComboBoxBaseMixin = (superClass) =>
24
16
  class ComboBoxMixinBaseClass extends KeyboardMixin(InputMixin(DisabledMixin(FocusMixin(superClass)))) {
25
17
  static get properties() {
@@ -6,9 +6,6 @@
6
6
  import { DataProviderController } from '@vaadin/component-base/src/data-provider-controller/data-provider-controller.js';
7
7
  import { ComboBoxPlaceholder } from './vaadin-combo-box-placeholder.js';
8
8
 
9
- /**
10
- * @polymerMixin
11
- */
12
9
  export const ComboBoxDataProviderMixin = (superClass) =>
13
10
  class DataProviderMixin extends superClass {
14
11
  static get properties() {
@@ -0,0 +1,96 @@
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
+ // Move the viewport and set the focused row. Rendering rows
40
+ // around `index` lets placeholder rows fire `index-requested`,
41
+ // which loads any missing pages via the data-provider chain.
42
+ this._scrollIntoView(index);
43
+ this._focusedIndex = index;
44
+
45
+ // A page-load may have kicked in during the scroll (placeholder
46
+ // rows in the new viewport requested their pages). Re-fire after
47
+ // the page lands so the viewport can settle around real items.
48
+ if (this.loading) {
49
+ this.__pendingFocusIndex = index;
50
+ return;
51
+ }
52
+
53
+ delete this.__pendingFocusIndex;
54
+ requestAnimationFrame(() => {
55
+ if (this.isConnected) {
56
+ this._updateActiveDescendant(index);
57
+ }
58
+ });
59
+ }
60
+
61
+ /** @private */
62
+ __focusPendingIndexIfNeeded() {
63
+ if (this.__pendingFocusIndex !== undefined && !this.loading) {
64
+ this.__focusIndex(this.__pendingFocusIndex);
65
+ }
66
+ }
67
+
68
+ /** @private */
69
+ __clearPendingFocusOnFilter() {
70
+ delete this.__pendingFocusIndex;
71
+ }
72
+
73
+ /**
74
+ * Override method from `ComboBoxBaseMixin` to flush any pending
75
+ * `__focusIndex` call after the overlay opens.
76
+ *
77
+ * @protected
78
+ * @override
79
+ */
80
+ _onOpened() {
81
+ super._onOpened();
82
+ this.__focusPendingIndexIfNeeded();
83
+ }
84
+
85
+ /**
86
+ * Override method from `ComboBoxDataProviderMixin` to flush any pending
87
+ * `__focusIndex` call after a data-provider page lands.
88
+ *
89
+ * @private
90
+ * @override
91
+ */
92
+ __onDataProviderPageLoaded() {
93
+ super.__onDataProviderPageLoaded();
94
+ this.__focusPendingIndexIfNeeded();
95
+ }
96
+ };
@@ -4,9 +4,6 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
 
7
- /**
8
- * @polymerMixin
9
- */
10
7
  export const ComboBoxItemMixin = (superClass) =>
11
8
  class ComboBoxItemMixinClass extends superClass {
12
9
  static get properties() {
@@ -35,9 +35,6 @@ import { ComboBoxItemMixin } from './vaadin-combo-box-item-mixin.js';
35
35
  * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
36
36
  *
37
37
  * @customElement vaadin-combo-box-item
38
- * @mixes ComboBoxItemMixin
39
- * @mixes ThemableMixin
40
- * @mixes DirMixin
41
38
  * @private
42
39
  */
43
40
  export class ComboBoxItem extends ComboBoxItemMixin(
@@ -35,10 +35,6 @@ function findItemIndex(items, callback) {
35
35
  });
36
36
  }
37
37
 
38
- /**
39
- * @polymerMixin
40
- * @mixes ComboBoxBaseMixin
41
- */
42
38
  export const ComboBoxItemsMixin = (superClass) =>
43
39
  class ComboBoxItemsMixinClass extends ComboBoxBaseMixin(superClass) {
44
40
  static get properties() {
@@ -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.
@@ -16,12 +17,6 @@ function isValidValue(value) {
16
17
  return value !== undefined && value !== null;
17
18
  }
18
19
 
19
- /**
20
- * @polymerMixin
21
- * @mixes ComboBoxItemsMixin
22
- * @mixes ValidateMixin
23
- * @param {function(new:HTMLElement)} superClass
24
- */
25
20
  export const ComboBoxMixin = (superClass) =>
26
21
  class ComboBoxMixinClass extends ValidateMixin(ComboBoxItemsMixin(superClass)) {
27
22
  static get properties() {
@@ -177,7 +172,7 @@ export const ComboBoxMixin = (superClass) =>
177
172
  _openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
178
173
  // Close the overlay if there are no items to display.
179
174
  // See https://github.com/vaadin/vaadin-combo-box/pull/964
180
- this._overlayOpened = opened && (keepOverlayOpened || loading || !!(items && items.length));
175
+ this._overlayOpened = opened && (keepOverlayOpened || loading || !!items?.length);
181
176
  }
182
177
 
183
178
  /**
@@ -524,6 +519,18 @@ export const ComboBoxMixin = (superClass) =>
524
519
  this.selectedItem = newItems[valueIndex];
525
520
  }
526
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
+
527
534
  // Try to first set focus on the item that had been focused before `newItems` were updated
528
535
  // if it is still present in the `newItems` array. Otherwise, set the focused index
529
536
  // depending on the selected item or the filter query.
@@ -552,43 +559,4 @@ export const ComboBoxMixin = (superClass) =>
552
559
 
553
560
  super._handleFocusOut();
554
561
  }
555
-
556
- /**
557
- * Fired when the value changes.
558
- *
559
- * @event value-changed
560
- * @param {Object} detail
561
- * @param {String} detail.value the combobox value
562
- */
563
-
564
- /**
565
- * Fired when selected item changes.
566
- *
567
- * @event selected-item-changed
568
- * @param {Object} detail
569
- * @param {Object|String} detail.value the selected item. Type is the same as the type of `items`.
570
- */
571
-
572
- /**
573
- * Fired when the user sets a custom value.
574
- * @event custom-value-set
575
- * @param {String} detail the custom value
576
- */
577
-
578
- /**
579
- * Fired when the user commits a value change.
580
- * @event change
581
- */
582
-
583
- /**
584
- * Fired after the `vaadin-combo-box-overlay` opens.
585
- *
586
- * @event vaadin-combo-box-dropdown-opened
587
- */
588
-
589
- /**
590
- * Fired after the `vaadin-combo-box-overlay` closes.
591
- *
592
- * @event vaadin-combo-box-dropdown-closed
593
- */
594
562
  };
@@ -6,10 +6,6 @@
6
6
  import { isElementFocusable } from '@vaadin/a11y-base/src/focus-utils.js';
7
7
  import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
8
8
 
9
- /**
10
- * @polymerMixin
11
- * @mixes PositionMixin
12
- */
13
9
  export const ComboBoxOverlayMixin = (superClass) =>
14
10
  class ComboBoxOverlayMixin extends PositionMixin(superClass) {
15
11
  static get observers() {
@@ -19,10 +19,6 @@ import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';
19
19
  *
20
20
  * @customElement vaadin-combo-box-overlay
21
21
  * @extends HTMLElement
22
- * @mixes ComboBoxOverlayMixin
23
- * @mixes DirMixin
24
- * @mixes OverlayMixin
25
- * @mixes ThemableMixin
26
22
  * @private
27
23
  */
28
24
  export class ComboBoxOverlay extends ComboBoxOverlayMixin(
@@ -8,9 +8,6 @@ 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';
10
10
 
11
- /**
12
- * @polymerMixin
13
- */
14
11
  export const ComboBoxScrollerMixin = (superClass) =>
15
12
  class ComboBoxScrollerMixin extends superClass {
16
13
  static get properties() {
@@ -195,18 +192,20 @@ export const ComboBoxScrollerMixin = (superClass) =>
195
192
  }
196
193
  this.__virtualizer.scrollToIndex(Math.max(0, targetIndex));
197
194
 
198
- // Sometimes the item is partly below the bottom edge, detect and adjust.
199
- const lastPhysicalItem = [...this.children].find(
200
- (el) => !el.hidden && el.index === this.__virtualizer.lastVisibleIndex,
201
- );
202
- if (!lastPhysicalItem || index !== lastPhysicalItem.index) {
195
+ // Flush so the rect-based correction below runs on settled positions.
196
+ this.__virtualizer.flush();
197
+
198
+ const target = [...this.children].find((el) => !el.hidden && el.index === index);
199
+ if (!target) {
203
200
  return;
204
201
  }
205
- const lastPhysicalItemRect = lastPhysicalItem.getBoundingClientRect();
202
+ const targetRect = target.getBoundingClientRect();
206
203
  const scrollerRect = this.getBoundingClientRect();
207
- const scrollTopAdjust = lastPhysicalItemRect.bottom - scrollerRect.bottom + this._viewportTotalPaddingBottom;
208
- if (scrollTopAdjust > 0) {
209
- this.scrollTop += scrollTopAdjust;
204
+ const targetBottom = targetRect.bottom + this._viewportTotalPaddingBottom;
205
+ if (targetBottom > scrollerRect.bottom) {
206
+ this.scrollTop += targetBottom - scrollerRect.bottom;
207
+ } else if (targetRect.top < scrollerRect.top) {
208
+ this.scrollTop -= scrollerRect.top - targetRect.top;
210
209
  }
211
210
  }
212
211
 
@@ -262,6 +261,25 @@ export const ComboBoxScrollerMixin = (superClass) =>
262
261
  }
263
262
 
264
263
  this.requestContentUpdate();
264
+ return;
265
+ }
266
+
267
+ // Reset the DOM scrollTop and the virtualizer adapter's
268
+ // `_scrollPosition` cache. Without the cache reset, the adapter's
269
+ // ResizeObserver restores the prior offset when the overlay becomes
270
+ // visible again, leaving the next open stuck at the previous offset
271
+ // (or blank with dataProvider) until the user scrolls. The
272
+ // virtualizer's own `scrollToIndex` can't help — by the time this
273
+ // observer runs, `offsetHeight` is already 0 and its scroll API is
274
+ // a no-op. Guarded on `_scrollPosition > 0` so the reset is skipped
275
+ // when there is nothing to reset (e.g. the initial observer run
276
+ // before the dropdown has ever been opened); unconditionally
277
+ // touching `scrollTop` on connect can affect the outer document's
278
+ // scroll position (see the combo-box re-layout integration test).
279
+ const adapter = this.__virtualizer && this.__virtualizer.__adapter;
280
+ if (adapter && adapter._scrollPosition > 0) {
281
+ this.scrollTop = 0;
282
+ adapter._scrollPosition = 0;
265
283
  }
266
284
  }
267
285
 
@@ -14,7 +14,6 @@ import { ComboBoxScrollerMixin } from './vaadin-combo-box-scroller-mixin.js';
14
14
  *
15
15
  * @customElement vaadin-combo-box-scroller
16
16
  * @extends HTMLElement
17
- * @mixes ComboBoxScrollerMixin
18
17
  * @private
19
18
  */
20
19
  export class ComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElement)) {
@@ -225,6 +225,8 @@ export interface ComboBoxEventMap<TItem> extends HTMLElementEventMap {
225
225
  * @fires {CustomEvent} selected-item-changed - Fired when the `selectedItem` property changes.
226
226
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
227
227
  * @fires {CustomEvent} validated - Fired whenever the field is validated.
228
+ * @fires {CustomEvent} vaadin-combo-box-dropdown-opened - Fired after the `vaadin-combo-box-overlay` opens.
229
+ * @fires {CustomEvent} vaadin-combo-box-dropdown-closed - Fired after the `vaadin-combo-box-overlay` closes.
228
230
  */
229
231
  declare class ComboBox<TItem = ComboBoxDefaultItem> extends HTMLElement {
230
232
  addEventListener<K extends keyof ComboBoxEventMap<TItem>>(
@@ -22,6 +22,7 @@ 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
27
 
27
28
  /**
@@ -157,19 +158,17 @@ import { ComboBoxMixin } from './vaadin-combo-box-mixin.js';
157
158
  * @fires {CustomEvent} selected-item-changed - Fired when the `selectedItem` property changes.
158
159
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
159
160
  * @fires {CustomEvent} validated - Fired whenever the field is validated.
161
+ * @fires {CustomEvent} vaadin-combo-box-dropdown-opened - Fired after the `vaadin-combo-box-overlay` opens.
162
+ * @fires {CustomEvent} vaadin-combo-box-dropdown-closed - Fired after the `vaadin-combo-box-overlay` closes.
160
163
  *
161
164
  * @customElement vaadin-combo-box
162
165
  * @extends HTMLElement
163
- * @mixes ElementMixin
164
- * @mixes ThemableMixin
165
- * @mixes InputControlMixin
166
- * @mixes PatternMixin
167
- * @mixes ComboBoxDataProviderMixin
168
- * @mixes ComboBoxMixin
169
166
  */
170
- class ComboBox extends ComboBoxDataProviderMixin(
171
- ComboBoxMixin(
172
- PatternMixin(InputControlMixin(ThemableMixin(ElementMixin(PolylitMixin(LumoInjectionMixin(LitElement)))))),
167
+ class ComboBox extends ComboBoxFocusIndexMixin(
168
+ ComboBoxDataProviderMixin(
169
+ ComboBoxMixin(
170
+ PatternMixin(InputControlMixin(ThemableMixin(ElementMixin(PolylitMixin(LumoInjectionMixin(LitElement)))))),
171
+ ),
173
172
  ),
174
173
  ) {
175
174
  static get is() {