@vaadin/combo-box 23.2.0-dev.8a7678b70 → 23.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.
- package/package.json +20 -14
- package/src/lit/renderer-directives.d.ts +3 -3
- package/src/vaadin-combo-box-data-provider-mixin.d.ts +3 -3
- package/src/vaadin-combo-box-data-provider-mixin.js +29 -31
- package/src/vaadin-combo-box-light.d.ts +21 -10
- package/src/vaadin-combo-box-light.js +43 -8
- package/src/vaadin-combo-box-mixin.d.ts +8 -24
- package/src/vaadin-combo-box-mixin.js +77 -80
- package/src/vaadin-combo-box.d.ts +31 -20
- package/src/vaadin-combo-box.js +2 -1
- package/theme/lumo/vaadin-combo-box-dropdown-styles.js +4 -33
- package/theme/material/vaadin-combo-box-dropdown-styles.js +3 -89
- package/web-types.json +1054 -0
- package/web-types.lit.json +496 -0
|
@@ -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';
|
|
@@ -236,7 +237,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
236
237
|
|
|
237
238
|
static get observers() {
|
|
238
239
|
return [
|
|
239
|
-
'_filterChanged(filter, itemValuePath, itemLabelPath)',
|
|
240
240
|
'_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)',
|
|
241
241
|
'_openedOrItemsChanged(opened, filteredItems, loading)',
|
|
242
242
|
'_updateScroller(_scroller, filteredItems, opened, loading, selectedItem, itemIdPath, _focusedIndex, renderer, theme)',
|
|
@@ -280,14 +280,26 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
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
|
+
|
|
283
293
|
/**
|
|
284
294
|
* Override method inherited from `InputMixin`
|
|
285
295
|
* to customize the input element.
|
|
286
296
|
* @protected
|
|
287
297
|
* @override
|
|
288
298
|
*/
|
|
289
|
-
_inputElementChanged(
|
|
290
|
-
super._inputElementChanged(
|
|
299
|
+
_inputElementChanged(inputElement) {
|
|
300
|
+
super._inputElementChanged(inputElement);
|
|
301
|
+
|
|
302
|
+
const input = this._nativeInput;
|
|
291
303
|
|
|
292
304
|
if (input) {
|
|
293
305
|
input.autocomplete = 'off';
|
|
@@ -382,6 +394,25 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
382
394
|
this.opened = false;
|
|
383
395
|
}
|
|
384
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Override Polymer lifecycle callback to handle `filter` property change after
|
|
399
|
+
* the observer for `opened` property is triggered. This is needed when opening
|
|
400
|
+
* combo-box on user input to ensure the focused index is set correctly.
|
|
401
|
+
*
|
|
402
|
+
* @param {!Object} currentProps Current accessor values
|
|
403
|
+
* @param {?Object} changedProps Properties changed since the last call
|
|
404
|
+
* @param {?Object} oldProps Previous values for each changed property
|
|
405
|
+
* @protected
|
|
406
|
+
* @override
|
|
407
|
+
*/
|
|
408
|
+
_propertiesChanged(currentProps, changedProps, oldProps) {
|
|
409
|
+
super._propertiesChanged(currentProps, changedProps, oldProps);
|
|
410
|
+
|
|
411
|
+
if (changedProps.filter !== undefined) {
|
|
412
|
+
this._filterChanged(changedProps.filter);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
385
416
|
/** @private */
|
|
386
417
|
_initOverlay() {
|
|
387
418
|
const overlay = this.$.overlay;
|
|
@@ -395,11 +426,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
395
426
|
// Prevent blurring the input when clicking inside the overlay
|
|
396
427
|
overlay.addEventListener('mousedown', (e) => e.preventDefault());
|
|
397
428
|
|
|
398
|
-
// Preventing the default modal behavior of the overlay on input click
|
|
399
|
-
overlay.addEventListener('vaadin-overlay-outside-click', (e) => {
|
|
400
|
-
e.preventDefault();
|
|
401
|
-
});
|
|
402
|
-
|
|
403
429
|
// Manual two-way binding for the overlay "opened" property
|
|
404
430
|
overlay.addEventListener('opened-changed', (e) => {
|
|
405
431
|
this._overlayOpened = e.detail.value;
|
|
@@ -424,9 +450,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
424
450
|
};
|
|
425
451
|
|
|
426
452
|
// Ensure the scroller is rendered
|
|
427
|
-
|
|
428
|
-
overlay.requestContentUpdate();
|
|
429
|
-
}
|
|
453
|
+
overlay.requestContentUpdate();
|
|
430
454
|
|
|
431
455
|
const scroller = overlay.querySelector(scrollerTag);
|
|
432
456
|
|
|
@@ -460,11 +484,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
460
484
|
}
|
|
461
485
|
}
|
|
462
486
|
|
|
463
|
-
/** @protected */
|
|
464
|
-
_isOverlayHidden(items, loading) {
|
|
465
|
-
return !loading && !(items && items.length);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
487
|
/** @private */
|
|
469
488
|
_openedOrItemsChanged(opened, items, loading) {
|
|
470
489
|
// Close the overlay if there are no items to display.
|
|
@@ -493,9 +512,14 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
493
512
|
this._updateActiveDescendant(index);
|
|
494
513
|
}
|
|
495
514
|
|
|
515
|
+
/** @protected */
|
|
516
|
+
_isInputFocused() {
|
|
517
|
+
return this.inputElement && isElementFocused(this.inputElement);
|
|
518
|
+
}
|
|
519
|
+
|
|
496
520
|
/** @private */
|
|
497
521
|
_updateActiveDescendant(index) {
|
|
498
|
-
const input = this.
|
|
522
|
+
const input = this._nativeInput;
|
|
499
523
|
if (!input) {
|
|
500
524
|
return;
|
|
501
525
|
}
|
|
@@ -519,19 +543,19 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
519
543
|
this._openedWithFocusRing = this.hasAttribute('focus-ring');
|
|
520
544
|
// For touch devices, we don't want to popup virtual keyboard
|
|
521
545
|
// unless input element is explicitly focused by the user.
|
|
522
|
-
if (!this.
|
|
546
|
+
if (!this._isInputFocused() && !isTouch) {
|
|
523
547
|
this.focus();
|
|
524
548
|
}
|
|
525
549
|
|
|
526
550
|
this.$.overlay.restoreFocusOnClose = true;
|
|
527
551
|
} else {
|
|
528
552
|
this._onClosed();
|
|
529
|
-
if (this._openedWithFocusRing && this.
|
|
553
|
+
if (this._openedWithFocusRing && this._isInputFocused()) {
|
|
530
554
|
this.setAttribute('focus-ring', '');
|
|
531
555
|
}
|
|
532
556
|
}
|
|
533
557
|
|
|
534
|
-
const input = this.
|
|
558
|
+
const input = this._nativeInput;
|
|
535
559
|
if (input) {
|
|
536
560
|
input.setAttribute('aria-expanded', !!opened);
|
|
537
561
|
|
|
@@ -600,8 +624,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
600
624
|
|
|
601
625
|
/** @private */
|
|
602
626
|
_onClick(e) {
|
|
603
|
-
this._closeOnBlurIsPrevented = true;
|
|
604
|
-
|
|
605
627
|
const path = e.composedPath();
|
|
606
628
|
|
|
607
629
|
if (this._isClearButton(e)) {
|
|
@@ -611,8 +633,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
611
633
|
} else {
|
|
612
634
|
this._onHostClick(e);
|
|
613
635
|
}
|
|
614
|
-
|
|
615
|
-
this._closeOnBlurIsPrevented = false;
|
|
616
636
|
}
|
|
617
637
|
|
|
618
638
|
/**
|
|
@@ -628,16 +648,12 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
628
648
|
if (e.key === 'Tab') {
|
|
629
649
|
this.$.overlay.restoreFocusOnClose = false;
|
|
630
650
|
} else if (e.key === 'ArrowDown') {
|
|
631
|
-
this._closeOnBlurIsPrevented = true;
|
|
632
651
|
this._onArrowDown();
|
|
633
|
-
this._closeOnBlurIsPrevented = false;
|
|
634
652
|
|
|
635
653
|
// Prevent caret from moving
|
|
636
654
|
e.preventDefault();
|
|
637
655
|
} else if (e.key === 'ArrowUp') {
|
|
638
|
-
this._closeOnBlurIsPrevented = true;
|
|
639
656
|
this._onArrowUp();
|
|
640
|
-
this._closeOnBlurIsPrevented = false;
|
|
641
657
|
|
|
642
658
|
// Prevent caret from moving
|
|
643
659
|
e.preventDefault();
|
|
@@ -708,8 +724,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
708
724
|
// and there's no need to modify the selection range if the input isn't focused anyway.
|
|
709
725
|
// This affects Safari. When the overlay is open, and then hitting tab, browser should focus
|
|
710
726
|
// the next focusable element instead of the combo-box itself.
|
|
711
|
-
|
|
712
|
-
if (this.hasAttribute('focused')) {
|
|
727
|
+
if (this._isInputFocused() && this.inputElement.setSelectionRange) {
|
|
713
728
|
this.inputElement.setSelectionRange(start, end);
|
|
714
729
|
}
|
|
715
730
|
}
|
|
@@ -819,7 +834,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
819
834
|
toggleElement.addEventListener('mousedown', (e) => e.preventDefault());
|
|
820
835
|
// Unfocus previously focused element if focus is not inside combo box (on touch devices)
|
|
821
836
|
toggleElement.addEventListener('click', () => {
|
|
822
|
-
if (isTouch && !this.
|
|
837
|
+
if (isTouch && !this._isInputFocused()) {
|
|
823
838
|
document.activeElement.blur();
|
|
824
839
|
}
|
|
825
840
|
});
|
|
@@ -926,9 +941,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
926
941
|
|
|
927
942
|
this._clearSelectionRange();
|
|
928
943
|
|
|
929
|
-
|
|
930
|
-
this.filter = '';
|
|
931
|
-
}
|
|
944
|
+
this.filter = '';
|
|
932
945
|
}
|
|
933
946
|
|
|
934
947
|
/**
|
|
@@ -946,19 +959,27 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
946
959
|
* @override
|
|
947
960
|
*/
|
|
948
961
|
_onInput(event) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
962
|
+
const filter = this._inputElementValue;
|
|
963
|
+
|
|
964
|
+
// When opening dropdown on user input, both `opened` and `filter` properties are set.
|
|
965
|
+
// Perform a batched property update instead of relying on sync property observers.
|
|
966
|
+
// This is necessary to avoid an extra data-provider request for loading first page.
|
|
967
|
+
const props = {};
|
|
952
968
|
|
|
953
|
-
|
|
954
|
-
if (this.filter === value) {
|
|
969
|
+
if (this.filter === filter) {
|
|
955
970
|
// Filter and input value might get out of sync, while keyboard navigating for example.
|
|
956
971
|
// Afterwards, input value might be changed to the same value as used in filtering.
|
|
957
972
|
// In situation like these, we need to make sure all the filter changes handlers are run.
|
|
958
|
-
this._filterChanged(this.filter
|
|
973
|
+
this._filterChanged(this.filter);
|
|
959
974
|
} else {
|
|
960
|
-
|
|
975
|
+
props.filter = filter;
|
|
961
976
|
}
|
|
977
|
+
|
|
978
|
+
if (!this.opened && !this._isClearButton(event) && !this.autoOpenDisabled) {
|
|
979
|
+
props.opened = true;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
this.setProperties(props);
|
|
962
983
|
}
|
|
963
984
|
|
|
964
985
|
/**
|
|
@@ -981,11 +1002,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
981
1002
|
}
|
|
982
1003
|
|
|
983
1004
|
/** @private */
|
|
984
|
-
_filterChanged(filter
|
|
985
|
-
if (filter === undefined) {
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
|
|
1005
|
+
_filterChanged(filter) {
|
|
989
1006
|
// Scroll to the top of the list whenever the filter changes.
|
|
990
1007
|
this._scrollIntoView(0);
|
|
991
1008
|
|
|
@@ -1028,7 +1045,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1028
1045
|
this.value = '';
|
|
1029
1046
|
}
|
|
1030
1047
|
|
|
1031
|
-
this._toggleHasValue(this.
|
|
1048
|
+
this._toggleHasValue(this._hasValue);
|
|
1032
1049
|
this._inputElementValue = this.value;
|
|
1033
1050
|
}
|
|
1034
1051
|
} else {
|
|
@@ -1064,21 +1081,21 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1064
1081
|
}
|
|
1065
1082
|
|
|
1066
1083
|
if (isValidValue(value)) {
|
|
1067
|
-
let item;
|
|
1068
1084
|
if (this._getItemValue(this.selectedItem) !== value) {
|
|
1069
1085
|
this._selectItemForValue(value);
|
|
1070
|
-
} else {
|
|
1071
|
-
item = this.selectedItem;
|
|
1072
1086
|
}
|
|
1073
1087
|
|
|
1074
|
-
if (!
|
|
1088
|
+
if (!this.selectedItem && this.allowCustomValue) {
|
|
1075
1089
|
this._inputElementValue = value;
|
|
1076
1090
|
}
|
|
1077
1091
|
|
|
1078
|
-
this._toggleHasValue(this.
|
|
1092
|
+
this._toggleHasValue(this._hasValue);
|
|
1079
1093
|
} else {
|
|
1080
1094
|
this.selectedItem = null;
|
|
1081
1095
|
}
|
|
1096
|
+
|
|
1097
|
+
this.filter = '';
|
|
1098
|
+
|
|
1082
1099
|
// In the next _detectAndDispatchChange() call, the change detection should pass
|
|
1083
1100
|
this._lastCommittedValue = undefined;
|
|
1084
1101
|
}
|
|
@@ -1236,9 +1253,6 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1236
1253
|
if (this.opened) {
|
|
1237
1254
|
this._focusedIndex = this.filteredItems.indexOf(e.detail.item);
|
|
1238
1255
|
this.close();
|
|
1239
|
-
} else if (this.selectedItem !== e.detail.item) {
|
|
1240
|
-
this.selectedItem = e.detail.item;
|
|
1241
|
-
this._detectAndDispatchChange();
|
|
1242
1256
|
}
|
|
1243
1257
|
}
|
|
1244
1258
|
|
|
@@ -1250,6 +1264,12 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1250
1264
|
|
|
1251
1265
|
/** @private */
|
|
1252
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
|
+
|
|
1253
1273
|
// Fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
|
|
1254
1274
|
if (event.relatedTarget === this.$.overlay) {
|
|
1255
1275
|
event.composedPath()[0].focus();
|
|
@@ -1277,35 +1297,12 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1277
1297
|
this._clear();
|
|
1278
1298
|
}
|
|
1279
1299
|
|
|
1280
|
-
/**
|
|
1281
|
-
* Returns true if `value` is valid, and sets the `invalid` flag appropriately.
|
|
1282
|
-
*
|
|
1283
|
-
* @return {boolean} True if the value is valid and sets the `invalid` flag appropriately
|
|
1284
|
-
*/
|
|
1285
|
-
validate() {
|
|
1286
|
-
return !(this.invalid = !this.checkValidity());
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
/**
|
|
1290
|
-
* Returns true if the current input value satisfies all constraints (if any).
|
|
1291
|
-
* You can override this method for custom validations.
|
|
1292
|
-
*
|
|
1293
|
-
* @return {boolean}
|
|
1294
|
-
*/
|
|
1295
|
-
checkValidity() {
|
|
1296
|
-
if (super.checkValidity) {
|
|
1297
|
-
return super.checkValidity();
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
return !this.required || !!this.value;
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
1300
|
/**
|
|
1304
1301
|
* Fired when the value changes.
|
|
1305
1302
|
*
|
|
1306
1303
|
* @event value-changed
|
|
1307
1304
|
* @param {Object} detail
|
|
1308
|
-
*
|
|
1305
|
+
* @param {String} detail.value the combobox value
|
|
1309
1306
|
*/
|
|
1310
1307
|
|
|
1311
1308
|
/**
|
|
@@ -1313,7 +1310,7 @@ export const ComboBoxMixin = (subclass) =>
|
|
|
1313
1310
|
*
|
|
1314
1311
|
* @event selected-item-changed
|
|
1315
1312
|
* @param {Object} detail
|
|
1316
|
-
*
|
|
1313
|
+
* @param {Object|String} detail.value the selected item. Type is the same as the type of `items`.
|
|
1317
1314
|
*/
|
|
1318
1315
|
|
|
1319
1316
|
/**
|
|
@@ -3,24 +3,25 @@
|
|
|
3
3
|
* Copyright (c) 2015 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
|
-
import { DisabledMixinClass } from '@vaadin/component-base/src/disabled-mixin.js';
|
|
8
|
-
import { ElementMixinClass } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
-
import { FocusMixinClass } from '@vaadin/component-base/src/focus-mixin.js';
|
|
10
|
-
import { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
11
|
-
import { DelegateFocusMixinClass } from '@vaadin/field-base/src/delegate-focus-mixin.js';
|
|
12
|
-
import { DelegateStateMixinClass } from '@vaadin/field-base/src/delegate-state-mixin.js';
|
|
13
|
-
import { FieldMixinClass } from '@vaadin/field-base/src/field-mixin.js';
|
|
14
|
-
import { InputConstraintsMixinClass } from '@vaadin/field-base/src/input-constraints-mixin.js';
|
|
15
|
-
import { InputControlMixinClass } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
16
|
-
import { InputMixinClass } from '@vaadin/field-base/src/input-mixin.js';
|
|
17
|
-
import { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';
|
|
18
|
-
import { PatternMixinClass } from '@vaadin/field-base/src/pattern-mixin.js';
|
|
19
|
-
import { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
|
|
20
|
-
import { ThemableMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
6
|
+
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
|
+
import type { DisabledMixinClass } from '@vaadin/component-base/src/disabled-mixin.js';
|
|
8
|
+
import type { ElementMixinClass } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import type { FocusMixinClass } from '@vaadin/component-base/src/focus-mixin.js';
|
|
10
|
+
import type { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
11
|
+
import type { DelegateFocusMixinClass } from '@vaadin/field-base/src/delegate-focus-mixin.js';
|
|
12
|
+
import type { DelegateStateMixinClass } from '@vaadin/field-base/src/delegate-state-mixin.js';
|
|
13
|
+
import type { FieldMixinClass } from '@vaadin/field-base/src/field-mixin.js';
|
|
14
|
+
import type { InputConstraintsMixinClass } from '@vaadin/field-base/src/input-constraints-mixin.js';
|
|
15
|
+
import type { InputControlMixinClass } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
16
|
+
import type { InputMixinClass } from '@vaadin/field-base/src/input-mixin.js';
|
|
17
|
+
import type { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';
|
|
18
|
+
import type { PatternMixinClass } from '@vaadin/field-base/src/pattern-mixin.js';
|
|
19
|
+
import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
|
|
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';
|
|
22
|
+
import type { ComboBoxDataProviderMixinClass } from './vaadin-combo-box-data-provider-mixin.js';
|
|
23
|
+
import type { ComboBoxMixinClass } from './vaadin-combo-box-mixin.js';
|
|
24
|
+
import type { ComboBoxDefaultItem } from './vaadin-combo-box-mixin.js';
|
|
24
25
|
export {
|
|
25
26
|
ComboBoxDataProvider,
|
|
26
27
|
ComboBoxDataProviderCallback,
|
|
@@ -65,6 +66,11 @@ export type ComboBoxFilterChangedEvent = CustomEvent<{ value: string }>;
|
|
|
65
66
|
*/
|
|
66
67
|
export type ComboBoxSelectedItemChangedEvent<TItem> = CustomEvent<{ value: TItem | null | undefined }>;
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Fired whenever the field is validated.
|
|
71
|
+
*/
|
|
72
|
+
export type ComboBoxValidatedEvent = CustomEvent<{ valid: boolean }>;
|
|
73
|
+
|
|
68
74
|
export interface ComboBoxEventMap<TItem> extends HTMLElementEventMap {
|
|
69
75
|
change: ComboBoxChangeEvent<TItem>;
|
|
70
76
|
|
|
@@ -79,6 +85,8 @@ export interface ComboBoxEventMap<TItem> extends HTMLElementEventMap {
|
|
|
79
85
|
'value-changed': ComboBoxValueChangedEvent;
|
|
80
86
|
|
|
81
87
|
'selected-item-changed': ComboBoxSelectedItemChangedEvent<TItem>;
|
|
88
|
+
|
|
89
|
+
validated: ComboBoxValidatedEvent;
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
/**
|
|
@@ -165,6 +173,7 @@ export interface ComboBoxEventMap<TItem> extends HTMLElementEventMap {
|
|
|
165
173
|
* Custom property | Description | Default
|
|
166
174
|
* ----------------------------------------|----------------------------|---------
|
|
167
175
|
* `--vaadin-field-default-width` | Default width of the field | `12em`
|
|
176
|
+
* `--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`
|
|
168
177
|
* `--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
|
|
169
178
|
*
|
|
170
179
|
* `<vaadin-combo-box>` provides the same set of shadow DOM parts and state attributes as `<vaadin-text-field>`.
|
|
@@ -207,18 +216,19 @@ export interface ComboBoxEventMap<TItem> extends HTMLElementEventMap {
|
|
|
207
216
|
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
208
217
|
* @fires {CustomEvent} selected-item-changed - Fired when the `selectedItem` property changes.
|
|
209
218
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
219
|
+
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
210
220
|
*/
|
|
211
221
|
declare class ComboBox<TItem = ComboBoxDefaultItem> extends HTMLElement {
|
|
212
222
|
addEventListener<K extends keyof ComboBoxEventMap<TItem>>(
|
|
213
223
|
type: K,
|
|
214
224
|
listener: (this: ComboBox<TItem>, ev: ComboBoxEventMap<TItem>[K]) => void,
|
|
215
|
-
options?:
|
|
225
|
+
options?: AddEventListenerOptions | boolean,
|
|
216
226
|
): void;
|
|
217
227
|
|
|
218
228
|
removeEventListener<K extends keyof ComboBoxEventMap<TItem>>(
|
|
219
229
|
type: K,
|
|
220
230
|
listener: (this: ComboBox<TItem>, ev: ComboBoxEventMap<TItem>[K]) => void,
|
|
221
|
-
options?:
|
|
231
|
+
options?: EventListenerOptions | boolean,
|
|
222
232
|
): void;
|
|
223
233
|
}
|
|
224
234
|
|
|
@@ -238,6 +248,7 @@ interface ComboBox<TItem = ComboBoxDefaultItem>
|
|
|
238
248
|
DelegateStateMixinClass,
|
|
239
249
|
DelegateFocusMixinClass,
|
|
240
250
|
ThemableMixinClass,
|
|
251
|
+
ThemePropertyMixinClass,
|
|
241
252
|
ElementMixinClass,
|
|
242
253
|
ControllerMixinClass {}
|
|
243
254
|
|
package/src/vaadin-combo-box.js
CHANGED
|
@@ -104,6 +104,7 @@ registerStyles('vaadin-combo-box', inputFieldShared, { moduleId: 'vaadin-combo-b
|
|
|
104
104
|
* Custom property | Description | Default
|
|
105
105
|
* ----------------------------------------|----------------------------|---------
|
|
106
106
|
* `--vaadin-field-default-width` | Default width of the field | `12em`
|
|
107
|
+
* `--vaadin-combo-box-overlay-width` | Width of the overlay | `auto`
|
|
107
108
|
* `--vaadin-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
|
|
108
109
|
*
|
|
109
110
|
* `<vaadin-combo-box>` provides the same set of shadow DOM parts and state attributes as `<vaadin-text-field>`.
|
|
@@ -146,6 +147,7 @@ registerStyles('vaadin-combo-box', inputFieldShared, { moduleId: 'vaadin-combo-b
|
|
|
146
147
|
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
147
148
|
* @fires {CustomEvent} selected-item-changed - Fired when the `selectedItem` property changes.
|
|
148
149
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
150
|
+
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
149
151
|
*
|
|
150
152
|
* @extends HTMLElement
|
|
151
153
|
* @mixes ElementMixin
|
|
@@ -200,7 +202,6 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
200
202
|
|
|
201
203
|
<vaadin-combo-box-overlay
|
|
202
204
|
id="overlay"
|
|
203
|
-
hidden$="[[_isOverlayHidden(filteredItems, loading)]]"
|
|
204
205
|
opened="[[_overlayOpened]]"
|
|
205
206
|
loading$="[[loading]]"
|
|
206
207
|
theme$="[[_theme]]"
|
|
@@ -2,6 +2,7 @@ import '@vaadin/vaadin-lumo-styles/color.js';
|
|
|
2
2
|
import '@vaadin/vaadin-lumo-styles/spacing.js';
|
|
3
3
|
import '@vaadin/vaadin-lumo-styles/style.js';
|
|
4
4
|
import '@vaadin/vaadin-overlay/theme/lumo/vaadin-overlay.js';
|
|
5
|
+
import { loader } from '@vaadin/vaadin-lumo-styles/mixins/loader.js';
|
|
5
6
|
import { menuOverlayCore } from '@vaadin/vaadin-lumo-styles/mixins/menu-overlay.js';
|
|
6
7
|
import { overlay } from '@vaadin/vaadin-lumo-styles/mixins/overlay.js';
|
|
7
8
|
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
@@ -36,10 +37,7 @@ const comboBoxOverlay = css`
|
|
|
36
37
|
margin-bottom: var(--lumo-space-xs);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
box-sizing: border-box;
|
|
41
|
-
width: var(--lumo-icon-size-s);
|
|
42
|
-
height: var(--lumo-icon-size-s);
|
|
40
|
+
[part~='loader'] {
|
|
43
41
|
position: absolute;
|
|
44
42
|
z-index: 1;
|
|
45
43
|
left: var(--lumo-space-s);
|
|
@@ -48,38 +46,11 @@ const comboBoxOverlay = css`
|
|
|
48
46
|
margin-left: auto;
|
|
49
47
|
margin-inline-start: auto;
|
|
50
48
|
margin-inline-end: 0;
|
|
51
|
-
border: 2px solid transparent;
|
|
52
|
-
border-color: var(--lumo-primary-color-50pct) var(--lumo-primary-color-50pct) var(--lumo-primary-color)
|
|
53
|
-
var(--lumo-primary-color);
|
|
54
|
-
border-radius: calc(0.5 * var(--lumo-icon-size-s));
|
|
55
|
-
opacity: 0;
|
|
56
|
-
animation: 1s linear infinite lumo-combo-box-loader-rotate, 0.3s 0.1s lumo-combo-box-loader-fade-in both;
|
|
57
|
-
pointer-events: none;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@keyframes lumo-combo-box-loader-fade-in {
|
|
61
|
-
0% {
|
|
62
|
-
opacity: 0;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
100% {
|
|
66
|
-
opacity: 1;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@keyframes lumo-combo-box-loader-rotate {
|
|
71
|
-
0% {
|
|
72
|
-
transform: rotate(0deg);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
100% {
|
|
76
|
-
transform: rotate(360deg);
|
|
77
|
-
}
|
|
78
49
|
}
|
|
79
50
|
|
|
80
51
|
/* RTL specific styles */
|
|
81
52
|
|
|
82
|
-
:host([
|
|
53
|
+
:host([dir='rtl']) [part~='loader'] {
|
|
83
54
|
left: auto;
|
|
84
55
|
margin-left: 0;
|
|
85
56
|
margin-right: auto;
|
|
@@ -88,6 +59,6 @@ const comboBoxOverlay = css`
|
|
|
88
59
|
}
|
|
89
60
|
`;
|
|
90
61
|
|
|
91
|
-
registerStyles('vaadin-combo-box-overlay', [overlay, menuOverlayCore, comboBoxOverlay], {
|
|
62
|
+
registerStyles('vaadin-combo-box-overlay', [overlay, menuOverlayCore, comboBoxOverlay, loader], {
|
|
92
63
|
moduleId: 'lumo-combo-box-overlay',
|
|
93
64
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@vaadin/vaadin-material-styles/color.js';
|
|
2
2
|
import '@vaadin/vaadin-overlay/theme/material/vaadin-overlay.js';
|
|
3
|
+
import { loader } from '@vaadin/vaadin-material-styles/mixins/loader.js';
|
|
3
4
|
import { menuOverlay } from '@vaadin/vaadin-material-styles/mixins/menu-overlay.js';
|
|
4
5
|
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
5
6
|
|
|
@@ -21,102 +22,15 @@ const comboBoxOverlay = css`
|
|
|
21
22
|
padding: 0;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
height: 2px;
|
|
25
|
+
[part~='loader'] {
|
|
26
26
|
position: absolute;
|
|
27
27
|
z-index: 1;
|
|
28
28
|
top: -2px;
|
|
29
29
|
left: 0;
|
|
30
30
|
right: 0;
|
|
31
|
-
background: var(--material-background-color)
|
|
32
|
-
linear-gradient(
|
|
33
|
-
90deg,
|
|
34
|
-
transparent 0%,
|
|
35
|
-
transparent 20%,
|
|
36
|
-
var(--material-primary-color) 20%,
|
|
37
|
-
var(--material-primary-color) 40%,
|
|
38
|
-
transparent 40%,
|
|
39
|
-
transparent 60%,
|
|
40
|
-
var(--material-primary-color) 60%,
|
|
41
|
-
var(--material-primary-color) 80%,
|
|
42
|
-
transparent 80%,
|
|
43
|
-
transparent 100%
|
|
44
|
-
)
|
|
45
|
-
0 0 / 400% 100% repeat-x;
|
|
46
|
-
opacity: 0;
|
|
47
|
-
animation: 3s linear infinite material-combo-box-loader-progress, 0.3s 0.1s both material-combo-box-loader-fade-in;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
[part='loader']::before {
|
|
51
|
-
content: '';
|
|
52
|
-
display: block;
|
|
53
|
-
height: 100%;
|
|
54
|
-
opacity: 0.16;
|
|
55
|
-
background: var(--material-primary-color);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@keyframes material-combo-box-loader-fade-in {
|
|
59
|
-
0% {
|
|
60
|
-
opacity: 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
100% {
|
|
64
|
-
opacity: 1;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@keyframes material-combo-box-loader-progress {
|
|
69
|
-
0% {
|
|
70
|
-
background-position: 0 0;
|
|
71
|
-
background-size: 300% 100%;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
33% {
|
|
75
|
-
background-position: -100% 0;
|
|
76
|
-
background-size: 400% 100%;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
67% {
|
|
80
|
-
background-position: -200% 0;
|
|
81
|
-
background-size: 250% 100%;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
100% {
|
|
85
|
-
background-position: -300% 0;
|
|
86
|
-
background-size: 300% 100%;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* RTL specific styles */
|
|
91
|
-
|
|
92
|
-
@keyframes material-combo-box-loader-progress-rtl {
|
|
93
|
-
0% {
|
|
94
|
-
background-position: 100% 0;
|
|
95
|
-
background-size: 300% 100%;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
33% {
|
|
99
|
-
background-position: 200% 0;
|
|
100
|
-
background-size: 400% 100%;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
67% {
|
|
104
|
-
background-position: 300% 0;
|
|
105
|
-
background-size: 250% 100%;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
100% {
|
|
109
|
-
background-position: 400% 0;
|
|
110
|
-
background-size: 300% 100%;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
:host([loading][dir='rtl']) [part='loader'] {
|
|
115
|
-
animation: 3s linear infinite material-combo-box-loader-progress-rtl,
|
|
116
|
-
0.3s 0.1s both material-combo-box-loader-fade-in;
|
|
117
31
|
}
|
|
118
32
|
`;
|
|
119
33
|
|
|
120
|
-
registerStyles('vaadin-combo-box-overlay', [menuOverlay, comboBoxOverlay], {
|
|
34
|
+
registerStyles('vaadin-combo-box-overlay', [menuOverlay, comboBoxOverlay, loader], {
|
|
121
35
|
moduleId: 'material-combo-box-overlay',
|
|
122
36
|
});
|