@angular/material 16.2.0-next.3 → 16.2.0-next.5
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/autocomplete/index.d.ts +34 -4
- package/badge/_badge-theme.scss +84 -118
- package/bottom-sheet/_bottom-sheet-theme.scss +10 -11
- package/button/_fab-theme.scss +70 -43
- package/button-toggle/_button-toggle-theme.scss +20 -97
- package/button-toggle/_button-toggle-variables.scss +2 -0
- package/card/_card-theme.scss +20 -4
- package/checkbox/_checkbox-theme.scss +5 -3
- package/chips/index.d.ts +11 -4
- package/core/index.d.ts +3 -2
- package/core/tokens/_token-utils.scss +2 -0
- package/core/tokens/m2/mat/_badge.scss +78 -0
- package/core/tokens/m2/mat/_bottom-sheet.scss +56 -0
- package/core/tokens/m2/mat/_datepicker.scss +184 -0
- package/core/tokens/m2/mat/_divider.scss +45 -0
- package/core/tokens/m2/mat/_expansion.scss +90 -0
- package/core/tokens/m2/mat/_legacy-button-toggle.scss +63 -0
- package/core/tokens/m2/mat/_sidenav.scss +65 -0
- package/core/tokens/m2/mat/_standard-button-toggle.scss +87 -0
- package/core/tokens/m2/mat/_toolbar.scss +79 -0
- package/core/tokens/m2/mdc/_extended-fab.scss +88 -0
- package/core/tokens/m2/mdc/_fab.scss +89 -0
- package/datepicker/_datepicker-theme.scss +44 -203
- package/divider/_divider-theme.scss +6 -8
- package/esm2022/autocomplete/autocomplete-trigger.mjs +85 -38
- package/esm2022/autocomplete/autocomplete.mjs +25 -4
- package/esm2022/bottom-sheet/bottom-sheet-container.mjs +3 -3
- package/esm2022/button/fab.mjs +8 -8
- package/esm2022/button-toggle/button-toggle.mjs +2 -2
- package/esm2022/chips/chip-option.mjs +4 -5
- package/esm2022/chips/chip-row.mjs +15 -10
- package/esm2022/chips/chip.mjs +29 -12
- package/esm2022/core/option/option.mjs +9 -5
- package/esm2022/core/private/ripple-loader.mjs +4 -1
- package/esm2022/core/version.mjs +1 -1
- package/esm2022/datepicker/calendar-body.mjs +2 -2
- package/esm2022/datepicker/calendar.mjs +2 -2
- package/esm2022/datepicker/date-range-input.mjs +2 -2
- package/esm2022/datepicker/datepicker-base.mjs +3 -3
- package/esm2022/datepicker/datepicker-toggle.mjs +2 -2
- package/esm2022/divider/divider.mjs +2 -2
- package/esm2022/expansion/expansion-animations.mjs +5 -2
- package/esm2022/expansion/expansion-panel-header.mjs +3 -3
- package/esm2022/expansion/expansion-panel.mjs +3 -3
- package/esm2022/form-field/form-field.mjs +3 -3
- package/esm2022/legacy-autocomplete/autocomplete.mjs +2 -1
- package/esm2022/legacy-dialog/testing/dialog-opener.mjs +6 -1
- package/esm2022/legacy-menu/menu.mjs +4 -2
- package/esm2022/legacy-table/text-column.mjs +6 -2
- package/esm2022/legacy-tabs/tab.mjs +6 -3
- package/esm2022/legacy-tooltip/tooltip.mjs +5 -2
- package/esm2022/menu/menu-item.mjs +4 -4
- package/esm2022/menu/menu.mjs +3 -3
- package/esm2022/radio/radio.mjs +13 -1
- package/esm2022/sidenav/drawer.mjs +3 -3
- package/esm2022/sidenav/sidenav.mjs +3 -3
- package/esm2022/slide-toggle/slide-toggle.mjs +2 -2
- package/esm2022/toolbar/toolbar.mjs +2 -2
- package/expansion/_expansion-legacy-index.scss +0 -1
- package/expansion/_expansion-theme.import.scss +0 -2
- package/expansion/_expansion-theme.scss +12 -68
- package/fesm2022/autocomplete.mjs +109 -41
- package/fesm2022/autocomplete.mjs.map +1 -1
- package/fesm2022/bottom-sheet.mjs +2 -2
- package/fesm2022/bottom-sheet.mjs.map +1 -1
- package/fesm2022/button-toggle.mjs +2 -2
- package/fesm2022/button-toggle.mjs.map +1 -1
- package/fesm2022/button.mjs +8 -8
- package/fesm2022/button.mjs.map +1 -1
- package/fesm2022/chips.mjs +41 -17
- package/fesm2022/chips.mjs.map +1 -1
- package/fesm2022/core.mjs +12 -5
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/datepicker.mjs +10 -10
- package/fesm2022/datepicker.mjs.map +1 -1
- package/fesm2022/divider.mjs +2 -2
- package/fesm2022/divider.mjs.map +1 -1
- package/fesm2022/expansion.mjs +8 -5
- package/fesm2022/expansion.mjs.map +1 -1
- package/fesm2022/form-field.mjs +2 -2
- package/fesm2022/form-field.mjs.map +1 -1
- package/fesm2022/legacy-autocomplete.mjs +1 -0
- package/fesm2022/legacy-autocomplete.mjs.map +1 -1
- package/fesm2022/legacy-dialog/testing.mjs +5 -0
- package/fesm2022/legacy-dialog/testing.mjs.map +1 -1
- package/fesm2022/legacy-menu.mjs +3 -1
- package/fesm2022/legacy-menu.mjs.map +1 -1
- package/fesm2022/legacy-table.mjs +5 -1
- package/fesm2022/legacy-table.mjs.map +1 -1
- package/fesm2022/legacy-tabs.mjs +5 -2
- package/fesm2022/legacy-tabs.mjs.map +1 -1
- package/fesm2022/legacy-tooltip.mjs +4 -1
- package/fesm2022/legacy-tooltip.mjs.map +1 -1
- package/fesm2022/menu.mjs +5 -5
- package/fesm2022/menu.mjs.map +1 -1
- package/fesm2022/radio.mjs +12 -0
- package/fesm2022/radio.mjs.map +1 -1
- package/fesm2022/sidenav.mjs +4 -4
- package/fesm2022/sidenav.mjs.map +1 -1
- package/fesm2022/slide-toggle.mjs +2 -2
- package/fesm2022/slide-toggle.mjs.map +1 -1
- package/fesm2022/toolbar.mjs +2 -2
- package/fesm2022/toolbar.mjs.map +1 -1
- package/legacy-autocomplete/index.d.ts +1 -0
- package/legacy-prebuilt-themes/legacy-deeppurple-amber.css +1 -1
- package/legacy-prebuilt-themes/legacy-indigo-pink.css +1 -1
- package/legacy-prebuilt-themes/legacy-pink-bluegrey.css +1 -1
- package/legacy-prebuilt-themes/legacy-purple-green.css +1 -1
- package/package.json +2 -2
- package/prebuilt-themes/deeppurple-amber.css +1 -1
- package/prebuilt-themes/indigo-pink.css +1 -1
- package/prebuilt-themes/pink-bluegrey.css +1 -1
- package/prebuilt-themes/purple-green.css +1 -1
- package/radio/index.d.ts +4 -1
- package/schematics/ng-add/index.js +1 -1
- package/schematics/ng-add/index.mjs +1 -1
- package/sidenav/_sidenav-theme.scss +6 -69
- package/toolbar/_toolbar-theme.scss +25 -71
- package/expansion/_expansion-mixins.import.scss +0 -1
- package/expansion/_expansion-mixins.scss +0 -11
|
@@ -13,7 +13,7 @@ import * as i1 from '@angular/cdk/platform';
|
|
|
13
13
|
import { _getEventTarget } from '@angular/cdk/platform';
|
|
14
14
|
import { trigger, state, style, transition, group, animate } from '@angular/animations';
|
|
15
15
|
import { Subscription, Subject, defer, merge, of, fromEvent } from 'rxjs';
|
|
16
|
-
import { hasModifierKey,
|
|
16
|
+
import { ESCAPE, hasModifierKey, UP_ARROW, ENTER, DOWN_ARROW, TAB } from '@angular/cdk/keycodes';
|
|
17
17
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
18
18
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
19
19
|
import * as i4 from '@angular/material/form-field';
|
|
@@ -69,6 +69,7 @@ function MAT_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY() {
|
|
|
69
69
|
autoActiveFirstOption: false,
|
|
70
70
|
autoSelectActiveOption: false,
|
|
71
71
|
hideSingleSelectionIndicator: false,
|
|
72
|
+
requireSelection: false,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
/** Base class with all of the `MatAutocomplete` functionality. */
|
|
@@ -99,6 +100,18 @@ class _MatAutocompleteBase extends _MatAutocompleteMixinBase {
|
|
|
99
100
|
set autoSelectActiveOption(value) {
|
|
100
101
|
this._autoSelectActiveOption = coerceBooleanProperty(value);
|
|
101
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Whether the user is required to make a selection when they're interacting with the
|
|
105
|
+
* autocomplete. If the user moves away from the autcomplete without selecting an option from
|
|
106
|
+
* the list, the value will be reset. If the user opens the panel and closes it without
|
|
107
|
+
* interacting or selecting a value, the initial value will be kept.
|
|
108
|
+
*/
|
|
109
|
+
get requireSelection() {
|
|
110
|
+
return this._requireSelection;
|
|
111
|
+
}
|
|
112
|
+
set requireSelection(value) {
|
|
113
|
+
this._requireSelection = coerceBooleanProperty(value);
|
|
114
|
+
}
|
|
102
115
|
/**
|
|
103
116
|
* Takes classes set on the host mat-autocomplete element and applies them to the panel
|
|
104
117
|
* inside the overlay container to allow for easy styling.
|
|
@@ -146,6 +159,7 @@ class _MatAutocompleteBase extends _MatAutocompleteMixinBase {
|
|
|
146
159
|
this.inertGroups = platform?.SAFARI || false;
|
|
147
160
|
this._autoActiveFirstOption = !!_defaults.autoActiveFirstOption;
|
|
148
161
|
this._autoSelectActiveOption = !!_defaults.autoSelectActiveOption;
|
|
162
|
+
this._requireSelection = !!_defaults.requireSelection;
|
|
149
163
|
}
|
|
150
164
|
ngAfterContentInit() {
|
|
151
165
|
this._keyManager = new ActiveDescendantKeyManager(this.options)
|
|
@@ -210,7 +224,7 @@ class _MatAutocompleteBase extends _MatAutocompleteMixinBase {
|
|
|
210
224
|
return option.disabled;
|
|
211
225
|
}
|
|
212
226
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: _MatAutocompleteBase, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: MAT_AUTOCOMPLETE_DEFAULT_OPTIONS }, { token: i1.Platform }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
213
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.1", type: _MatAutocompleteBase, inputs: { ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], displayWith: "displayWith", autoActiveFirstOption: "autoActiveFirstOption", autoSelectActiveOption: "autoSelectActiveOption", panelWidth: "panelWidth", classList: ["class", "classList"] }, outputs: { optionSelected: "optionSelected", opened: "opened", closed: "closed", optionActivated: "optionActivated" }, viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true, static: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], usesInheritance: true, ngImport: i0 }); }
|
|
227
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.1", type: _MatAutocompleteBase, inputs: { ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], displayWith: "displayWith", autoActiveFirstOption: "autoActiveFirstOption", autoSelectActiveOption: "autoSelectActiveOption", requireSelection: "requireSelection", panelWidth: "panelWidth", classList: ["class", "classList"] }, outputs: { optionSelected: "optionSelected", opened: "opened", closed: "closed", optionActivated: "optionActivated" }, viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true, static: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], usesInheritance: true, ngImport: i0 }); }
|
|
214
228
|
}
|
|
215
229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: _MatAutocompleteBase, decorators: [{
|
|
216
230
|
type: Directive
|
|
@@ -235,6 +249,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.1", ngImpor
|
|
|
235
249
|
type: Input
|
|
236
250
|
}], autoSelectActiveOption: [{
|
|
237
251
|
type: Input
|
|
252
|
+
}], requireSelection: [{
|
|
253
|
+
type: Input
|
|
238
254
|
}], panelWidth: [{
|
|
239
255
|
type: Input
|
|
240
256
|
}], optionSelected: [{
|
|
@@ -254,6 +270,7 @@ class MatAutocomplete extends _MatAutocompleteBase {
|
|
|
254
270
|
super(...arguments);
|
|
255
271
|
this._visibleClass = 'mat-mdc-autocomplete-visible';
|
|
256
272
|
this._hiddenClass = 'mat-mdc-autocomplete-hidden';
|
|
273
|
+
this._animationDone = new EventEmitter();
|
|
257
274
|
this._hideSingleSelectionIndicator = this._defaults.hideSingleSelectionIndicator ?? false;
|
|
258
275
|
}
|
|
259
276
|
/** Whether checkmark indicator for single-selection options is hidden. */
|
|
@@ -272,6 +289,10 @@ class MatAutocomplete extends _MatAutocompleteBase {
|
|
|
272
289
|
}
|
|
273
290
|
}
|
|
274
291
|
}
|
|
292
|
+
ngOnDestroy() {
|
|
293
|
+
super.ngOnDestroy();
|
|
294
|
+
this._animationDone.complete();
|
|
295
|
+
}
|
|
275
296
|
// `skipPredicate` determines if key manager should avoid putting a given option in the tab
|
|
276
297
|
// order. Allow disabled list items to receive focus via keyboard to align with WAI ARIA
|
|
277
298
|
// recommendation.
|
|
@@ -290,14 +311,14 @@ class MatAutocomplete extends _MatAutocompleteBase {
|
|
|
290
311
|
return false;
|
|
291
312
|
}
|
|
292
313
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: MatAutocomplete, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
293
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.1", type: MatAutocomplete, selector: "mat-autocomplete", inputs: { disableRipple: "disableRipple", hideSingleSelectionIndicator: "hideSingleSelectionIndicator" }, host: { attributes: { "ngSkipHydration": "" }, classAttribute: "mat-mdc-autocomplete" }, providers: [{ provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatAutocomplete }], queries: [{ propertyName: "optionGroups", predicate: MAT_OPTGROUP, descendants: true }, { propertyName: "options", predicate: MatOption, descendants: true }], exportAs: ["matAutocomplete"], usesInheritance: true, ngImport: i0, template: "<ng-template let-formFieldId=\"id\">\n <div\n class=\"mat-mdc-autocomplete-panel mdc-menu-surface mdc-menu-surface--open\"\n role=\"listbox\"\n [id]=\"id\"\n [ngClass]=\"_classList\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby(formFieldId)\"\n [@panelAnimation]=\"isOpen ? 'visible' : 'hidden'\"\n #panel>\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["div.mat-mdc-autocomplete-panel{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);width:100%;max-height:256px;visibility:hidden;transform-origin:center top;overflow:auto;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-autocomplete-background-color)}.cdk-high-contrast-active div.mat-mdc-autocomplete-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-top-left-radius:0;border-top-right-radius:0}.mat-mdc-autocomplete-panel-above div.mat-mdc-autocomplete-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:center bottom}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-visible{visibility:visible}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-hidden{visibility:hidden}mat-autocomplete{display:none}"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], animations: [panelAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
314
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.1", type: MatAutocomplete, selector: "mat-autocomplete", inputs: { disableRipple: "disableRipple", hideSingleSelectionIndicator: "hideSingleSelectionIndicator" }, host: { attributes: { "ngSkipHydration": "" }, classAttribute: "mat-mdc-autocomplete" }, providers: [{ provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatAutocomplete }], queries: [{ propertyName: "optionGroups", predicate: MAT_OPTGROUP, descendants: true }, { propertyName: "options", predicate: MatOption, descendants: true }], exportAs: ["matAutocomplete"], usesInheritance: true, ngImport: i0, template: "<ng-template let-formFieldId=\"id\">\n <div\n class=\"mat-mdc-autocomplete-panel mdc-menu-surface mdc-menu-surface--open\"\n role=\"listbox\"\n [id]=\"id\"\n [ngClass]=\"_classList\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby(formFieldId)\"\n [@panelAnimation]=\"isOpen ? 'visible' : 'hidden'\"\n (@panelAnimation.done)=\"_animationDone.next($event)\"\n #panel>\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["div.mat-mdc-autocomplete-panel{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);width:100%;max-height:256px;visibility:hidden;transform-origin:center top;overflow:auto;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-autocomplete-background-color)}.cdk-high-contrast-active div.mat-mdc-autocomplete-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-top-left-radius:0;border-top-right-radius:0}.mat-mdc-autocomplete-panel-above div.mat-mdc-autocomplete-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:center bottom}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-visible{visibility:visible}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-hidden{visibility:hidden}mat-autocomplete{display:none}"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], animations: [panelAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
294
315
|
}
|
|
295
316
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: MatAutocomplete, decorators: [{
|
|
296
317
|
type: Component,
|
|
297
318
|
args: [{ selector: 'mat-autocomplete', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'matAutocomplete', inputs: ['disableRipple'], host: {
|
|
298
319
|
'class': 'mat-mdc-autocomplete',
|
|
299
320
|
'ngSkipHydration': '',
|
|
300
|
-
}, providers: [{ provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatAutocomplete }], animations: [panelAnimation], template: "<ng-template let-formFieldId=\"id\">\n <div\n class=\"mat-mdc-autocomplete-panel mdc-menu-surface mdc-menu-surface--open\"\n role=\"listbox\"\n [id]=\"id\"\n [ngClass]=\"_classList\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby(formFieldId)\"\n [@panelAnimation]=\"isOpen ? 'visible' : 'hidden'\"\n #panel>\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["div.mat-mdc-autocomplete-panel{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);width:100%;max-height:256px;visibility:hidden;transform-origin:center top;overflow:auto;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-autocomplete-background-color)}.cdk-high-contrast-active div.mat-mdc-autocomplete-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-top-left-radius:0;border-top-right-radius:0}.mat-mdc-autocomplete-panel-above div.mat-mdc-autocomplete-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:center bottom}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-visible{visibility:visible}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-hidden{visibility:hidden}mat-autocomplete{display:none}"] }]
|
|
321
|
+
}, providers: [{ provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatAutocomplete }], animations: [panelAnimation], template: "<ng-template let-formFieldId=\"id\">\n <div\n class=\"mat-mdc-autocomplete-panel mdc-menu-surface mdc-menu-surface--open\"\n role=\"listbox\"\n [id]=\"id\"\n [ngClass]=\"_classList\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby(formFieldId)\"\n [@panelAnimation]=\"isOpen ? 'visible' : 'hidden'\"\n (@panelAnimation.done)=\"_animationDone.next($event)\"\n #panel>\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["div.mat-mdc-autocomplete-panel{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);width:100%;max-height:256px;visibility:hidden;transform-origin:center top;overflow:auto;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-autocomplete-background-color)}.cdk-high-contrast-active div.mat-mdc-autocomplete-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-top-left-radius:0;border-top-right-radius:0}.mat-mdc-autocomplete-panel-above div.mat-mdc-autocomplete-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:center bottom}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-visible{visibility:visible}div.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-hidden{visibility:hidden}mat-autocomplete{display:none}"] }]
|
|
301
322
|
}], propDecorators: { optionGroups: [{
|
|
302
323
|
type: ContentChildren,
|
|
303
324
|
args: [MAT_OPTGROUP, { descendants: true }]
|
|
@@ -443,6 +464,26 @@ class _MatAutocompleteTriggerBase {
|
|
|
443
464
|
// Return a stream that we'll replace with the real one once everything is in place.
|
|
444
465
|
return this._zone.onStable.pipe(take(1), switchMap(() => this.optionSelections));
|
|
445
466
|
});
|
|
467
|
+
/** Handles keyboard events coming from the overlay panel. */
|
|
468
|
+
this._handlePanelKeydown = (event) => {
|
|
469
|
+
// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
|
|
470
|
+
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
|
|
471
|
+
if ((event.keyCode === ESCAPE && !hasModifierKey(event)) ||
|
|
472
|
+
(event.keyCode === UP_ARROW && hasModifierKey(event, 'altKey'))) {
|
|
473
|
+
// If the user had typed something in before we autoselected an option, and they decided
|
|
474
|
+
// to cancel the selection, restore the input value to the one they had typed in.
|
|
475
|
+
if (this._pendingAutoselectedOption) {
|
|
476
|
+
this._updateNativeInputValue(this._valueBeforeAutoSelection ?? '');
|
|
477
|
+
this._pendingAutoselectedOption = null;
|
|
478
|
+
}
|
|
479
|
+
this._closeKeyEventStream.next();
|
|
480
|
+
this._resetActiveItem();
|
|
481
|
+
// We need to stop propagation, otherwise the event will eventually
|
|
482
|
+
// reach the input itself and cause the overlay to be reopened.
|
|
483
|
+
event.stopPropagation();
|
|
484
|
+
event.preventDefault();
|
|
485
|
+
}
|
|
486
|
+
};
|
|
446
487
|
/**
|
|
447
488
|
* Track which modal we have modified the `aria-owns` attribute of. When the combobox trigger is
|
|
448
489
|
* inside an aria-modal, we apply aria-owns to the parent modal with the `id` of the options
|
|
@@ -506,6 +547,7 @@ class _MatAutocompleteTriggerBase {
|
|
|
506
547
|
this._overlayRef.detach();
|
|
507
548
|
this._closingActionsSubscription.unsubscribe();
|
|
508
549
|
}
|
|
550
|
+
this._updatePanelState();
|
|
509
551
|
// Note that in some cases this can end up being called after the component is destroyed.
|
|
510
552
|
// Add a check to ensure that we don't try to run change detection on a destroyed view.
|
|
511
553
|
if (!this._componentDestroyed) {
|
|
@@ -632,6 +674,9 @@ class _MatAutocompleteTriggerBase {
|
|
|
632
674
|
this._previousValue = value;
|
|
633
675
|
this._pendingAutoselectedOption = null;
|
|
634
676
|
this._onChange(value);
|
|
677
|
+
if (!value) {
|
|
678
|
+
this._clearPreviousSelectedOption(null, false);
|
|
679
|
+
}
|
|
635
680
|
if (this._canOpen() && this._document.activeElement === event.target) {
|
|
636
681
|
this.openPanel();
|
|
637
682
|
}
|
|
@@ -700,7 +745,7 @@ class _MatAutocompleteTriggerBase {
|
|
|
700
745
|
this._zone.run(() => {
|
|
701
746
|
const wasOpen = this.panelOpen;
|
|
702
747
|
this._resetActiveItem();
|
|
703
|
-
this.
|
|
748
|
+
this._updatePanelState();
|
|
704
749
|
this._changeDetectorRef.detectChanges();
|
|
705
750
|
if (this.panelOpen) {
|
|
706
751
|
this._overlayRef.updatePosition();
|
|
@@ -714,7 +759,7 @@ class _MatAutocompleteTriggerBase {
|
|
|
714
759
|
// of the available options,
|
|
715
760
|
// - if a valid string is entered after an invalid one.
|
|
716
761
|
if (this.panelOpen) {
|
|
717
|
-
this.
|
|
762
|
+
this._emitOpened();
|
|
718
763
|
}
|
|
719
764
|
else {
|
|
720
765
|
this.autocomplete.closed.emit();
|
|
@@ -728,6 +773,14 @@ class _MatAutocompleteTriggerBase {
|
|
|
728
773
|
// set the value, close the panel, and complete.
|
|
729
774
|
.subscribe(event => this._setValueAndClose(event)));
|
|
730
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Emits the opened event once it's known that the panel will be shown and stores
|
|
778
|
+
* the state of the trigger right before the opening sequence was finished.
|
|
779
|
+
*/
|
|
780
|
+
_emitOpened() {
|
|
781
|
+
this._valueOnOpen = this._element.nativeElement.value;
|
|
782
|
+
this.autocomplete.opened.emit();
|
|
783
|
+
}
|
|
731
784
|
/** Destroys the autocomplete suggestion panel. */
|
|
732
785
|
_destroyPanel() {
|
|
733
786
|
if (this._overlayRef) {
|
|
@@ -761,23 +814,41 @@ class _MatAutocompleteTriggerBase {
|
|
|
761
814
|
* stemmed from the user.
|
|
762
815
|
*/
|
|
763
816
|
_setValueAndClose(event) {
|
|
817
|
+
const panel = this.autocomplete;
|
|
764
818
|
const toSelect = event ? event.source : this._pendingAutoselectedOption;
|
|
765
819
|
if (toSelect) {
|
|
766
820
|
this._clearPreviousSelectedOption(toSelect);
|
|
767
821
|
this._assignOptionValue(toSelect.value);
|
|
822
|
+
// TODO(crisbeto): this should wait until the animation is done, otherwise the value
|
|
823
|
+
// gets reset while the panel is still animating which looks glitchy. It'll likely break
|
|
824
|
+
// some tests to change it at this point.
|
|
768
825
|
this._onChange(toSelect.value);
|
|
769
|
-
|
|
826
|
+
panel._emitSelectEvent(toSelect);
|
|
770
827
|
this._element.nativeElement.focus();
|
|
771
828
|
}
|
|
829
|
+
else if (panel.requireSelection && this._element.nativeElement.value !== this._valueOnOpen) {
|
|
830
|
+
this._clearPreviousSelectedOption(null);
|
|
831
|
+
this._assignOptionValue(null);
|
|
832
|
+
// Wait for the animation to finish before clearing the form control value, otherwise
|
|
833
|
+
// the options might change while the animation is running which looks glitchy.
|
|
834
|
+
if (panel._animationDone) {
|
|
835
|
+
panel._animationDone.pipe(take(1)).subscribe(() => this._onChange(null));
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
this._onChange(null);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
772
841
|
this.closePanel();
|
|
773
842
|
}
|
|
774
843
|
/**
|
|
775
844
|
* Clear any previous selected option and emit a selection change event for this option
|
|
776
845
|
*/
|
|
777
|
-
_clearPreviousSelectedOption(skip) {
|
|
778
|
-
|
|
846
|
+
_clearPreviousSelectedOption(skip, emitEvent) {
|
|
847
|
+
// Null checks are necessary here, because the autocomplete
|
|
848
|
+
// or its options may not have been assigned yet.
|
|
849
|
+
this.autocomplete?.options?.forEach(option => {
|
|
779
850
|
if (option !== skip && option.selected) {
|
|
780
|
-
option.deselect();
|
|
851
|
+
option.deselect(emitEvent);
|
|
781
852
|
}
|
|
782
853
|
});
|
|
783
854
|
}
|
|
@@ -792,7 +863,6 @@ class _MatAutocompleteTriggerBase {
|
|
|
792
863
|
});
|
|
793
864
|
overlayRef = this._overlay.create(this._getOverlayConfig());
|
|
794
865
|
this._overlayRef = overlayRef;
|
|
795
|
-
this._handleOverlayEvents(overlayRef);
|
|
796
866
|
this._viewportSubscription = this._viewportRuler.change().subscribe(() => {
|
|
797
867
|
if (this.panelOpen && overlayRef) {
|
|
798
868
|
overlayRef.updateSize({ width: this._getPanelWidth() });
|
|
@@ -809,14 +879,40 @@ class _MatAutocompleteTriggerBase {
|
|
|
809
879
|
this._closingActionsSubscription = this._subscribeToClosingActions();
|
|
810
880
|
}
|
|
811
881
|
const wasOpen = this.panelOpen;
|
|
812
|
-
this.autocomplete._setVisibility();
|
|
813
882
|
this.autocomplete._isOpen = this._overlayAttached = true;
|
|
814
883
|
this.autocomplete._setColor(this._formField?.color);
|
|
884
|
+
this._updatePanelState();
|
|
815
885
|
this._applyModalPanelOwnership();
|
|
816
886
|
// We need to do an extra `panelOpen` check in here, because the
|
|
817
887
|
// autocomplete won't be shown if there are no options.
|
|
818
888
|
if (this.panelOpen && wasOpen !== this.panelOpen) {
|
|
819
|
-
this.
|
|
889
|
+
this._emitOpened();
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
/** Updates the panel's visibility state and any trigger state tied to id. */
|
|
893
|
+
_updatePanelState() {
|
|
894
|
+
this.autocomplete._setVisibility();
|
|
895
|
+
// Note that here we subscribe and unsubscribe based on the panel's visiblity state,
|
|
896
|
+
// because the act of subscribing will prevent events from reaching other overlays and
|
|
897
|
+
// we don't want to block the events if there are no options.
|
|
898
|
+
if (this.panelOpen) {
|
|
899
|
+
const overlayRef = this._overlayRef;
|
|
900
|
+
if (!this._keydownSubscription) {
|
|
901
|
+
// Use the `keydownEvents` in order to take advantage of
|
|
902
|
+
// the overlay event targeting provided by the CDK overlay.
|
|
903
|
+
this._keydownSubscription = overlayRef.keydownEvents().subscribe(this._handlePanelKeydown);
|
|
904
|
+
}
|
|
905
|
+
if (!this._outsideClickSubscription) {
|
|
906
|
+
// Subscribe to the pointer events stream so that it doesn't get picked up by other overlays.
|
|
907
|
+
// TODO(crisbeto): we should switch `_getOutsideClickStream` eventually to use this stream,
|
|
908
|
+
// but the behvior isn't exactly the same and it ends up breaking some internal tests.
|
|
909
|
+
this._outsideClickSubscription = overlayRef.outsidePointerEvents().subscribe();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
this._keydownSubscription?.unsubscribe();
|
|
914
|
+
this._outsideClickSubscription?.unsubscribe();
|
|
915
|
+
this._keydownSubscription = this._outsideClickSubscription = null;
|
|
820
916
|
}
|
|
821
917
|
}
|
|
822
918
|
_getOverlayConfig() {
|
|
@@ -941,34 +1037,6 @@ class _MatAutocompleteTriggerBase {
|
|
|
941
1037
|
}
|
|
942
1038
|
}
|
|
943
1039
|
}
|
|
944
|
-
/** Handles keyboard events coming from the overlay panel. */
|
|
945
|
-
_handleOverlayEvents(overlayRef) {
|
|
946
|
-
// Use the `keydownEvents` in order to take advantage of
|
|
947
|
-
// the overlay event targeting provided by the CDK overlay.
|
|
948
|
-
overlayRef.keydownEvents().subscribe(event => {
|
|
949
|
-
// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
|
|
950
|
-
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
|
|
951
|
-
if ((event.keyCode === ESCAPE && !hasModifierKey(event)) ||
|
|
952
|
-
(event.keyCode === UP_ARROW && hasModifierKey(event, 'altKey'))) {
|
|
953
|
-
// If the user had typed something in before we autoselected an option, and they decided
|
|
954
|
-
// to cancel the selection, restore the input value to the one they had typed in.
|
|
955
|
-
if (this._pendingAutoselectedOption) {
|
|
956
|
-
this._updateNativeInputValue(this._valueBeforeAutoSelection ?? '');
|
|
957
|
-
this._pendingAutoselectedOption = null;
|
|
958
|
-
}
|
|
959
|
-
this._closeKeyEventStream.next();
|
|
960
|
-
this._resetActiveItem();
|
|
961
|
-
// We need to stop propagation, otherwise the event will eventually
|
|
962
|
-
// reach the input itself and cause the overlay to be reopened.
|
|
963
|
-
event.stopPropagation();
|
|
964
|
-
event.preventDefault();
|
|
965
|
-
}
|
|
966
|
-
});
|
|
967
|
-
// Subscribe to the pointer events stream so that it doesn't get picked up by other overlays.
|
|
968
|
-
// TODO(crisbeto): we should switch `_getOutsideClickStream` eventually to use this stream,
|
|
969
|
-
// but the behvior isn't exactly the same and it ends up breaking some internal tests.
|
|
970
|
-
overlayRef.outsidePointerEvents().subscribe();
|
|
971
|
-
}
|
|
972
1040
|
/**
|
|
973
1041
|
* If the autocomplete trigger is inside of an `aria-modal` element, connect
|
|
974
1042
|
* that modal to the options panel with `aria-owns`.
|