@vaadin/combo-box 24.0.0-alpha9 → 24.0.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.
Files changed (29) hide show
  1. package/package.json +13 -13
  2. package/src/vaadin-combo-box-data-provider-mixin.js +4 -6
  3. package/src/vaadin-combo-box-item-mixin.d.ts +65 -0
  4. package/src/vaadin-combo-box-item-mixin.js +127 -0
  5. package/src/vaadin-combo-box-item.d.ts +45 -0
  6. package/src/vaadin-combo-box-item.js +3 -114
  7. package/src/vaadin-combo-box-light.js +25 -25
  8. package/src/vaadin-combo-box-mixin.d.ts +4 -12
  9. package/src/vaadin-combo-box-mixin.js +42 -40
  10. package/src/vaadin-combo-box-overlay-mixin.d.ts +11 -0
  11. package/src/vaadin-combo-box-overlay-mixin.js +60 -0
  12. package/src/vaadin-combo-box-overlay.d.ts +20 -0
  13. package/src/vaadin-combo-box-overlay.js +12 -45
  14. package/src/vaadin-combo-box-scroller-mixin.d.ts +82 -0
  15. package/src/vaadin-combo-box-scroller-mixin.js +361 -0
  16. package/src/vaadin-combo-box-scroller.d.ts +19 -0
  17. package/src/vaadin-combo-box-scroller.js +5 -345
  18. package/src/vaadin-combo-box.d.ts +2 -0
  19. package/src/vaadin-combo-box.js +5 -4
  20. package/theme/lumo/vaadin-combo-box-item-styles.js +2 -0
  21. package/theme/lumo/vaadin-combo-box-light.js +1 -1
  22. package/theme/lumo/{vaadin-combo-box-dropdown-styles.js → vaadin-combo-box-overlay-styles.js} +21 -13
  23. package/theme/lumo/vaadin-combo-box.js +1 -1
  24. package/theme/material/vaadin-combo-box-item-styles.js +2 -0
  25. package/theme/material/vaadin-combo-box-light.js +1 -1
  26. package/theme/material/{vaadin-combo-box-dropdown-styles.js → vaadin-combo-box-overlay-styles.js} +20 -9
  27. package/theme/material/vaadin-combo-box.js +1 -1
  28. package/web-types.json +18 -18
  29. package/web-types.lit.json +6 -6
@@ -270,6 +270,24 @@ export const ComboBoxMixin = (subclass) =>
270
270
  return 'vaadin-combo-box';
271
271
  }
272
272
 
273
+ /**
274
+ * Get a reference to the native `<input>` element.
275
+ * Override to provide a custom input.
276
+ * @protected
277
+ * @return {HTMLInputElement | undefined}
278
+ */
279
+ get _nativeInput() {
280
+ return this.inputElement;
281
+ }
282
+
283
+ /**
284
+ * @return {string}
285
+ * @protected
286
+ */
287
+ get _propertyForValue() {
288
+ return 'value';
289
+ }
290
+
273
291
  /**
274
292
  * @return {string | undefined}
275
293
  * @protected
@@ -288,16 +306,6 @@ export const ComboBoxMixin = (subclass) =>
288
306
  }
289
307
  }
290
308
 
291
- /**
292
- * Get a reference to the native `<input>` element.
293
- * Override to provide a custom input.
294
- * @protected
295
- * @return {HTMLInputElement | undefined}
296
- */
297
- get _nativeInput() {
298
- return this.inputElement;
299
- }
300
-
301
309
  /**
302
310
  * Override method inherited from `InputMixin`
303
311
  * to customize the input element.
@@ -464,7 +472,7 @@ export const ComboBoxMixin = (subclass) =>
464
472
 
465
473
  const scroller = overlay.querySelector(scrollerTag);
466
474
 
467
- scroller.comboBox = host || this;
475
+ scroller.owner = host || this;
468
476
  scroller.getItemLabel = this._getItemLabel.bind(this);
469
477
  scroller.addEventListener('selection-changed', this._boundOverlaySelectedItemChanged);
470
478
 
@@ -591,13 +599,19 @@ export const ComboBoxMixin = (subclass) =>
591
599
  return event.composedPath()[0] === this.clearElement;
592
600
  }
593
601
 
602
+ /** @private */
603
+ __onClearButtonMouseDown(event) {
604
+ event.preventDefault(); // Prevent native focusout event
605
+ this.inputElement.focus();
606
+ }
607
+
594
608
  /**
595
609
  * @param {Event} event
596
610
  * @protected
597
611
  */
