@vaadin/combo-box 23.2.0-alpha3 → 23.2.0-alpha6

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": "23.2.0-alpha3",
3
+ "version": "23.2.0-alpha6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,7 +24,9 @@
24
24
  "src",
25
25
  "theme",
26
26
  "vaadin-*.d.ts",
27
- "vaadin-*.js"
27
+ "vaadin-*.js",
28
+ "web-types.json",
29
+ "web-types.lit.json"
28
30
  ],
29
31
  "keywords": [
30
32
  "Vaadin",
@@ -36,23 +38,27 @@
36
38
  "dependencies": {
37
39
  "@open-wc/dedupe-mixin": "^1.3.0",
38
40
  "@polymer/polymer": "^3.0.0",
39
- "@vaadin/component-base": "23.2.0-alpha3",
40
- "@vaadin/field-base": "23.2.0-alpha3",
41
- "@vaadin/input-container": "23.2.0-alpha3",
42
- "@vaadin/item": "23.2.0-alpha3",
43
- "@vaadin/lit-renderer": "23.2.0-alpha3",
44
- "@vaadin/vaadin-lumo-styles": "23.2.0-alpha3",
45
- "@vaadin/vaadin-material-styles": "23.2.0-alpha3",
46
- "@vaadin/vaadin-overlay": "23.2.0-alpha3",
47
- "@vaadin/vaadin-themable-mixin": "23.2.0-alpha3"
41
+ "@vaadin/component-base": "23.2.0-alpha6",
42
+ "@vaadin/field-base": "23.2.0-alpha6",
43
+ "@vaadin/input-container": "23.2.0-alpha6",
44
+ "@vaadin/item": "23.2.0-alpha6",
45
+ "@vaadin/lit-renderer": "23.2.0-alpha6",
46
+ "@vaadin/vaadin-lumo-styles": "23.2.0-alpha6",
47
+ "@vaadin/vaadin-material-styles": "23.2.0-alpha6",
48
+ "@vaadin/vaadin-overlay": "23.2.0-alpha6",
49
+ "@vaadin/vaadin-themable-mixin": "23.2.0-alpha6"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@esm-bundle/chai": "^4.3.4",
51
- "@vaadin/polymer-legacy-adapter": "23.2.0-alpha3",
53
+ "@vaadin/polymer-legacy-adapter": "23.2.0-alpha6",
52
54
  "@vaadin/testing-helpers": "^0.3.2",
53
- "@vaadin/text-field": "23.2.0-alpha3",
55
+ "@vaadin/text-field": "23.2.0-alpha6",
54
56
  "lit": "^2.0.0",
55
57
  "sinon": "^13.0.2"
56
58
  },
57
- "gitHead": "06e5875be93ca50da2846dafc65a8531010c0576"
59
+ "web-types": [
60
+ "web-types.json",
61
+ "web-types.lit.json"
62
+ ],
63
+ "gitHead": "61f1fb56953434e97d34a8819640064301dd3d8a"
58
64
  }
@@ -20,7 +20,7 @@ export type ComboBoxDataProvider<TItem> = (
20
20
 
21
21
  export declare function ComboBoxDataProviderMixin<TItem, T extends Constructor<HTMLElement>>(
22
22
  base: T,
23
- ): T & Constructor<ComboBoxDataProviderMixinClass<TItem>>;
23
+ ): Constructor<ComboBoxDataProviderMixinClass<TItem>> & T;
24
24
 
