@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.
Files changed (120) hide show
  1. package/autocomplete/index.d.ts +34 -4
  2. package/badge/_badge-theme.scss +84 -118
  3. package/bottom-sheet/_bottom-sheet-theme.scss +10 -11
  4. package/button/_fab-theme.scss +70 -43
  5. package/button-toggle/_button-toggle-theme.scss +20 -97
  6. package/button-toggle/_button-toggle-variables.scss +2 -0
  7. package/card/_card-theme.scss +20 -4
  8. package/checkbox/_checkbox-theme.scss +5 -3
  9. package/chips/index.d.ts +11 -4
  10. package/core/index.d.ts +3 -2
  11. package/core/tokens/_token-utils.scss +2 -0
  12. package/core/tokens/m2/mat/_badge.scss +78 -0
  13. package/core/tokens/m2/mat/_bottom-sheet.scss +56 -0
  14. package/core/tokens/m2/mat/_datepicker.scss +184 -0
  15. package/core/tokens/m2/mat/_divider.scss +45 -0
  16. package/core/tokens/m2/mat/_expansion.scss +90 -0
  17. package/core/tokens/m2/mat/_legacy-button-toggle.scss +63 -0
  18. package/core/tokens/m2/mat/_sidenav.scss +65 -0
  19. package/core/tokens/m2/mat/_standard-button-toggle.scss +87 -0
  20. package/core/tokens/m2/mat/_toolbar.scss +79 -0
  21. package/core/tokens/m2/mdc/_extended-fab.scss +88 -0
  22. package/core/tokens/m2/mdc/_fab.scss +89 -0
  23. package/datepicker/_datepicker-theme.scss +44 -203
  24. package/divider/_divider-theme.scss +6 -8
  25. package/esm2022/autocomplete/autocomplete-trigger.mjs +85 -38
  26. package/esm2022/autocomplete/autocomplete.mjs +25 -4
  27. package/esm2022/bottom-sheet/bottom-sheet-container.mjs +3 -3
  28. package/esm2022/button/fab.mjs +8 -8
  29. package/esm2022/button-toggle/button-toggle.mjs +2 -2
  30. package/esm2022/chips/chip-option.mjs +4 -5
  31. package/esm2022/chips/chip-row.mjs +15 -10
  32. package/esm2022/chips/chip.mjs +29 -12
  33. package/esm2022/core/option/option.mjs +9 -5
  34. package/esm2022/core/private/ripple-loader.mjs +4 -1
  35. package/esm2022/core/version.mjs +1 -1
  36. package/esm2022/datepicker/calendar-body.mjs +2 -2
  37. package/esm2022/datepicker/calendar.mjs +2 -2
  38. package/esm2022/datepicker/date-range-input.mjs +2 -2
  39. package/esm2022/datepicker/datepicker-base.mjs +3 -3
  40. package/esm2022/datepicker/datepicker-toggle.mjs +2 -2
  41. package/esm2022/divider/divider.mjs +2 -2
  42. package/esm2022/expansion/expansion-animations.mjs +5 -2
  43. package/esm2022/expansion/expansion-panel-header.mjs +3 -3
  44. package/esm2022/expansion/expansion-panel.mjs +3 -3
  45. package/esm2022/form-field/form-field.mjs +3 -3
  46. package/esm2022/legacy-autocomplete/autocomplete.mjs +2 -1
  47. package/esm2022/legacy-dialog/testing/dialog-opener.mjs +6 -1
  48. package/esm2022/legacy-menu/menu.mjs +4 -2
  49. package/esm2022/legacy-table/text-column.mjs +6 -2
  50. package/esm2022/legacy-tabs/tab.mjs +6 -3
  51. package/esm2022/legacy-tooltip/tooltip.mjs +5 -2
  52. package/esm2022/menu/menu-item.mjs +4 -4
  53. package/esm2022/menu/menu.mjs +3 -3
  54. package/esm2022/radio/radio.mjs +13 -1
  55. package/esm2022/sidenav/drawer.mjs +3 -3
  56. package/esm2022/sidenav/sidenav.mjs +3 -3
  57. package/esm2022/slide-toggle/slide-toggle.mjs +2 -2
  58. package/esm2022/toolbar/toolbar.mjs +2 -2
  59. package/expansion/_expansion-legacy-index.scss +0 -1
  60. package/expansion/_expansion-theme.import.scss +0 -2
  61. package/expansion/_expansion-theme.scss +12 -68
  62. package/fesm2022/autocomplete.mjs +109 -41
  63. package/fesm2022/autocomplete.mjs.map +1 -1
  64. package/fesm2022/bottom-sheet.mjs +2 -2
  65. package/fesm2022/bottom-sheet.mjs.map +1 -1
  66. package/fesm2022/button-toggle.mjs +2 -2
  67. package/fesm2022/button-toggle.mjs.map +1 -1
  68. package/fesm2022/button.mjs +8 -8
  69. package/fesm2022/button.mjs.map +1 -1
  70. package/fesm2022/chips.mjs +41 -17
  71. package/fesm2022/chips.mjs.map +1 -1
  72. package/fesm2022/core.mjs +12 -5
  73. package/fesm2022/core.mjs.map +1 -1
  74. package/fesm2022/datepicker.mjs +10 -10
  75. package/fesm2022/datepicker.mjs.map +1 -1
  76. package/fesm2022/divider.mjs +2 -2
  77. package/fesm2022/divider.mjs.map +1 -1
  78. package/fesm2022/expansion.mjs +8 -5
  79. package/fesm2022/expansion.mjs.map +1 -1
  80. package/fesm2022/form-field.mjs +2 -2
  81. package/fesm2022/form-field.mjs.map +1 -1
  82. package/fesm2022/legacy-autocomplete.mjs +1 -0
  83. package/fesm2022/legacy-autocomplete.mjs.map +1 -1
  84. package/fesm2022/legacy-dialog/testing.mjs +5 -0
  85. package/fesm2022/legacy-dialog/testing.mjs.map +1 -1
  86. package/fesm2022/legacy-menu.mjs +3 -1
  87. package/fesm2022/legacy-menu.mjs.map +1 -1
  88. package/fesm2022/legacy-table.mjs +5 -1
  89. package/fesm2022/legacy-table.mjs.map +1 -1
  90. package/fesm2022/legacy-tabs.mjs +5 -2
  91. package/fesm2022/legacy-tabs.mjs.map +1 -1
  92. package/fesm2022/legacy-tooltip.mjs +4 -1
  93. package/fesm2022/legacy-tooltip.mjs.map +1 -1
  94. package/fesm2022/menu.mjs +5 -5
  95. package/fesm2022/menu.mjs.map +1 -1
  96. package/fesm2022/radio.mjs +12 -0
  97. package/fesm2022/radio.mjs.map +1 -1
  98. package/fesm2022/sidenav.mjs +4 -4
  99. package/fesm2022/sidenav.mjs.map +1 -1
  100. package/fesm2022/slide-toggle.mjs +2 -2
  101. package/fesm2022/slide-toggle.mjs.map +1 -1
  102. package/fesm2022/toolbar.mjs +2 -2
  103. package/fesm2022/toolbar.mjs.map +1 -1
  104. package/legacy-autocomplete/index.d.ts +1 -0
  105. package/legacy-prebuilt-themes/legacy-deeppurple-amber.css +1 -1
  106. package/legacy-prebuilt-themes/legacy-indigo-pink.css +1 -1
  107. package/legacy-prebuilt-themes/legacy-pink-bluegrey.css +1 -1
  108. package/legacy-prebuilt-themes/legacy-purple-green.css +1 -1
  109. package/package.json +2 -2
  110. package/prebuilt-themes/deeppurple-amber.css +1 -1
  111. package/prebuilt-themes/indigo-pink.css +1 -1
  112. package/prebuilt-themes/pink-bluegrey.css +1 -1
  113. package/prebuilt-themes/purple-green.css +1 -1
  114. package/radio/index.d.ts +4 -1
  115. package/schematics/ng-add/index.js +1 -1
  116. package/schematics/ng-add/index.mjs +1 -1
  117. package/sidenav/_sidenav-theme.scss +6 -69
  118. package/toolbar/_toolbar-theme.scss +25 -71
  119. package/expansion/_expansion-mixins.import.scss +0 -1
  120. 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, ESCAPE, ENTER, UP_ARROW, DOWN_ARROW, TAB } from '@angular/cdk/keycodes';
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.autocomplete._setVisibility();
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.autocomplete.opened.emit();
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
- this.autocomplete._emitSelectEvent(toSelect);
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
- this.autocomplete.options.forEach(option => {
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.autocomplete.opened.emit();
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`.