@vaadin/multi-select-combo-box 25.3.0-dev.3a3c2d7d2a → 25.3.0

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.
@@ -79,15 +79,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
79
79
  sync: true,
80
80
  },
81
81
 
82
- /**
83
- * Path for the id of the item, used to detect whether the item is selected.
84
- * @attr {string} item-id-path
85
- */
86
- itemIdPath: {
87
- type: String,
88
- sync: true,
89
- },
90
-
91
82
  /**
92
83
  * When true, filter string isn't cleared after selecting an item.
93
84
  */
@@ -216,9 +207,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
216
207
  static get observers() {
217
208
  return [
218
209
  '_selectedItemsChanged(selectedItems)',
219
- '__openedOrItemsChanged(opened, _dropdownItems, loading, __keepOverlayOpened)',
220
210
  '__updateOverflowChip(_overflow, _overflowItems, disabled, readonly)',
221
- '__updateScroller(opened, _dropdownItems, _focusedIndex, _theme)',
222
211
  '__updateTopGroup(selectedItemsOnTop, selectedItems, opened)',
223
212
  ];
224
213
  }
@@ -342,7 +331,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
342
331
  updated(props) {
343
332
  super.updated(props);
344
333
 
345
- ['loading', 'itemIdPath', 'itemClassNameGenerator', 'renderer'].forEach((prop) => {
334
+ ['loading', 'itemClassNameGenerator', 'renderer'].forEach((prop) => {
346
335
  if (props.has(prop)) {
347
336
  this._scroller[prop] = this[prop];
348
337
  }
@@ -433,21 +422,18 @@ export const MultiSelectComboBoxMixin = (superClass) =>
433
422
  }
434
423
 
435
424
  /**
436
- * Requests an update for the content of items.
437
- * While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
438
- *
439
- * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
425
+ * Override method from `ComboBoxBaseMixin` to only open the dropdown
426
+ * when clicking on the label or the input field.
427
+ * @param {Event} event
428
+ * @protected
429
+ * @override
440
430
  */
441
- requestContentUpdate() {
442
- if (!this._scroller) {
443
- return;
444
- }
431
+ _onHostClick(event) {
432
+ const path = event.composedPath();
445
433
 
446
- this._scroller.requestContentUpdate();
447
-
448
- this._getItemElements().forEach((item) => {
449
- item.requestContentUpdate();
450
- });
434
+ if (path.includes(this._labelNode) || path.includes(this._inputField)) {
435
+ super._onHostClick(event);
436
+ }
451
437
  }
452
438
 
453
439
  /**
@@ -474,37 +460,15 @@ export const MultiSelectComboBoxMixin = (superClass) =>
474
460
  }
475
461
  }
476
462
 
477
- /** @private */
478
- __updateScroller(opened, items, focusedIndex, theme) {
479
- if (opened) {
480
- this._scroller.style.maxHeight =
481
- getComputedStyle(this).getPropertyValue(`--${this._tagNamePrefix}-overlay-max-height`) || '65vh';
482
- }
483
-
484
- this._scroller.setProperties({
485
- items: opened ? items : [],
486
- opened,
487
- focusedIndex,
488
- theme,
489
- });
490
- }
491
-
492
- /** @private */
493
- __openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
494
- // Close the overlay if there are no items to display.
495
- // See https://github.com/vaadin/vaadin-combo-box/pull/964
496
- this._overlayOpened = opened && (keepOverlayOpened || loading || !!items?.length);
497
- }
498
-
499
463
  /**
464
+ * Override method from `ComboBoxBaseMixin` to revert the input to the
465
+ * filter, as this component does not use `value` for the input text.
500
466
  * @protected
467
+ * @override
501
468
  */
502
- _closeOrCommit() {
503
- if (!this.opened) {
504
- this._commitValue();
505
- } else {
506
- this.close();
507
- }
469
+ _revertInputValue() {
470
+ this._inputElementValue = this.filter;
471
+ this._clearSelectionRange();
508
472
  }
509
473
 
510
474
  /**
@@ -535,6 +499,16 @@ export const MultiSelectComboBoxMixin = (superClass) =>
535
499
  __commitUserInput() {
536
500
  if (this._focusedIndex > -1) {
537
501
  const focusedItem = this._dropdownItems[this._focusedIndex];
502
+ // Do not unselect an already selected item when it was focused by
503
+ // filtering, in which case the input value still equals the filter.
504
+ if (
505
+ this._lastFilter &&
506
+ this._lastFilter === this._inputElementValue &&
507
+ this._findIndex(focusedItem, this.selectedItems) !== -1
508
+ ) {
509
+ this.__clearInternalValue();
510
+ return;
511
+ }
538
512
  this.__selectItem(focusedItem);
539
513
  } else if (this._inputElementValue) {
540
514
  // Detect if input value doesn't match an existing item
@@ -572,7 +546,11 @@ export const MultiSelectComboBoxMixin = (superClass) =>
572
546
  * @protected
573
547
  */
574
548
  _setFocused(focused) {
575
- if (!focused) {
549
+ // Do not validate, commit or close on internal blur triggered
550
+ // on overlay touch action needed to hide the virtual keyboard.
551
+ const blurred = !focused && !this._closeOnBlurIsPrevented;
552
+
553
+ if (blurred) {
576
554
  this._ignoreCommitValue = true;
577
555
  }
578
556
 
@@ -580,12 +558,12 @@ export const MultiSelectComboBoxMixin = (superClass) =>
580
558
 
581
559
  // Do not validate when focusout is caused by document
582
560
  // losing focus, which happens on browser tab switch.
583
- if (!focused && document.hasFocus()) {
561
+ if (blurred && document.hasFocus()) {
584
562
  this._focusedChipIndex = -1;
585
563
  this._requestValidation();
586
564
  }
587
565
 
588
- if (!focused && this.readonly && !this._closeOnBlurIsPrevented) {
566
+ if (blurred && this.readonly) {
589
567
  this.close();
590
568
  }
591
569
  }
@@ -689,67 +667,34 @@ export const MultiSelectComboBoxMixin = (superClass) =>
689
667
  }
690
668
 
691
669
  /**
692
- * Override combo-box method to group selected
693
- * items at the top of the overlay.
670
+ * Override method from `ComboBoxItemsMixin` to show only selected
671
+ * items when read-only, and to group selected items at the top
672
+ * of the overlay when `selectedItemsOnTop` is set.
694
673
  *
695
674
  * @protected
696
675
  * @override
697
676
  */
698
677
  _setDropdownItems(items) {
678
+ super._setDropdownItems(this.__generateDropdownItems(items));
679
+ }
680
+
681
+ /** @private */
682
+ __generateDropdownItems(items) {
699
683
  if (this.readonly) {
700
- this.__setDropdownItems(this.selectedItems);
701
- return;
684
+ return this.selectedItems;
702
685
  }
703
686
 
704
687
  if (this.filter || !this.selectedItemsOnTop) {
705
- this.__setDropdownItems(items);
706
- return;
688
+ return items;
707
689
  }
708
690
 
709
691
  if (items?.length && this._topGroup?.length) {
710
692
  // Filter out items included to the top group.
711
- const filteredItems = items.filter((item) => this._findIndex(item, this._topGroup, this.itemIdPath) === -1);
712
-
713
- this.__setDropdownItems(this._topGroup.concat(filteredItems));
714
- return;
693
+ const filteredItems = items.filter((item) => this._findIndex(item, this._topGroup) === -1);
694
+ return this._topGroup.concat(filteredItems);
715
695
  }
716
696
 
717
- this.__setDropdownItems(items);
718
- }
719
-
720
- /** @private */
721
- __setDropdownItems(newItems) {
722
- const oldItems = this._dropdownItems;
723
- this._dropdownItems = newItems;
724
-
725
- // Store the currently focused item if any. The focused index preserves
726
- // in the case when more filtered items are loading but it is reset
727
- // when the user types in a filter query.
728
- const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
729
-
730
- // When both the previously-focused entry and the new entry at the
731
- // same index are placeholders (e.g. the Flow connector mid-scroll
732
- // re-pushing `__setDropdownItems`), preserve `_focusedIndex` until
733
- // a follow-up call lands a real item at that position.
734
- if (
735
- oldItems &&
736
- oldItems[this._focusedIndex] instanceof ComboBoxPlaceholder &&
737
- newItems[this._focusedIndex] instanceof ComboBoxPlaceholder
738
- ) {
739
- return;
740
- }
741
-
742
- // Try to first set focus on the item that had been focused before `newItems` were updated
743
- // if it is still present in the `newItems` array. Otherwise, set the focused index
744
- // depending on the selected item or the filter query.
745
- const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
746
- if (focusedItemIndex > -1) {
747
- this._focusedIndex = focusedItemIndex;
748
- } else {
749
- // When the user filled in something that is different from the current value = filtering is enabled,
750
- // set the focused index to the item that matches the filter query.
751
- this._focusedIndex = this.__getItemIndexByLabel(newItems, this.filter);
752
- }
697
+ return items;
753
698
  }
754
699
 
755
700
  /** @private */
@@ -758,17 +703,8 @@ export const MultiSelectComboBoxMixin = (superClass) =>
758
703
  }
759
704
 
760
705
  /** @private */
761
- _findIndex(item, selectedItems, itemIdPath) {
762
- if (itemIdPath && item) {
763
- for (let index = 0; index < selectedItems.length; index++) {
764
- if (selectedItems[index] && selectedItems[index][itemIdPath] === item[itemIdPath]) {
765
- return index;
766
- }
767
- }
768
- return -1;
769
- }
770
-
771
- return selectedItems.indexOf(item);
706
+ _findIndex(item, items) {
707
+ return items.findIndex((other) => this._isSameItem(item, other));
772
708
  }
773
709
 
774
710
  /**
@@ -801,7 +737,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
801
737
  /** @private */
802
738
  __removeItem(item) {
803
739
  const itemsCopy = [...this.selectedItems];
804
- itemsCopy.splice(itemsCopy.indexOf(item), 1);
740
+ itemsCopy.splice(this._findIndex(item, itemsCopy), 1);
805
741
  this.__updateSelection(itemsCopy);
806
742
  const itemLabel = this._getItemLabel(item);
807
743
  this.__announceItem(itemLabel, false, itemsCopy.length);
@@ -811,7 +747,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
811
747
  __selectItem(item) {
812
748
  const itemsCopy = [...this.selectedItems];
813
749
 
814
- const index = this._findIndex(item, itemsCopy, this.itemIdPath);
750
+ const index = this._findIndex(item, itemsCopy);
815
751
  const itemLabel = this._getItemLabel(item);
816
752
 
817
753
  let isSelected = false;
@@ -865,7 +801,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
865
801
  return (
866
802
  this._topGroup &&
867
803
  this._topGroup.some((item) => {
868
- const selectedItem = this.selectedItems[this._findIndex(item, this.selectedItems, this.itemIdPath)];
804
+ const selectedItem = this.selectedItems[this._findIndex(item, this.selectedItems)];
869
805
  return selectedItem && item !== selectedItem;
870
806
  })
871
807
  );
@@ -1060,19 +996,16 @@ export const MultiSelectComboBoxMixin = (superClass) =>
1060
996
  }
1061
997
 
1062
998
  /**
1063
- * Override method from `ComboBoxBaseMixin` to deselect
1064
- * dropdown item by requesting content update on clear.
999
+ * Override method from `ComboBoxItemsMixin` to stop
1000
+ * propagation of the clear button click event.
1065
1001
  * @param {Event} event
1066
1002
  * @protected
1003
+ * @override
1067
1004
  */
1068
1005
  _onClearButtonClick(event) {
1069
1006
  event.stopPropagation();
1070
1007
 
1071
1008
  super._onClearButtonClick(event);
1072
-
1073
- if (this.opened) {
1074
- this.requestContentUpdate();
1075
- }
1076
1009
  }
1077
1010
 
1078
1011
  /**
@@ -1314,6 +1247,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
1314
1247
  _overlaySelectedItemChanged(event) {
1315
1248
  event.stopPropagation();
1316
1249
 
1250
+ if (this.hasAttribute('closing')) {
1251
+ return;
1252
+ }
1253
+
1317
1254
  // Do not un-select on click when readonly
1318
1255
  if (this.readonly) {
1319
1256
  return;
@@ -17,6 +17,7 @@ import { multiSelectComboBoxOverlayStyles } from './styles/vaadin-multi-select-c
17
17
  /**
18
18
  * An element used internally by `<vaadin-multi-select-combo-box>`. Not intended to be used separately.
19
19
  *
20
+ * @attr {string} theme - The theme variants to apply to the component.
20
21
  * @customElement vaadin-multi-select-combo-box-overlay
21
22
  * @extends HTMLElement
22
23
  * @private
@@ -17,7 +17,7 @@ import { multiSelectComboBoxScrollerStyles } from './styles/vaadin-multi-select-
17
17
  * @extends HTMLElement
18
18
  * @private
19
19
  */
20
- export class MultiSelectComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElement)) {
20
+ class MultiSelectComboBoxScroller extends ComboBoxScrollerMixin(PolylitMixin(LitElement)) {
21
21
  static get is() {
22
22
  return 'vaadin-multi-select-combo-box-scroller';
23
23
  }
@@ -43,10 +43,12 @@ export class MultiSelectComboBoxScroller extends ComboBoxScrollerMixin(PolylitMi
43
43
  }
44
44
 
45
45
  /**
46
+ * Override method from `ComboBoxScrollerMixin` to check
47
+ * the item against the selected items of the owner.
46
48
  * @protected
47
49
  * @override
48
50
  */
49
- _isItemSelected(item, _selectedItem, itemIdPath) {
51
+ _isItemSelected(item) {
50
52
  if (item instanceof ComboBoxPlaceholder) {
51
53
  return false;
52
54
  }
@@ -55,7 +57,7 @@ export class MultiSelectComboBoxScroller extends ComboBoxScrollerMixin(PolylitMi
55
57
  return false;
56
58
  }
57
59
 
58
- return this.owner._findIndex(item, this.owner.selectedItems, itemIdPath) > -1;
60
+ return this.owner.selectedItems.some((selectedItem) => this.owner._isSameItem(item, selectedItem));
59
61
  }
60
62
 
61
63
  /**
@@ -72,3 +74,5 @@ export class MultiSelectComboBoxScroller extends ComboBoxScrollerMixin(PolylitMi
72
74
  }
73
75
 
74
76
  defineCustomElement(MultiSelectComboBoxScroller);
77
+
78
+ export { MultiSelectComboBoxScroller };
@@ -133,12 +133,59 @@ export interface MultiSelectComboBoxEventMap<TItem> extends HTMLElementEventMap
133
133
  *
134
134
  * The following custom CSS properties are available for styling:
135
135
  *
136
- * Custom property | Description | Default
137
- * -----------------------------------------------------|----------------------------|--------
138
- * `--vaadin-field-default-width` | Default width of the field | `12em`
139
- * `--vaadin-multi-select-combo-box-overlay-width` | Width of the overlay | `auto`
140
- * `--vaadin-multi-select-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
141
- * `--vaadin-multi-select-combo-box-input-min-width` | Min width of the input | `4em`
136
+ * Custom CSS property |
137
+ * :-------------------------------------------------------|
138
+ * | `--vaadin-chip-background` |
139
+ * | `--vaadin-chip-border-color` |
140
+ * | `--vaadin-chip-border-radius` |
141
+ * | `--vaadin-chip-border-width` |
142
+ * | `--vaadin-chip-font-size` |
143
+ * | `--vaadin-chip-font-weight` |
144
+ * | `--vaadin-chip-gap` |
145
+ * | `--vaadin-chip-height` |
146
+ * | `--vaadin-chip-padding` |
147
+ * | `--vaadin-chip-remove-button-text-color` |
148
+ * | `--vaadin-chip-text-color` |
149
+ * | `--vaadin-field-default-width` |
150
+ * | `--vaadin-input-field-background` |
151
+ * | `--vaadin-input-field-border-color` |
152
+ * | `--vaadin-input-field-border-radius` |
153
+ * | `--vaadin-input-field-border-width` |
154
+ * | `--vaadin-input-field-bottom-end-radius` |
155
+ * | `--vaadin-input-field-bottom-start-radius` |
156
+ * | `--vaadin-input-field-button-text-color` |
157
+ * | `--vaadin-input-field-container-gap` |
158
+ * | `--vaadin-input-field-disabled-background` |
159
+ * | `--vaadin-input-field-disabled-text-color` |
160
+ * | `--vaadin-input-field-error-color` |
161
+ * | `--vaadin-input-field-error-font-size` |
162
+ * | `--vaadin-input-field-error-font-weight` |
163
+ * | `--vaadin-input-field-error-line-height` |
164
+ * | `--vaadin-input-field-gap` |
165
+ * | `--vaadin-input-field-helper-color` |
166
+ * | `--vaadin-input-field-helper-font-size` |
167
+ * | `--vaadin-input-field-helper-font-weight` |
168
+ * | `--vaadin-input-field-helper-line-height` |
169
+ * | `--vaadin-input-field-label-color` |
170
+ * | `--vaadin-input-field-label-font-size` |
171
+ * | `--vaadin-input-field-label-font-weight` |
172
+ * | `--vaadin-input-field-label-line-height` |
173
+ * | `--vaadin-input-field-padding` |
174
+ * | `--vaadin-input-field-placeholder-color` |
175
+ * | `--vaadin-input-field-required-indicator` |
176
+ * | `--vaadin-input-field-required-indicator-color` |
177
+ * | `--vaadin-input-field-top-end-radius` |
178
+ * | `--vaadin-input-field-top-start-radius` |
179
+ * | `--vaadin-input-field-value-color` |
180
+ * | `--vaadin-input-field-value-font-size` |
181
+ * | `--vaadin-input-field-value-font-weight` |
182
+ * | `--vaadin-input-field-value-line-height` |
183
+ * | `--vaadin-item-overlay-padding` |
184
+ * | `--vaadin-multi-select-combo-box-chip-min-width` |
185
+ * | `--vaadin-multi-select-combo-box-chips-gap` |
186
+ * | `--vaadin-multi-select-combo-box-input-min-width` |
187
+ * | `--vaadin-multi-select-combo-box-overlay-max-height` |
188
+ * | `--vaadin-multi-select-combo-box-overlay-width` |
142
189
  *
143
190
  * ### Internal components
144
191
  *
@@ -71,12 +71,59 @@ import { MultiSelectComboBoxMixin } from './vaadin-multi-select-combo-box-mixin.
71
71
  *
72
72
  * The following custom CSS properties are available for styling:
73
73
  *
74
- * Custom property | Description | Default
75
- * -----------------------------------------------------|----------------------------|--------
76
- * `--vaadin-field-default-width` | Default width of the field | `12em`
77
- * `--vaadin-multi-select-combo-box-overlay-width` | Width of the overlay | `auto`
78
- * `--vaadin-multi-select-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
79
- * `--vaadin-multi-select-combo-box-input-min-width` | Min width of the input | `4em`
74
+ * Custom CSS property |
75
+ * :-------------------------------------------------------|
76
+ * | `--vaadin-chip-background` |
77
+ * | `--vaadin-chip-border-color` |
78
+ * | `--vaadin-chip-border-radius` |
79
+ * | `--vaadin-chip-border-width` |
80
+ * | `--vaadin-chip-font-size` |
81
+ * | `--vaadin-chip-font-weight` |
82
+ * | `--vaadin-chip-gap` |
83
+ * | `--vaadin-chip-height` |
84
+ * | `--vaadin-chip-padding` |
85
+ * | `--vaadin-chip-remove-button-text-color` |
86
+ * | `--vaadin-chip-text-color` |
87
+ * | `--vaadin-field-default-width` |
88
+ * | `--vaadin-input-field-background` |
89
+ * | `--vaadin-input-field-border-color` |
90
+ * | `--vaadin-input-field-border-radius` |
91
+ * | `--vaadin-input-field-border-width` |
92
+ * | `--vaadin-input-field-bottom-end-radius` |
93
+ * | `--vaadin-input-field-bottom-start-radius` |
94
+ * | `--vaadin-input-field-button-text-color` |
95
+ * | `--vaadin-input-field-container-gap` |
96
+ * | `--vaadin-input-field-disabled-background` |
97
+ * | `--vaadin-input-field-disabled-text-color` |
98
+ * | `--vaadin-input-field-error-color` |
99
+ * | `--vaadin-input-field-error-font-size` |
100
+ * | `--vaadin-input-field-error-font-weight` |
101
+ * | `--vaadin-input-field-error-line-height` |
102
+ * | `--vaadin-input-field-gap` |
103
+ * | `--vaadin-input-field-helper-color` |
104
+ * | `--vaadin-input-field-helper-font-size` |
105
+ * | `--vaadin-input-field-helper-font-weight` |
106
+ * | `--vaadin-input-field-helper-line-height` |
107
+ * | `--vaadin-input-field-label-color` |
108
+ * | `--vaadin-input-field-label-font-size` |
109
+ * | `--vaadin-input-field-label-font-weight` |
110
+ * | `--vaadin-input-field-label-line-height` |
111
+ * | `--vaadin-input-field-padding` |
112
+ * | `--vaadin-input-field-placeholder-color` |
113
+ * | `--vaadin-input-field-required-indicator` |
114
+ * | `--vaadin-input-field-required-indicator-color` |
115
+ * | `--vaadin-input-field-top-end-radius` |
116
+ * | `--vaadin-input-field-top-start-radius` |
117
+ * | `--vaadin-input-field-value-color` |
118
+ * | `--vaadin-input-field-value-font-size` |
119
+ * | `--vaadin-input-field-value-font-weight` |
120
+ * | `--vaadin-input-field-value-line-height` |
121
+ * | `--vaadin-item-overlay-padding` |
122
+ * | `--vaadin-multi-select-combo-box-chip-min-width` |
123
+ * | `--vaadin-multi-select-combo-box-chips-gap` |
124
+ * | `--vaadin-multi-select-combo-box-input-min-width` |
125
+ * | `--vaadin-multi-select-combo-box-overlay-max-height` |
126
+ * | `--vaadin-multi-select-combo-box-overlay-width` |
80
127
  *
81
128
  * ### Internal components
82
129
  *
@@ -96,6 +143,7 @@ import { MultiSelectComboBoxMixin } from './vaadin-multi-select-combo-box-mixin.
96
143
  * @fires {CustomEvent} selected-items-changed - Fired when the `selectedItems` property changes.
97
144
  * @fires {CustomEvent} validated - Fired whenever the field is validated.
98
145
  *
146
+ * @attr {string} theme - The theme variants to apply to the component.
99
147
  * @customElement vaadin-multi-select-combo-box
100
148
  * @extends HTMLElement
101
149
  */