25
25
  export declare class ComboBoxDataProviderMixinClass<TItem> {
26
26
  /**
@@ -186,7 +186,7 @@ export const ComboBoxDataProviderMixin = (superClass) =>
186
186
  filteredItems.splice(params.page * params.pageSize, items.length, ...items);
187
187
  this.filteredItems = filteredItems;
188
188
 
189
- if (!this.opened && !this.hasAttribute('focused')) {
189
+ if (!this.opened && !this._isInputFocused()) {
190
190
  this._commitValue();
191
191
  }
192
192
 
@@ -8,6 +8,7 @@ import type { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mix
8
8
  import type { InputMixinClass } from '@vaadin/field-base/src/input-mixin.js';
9
9
  import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
10
10
  import type { ThemableMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
11
+ import type { ThemePropertyMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
11
12
  import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-provider-mixin.js';
12
13
  import type { ComboBoxDefaultItem, ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
13
14
  export {
@@ -134,13 +135,13 @@ declare class ComboBoxLight<TItem = ComboBoxDefaultItem> extends HTMLElement {
134
135
  addEventListener<K extends keyof ComboBoxLightEventMap<TItem>>(
135
136
  type: K,
136
137
  listener: (this: ComboBoxLight<TItem>, ev: ComboBoxLightEventMap<TItem>[K]) => void,
137
- options?: boolean | AddEventListenerOptions,
138
+ options?: AddEventListenerOptions | boolean,
138
139
  ): void;
139
140
 
140
141
  removeEventListener<K extends keyof ComboBoxLightEventMap<TItem>>(
141
142
  type: K,
142
143
  listener: (this: ComboBoxLight<TItem>, ev: ComboBoxLightEventMap<TItem>[K]) => void,
143
- options?: boolean | EventListenerOptions,
144
+ options?: EventListenerOptions | boolean,
144
145
  ): void;
145
146
  }
146
147
 
@@ -151,6 +152,7 @@ interface ComboBoxLight<TItem = ComboBoxDefaultItem>
151
152
  InputMixinClass,
152
153
  DisabledMixinClass,
153
154
  ThemableMixinClass,
155
+ ThemePropertyMixinClass,
154
156
  ValidateMixinClass {}
155
157
 
156
158
  declare global {
@@ -7,6 +7,7 @@ import './vaadin-combo-box-item.js';
7
7
  import './vaadin-combo-box-overlay.js';
8
8
  import './vaadin-combo-box-scroller.js';
9
9
  import { dashToCamelCase } from '@polymer/polymer/lib/utils/case-map.js';
10
+ import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
10
11
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
11
12
  import { ValidateMixin } from '@vaadin/field-base/src/validate-mixin.js';
12
13
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
@@ -82,7 +83,6 @@ class ComboBoxLight extends ComboBoxDataProviderMixin(ComboBoxMixin(ValidateMixi
82
83
 
83
84
  <vaadin-combo-box-overlay
84
85
  id="overlay"
85
- hidden$="[[_isOverlayHidden(filteredItems, loading)]]"
86
86
  opened="[[_overlayOpened]]"
87
87
  loading$="[[loading]]"
88
88
  theme$="[[_theme]]"
@@ -120,14 +120,14 @@ class ComboBoxLight extends ComboBoxDataProviderMixin(ComboBoxMixin(ValidateMixi
120
120
  /** @protected */
121
121
  ready() {
122
122
  super.ready();
123
+
123
124
  this._toggleElement = this.querySelector('.toggle-button');
124
- }
125
125
 
126
- /** @protected */
127
- connectedCallback() {
128
- super.connectedCallback();
129
- this._setInputElement(this.querySelector('vaadin-text-field,.input'));
130
- this._revertInputValue();
126
+ // Wait until the slotted input DOM is ready
127
+ afterNextRender(this, () => {
128
+ this._setInputElement(this.querySelector('vaadin-text-field,.input'));
129
+ this._revertInputValue();
130
+ });
131
131
  }
132
132
 
133
133
  /**
@@ -149,6 +149,38 @@ class ComboBoxLight extends ComboBoxDataProviderMixin(ComboBoxMixin(ValidateMixi
149
149
  return dashToCamelCase(this.attrForValue);
150
150
  }
151
151
 
152
+ /**
153
+ * @protected
154
+ * @override
155
+ * @return {HTMLInputElement | undefined}
156
+ */
157
+ get _nativeInput() {
158
+ const input = this.inputElement;
159
+
160
+ if (input) {
161
+ // Support `<input class="input">`
162
+ if (input instanceof HTMLInputElement) {
163
+ return input;
164
+ }
165
+
166
+ // Support `<input>` in light DOM (e.g. `vaadin-text-field`)
167
+ const slottedInput = input.querySelector('input');
168
+ if (slottedInput) {
169
+ return slottedInput;
170
+ }
171
+
172
+ if (input.shadowRoot) {
173
+ // Support `<input>` in Shadow DOM (e.g. `mwc-textfield`)
174
+ const shadowInput = input.shadowRoot.querySelector('input');
175
+ if (shadowInput) {
176
+ return shadowInput;
177
+ }
178
+ }
179
+ }
180
+
181
+ return undefined;
182
+ }
183
+
152
184
  /** @protected */
153
185
  _isClearButton(event) {
154
186
  return (
@@ -24,11 +24,11 @@ export type ComboBoxRenderer<TItem> = (
24
24
 
25
25
  export declare function ComboBoxMixin<TItem, T extends Constructor<HTMLElement>>(
26
26
  base: T,
27
- ): T &
28
- Constructor<ComboBoxMixinClass<TItem>> &
27
+ ): Constructor<ComboBoxMixinClass<TItem>> &
29
28
  Constructor<DisabledMixinClass> &
30
29
  Constructor<InputMixinClass> &
31
- Constructor<KeyboardMixinClass>;
30
+ Constructor<KeyboardMixinClass> &
31
+ T;
32
32
 
33
33
  export declare class ComboBoxMixinClass<TItem> {
34
34
  /**
@@ -165,16 +165,5 @@ export declare class ComboBoxMixinClass<TItem> {
165
165
  */
166
166
  close(): void;
167
167
 
168
- /**
169
- * Returns true if `value` is valid, and sets the `invalid` flag appropriately.
170
- */
171
- validate(): boolean;
172
-
173
- /**
174
- * Returns true if the current input value satisfies all constraints (if any).
175
- * You can override this method for custom validations.
176
- */
177
- checkValidity(): boolean;
178
-
179
168
  protected _revertInputValue(): void;
180
169
  }
@@ -6,6 +6,7 @@
6
6
  import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
7
7
  import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
8
8
  import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
9
+ import { isElementFocused } from '@vaadin/component-base/src/focus-utils.js';
9
10
  import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
10
11
  import { processTemplates } from '@vaadin/component-base/src/templates.js';
11
12
  import { InputMixin } from '@vaadin/field-base/src/input-mixin.js';
@@ -279,14 +280,26 @@ export const ComboBoxMixin = (subclass) =>
279
280
  }
280
281
  }
281
282
 
283
+ /**
284
+ * Get a reference to the native `<input>` element.
285
+ * Override to provide a custom input.
286
+ * @protected
287
+ * @return {HTMLInputElement | undefined}
288
+ */
289
+ get _nativeInput() {
290
+ return this.inputElement;
291
+ }
292
+
282
293
  /**
283
294
  * Override method inherited from `InputMixin`
284
295
  * to customize the input element.
285
296
  * @protected
286
297
  * @override
287
298
  */
288
- _inputElementChanged(input) {
289
- super._inputElementChanged(input);
299
+ _inputElementChanged(inputElement) {
300
+ super._inputElementChanged(inputElement);
301
+
302
+ const input = this._nativeInput;
290
303
 
291
304
  if (input) {
292
305
  input.autocomplete = 'off';
@@ -413,11 +426,6 @@ export const ComboBoxMixin = (subclass) =>
413
426
  // Prevent blurring the input when clicking inside the overlay
414
427
  overlay.addEventListener('mousedown', (e) => e.preventDefault());
415
428
 
416
- // Preventing the default modal behavior of the overlay on input click
417
- overlay.addEventListener('vaadin-overlay-outside-click', (e) => {
418
- e.preventDefault();
419
- });
420
-
421
429
  // Manual two-way binding for the overlay "opened" property
422
430
  overlay.addEventListener('opened-changed', (e) => {
423
431
  this._overlayOpened = e.detail.value;
@@ -442,9 +450,7 @@ export const ComboBoxMixin = (subclass) =>
442
450
  };
443
451
 
444
452
  // Ensure the scroller is rendered
445
- if (!this.opened) {
446
- overlay.requestContentUpdate();
447
- }
453
+ overlay.requestContentUpdate();
448
454
 
449
455
  const scroller = overlay.querySelector(scrollerTag);
450
456
 
@@ -478,11 +484,6 @@ export const ComboBoxMixin = (subclass) =>
478
484
  }
479
485
  }
480
486
 
481
- /** @protected */
482
- _isOverlayHidden(items, loading) {
483
- return !loading && !(items && items.length);
484
- }
485
-
486
487
  /** @private */
487
488
  _openedOrItemsChanged(opened, items, loading) {
488
489
  // Close the overlay if there are no items to display.
@@ -511,9 +512,14 @@ export const ComboBoxMixin = (subclass) =>
511
512
  this._updateActiveDescendant(index);
512
513
  }
513
514
 
515
+ /** @protected */
516
+ _isInputFocused() {
517
+ return this.inputElement && isElementFocused(this.inputElement);
518
+ }
519
+
514
520
  /** @private */
515
521
  _updateActiveDescendant(index) {
516
- const input = this.inputElement;
522
+ const input = this._nativeInput;
517
523
  if (!input) {
518
524
  return;
519
525
  }
@@ -537,19 +543,19 @@ export const ComboBoxMixin = (subclass) =>
537
543
  this._openedWithFocusRing = this.hasAttribute('focus-ring');
538
544
  // For touch devices, we don't want to popup virtual keyboard
539
545
  // unless input element is explicitly focused by the user.
540
- if (!this.hasAttribute('focused') && !isTouch) {
546
+ if (!this._isInputFocused() && !isTouch) {
541
547
  this.focus();
542
548
  }
543
549
 
544
550
  this.$.overlay.restoreFocusOnClose = true;
545
551
  } else {
546
552
  this._onClosed();
547
- if (this._openedWithFocusRing && this.hasAttribute('focused')) {
553
+ if (this._openedWithFocusRing && this._isInputFocused()) {
548
554
  this.setAttribute('focus-ring', '');
549
555
  }
550
556
  }
551
557
 
552
- const input = this.inputElement;
558
+ const input = this._nativeInput;
553
559
  if (input) {
554
560
  input.setAttribute('aria-expanded', !!opened);
555
561
 
@@ -618,8 +624,6 @@ export const ComboBoxMixin = (subclass) =>
618
624
 
619
625
  /** @private */
620
626
  _onClick(e) {
621
- this._closeOnBlurIsPrevented = true;
622
-
623
627
  const path = e.composedPath();
624
628
 
625
629
  if (this._isClearButton(e)) {
@@ -629,8 +633,6 @@ export const ComboBoxMixin = (subclass) =>
629
633
  } else {
630
634
  this._onHostClick(e);
631
635
  }
632
-
633
- this._closeOnBlurIsPrevented = false;
634
636
  }
635
637
 
636
638
  /**
@@ -646,16 +648,12 @@ export const ComboBoxMixin = (subclass) =>
646
648
  if (e.key === 'Tab') {
647
649
  this.$.overlay.restoreFocusOnClose = false;
648
650
  } else if (e.key === 'ArrowDown') {
649
- this._closeOnBlurIsPrevented = true;
650
651
  this._onArrowDown();
651
- this._closeOnBlurIsPrevented = false;
652
652
 
653
653
  // Prevent caret from moving
654
654
  e.preventDefault();
655
655
  } else if (e.key === 'ArrowUp') {
656
- this._closeOnBlurIsPrevented = true;
657
656
  this._onArrowUp();
658
- this._closeOnBlurIsPrevented = false;
659
657
 
660
658
  // Prevent caret from moving
661
659
  e.preventDefault();
@@ -726,8 +724,7 @@ export const ComboBoxMixin = (subclass) =>
726
724
  // and there's no need to modify the selection range if the input isn't focused anyway.
727
725
  // This affects Safari. When the overlay is open, and then hitting tab, browser should focus
728
726
  // the next focusable element instead of the combo-box itself.
729
- // Checking the focused property here is enough instead of checking the activeElement.
730
- if (this.hasAttribute('focused')) {
727
+ if (this._isInputFocused() && this.inputElement.setSelectionRange) {
731
728
  this.inputElement.setSelectionRange(start, end);
732
729
  }
733
730
  }
@@ -837,7 +834,7 @@ export const ComboBoxMixin = (subclass) =>
837
834
  toggleElement.addEventListener('mousedown', (e) => e.preventDefault());
838
835
  // Unfocus previously focused element if focus is not inside combo box (on touch devices)
839
836
  toggleElement.addEventListener('click', () => {
840
- if (isTouch && !this.hasAttribute('focused')) {
837
+ if (isTouch && !this._isInputFocused()) {
841
838
  document.activeElement.blur();
842
839
  }
843
840
  });
@@ -1256,9 +1253,6 @@ export const ComboBoxMixin = (subclass) =>
1256
1253
  if (this.opened) {
1257
1254
  this._focusedIndex = this.filteredItems.indexOf(e.detail.item);
1258
1255
  this.close();
1259
- } else if (this.selectedItem !== e.detail.item) {
1260
- this.selectedItem = e.detail.item;
1261
- this._detectAndDispatchChange();
1262
1256
  }
1263
1257
  }
1264
1258
 
@@ -1270,6 +1264,12 @@ export const ComboBoxMixin = (subclass) =>
1270
1264
 
1271
1265
  /** @private */
1272
1266
  _onFocusout(event) {
1267
+ // VoiceOver on iOS fires `focusout` event when moving focus to the item in the dropdown.
1268
+ // Do not focus the input in this case, because it would break announcement for the item.
1269
+ if (event.relatedTarget && event.relatedTarget.localName === `${this._tagNamePrefix}-item`) {
1270
+ return;
1271
+ }
1272
+
1273
1273
  // Fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
1274
1274
  if (event.relatedTarget === this.$.overlay) {
1275
1275
  event.composedPath()[0].focus();
@@ -18,6 +18,7 @@ import type { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';
18
18
  import type { PatternMixinClass } from '@vaadin/field-base/src/pattern-mixin.js';
19
19
  import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
20
20
  import type { ThemableMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
21
+ import type { ThemePropertyMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
21
22
  import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-provider-mixin.js';
22
23
  import type { ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
23
24
  import type { ComboBoxDefaultItem } from './vaadin-combo-box-mixin.js';
@@ -221,13 +222,13 @@ declare class ComboBox<TItem = ComboBoxDefaultItem> extends HTMLElement {
221
222
  addEventListener<K extends keyof ComboBoxEventMap<TItem>>(
222
223
  type: K,
223
224
  listener: (this: ComboBox<TItem>, ev: ComboBoxEventMap<TItem>[K]) => void,
224
- options?: boolean | AddEventListenerOptions,
225
+ options?: AddEventListenerOptions | boolean,
225
226
  ): void;
226
227
 
227
228
  removeEventListener<K extends keyof ComboBoxEventMap<TItem>>(
228
229
  type: K,
229
230
  listener: (this: ComboBox<TItem>, ev: ComboBoxEventMap<TItem>[K]) => void,
230
- options?: boolean | EventListenerOptions,
231
+ options?: EventListenerOptions | boolean,
231
232
  ): void;
232
233
  }
233
234
 
@@ -247,6 +248,7 @@ interface ComboBox<TItem = ComboBoxDefaultItem>
247
248
  DelegateStateMixinClass,
248
249
  DelegateFocusMixinClass,
249
250
  ThemableMixinClass,
251
+ ThemePropertyMixinClass,
250
252
  ElementMixinClass,
251
253
  ControllerMixinClass {}
252
254
 
@@ -202,7 +202,6 @@ class ComboBox extends ComboBoxDataProviderMixin(
202
202
 
203
203
  <vaadin-combo-box-overlay
204
204
  id="overlay"
205
- hidden$="[[_isOverlayHidden(filteredItems, loading)]]"
206
205
  opened="[[_overlayOpened]]"
207
206
  loading$="[[loading]]"
208
207
  theme$="[[_theme]]"