598
- _handleClearButtonClick(event) {
612
+ _onClearButtonClick(event) {
599
613
  event.preventDefault();
600
- this._clear();
614
+ this._onClearAction();
601
615
 
602
616
  // De-select dropdown item
603
617
  if (this.opened) {
@@ -633,15 +647,13 @@ export const ComboBoxMixin = (subclass) =>
633
647
  }
634
648
 
635
649
  /** @private */
636
- _onClick(e) {
637
- const path = e.composedPath();
638
-
639
- if (this._isClearButton(e)) {
640
- this._handleClearButtonClick(e);
641
- } else if (path.indexOf(this._toggleElement) > -1) {
642
- this._onToggleButtonClick(e);
650
+ _onClick(event) {
651
+ if (this._isClearButton(event)) {
652
+ this._onClearButtonClick(event);
653
+ } else if (event.composedPath().includes(this._toggleElement)) {
654
+ this._onToggleButtonClick(event);
643
655
  } else {
644
- this._onHostClick(e);
656
+ this._onHostClick(event);
645
657
  }
646
658
  }
647
659
 
@@ -774,7 +786,11 @@ export const ComboBoxMixin = (subclass) =>
774
786
  // Do not commit value when custom values are disallowed and input value is not a valid option
775
787
  // also stop propagation of the event, otherwise the user could submit a form while the input
776
788
  // still contains an invalid value
777
- if (!this.allowCustomValue && this._inputElementValue !== '' && this._focusedIndex < 0) {
789
+ const hasInvalidOption =
790
+ this._focusedIndex < 0 &&
791
+ this._inputElementValue !== '' &&
792
+ this._getItemLabel(this.selectedItem) !== this._inputElementValue;
793
+ if (!this.allowCustomValue && hasInvalidOption) {
778
794
  // Do not submit the surrounding form.
779
795
  e.preventDefault();
780
796
  // Do not trigger global listeners
@@ -815,7 +831,7 @@ export const ComboBoxMixin = (subclass) =>
815
831
  } else if (this.clearButtonVisible && !this.opened && !!this.value) {
816
832
  e.stopPropagation();
817
833
  // The clear button is visible and the overlay is closed, so clear the value.
818
- this._clear();
834
+ this._onClearAction();
819
835
  }
820
836
  } else if (this.opened) {
821
837
  // Auto-open is enabled
@@ -833,7 +849,7 @@ export const ComboBoxMixin = (subclass) =>
833
849
  } else if (this.clearButtonVisible && !!this.value) {
834
850
  e.stopPropagation();
835
851
  // The clear button is visible and the overlay is closed, so clear the value.
836
- this._clear();
852
+ this._onClearAction();
837
853
  }
838
854
  }
839
855
 
@@ -855,7 +871,7 @@ export const ComboBoxMixin = (subclass) =>
855
871
  * Clears the current value.
856
872
  * @protected
857
873
  */
858
- _clear() {
874
+ _onClearAction() {
859
875
  this.selectedItem = null;
860
876
 
861
877
  if (this.allowCustomValue) {
@@ -954,14 +970,6 @@ export const ComboBoxMixin = (subclass) =>
954
970
  this.filter = '';
955
971
  }
956
972
 
957
- /**
958
- * @return {string}
959
- * @protected
960
- */
961
- get _propertyForValue() {
962
- return 'value';
963
- }
964
-
965
973
  /**
966
974
  * Override an event listener from `InputMixin`.
967
975
  * @param {!Event} event
@@ -1266,12 +1274,6 @@ export const ComboBoxMixin = (subclass) =>
1266
1274
  }
1267
1275
  }
1268
1276
 
1269
- /** @private */
1270
- __onClearButtonMouseDown(event) {
1271
- event.preventDefault(); // Prevent native focusout event
1272
- this.inputElement.focus();
1273
- }
1274
-
1275
1277
  /** @private */
1276
1278
  _onFocusout(event) {
1277
1279
  // VoiceOver on iOS fires `focusout` event when moving focus to the item in the dropdown.
@@ -1304,7 +1306,7 @@ export const ComboBoxMixin = (subclass) =>
1304
1306
  }
1305
1307
 
1306
1308
  event.preventDefault();
1307
- this._clear();
1309
+ this._onClearAction();
1308
1310
  }
1309
1311
 
1310
1312
  /**
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2015 - 2023 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
+ import type { PositionMixinClass } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
8
+
9
+ export declare function ComboBoxOverlayMixin<T extends Constructor<HTMLElement>>(
10
+ base: T,
11
+ ): Constructor<PositionMixinClass> & T;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2015 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
7
+
8
+ /**
9
+ * @polymerMixin
10
+ * @mixes PositionMixin
11
+ */
12
+ export const ComboBoxOverlayMixin = (superClass) =>
13
+ class ComboBoxOverlayMixin extends PositionMixin(superClass) {
14
+ static get observers() {
15
+ return ['_setOverlayWidth(positionTarget, opened)'];
16
+ }
17
+
18
+ /** @protected */
19
+ connectedCallback() {
20
+ super.connectedCallback();
21
+
22
+ const comboBox = this._comboBox;
23
+
24
+ const hostDir = comboBox && comboBox.getAttribute('dir');
25
+ if (hostDir) {
26
+ this.setAttribute('dir', hostDir);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Override method inherited from `Overlay`
32
+ * to not close on position target click.
33
+ *
34
+ * @param {Event} event
35
+ * @return {boolean}
36
+ * @protected
37
+ */
38
+ _shouldCloseOnOutsideClick(event) {
39
+ const eventPath = event.composedPath();
40
+ return !eventPath.includes(this.positionTarget) && !eventPath.includes(this);
41
+ }
42
+
43
+ /** @private */
44
+ _setOverlayWidth(positionTarget, opened) {
45
+ if (positionTarget && opened) {
46
+ const propPrefix = this.localName;
47
+ this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);
48
+
49
+ const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);
50
+
51
+ if (customWidth === '') {
52
+ this.style.removeProperty(`--${propPrefix}-width`);
53
+ } else {
54
+ this.style.setProperty(`--${propPrefix}-width`, customWidth);
55
+ }
56
+
57
+ this._updatePosition();
58
+ }
59
+ }
60
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2015 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
7
+ import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';
8
+
9
+ /**
10
+ * An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
11
+ */
12
+ declare class ComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {}
13
+
14
+ declare global {
15
+ interface HTMLElementTagNameMap {
16
+ 'vaadin-combo-box-overlay': ComboBoxOverlay;
17
+ }
18
+ }
19
+
20
+ export { ComboBoxOverlay };
@@ -4,8 +4,8 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
7
- import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
8
7
  import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
8
+ import { ComboBoxOverlayMixin } from './vaadin-combo-box-overlay-mixin.js';
9
9
 
10
10
  registerStyles(
11
11
  'vaadin-combo-box-overlay',
@@ -29,9 +29,10 @@ let memoizedTemplate;
29
29
  * An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
30
30
  *
31
31
  * @extends Overlay
32
+ * @mixes ComboBoxOverlayMixin
32
33
  * @private
33
34
  */
34
- export class ComboBoxOverlay extends PositionMixin(Overlay) {
35
+ export class ComboBoxOverlay extends ComboBoxOverlayMixin(Overlay) {
35
36
  static get is() {
36
37
  return 'vaadin-combo-box-overlay';
37
38
  }
@@ -39,57 +40,23 @@ export class ComboBoxOverlay extends PositionMixin(Overlay) {
39
40
  static get template() {
40
41
  if (!memoizedTemplate) {
41
42
  memoizedTemplate = super.template.cloneNode(true);
42
- memoizedTemplate.content.querySelector('[part~="overlay"]').removeAttribute('tabindex');
43
- }
44
43
 
45
- return memoizedTemplate;
46
- }
44
+ const overlay = memoizedTemplate.content.querySelector('[part~="overlay"]');
45
+ overlay.removeAttribute('tabindex');
47
46
 
48
- static get observers() {
49
- return ['_setOverlayWidth(positionTarget, opened)'];
50
- }
47
+ const loader = document.createElement('div');
48
+ loader.setAttribute('part', 'loader');
51
49
 
52
- connectedCallback() {
53
- super.connectedCallback();
54
-
55
- const comboBox = this._comboBox;
56
-
57
- const hostDir = comboBox && comboBox.getAttribute('dir');
58
- if (hostDir) {
59
- this.setAttribute('dir', hostDir);
50
+ overlay.insertBefore(loader, overlay.firstElementChild);
60
51
  }
61
- }
62
52
 
63
- ready() {
64
- super.ready();
65
- const loader = document.createElement('div');
66
- loader.setAttribute('part', 'loader');
67
- const content = this.shadowRoot.querySelector('[part~="content"]');
68
- content.parentNode.insertBefore(loader, content);
69
- }
70
-
71
- _outsideClickListener(event) {
72
- const eventPath = event.composedPath();
73
- if (!eventPath.includes(this.positionTarget) && !eventPath.includes(this)) {
74
- this.close();
75
- }
53
+ return memoizedTemplate;
76
54
  }
77
55
 
78
- _setOverlayWidth(positionTarget, opened) {
79
- if (positionTarget && opened) {
80
- const propPrefix = this.localName;
81
- this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);
56
+ constructor() {
57
+ super();
82
58
 
83
- const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);
84
-
85
- if (customWidth === '') {
86
- this.style.removeProperty(`--${propPrefix}-width`);
87
- } else {
88
- this.style.setProperty(`--${propPrefix}-width`, customWidth);
89
- }
90
-
91
- this._updatePosition();
92
- }
59
+ this.requiredVerticalSpace = 200;
93
60
  }
94
61
  }
95
62
 
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2015 - 2023 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
+ import type { ComboBoxItemRenderer } from './vaadin-combo-box-item-mixin.js';
8
+
9
+ export declare function ComboBoxScrollerMixin<TItem, TOwner, T extends Constructor<HTMLElement>>(
10
+ base: T,
11
+ ): Constructor<ComboBoxScrollerMixinClass<TItem, TOwner>> & T;
12
+
13
+ export declare class ComboBoxScrollerMixinClass<TItem, TOwner> {
14
+ /**
15
+ * Index of an item that has focus outline and is scrolled into view.
16
+ * The actual focus still remains in the input field.
17
+ */
18
+ focusedIndex: number;
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
+ /**
26
+ * A full set of items to filter the visible options from.
27
+ * Set to an empty array when combo-box is not opened.
28
+ */
29
+ items: TItem[];
30
+
31
+ /**
32
+ * Set to true while combo-box fetches new page from the data provider.
33
+ */
34
+ loading: boolean;
35
+
36
+ /**
37
+ * Whether the combo-box is currently opened or not. If set to false,
38
+ * calling `scrollIntoView` does not have any effect.
39
+ */
40
+ opened: boolean;
41
+
42
+ /**
43
+ * Reference to the owner (combo-box owner), used by the item elements.
44
+ */
45
+ owner: TOwner;
46
+
47
+ /**
48
+ * Set true to prevent the overlay from opening automatically.
49
+ * @attr {boolean} auto-open-disabled
50
+ */
51
+ renderer: ComboBoxItemRenderer<TItem, TOwner> | null | undefined;
52
+
53
+ /**
54
+ * The selected item from the `items` array.
55
+ */
56
+ selectedItem: TItem;
57
+
58
+ /**
59
+ * Used to propagate the `theme` attribute from the host element.
60
+ */
61
+ theme: string;
62
+
63
+ /**
64
+ * Function used to set a label for every combo-box item.
65
+ */
66
+ getItemLabel: (item: TItem) => string;
67
+
68
+ /**
69
+ * Requests an update for the virtualizer to re-render items.
70
+ */
71
+ requestContentUpdate(): void;
72
+
73
+ /**
74
+ * Scrolls an item at given index into view and adjusts `scrollTop`
75
+ * so that the element gets fully visible on Arrow Down key press.
76
+ */
77
+ scrollIntoView(index: number): void;
78
+
79
+ protected _isItemSelected(item: TItem, selectedItem: TItem, itemIdPath: string | null | undefined): void;
80
+
81
+ protected _updateElement(el: HTMLElement, index: number): void;
82
+ }