@angular/cdk-experimental 14.0.0-next.0 → 14.0.0-next.11

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 (143) hide show
  1. package/combobox/combobox-module.d.ts +3 -4
  2. package/combobox/combobox-popup.d.ts +6 -8
  3. package/combobox/combobox.d.ts +15 -18
  4. package/combobox/public-api.d.ts +0 -1
  5. package/dialog/dialog-config.d.ts +74 -40
  6. package/dialog/dialog-container.d.ts +47 -51
  7. package/dialog/dialog-injectors.d.ts +6 -12
  8. package/dialog/dialog-ref.d.ts +45 -38
  9. package/dialog/dialog.d.ts +59 -56
  10. package/esm2020/column-resize/column-resize-directives/column-resize-flex.mjs +4 -4
  11. package/esm2020/column-resize/column-resize-directives/column-resize.mjs +4 -4
  12. package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize-flex.mjs +4 -4
  13. package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize.mjs +4 -4
  14. package/esm2020/column-resize/column-resize-module.mjs +9 -9
  15. package/esm2020/column-resize/column-resize-notifier.mjs +7 -7
  16. package/esm2020/column-resize/column-resize.mjs +6 -6
  17. package/esm2020/column-resize/column-size-store.mjs +4 -4
  18. package/esm2020/column-resize/event-dispatcher.mjs +4 -4
  19. package/esm2020/column-resize/overlay-handle.mjs +4 -4
  20. package/esm2020/column-resize/resizable.mjs +4 -4
  21. package/esm2020/column-resize/resize-strategy.mjs +10 -10
  22. package/esm2020/combobox/combobox-module.mjs +6 -7
  23. package/esm2020/combobox/combobox-popup.mjs +12 -23
  24. package/esm2020/combobox/combobox.mjs +50 -34
  25. package/esm2020/combobox/public-api.mjs +1 -2
  26. package/esm2020/dialog/dialog-config.mjs +33 -22
  27. package/esm2020/dialog/dialog-container.mjs +114 -142
  28. package/esm2020/dialog/dialog-injectors.mjs +6 -10
  29. package/esm2020/dialog/dialog-module.mjs +7 -21
  30. package/esm2020/dialog/dialog-ref.mjs +49 -88
  31. package/esm2020/dialog/dialog.mjs +188 -155
  32. package/esm2020/listbox/listbox-module.mjs +5 -5
  33. package/esm2020/listbox/listbox.mjs +26 -44
  34. package/esm2020/menu/context-menu-trigger.mjs +227 -0
  35. package/esm2020/menu/menu-aim.mjs +24 -19
  36. package/esm2020/menu/menu-bar.mjs +72 -165
  37. package/esm2020/menu/menu-base.mjs +203 -0
  38. package/esm2020/menu/menu-errors.mjs +1 -9
  39. package/esm2020/menu/menu-group.mjs +7 -45
  40. package/esm2020/menu/menu-interface.mjs +1 -1
  41. package/esm2020/menu/menu-item-checkbox.mjs +11 -11
  42. package/esm2020/menu/menu-item-radio.mjs +50 -26
  43. package/esm2020/menu/menu-item-selectable.mjs +18 -33
  44. package/esm2020/menu/menu-item.mjs +133 -88
  45. package/esm2020/menu/menu-module.mjs +12 -14
  46. package/esm2020/menu/menu-stack.mjs +69 -19
  47. package/esm2020/menu/menu-trigger-base.mjs +114 -0
  48. package/esm2020/menu/menu-trigger.mjs +302 -0
  49. package/esm2020/menu/menu.mjs +75 -208
  50. package/esm2020/menu/pointer-focus-tracker.mjs +11 -9
  51. package/esm2020/menu/public-api.mjs +10 -7
  52. package/esm2020/popover-edit/edit-event-dispatcher.mjs +4 -4
  53. package/esm2020/popover-edit/edit-ref.mjs +4 -4
  54. package/esm2020/popover-edit/edit-services.mjs +4 -4
  55. package/esm2020/popover-edit/focus-dispatcher.mjs +4 -4
  56. package/esm2020/popover-edit/focus-escape-notifier.mjs +4 -4
  57. package/esm2020/popover-edit/lens-directives.mjs +10 -10
  58. package/esm2020/popover-edit/polyfill.mjs +3 -21
  59. package/esm2020/popover-edit/popover-edit-module.mjs +5 -5
  60. package/esm2020/popover-edit/popover-edit-position-strategy-factory.mjs +7 -7
  61. package/esm2020/popover-edit/public-api.mjs +2 -2
  62. package/esm2020/popover-edit/table-directives.mjs +16 -16
  63. package/esm2020/scrolling/auto-size-virtual-scroll.mjs +4 -4
  64. package/esm2020/scrolling/scrolling-module.mjs +5 -5
  65. package/esm2020/selection/row-selection.mjs +4 -4
  66. package/esm2020/selection/select-all.mjs +4 -4
  67. package/esm2020/selection/selection-column.mjs +4 -4
  68. package/esm2020/selection/selection-module.mjs +5 -5
  69. package/esm2020/selection/selection-toggle.mjs +4 -4
  70. package/esm2020/selection/selection.mjs +4 -4
  71. package/esm2020/table-scroll-container/table-scroll-container-module.mjs +5 -5
  72. package/esm2020/table-scroll-container/table-scroll-container.mjs +4 -4
  73. package/esm2020/version.mjs +1 -1
  74. package/fesm2015/cdk-experimental.mjs +1 -1
  75. package/fesm2015/cdk-experimental.mjs.map +1 -1
  76. package/fesm2015/column-resize.mjs +52 -52
  77. package/fesm2015/column-resize.mjs.map +1 -1
  78. package/fesm2015/combobox.mjs +63 -106
  79. package/fesm2015/combobox.mjs.map +1 -1
  80. package/fesm2015/dialog.mjs +385 -434
  81. package/fesm2015/dialog.mjs.map +1 -1
  82. package/fesm2015/listbox.mjs +31 -47
  83. package/fesm2015/listbox.mjs.map +1 -1
  84. package/fesm2015/menu.mjs +1118 -1142
  85. package/fesm2015/menu.mjs.map +1 -1
  86. package/fesm2015/popover-edit.mjs +53 -70
  87. package/fesm2015/popover-edit.mjs.map +1 -1
  88. package/fesm2015/scrolling.mjs +7 -7
  89. package/fesm2015/scrolling.mjs.map +1 -1
  90. package/fesm2015/selection.mjs +19 -19
  91. package/fesm2015/selection.mjs.map +1 -1
  92. package/fesm2015/table-scroll-container.mjs +7 -7
  93. package/fesm2015/table-scroll-container.mjs.map +1 -1
  94. package/fesm2020/cdk-experimental.mjs +1 -1
  95. package/fesm2020/cdk-experimental.mjs.map +1 -1
  96. package/fesm2020/column-resize.mjs +52 -52
  97. package/fesm2020/column-resize.mjs.map +1 -1
  98. package/fesm2020/combobox.mjs +62 -103
  99. package/fesm2020/combobox.mjs.map +1 -1
  100. package/fesm2020/dialog.mjs +386 -426
  101. package/fesm2020/dialog.mjs.map +1 -1
  102. package/fesm2020/listbox.mjs +29 -46
  103. package/fesm2020/listbox.mjs.map +1 -1
  104. package/fesm2020/menu.mjs +1045 -1061
  105. package/fesm2020/menu.mjs.map +1 -1
  106. package/fesm2020/popover-edit.mjs +52 -70
  107. package/fesm2020/popover-edit.mjs.map +1 -1
  108. package/fesm2020/scrolling.mjs +7 -7
  109. package/fesm2020/scrolling.mjs.map +1 -1
  110. package/fesm2020/selection.mjs +19 -19
  111. package/fesm2020/selection.mjs.map +1 -1
  112. package/fesm2020/table-scroll-container.mjs +7 -7
  113. package/fesm2020/table-scroll-container.mjs.map +1 -1
  114. package/listbox/listbox.d.ts +16 -13
  115. package/menu/context-menu-trigger.d.ts +98 -0
  116. package/menu/menu-aim.d.ts +20 -9
  117. package/menu/menu-bar.d.ts +24 -60
  118. package/menu/menu-base.d.ts +104 -0
  119. package/menu/menu-errors.d.ts +0 -5
  120. package/menu/menu-group.d.ts +3 -28
  121. package/menu/menu-interface.d.ts +7 -5
  122. package/menu/menu-item-checkbox.d.ts +8 -2
  123. package/menu/menu-item-radio.d.ts +32 -8
  124. package/menu/menu-item-selectable.d.ts +4 -14
  125. package/menu/menu-item.d.ts +64 -24
  126. package/menu/menu-module.d.ts +10 -10
  127. package/menu/menu-stack.d.ts +64 -20
  128. package/menu/menu-trigger-base.d.ts +77 -0
  129. package/menu/menu-trigger.d.ts +104 -0
  130. package/menu/menu.d.ts +34 -87
  131. package/menu/pointer-focus-tracker.d.ts +7 -4
  132. package/menu/public-api.d.ts +9 -6
  133. package/package.json +3 -3
  134. package/popover-edit/polyfill.d.ts +1 -3
  135. package/popover-edit/public-api.d.ts +1 -1
  136. package/combobox/combobox-panel.d.ts +0 -27
  137. package/esm2020/combobox/combobox-panel.mjs +0 -48
  138. package/esm2020/menu/context-menu.mjs +0 -287
  139. package/esm2020/menu/menu-item-trigger.mjs +0 -361
  140. package/esm2020/menu/menu-panel.mjs +0 -37
  141. package/menu/context-menu.d.ts +0 -122
  142. package/menu/menu-item-trigger.d.ts +0 -119
  143. package/menu/menu-panel.d.ts +0 -30
@@ -1,70 +1,72 @@
1
- import { trigger, state, style, transition, animate } from '@angular/animations';
2
1
  import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
3
2
  import * as i3 from '@angular/cdk/portal';
4
3
  import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal, PortalModule } from '@angular/cdk/portal';
5
- import * as i2 from '@angular/common';
6
4
  import { DOCUMENT } from '@angular/common';
7
5
  import * as i0 from '@angular/core';
8
- import { Component, ViewEncapsulation, ChangeDetectionStrategy, Optional, Inject, ViewChild, InjectionToken, Injector, InjectFlags, Injectable, SkipSelf, NgModule } from '@angular/core';
9
- import { Subject, defer, of } from 'rxjs';
10
- import { distinctUntilChanged, filter, map, startWith } from 'rxjs/operators';
6
+ import { Component, ViewEncapsulation, ChangeDetectionStrategy, Optional, Inject, ViewChild, InjectionToken, Injector, TemplateRef, InjectFlags, Injectable, SkipSelf, NgModule } from '@angular/core';
11
7
  import * as i1 from '@angular/cdk/a11y';
12
8
  import { A11yModule } from '@angular/cdk/a11y';
9
+ import * as i1$1 from '@angular/cdk/overlay';
10
+ import { Overlay, OverlayConfig, OverlayRef, OverlayModule } from '@angular/cdk/overlay';
13
11
  import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
12
+ import { Subject, defer, of } from 'rxjs';
14
13
  import { Directionality } from '@angular/cdk/bidi';
15
- import * as i1$1 from '@angular/cdk/overlay';
16
- import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
14
+ import { startWith } from 'rxjs/operators';
17
15
 
16
+ /**
17
+ * @license
18
+ * Copyright Google LLC All Rights Reserved.
19
+ *
20
+ * Use of this source code is governed by an MIT-style license that can be
21
+ * found in the LICENSE file at https://angular.io/license
22
+ */
23
+ /** Configuration for opening a modal dialog. */
18
24
  class DialogConfig {
19
25
  constructor() {
20
- /** The ARIA role of the dialog. */
26
+ /** The ARIA role of the dialog element. */
21
27
  this.role = 'dialog';
22
- /** Custom class(es) for the overlay panel. */
28
+ /** Optional CSS class or classes applied to the overlay panel. */
23
29
  this.panelClass = '';
24
- /** Whether the dialog has a background. */
30
+ /** Whether the dialog has a backdrop. */
25
31
  this.hasBackdrop = true;
26
- /** Custom class(es) for the backdrop. */
32
+ /** Optional CSS class or classes applied to the overlay backdrop. */
27
33
  this.backdropClass = '';
28
- /** Whether the dialog can be closed by user interaction. */
34
+ /** Whether the dialog closes with the escape key or pointer events outside the panel element. */
29
35
  this.disableClose = false;
30
- /** The width of the dialog. */
36
+ /** Width of the dialog. */
31
37
  this.width = '';
32
- /** The height of the dialog. */
38
+ /** Height of the dialog. */
33
39
  this.height = '';
34
- /** The minimum width of the dialog. */
35
- this.minWidth = '';
36
- /** The minimum height of the dialog. */
37
- this.minHeight = '';
38
- /** The maximum width of the dialog. */
39
- this.maxWidth = '80vw';
40
- /** The maximum height of the dialog. */
41
- this.maxHeight = '';
42
- /** Data to be injected into the dialog content. */
40
+ /** Data being injected into the child component. */
43
41
  this.data = null;
44
42
  /** ID of the element that describes the dialog. */
45
43
  this.ariaDescribedBy = null;
46
- /** Aria label to assign to the dialog element */
44
+ /** ID of the element that labels the dialog. */
45
+ this.ariaLabelledBy = null;
46
+ /** Dialog label applied via `aria-label` */
47
47
  this.ariaLabel = null;
48
+ /** Whether this a modal dialog. Used to set the `aria-modal` attribute. */
49
+ this.ariaModal = true;
48
50
  /**
49
51
  * Where the dialog should focus on open.
50
52
  * @breaking-change 14.0.0 Remove boolean option from autoFocus. Use string or
51
53
  * AutoFocusTarget instead.
52
54
  */
53
55
  this.autoFocus = 'first-tabbable';
54
- /** Duration of the enter animation. Has to be a valid CSS value (e.g. 100ms). */
55
- this.enterAnimationDuration = '225ms';
56
- /** Duration of the exit animation. Has to be a valid CSS value (e.g. 50ms). */
57
- this.exitAnimationDuration = '225ms';
56
+ /**
57
+ * Whether the dialog should restore focus to the
58
+ * previously-focused element upon closing.
59
+ */
60
+ this.restoreFocus = true;
61
+ /**
62
+ * Whether the dialog should close when the user navigates backwards or forwards through browser
63
+ * history. This does not apply to navigation via anchor element unless using URL-hash based
64
+ * routing (`HashLocationStrategy` in the Angular router).
65
+ */
66
+ this.closeOnNavigation = true;
58
67
  }
59
68
  }
60
69
 
61
- /**
62
- * @license
63
- * Copyright Google LLC All Rights Reserved.
64
- *
65
- * Use of this source code is governed by an MIT-style license that can be
66
- * found in the LICENSE file at https://angular.io/license
67
- */
68
70
  function throwDialogContentAlreadyAttachedError() {
69
71
  throw Error('Attempting to attach dialog content after content is already attached');
70
72
  }
@@ -73,32 +75,23 @@ function throwDialogContentAlreadyAttachedError() {
73
75
  * @docs-private
74
76
  */
75
77
  class CdkDialogContainer extends BasePortalOutlet {
76
- constructor(_elementRef, _focusTrapFactory, _changeDetectorRef, _interactivityChecker, _ngZone, _document,
77
- /** The dialog configuration. */
78
- _config) {
78
+ constructor(_elementRef, _focusTrapFactory, _document, _config, _interactivityChecker, _ngZone, _overlayRef, _focusMonitor) {
79
79
  super();
80
80
  this._elementRef = _elementRef;
81
81
  this._focusTrapFactory = _focusTrapFactory;
82
- this._changeDetectorRef = _changeDetectorRef;
82
+ this._config = _config;
83
83
  this._interactivityChecker = _interactivityChecker;
84
84
  this._ngZone = _ngZone;
85
- this._config = _config;
86
- /** State of the dialog animation. */
87
- this._state = 'enter';
85
+ this._overlayRef = _overlayRef;
86
+ this._focusMonitor = _focusMonitor;
88
87
  /** Element that was focused before the dialog was opened. Save this to restore upon close. */
89
88
  this._elementFocusedBeforeDialogWasOpened = null;
90
- /** The class that traps and manages focus within the dialog. */
91
- this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
92
- /** A subject emitting before the dialog enters the view. */
93
- this._beforeEnter = new Subject();
94
- /** A subject emitting after the dialog enters the view. */
95
- this._afterEnter = new Subject();
96
- /** A subject emitting before the dialog exits the view. */
97
- this._beforeExit = new Subject();
98
- /** A subject emitting after the dialog exits the view. */
99
- this._afterExit = new Subject();
100
- /** Stream of animation `done` events. */
101
- this._animationDone = new Subject();
89
+ /**
90
+ * Type of interaction that led to the dialog being closed. This is used to determine
91
+ * whether the focus style will be applied when returning focus to its original location
92
+ * after the dialog is closed.
93
+ */
94
+ this._closeInteractionType = null;
102
95
  /**
103
96
  * Attaches a DOM portal to the dialog container.
104
97
  * @param portal Portal to be attached.
@@ -106,97 +99,48 @@ class CdkDialogContainer extends BasePortalOutlet {
106
99
  * @breaking-change 10.0.0
107
100
  */
108
101
  this.attachDomPortal = (portal) => {
109
- if (this._portalHost.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
102
+ if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
110
103
  throwDialogContentAlreadyAttachedError();
111
104
  }
112
- return this._portalHost.attachDomPortal(portal);
105
+ return this._portalOutlet.attachDomPortal(portal);
113
106
  };
107
+ this._ariaLabelledBy = this._config.ariaLabelledBy || null;
114
108
  this._document = _document;
115
- // We use a Subject with a distinctUntilChanged, rather than a callback attached to .done,
116
- // because some browsers fire the done event twice and we don't want to emit duplicate events.
117
- // See: https://github.com/angular/angular/issues/24084
118
- this._animationDone
119
- .pipe(distinctUntilChanged((x, y) => {
120
- return x.fromState === y.fromState && x.toState === y.toState;
121
- }))
122
- .subscribe(event => {
123
- // Emit lifecycle events based on animation `done` callback.
124
- if (event.toState === 'enter') {
125
- this._autoFocus();
126
- this._afterEnter.next();
127
- this._afterEnter.complete();
128
- }
129
- if (event.fromState === 'enter' && (event.toState === 'void' || event.toState === 'exit')) {
130
- this._returnFocusAfterDialog();
131
- this._afterExit.next();
132
- this._afterExit.complete();
133
- }
134
- });
135
109
  }
136
- /** Initializes the dialog container with the attached content. */
137
- _initializeWithAttachedContent() {
138
- // Save the previously focused element. This element will be re-focused
139
- // when the dialog closes.
140
- this._savePreviouslyFocusedElement();
141
- // Move focus onto the dialog immediately in order to prevent the user
142
- // from accidentally opening multiple dialogs at the same time.
143
- this._focusDialogContainer();
110
+ ngAfterViewInit() {
111
+ this._initializeFocusTrap();
112
+ this._handleBackdropClicks();
113
+ this._captureInitialFocus();
114
+ }
115
+ /**
116
+ * Can be used by child classes to customize the initial focus
117
+ * capturing behavior (e.g. if it's tied to an animation).
118
+ */
119
+ _captureInitialFocus() {
120
+ this._trapFocus();
144
121
  }
145
- /** Destroy focus trap to place focus back to the element focused before the dialog opened. */
146
122
  ngOnDestroy() {
147
- this._focusTrap.destroy();
148
- this._animationDone.complete();
123
+ this._restoreFocus();
149
124
  }
150
125
  /**
151
126
  * Attach a ComponentPortal as content to this dialog container.
152
127
  * @param portal Portal to be attached as the dialog content.
153
128
  */
154
129
  attachComponentPortal(portal) {
155
- if (this._portalHost.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
130
+ if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
156
131
  throwDialogContentAlreadyAttachedError();
157
132
  }
158
- return this._portalHost.attachComponentPortal(portal);
133
+ return this._portalOutlet.attachComponentPortal(portal);
159
134
  }
160
135
  /**
161
136
  * Attach a TemplatePortal as content to this dialog container.
162
137
  * @param portal Portal to be attached as the dialog content.
163
138
  */
164
139
  attachTemplatePortal(portal) {
165
- if (this._portalHost.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
140
+ if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
166
141
  throwDialogContentAlreadyAttachedError();
167
142
  }
168
- return this._portalHost.attachTemplatePortal(portal);
169
- }
170
- /** Emit lifecycle events based on animation `start` callback. */
171
- _onAnimationStart(event) {
172
- if (event.toState === 'enter') {
173
- this._beforeEnter.next();
174
- this._beforeEnter.complete();
175
- }
176
- if (event.fromState === 'enter' && (event.toState === 'void' || event.toState === 'exit')) {
177
- this._beforeExit.next();
178
- this._beforeExit.complete();
179
- }
180
- }
181
- /** Starts the dialog exit animation. */
182
- _startExiting() {
183
- this._state = 'exit';
184
- // Mark the container for check so it can react if the
185
- // view container is using OnPush change detection.
186
- this._changeDetectorRef.markForCheck();
187
- }
188
- /** Saves a reference to the element that was focused before the dialog was opened. */
189
- _savePreviouslyFocusedElement() {
190
- if (this._document) {
191
- this._elementFocusedBeforeDialogWasOpened = _getFocusedElementPierceShadowDom();
192
- }
193
- }
194
- /** Focuses the dialog container. */
195
- _focusDialogContainer() {
196
- // Note that there is no focus method when rendering on the server.
197
- if (this._elementRef.nativeElement.focus) {
198
- this._elementRef.nativeElement.focus();
199
- }
143
+ return this._portalOutlet.attachTemplatePortal(portal);
200
144
  }
201
145
  /**
202
146
  * Focuses the provided element. If the element is not focusable, it will add a tabIndex
@@ -208,8 +152,13 @@ class CdkDialogContainer extends BasePortalOutlet {
208
152
  element.tabIndex = -1;
209
153
  // The tabindex attribute should be removed to avoid navigating to that element again
210
154
  this._ngZone.runOutsideAngular(() => {
211
- element.addEventListener('blur', () => element.removeAttribute('tabindex'));
212
- element.addEventListener('mousedown', () => element.removeAttribute('tabindex'));
155
+ const callback = () => {
156
+ element.removeEventListener('blur', callback);
157
+ element.removeEventListener('mousedown', callback);
158
+ element.removeAttribute('tabindex');
159
+ };
160
+ element.addEventListener('blur', callback);
161
+ element.addEventListener('mousedown', callback);
213
162
  });
214
163
  }
215
164
  element.focus(options);
@@ -225,10 +174,10 @@ class CdkDialogContainer extends BasePortalOutlet {
225
174
  }
226
175
  }
227
176
  /**
228
- * Autofocus the element specified by the autoFocus field. When autoFocus is not 'dialog', if
229
- * for some reason the element cannot be focused, the dialog container will be focused.
177
+ * Moves the focus inside the focus trap. When autoFocus is not set to 'dialog', if focus
178
+ * cannot be moved then focus will go to the dialog container.
230
179
  */
231
- _autoFocus() {
180
+ _trapFocus() {
232
181
  const element = this._elementRef.nativeElement;
233
182
  // If were to attempt to focus immediately, then the content of the dialog would not yet be
234
183
  // ready in instances where change detection has to run first. To deal with this, we simply
@@ -238,21 +187,22 @@ class CdkDialogContainer extends BasePortalOutlet {
238
187
  switch (this._config.autoFocus) {
239
188
  case false:
240
189
  case 'dialog':
241
- const activeElement = _getFocusedElementPierceShadowDom();
242
190
  // Ensure that focus is on the dialog container. It's possible that a different
243
191
  // component tried to move focus while the open animation was running. See:
244
192
  // https://github.com/angular/components/issues/16215. Note that we only want to do this
245
193
  // if the focus isn't inside the dialog already, because it's possible that the consumer
246
194
  // turned off `autoFocus` in order to move focus themselves.
247
- if (activeElement !== element && !element.contains(activeElement)) {
195
+ if (!this._containsFocus()) {
248
196
  element.focus();
249
197
  }
250
198
  break;
251
199
  case true:
252
200
  case 'first-tabbable':
253
- this._focusTrap.focusInitialElementWhenReady().then(hasMovedFocus => {
254
- if (!hasMovedFocus) {
255
- element.focus();
201
+ this._focusTrap.focusInitialElementWhenReady().then(focusedSuccessfully => {
202
+ // If we weren't able to find a focusable element in the dialog, then focus the dialog
203
+ // container instead.
204
+ if (!focusedSuccessfully) {
205
+ this._focusDialogContainer();
256
206
  }
257
207
  });
258
208
  break;
@@ -264,11 +214,13 @@ class CdkDialogContainer extends BasePortalOutlet {
264
214
  break;
265
215
  }
266
216
  }
267
- /** Returns the focus to the element focused before the dialog was open. */
268
- _returnFocusAfterDialog() {
269
- const toFocus = this._elementFocusedBeforeDialogWasOpened;
217
+ /** Restores focus to the element that was focused before the dialog opened. */
218
+ _restoreFocus() {
219
+ const previousElement = this._elementFocusedBeforeDialogWasOpened;
270
220
  // We need the extra check, because IE can set the `activeElement` to null in some cases.
271
- if (toFocus && typeof toFocus.focus === 'function') {
221
+ if (this._config.restoreFocus &&
222
+ previousElement &&
223
+ typeof previousElement.focus === 'function') {
272
224
  const activeElement = _getFocusedElementPierceShadowDom();
273
225
  const element = this._elementRef.nativeElement;
274
226
  // Make sure that focus is still inside the dialog or is on the body (usually because a
@@ -279,53 +231,77 @@ class CdkDialogContainer extends BasePortalOutlet {
279
231
  activeElement === this._document.body ||
280
232
  activeElement === element ||
281
233
  element.contains(activeElement)) {
282
- toFocus.focus();
234
+ if (this._focusMonitor) {
235
+ this._focusMonitor.focusVia(previousElement, this._closeInteractionType);
236
+ this._closeInteractionType = null;
237
+ }
238
+ else {
239
+ previousElement.focus();
240
+ }
283
241
  }
284
242
  }
243
+ if (this._focusTrap) {
244
+ this._focusTrap.destroy();
245
+ }
246
+ }
247
+ /** Focuses the dialog container. */
248
+ _focusDialogContainer() {
249
+ // Note that there is no focus method when rendering on the server.
250
+ if (this._elementRef.nativeElement.focus) {
251
+ this._elementRef.nativeElement.focus();
252
+ }
253
+ }
254
+ /** Returns whether focus is inside the dialog. */
255
+ _containsFocus() {
256
+ const element = this._elementRef.nativeElement;
257
+ const activeElement = _getFocusedElementPierceShadowDom();
258
+ return element === activeElement || element.contains(activeElement);
259
+ }
260
+ /** Sets up the focus trap. */
261
+ _initializeFocusTrap() {
262
+ this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
263
+ // Save the previously focused element. This element will be re-focused
264
+ // when the dialog closes.
265
+ if (this._document) {
266
+ this._elementFocusedBeforeDialogWasOpened = _getFocusedElementPierceShadowDom();
267
+ }
268
+ }
269
+ /** Sets up the listener that handles clicks on the dialog backdrop. */
270
+ _handleBackdropClicks() {
271
+ // Clicking on the backdrop will move focus out of dialog.
272
+ // Recapture it if closing via the backdrop is disabled.
273
+ this._overlayRef.backdropClick().subscribe(() => {
274
+ if (this._config.disableClose && !this._containsFocus()) {
275
+ this._trapFocus();
276
+ }
277
+ });
285
278
  }
286
279
  }
287
- CdkDialogContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: CdkDialogContainer, deps: [{ token: i0.ElementRef }, { token: i1.FocusTrapFactory }, { token: i0.ChangeDetectorRef }, { token: i1.InteractivityChecker }, { token: i0.NgZone }, { token: DOCUMENT, optional: true }, { token: DialogConfig }], target: i0.ɵɵFactoryTarget.Component });
288
- CdkDialogContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.0-rc.1", type: CdkDialogContainer, selector: "cdk-dialog-container", host: { attributes: { "tabindex": "-1", "aria-modal": "true" }, listeners: { "@dialog.start": "_onAnimationStart($event)", "@dialog.done": "_animationDone.next($event)" }, properties: { "@dialog": "{\n value: _state,\n params: {\n enterAnimationDuration: _config.enterAnimationDuration,\n exitAnimationDuration: _config.exitAnimationDuration\n }\n }", "attr.role": "_config.role", "attr.aria-label": "_config.ariaLabel || null", "attr.aria-describedby": "_config.ariaDescribedBy" } }, viewQueries: [{ propertyName: "_portalHost", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<ng-template cdkPortalOutlet></ng-template>\n", styles: ["cdk-dialog-container{background:#fff;border-radius:5px;display:block;padding:10px}\n"], directives: [{ type: i3.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], animations: [
289
- trigger('dialog', [
290
- state('enter', style({ opacity: 1 })),
291
- state('exit, void', style({ opacity: 0 })),
292
- transition('* => enter', animate('{{enterAnimationDuration}}')),
293
- transition('* => exit, * => void', animate('{{exitAnimationDuration}}')),
294
- ]),
295
- ], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None });
296
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: CdkDialogContainer, decorators: [{
280
+ CdkDialogContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: CdkDialogContainer, deps: [{ token: i0.ElementRef }, { token: i1.FocusTrapFactory }, { token: DOCUMENT, optional: true }, { token: DialogConfig }, { token: i1.InteractivityChecker }, { token: i0.NgZone }, { token: i1$1.OverlayRef }, { token: i1.FocusMonitor }], target: i0.ɵɵFactoryTarget.Component });
281
+ CdkDialogContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "14.0.0-next.13", type: CdkDialogContainer, selector: "cdk-dialog-container", host: { attributes: { "tabindex": "-1" }, properties: { "attr.id": "_config.id || null", "attr.role": "_config.role", "attr.aria-modal": "_config.ariaModal", "attr.aria-labelledby": "_config.ariaLabel ? null : _ariaLabelledBy", "attr.aria-label": "_config.ariaLabel", "attr.aria-describedby": "_config.ariaDescribedBy || null" }, classAttribute: "cdk-dialog-container" }, viewQueries: [{ propertyName: "_portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<ng-template cdkPortalOutlet></ng-template>\n", directives: [{ type: i3.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None });
282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: CdkDialogContainer, decorators: [{
297
283
  type: Component,
298
- args: [{ selector: 'cdk-dialog-container', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, animations: [
299
- trigger('dialog', [
300
- state('enter', style({ opacity: 1 })),
301
- state('exit, void', style({ opacity: 0 })),
302
- transition('* => enter', animate('{{enterAnimationDuration}}')),
303
- transition('* => exit, * => void', animate('{{exitAnimationDuration}}')),
304
- ]),
305
- ], host: {
306
- '[@dialog]': `{
307
- value: _state,
308
- params: {
309
- enterAnimationDuration: _config.enterAnimationDuration,
310
- exitAnimationDuration: _config.exitAnimationDuration
311
- }
312
- }`,
313
- '(@dialog.start)': '_onAnimationStart($event)',
314
- '(@dialog.done)': '_animationDone.next($event)',
284
+ args: [{ selector: 'cdk-dialog-container', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, host: {
285
+ 'class': 'cdk-dialog-container',
315
286
  'tabindex': '-1',
287
+ '[attr.id]': '_config.id || null',
316
288
  '[attr.role]': '_config.role',
317
- 'aria-modal': 'true',
318
- '[attr.aria-label]': '_config.ariaLabel || null',
319
- '[attr.aria-describedby]': '_config.ariaDescribedBy',
320
- }, template: "<ng-template cdkPortalOutlet></ng-template>\n", styles: ["cdk-dialog-container{background:#fff;border-radius:5px;display:block;padding:10px}\n"] }]
289
+ '[attr.aria-modal]': '_config.ariaModal',
290
+ '[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',
291
+ '[attr.aria-label]': '_config.ariaLabel',
292
+ '[attr.aria-describedby]': '_config.ariaDescribedBy || null',
293
+ }, template: "<ng-template cdkPortalOutlet></ng-template>\n" }]
321
294
  }], ctorParameters: function () {
322
- return [{ type: i0.ElementRef }, { type: i1.FocusTrapFactory }, { type: i0.ChangeDetectorRef }, { type: i1.InteractivityChecker }, { type: i0.NgZone }, { type: undefined, decorators: [{
295
+ return [{ type: i0.ElementRef }, { type: i1.FocusTrapFactory }, { type: undefined, decorators: [{
323
296
  type: Optional
324
297
  }, {
325
298
  type: Inject,
326
299
  args: [DOCUMENT]
327
- }] }, { type: DialogConfig }];
328
- }, propDecorators: { _portalHost: [{
300
+ }] }, { type: undefined, decorators: [{
301
+ type: Inject,
302
+ args: [DialogConfig]
303
+ }] }, { type: i1.InteractivityChecker }, { type: i0.NgZone }, { type: i1$1.OverlayRef }, { type: i1.FocusMonitor }];
304
+ }, propDecorators: { _portalOutlet: [{
329
305
  type: ViewChild,
330
306
  args: [CdkPortalOutlet, { static: true }]
331
307
  }] } });
@@ -337,110 +313,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ng
337
313
  * Use of this source code is governed by an MIT-style license that can be
338
314
  * found in the LICENSE file at https://angular.io/license
339
315
  */
340
- /** Unique id for the created dialog. */
341
- let uniqueId = 0;
342
316
  /**
343
317
  * Reference to a dialog opened via the Dialog service.
344
318
  */
345
319
  class DialogRef {
346
- constructor(_overlayRef, _containerInstance, id = `dialog-${uniqueId++}`) {
347
- this._overlayRef = _overlayRef;
348
- this._containerInstance = _containerInstance;
349
- this.id = id;
350
- // If the dialog has a backdrop, handle clicks from the backdrop.
351
- if (_containerInstance._config.hasBackdrop) {
352
- _overlayRef.backdropClick().subscribe(() => {
353
- if (!this.disableClose) {
354
- this.close();
355
- }
356
- });
357
- }
358
- this.beforeClosed().subscribe(() => {
359
- this._overlayRef.detachBackdrop();
360
- });
361
- this.afterClosed().subscribe(() => {
362
- this._overlayRef.detach();
363
- this._overlayRef.dispose();
364
- this.componentInstance = null;
320
+ constructor(overlayRef, config) {
321
+ this.overlayRef = overlayRef;
322
+ this.config = config;
323
+ /** Emits when the dialog has been closed. */
324
+ this.closed = new Subject();
325
+ this.disableClose = config.disableClose;
326
+ this.backdropClick = overlayRef.backdropClick();
327
+ this.keydownEvents = overlayRef.keydownEvents();
328
+ this.outsidePointerEvents = overlayRef.outsidePointerEvents();
329
+ this.id = config.id; // By the time the dialog is created we are guaranteed to have an ID.
330
+ this.keydownEvents.subscribe(event => {
331
+ if (event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)) {
332
+ event.preventDefault();
333
+ this.close(undefined, { focusOrigin: 'keyboard' });
334
+ }
365
335
  });
366
- // Close when escape keydown event occurs
367
- _overlayRef
368
- .keydownEvents()
369
- .pipe(filter(event => {
370
- return event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event);
371
- }))
372
- .subscribe(event => {
373
- event.preventDefault();
374
- this.close();
336
+ this.backdropClick.subscribe(() => {
337
+ if (!this.disableClose) {
338
+ this.close(undefined, { focusOrigin: 'mouse' });
339
+ }
375
340
  });
376
341
  }
377
- /** Gets an observable that emits when the overlay's backdrop has been clicked. */
378
- backdropClick() {
379
- return this._overlayRef.backdropClick();
380
- }
381
342
  /**
382
343
  * Close the dialog.
383
- * @param dialogResult Optional result to return to the dialog opener.
344
+ * @param result Optional result to return to the dialog opener.
345
+ * @param options Additional options to customize the closing behavior.
384
346
  */
385
- close(dialogResult) {
386
- this._result = dialogResult;
387
- this._containerInstance._startExiting();
388
- }
389
- /**
390
- * Updates the dialog's position.
391
- * @param position New dialog position.
392
- */
393
- updatePosition(position) {
394
- let strategy = this._getPositionStrategy();
395
- if (position && (position.left || position.right)) {
396
- position.left ? strategy.left(position.left) : strategy.right(position.right);
397
- }
398
- else {
399
- strategy.centerHorizontally();
400
- }
401
- if (position && (position.top || position.bottom)) {
402
- position.top ? strategy.top(position.top) : strategy.bottom(position.bottom);
347
+ close(result, options) {
348
+ if (this.containerInstance) {
349
+ const closedSubject = this.closed;
350
+ this.containerInstance._closeInteractionType = (options === null || options === void 0 ? void 0 : options.focusOrigin) || 'program';
351
+ this.overlayRef.dispose();
352
+ closedSubject.next(result);
353
+ closedSubject.complete();
354
+ this.componentInstance = this.containerInstance = null;
403
355
  }
404
- else {
405
- strategy.centerVertically();
406
- }
407
- this._overlayRef.updatePosition();
408
- return this;
409
356
  }
410
- /**
411
- * Gets an observable that emits when keydown events are targeted on the overlay.
412
- */
413
- keydownEvents() {
414
- return this._overlayRef.keydownEvents();
357
+ /** Updates the dialog's position. */
358
+ updatePosition() {
359
+ this.overlayRef.updatePosition();
360
+ return this;
415
361
  }
416
362
  /**
417
- * Updates the dialog's width and height, defined, min and max.
418
- * @param size New size for the overlay.
363
+ * Updates the dialog's width and height.
364
+ * @param width New width of the dialog.
365
+ * @param height New height of the dialog.
419
366
  */
420
- updateSize(size) {
421
- this._overlayRef.updateSize(size);
422
- this._overlayRef.updatePosition();
367
+ updateSize(width = '', height = '') {
368
+ this.overlayRef.updateSize({ width, height });
369
+ this.overlayRef.updatePosition();
423
370
  return this;
424
371
  }
425
- /** Fetches the position strategy object from the overlay ref. */
426
- _getPositionStrategy() {
427
- return this._overlayRef.getConfig().positionStrategy;
428
- }
429
- /** Gets an observable that emits when dialog begins opening. */
430
- beforeOpened() {
431
- return this._containerInstance._beforeEnter;
432
- }
433
- /** Gets an observable that emits when dialog is finished opening. */
434
- afterOpened() {
435
- return this._containerInstance._afterEnter;
436
- }
437
- /** Gets an observable that emits when dialog begins closing. */
438
- beforeClosed() {
439
- return this._containerInstance._beforeExit.pipe(map(() => this._result));
372
+ /** Add a CSS class or an array of classes to the overlay pane. */
373
+ addPanelClass(classes) {
374
+ this.overlayRef.addPanelClass(classes);
375
+ return this;
440
376
  }
441
- /** Gets an observable that emits when dialog is finished closing. */
442
- afterClosed() {
443
- return this._containerInstance._afterExit.pipe(map(() => this._result));
377
+ /** Remove a CSS class or an array of classes from the overlay pane. */
378
+ removePanelClass(classes) {
379
+ this.overlayRef.removePanelClass(classes);
380
+ return this;
444
381
  }
445
382
  }
446
383
 
@@ -455,21 +392,17 @@ class DialogRef {
455
392
  const DIALOG_SCROLL_STRATEGY = new InjectionToken('DialogScrollStrategy');
456
393
  /** Injection token for the Dialog's Data. */
457
394
  const DIALOG_DATA = new InjectionToken('DialogData');
458
- /** Injection token for the DialogRef constructor. */
459
- const DIALOG_REF = new InjectionToken('DialogRef');
460
- /** Injection token for the DialogConfig. */
461
- const DIALOG_CONFIG = new InjectionToken('DialogConfig');
462
- /** Injection token for the Dialog's DialogContainer component. */
463
- const DIALOG_CONTAINER = new InjectionToken('DialogContainer');
395
+ /** Injection token that can be used to provide default options for the dialog module. */
396
+ const DEFAULT_DIALOG_CONFIG = new InjectionToken('DefaultDialogConfig');
464
397
  /** @docs-private */
465
- function MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
398
+ function DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
466
399
  return () => overlay.scrollStrategies.block();
467
400
  }
468
401
  /** @docs-private */
469
- const MAT_DIALOG_SCROLL_STRATEGY_PROVIDER = {
402
+ const DIALOG_SCROLL_STRATEGY_PROVIDER = {
470
403
  provide: DIALOG_SCROLL_STRATEGY,
471
404
  deps: [Overlay],
472
- useFactory: MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,
405
+ useFactory: DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,
473
406
  };
474
407
 
475
408
  /**
@@ -479,112 +412,90 @@ const MAT_DIALOG_SCROLL_STRATEGY_PROVIDER = {
479
412
  * Use of this source code is governed by an MIT-style license that can be
480
413
  * found in the LICENSE file at https://angular.io/license
481
414
  */
482
- /**
483
- * Service to open modal dialogs.
484
- */
415
+ /** Unique id for the created dialog. */
416
+ let uniqueId = 0;
485
417
  class Dialog {
486
- constructor(_overlay, _injector, _dialogRefConstructor,
487
- // TODO(crisbeto): the `any` here can be replaced
488
- // with the proper type once we start using Ivy.
489
- scrollStrategy, _parentDialog, location) {
418
+ constructor(_overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, scrollStrategy) {
490
419
  this._overlay = _overlay;
491
420
  this._injector = _injector;
492
- this._dialogRefConstructor = _dialogRefConstructor;
421
+ this._defaultOptions = _defaultOptions;
493
422
  this._parentDialog = _parentDialog;
494
- this._afterAllClosedBase = new Subject();
495
- // TODO(jelbourn): tighten the type on the right-hand side of this expression.
423
+ this._overlayContainer = _overlayContainer;
424
+ this._openDialogsAtThisLevel = [];
425
+ this._afterAllClosedAtThisLevel = new Subject();
426
+ this._afterOpenedAtThisLevel = new Subject();
427
+ this._ariaHiddenElements = new Map();
428
+ /**
429
+ * Stream that emits when all open dialog have finished closing.
430
+ * Will emit on subscribe if there are no open dialogs to begin with.
431
+ */
496
432
  this.afterAllClosed = defer(() => this.openDialogs.length
497
433
  ? this._getAfterAllClosed()
498
434
  : this._getAfterAllClosed().pipe(startWith(undefined)));
499
- this._afterOpened = new Subject();
500
- this._openDialogs = [];
501
- // Close all of the dialogs when the user goes forwards/backwards in history or when the
502
- // location hash changes. Note that this usually doesn't include clicking on links (unless
503
- // the user is using the `HashLocationStrategy`).
504
- if (!_parentDialog && location) {
505
- location.subscribe(() => this.closeAll());
506
- }
507
435
  this._scrollStrategy = scrollStrategy;
508
436
  }
509
- /** Stream that emits when all dialogs are closed. */
510
- _getAfterAllClosed() {
511
- return this._parentDialog ? this._parentDialog.afterAllClosed : this._afterAllClosedBase;
512
- }
513
- /** Stream that emits when a dialog is opened. */
514
- get afterOpened() {
515
- return this._parentDialog ? this._parentDialog.afterOpened : this._afterOpened;
516
- }
517
- /** Stream that emits when a dialog is opened. */
437
+ /** Keeps track of the currently-open dialogs. */
518
438
  get openDialogs() {
519
- return this._parentDialog ? this._parentDialog.openDialogs : this._openDialogs;
520
- }
521
- /** Gets an open dialog by id. */
522
- getById(id) {
523
- return this._openDialogs.find(ref => ref.id === id);
439
+ return this._parentDialog ? this._parentDialog.openDialogs : this._openDialogsAtThisLevel;
524
440
  }
525
- /** Closes all open dialogs. */
526
- closeAll() {
527
- this.openDialogs.forEach(ref => ref.close());
528
- }
529
- /** Opens a dialog from a component. */
530
- openFromComponent(component, config) {
531
- config = this._applyConfigDefaults(config);
532
- if (config.id && this.getById(config.id) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
441
+ /** Stream that emits when a dialog has been opened. */
442
+ get afterOpened() {
443
+ return this._parentDialog ? this._parentDialog.afterOpened : this._afterOpenedAtThisLevel;
444
+ }
445
+ open(componentOrTemplateRef, config) {
446
+ const defaults = (this._defaultOptions || new DialogConfig());
447
+ config = Object.assign(Object.assign({}, defaults), config);
448
+ config.id = config.id || `cdk-dialog-${uniqueId++}`;
449
+ if (config.id &&
450
+ this.getDialogById(config.id) &&
451
+ (typeof ngDevMode === 'undefined' || ngDevMode)) {
533
452
  throw Error(`Dialog with id "${config.id}" exists already. The dialog id must be unique.`);
534
453
  }
535
- const overlayRef = this._createOverlay(config);
536
- const dialogContainer = this._attachDialogContainer(overlayRef, config);
537
- const dialogRef = this._attachDialogContentForComponent(component, dialogContainer, overlayRef, config);
538
- this._registerDialogRef(dialogRef);
539
- dialogContainer._initializeWithAttachedContent();
540
- return dialogRef;
541
- }
542
- /** Opens a dialog from a template. */
543
- openFromTemplate(template, config) {
544
- config = this._applyConfigDefaults(config);
545
- if (config.id && this.getById(config.id) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
546
- throw Error(`Dialog with id "${config.id}" exists already. The dialog id must be unique.`);
454
+ const overlayConfig = this._getOverlayConfig(config);
455
+ const overlayRef = this._overlay.create(overlayConfig);
456
+ const dialogRef = new DialogRef(overlayRef, config);
457
+ const dialogContainer = this._attachContainer(overlayRef, dialogRef, config);
458
+ dialogRef.containerInstance = dialogContainer;
459
+ this._attachDialogContent(componentOrTemplateRef, dialogRef, dialogContainer, config);
460
+ // If this is the first dialog that we're opening, hide all the non-overlay content.
461
+ if (!this.openDialogs.length) {
462
+ this._hideNonDialogContentFromAssistiveTechnology();
547
463
  }
548
- const overlayRef = this._createOverlay(config);
549
- const dialogContainer = this._attachDialogContainer(overlayRef, config);
550
- const dialogRef = this._attachDialogContentForTemplate(template, dialogContainer, overlayRef, config);
551
- this._registerDialogRef(dialogRef);
552
- dialogContainer._initializeWithAttachedContent();
464
+ this.openDialogs.push(dialogRef);
465
+ dialogRef.closed.subscribe(() => this._removeOpenDialog(dialogRef));
466
+ this.afterOpened.next(dialogRef);
553
467
  return dialogRef;
554
468
  }
555
- ngOnDestroy() {
556
- // Only close all the dialogs at this level.
557
- this._openDialogs.forEach(ref => ref.close());
469
+ /**
470
+ * Closes all of the currently-open dialogs.
471
+ */
472
+ closeAll() {
473
+ this._closeDialogs(this.openDialogs);
558
474
  }
559
475
  /**
560
- * Forwards emitting events for when dialogs are opened and all dialogs are closed.
476
+ * Finds an open dialog by its id.
477
+ * @param id ID to use when looking up the dialog.
561
478
  */
562
- _registerDialogRef(dialogRef) {
563
- this.openDialogs.push(dialogRef);
564
- const dialogOpenSub = dialogRef.afterOpened().subscribe(() => {
565
- this.afterOpened.next(dialogRef);
566
- dialogOpenSub.unsubscribe();
567
- });
568
- const dialogCloseSub = dialogRef.afterClosed().subscribe(() => {
569
- let dialogIndex = this._openDialogs.indexOf(dialogRef);
570
- if (dialogIndex > -1) {
571
- this._openDialogs.splice(dialogIndex, 1);
572
- }
573
- if (!this._openDialogs.length) {
574
- this._afterAllClosedBase.next();
575
- dialogCloseSub.unsubscribe();
576
- }
577
- });
479
+ getDialogById(id) {
480
+ return this.openDialogs.find(dialog => dialog.id === id);
481
+ }
482
+ ngOnDestroy() {
483
+ // Only close the dialogs at this level on destroy
484
+ // since the parent service may still be active.
485
+ this._closeDialogs(this._openDialogsAtThisLevel);
486
+ this._afterAllClosedAtThisLevel.complete();
487
+ this._afterOpenedAtThisLevel.complete();
578
488
  }
579
489
  /**
580
490
  * Creates an overlay config from a dialog config.
581
491
  * @param config The dialog configuration.
582
492
  * @returns The overlay configuration.
583
493
  */
584
- _createOverlay(config) {
585
- const overlayConfig = new OverlayConfig({
586
- positionStrategy: this._overlay.position().global(),
587
- scrollStrategy: this._scrollStrategy(),
494
+ _getOverlayConfig(config) {
495
+ const state = new OverlayConfig({
496
+ positionStrategy: config.positionStrategy ||
497
+ this._overlay.position().global().centerHorizontally().centerVertically(),
498
+ scrollStrategy: config.scrollStrategy || this._scrollStrategy(),
588
499
  panelClass: config.panelClass,
589
500
  hasBackdrop: config.hasBackdrop,
590
501
  direction: config.direction,
@@ -592,82 +503,92 @@ class Dialog {
592
503
  minHeight: config.minHeight,
593
504
  maxWidth: config.maxWidth,
594
505
  maxHeight: config.maxHeight,
506
+ width: config.width,
507
+ height: config.height,
508
+ disposeOnNavigation: config.closeOnNavigation,
595
509
  });
596
510
  if (config.backdropClass) {
597
- overlayConfig.backdropClass = config.backdropClass;
511
+ state.backdropClass = config.backdropClass;
598
512
  }
599
- return this._overlay.create(overlayConfig);
513
+ return state;
600
514
  }
601
515
  /**
602
- * Attaches an MatDialogContainer to a dialog's already-created overlay.
516
+ * Attaches a dialog container to a dialog's already-created overlay.
603
517
  * @param overlay Reference to the dialog's underlying overlay.
604
518
  * @param config The dialog configuration.
605
519
  * @returns A promise resolving to a ComponentRef for the attached container.
606
520
  */
607
- _attachDialogContainer(overlay, config) {
608
- const container = config.containerComponent || this._injector.get(DIALOG_CONTAINER);
609
- const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
610
- const injector = Injector.create({
611
- parent: userInjector || this._injector,
612
- providers: [{ provide: DialogConfig, useValue: config }],
613
- });
614
- const containerPortal = new ComponentPortal(container, config.viewContainerRef, injector);
521
+ _attachContainer(overlay, dialogRef, config) {
522
+ var _a, _b;
523
+ const userInjector = (_a = config.injector) !== null && _a !== void 0 ? _a : (_b = config.viewContainerRef) === null || _b === void 0 ? void 0 : _b.injector;
524
+ const providers = [
525
+ { provide: DialogConfig, useValue: config },
526
+ { provide: DialogRef, useValue: dialogRef },
527
+ { provide: OverlayRef, useValue: overlay },
528
+ ];
529
+ let containerType;
530
+ if (config.container) {
531
+ if (typeof config.container === 'function') {
532
+ containerType = config.container;
533
+ }
534
+ else {
535
+ containerType = config.container.type;
536
+ providers.push(...config.container.providers(config));
537
+ }
538
+ }
539
+ else {
540
+ containerType = CdkDialogContainer;
541
+ }
542
+ const containerPortal = new ComponentPortal(containerType, config.viewContainerRef, Injector.create({ parent: userInjector || this._injector, providers }), config.componentFactoryResolver);
615
543
  const containerRef = overlay.attach(containerPortal);
616
- containerRef.instance._config = config;
617
544
  return containerRef.instance;
618
545
  }
619
546
  /**
620
- * Attaches the user-provided component to the already-created MatDialogContainer.
547
+ * Attaches the user-provided component to the already-created dialog container.
621
548
  * @param componentOrTemplateRef The type of component being loaded into the dialog,
622
549
  * or a TemplateRef to instantiate as the content.
623
- * @param dialogContainer Reference to the wrapping MatDialogContainer.
624
- * @param overlayRef Reference to the overlay in which the dialog resides.
625
- * @param config The dialog configuration.
626
- * @returns A promise resolving to the MatDialogRef that should be returned to the user.
550
+ * @param dialogRef Reference to the dialog being opened.
551
+ * @param dialogContainer Component that is going to wrap the dialog content.
552
+ * @param config Configuration used to open the dialog.
627
553
  */
628
- _attachDialogContentForComponent(componentOrTemplateRef, dialogContainer, overlayRef, config) {
629
- // Create a reference to the dialog we're creating in order to give the user a handle
630
- // to modify and close it.
631
- const dialogRef = this._createDialogRef(overlayRef, dialogContainer, config);
554
+ _attachDialogContent(componentOrTemplateRef, dialogRef, dialogContainer, config) {
632
555
  const injector = this._createInjector(config, dialogRef, dialogContainer);
633
- const contentRef = dialogContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, undefined, injector));
634
- dialogRef.componentInstance = contentRef.instance;
635
- return dialogRef;
636
- }
637
- /**
638
- * Attaches the user-provided component to the already-created MatDialogContainer.
639
- * @param componentOrTemplateRef The type of component being loaded into the dialog,
640
- * or a TemplateRef to instantiate as the content.
641
- * @param dialogContainer Reference to the wrapping MatDialogContainer.
642
- * @param overlayRef Reference to the overlay in which the dialog resides.
643
- * @param config The dialog configuration.
644
- * @returns A promise resolving to the MatDialogRef that should be returned to the user.
645
- */
646
- _attachDialogContentForTemplate(componentOrTemplateRef, dialogContainer, overlayRef, config) {
647
- // Create a reference to the dialog we're creating in order to give the user a handle
648
- // to modify and close it.
649
- const dialogRef = this._createDialogRef(overlayRef, dialogContainer, config);
650
- dialogContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null, {
651
- $implicit: config.data,
652
- dialogRef,
653
- }));
654
- return dialogRef;
556
+ if (componentOrTemplateRef instanceof TemplateRef) {
557
+ let context = { $implicit: config.data, dialogRef };
558
+ if (config.templateContext) {
559
+ context = Object.assign(Object.assign({}, context), (typeof config.templateContext === 'function'
560
+ ? config.templateContext()
561
+ : config.templateContext));
562
+ }
563
+ dialogContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null, context, injector));
564
+ }
565
+ else {
566
+ const contentRef = dialogContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.viewContainerRef, injector, config.componentFactoryResolver));
567
+ dialogRef.componentInstance = contentRef.instance;
568
+ }
655
569
  }
656
570
  /**
657
571
  * Creates a custom injector to be used inside the dialog. This allows a component loaded inside
658
572
  * of a dialog to close itself and, optionally, to return a value.
659
573
  * @param config Config object that is used to construct the dialog.
660
- * @param dialogRef Reference to the dialog.
661
- * @param container Dialog container element that wraps all of the contents.
574
+ * @param dialogRef Reference to the dialog being opened.
575
+ * @param dialogContainer Component that is going to wrap the dialog content.
662
576
  * @returns The custom injector that can be used inside the dialog.
663
577
  */
664
578
  _createInjector(config, dialogRef, dialogContainer) {
665
579
  const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
666
580
  const providers = [
667
- { provide: this._injector.get(DIALOG_REF), useValue: dialogRef },
668
- { provide: this._injector.get(DIALOG_CONTAINER), useValue: dialogContainer },
669
581
  { provide: DIALOG_DATA, useValue: config.data },
582
+ { provide: DialogRef, useValue: dialogRef },
670
583
  ];
584
+ if (config.providers) {
585
+ if (typeof config.providers === 'function') {
586
+ providers.push(...config.providers(dialogRef, config, dialogContainer));
587
+ }
588
+ else {
589
+ providers.push(...config.providers);
590
+ }
591
+ }
671
592
  if (config.direction &&
672
593
  (!userInjector ||
673
594
  !userInjector.get(Directionality, null, InjectFlags.Optional))) {
@@ -678,39 +599,81 @@ class Dialog {
678
599
  }
679
600
  return Injector.create({ parent: userInjector || this._injector, providers });
680
601
  }
681
- /** Creates a new dialog ref. */
682
- _createDialogRef(overlayRef, dialogContainer, config) {
683
- const dialogRef = new this._dialogRefConstructor(overlayRef, dialogContainer, config.id);
684
- dialogRef.disableClose = config.disableClose;
685
- dialogRef.updateSize(config).updatePosition(config.position);
686
- return dialogRef;
687
- }
688
602
  /**
689
- * Expands the provided configuration object to include the default values for properties which
690
- * are undefined.
603
+ * Removes a dialog from the array of open dialogs.
604
+ * @param dialogRef Dialog to be removed.
691
605
  */
692
- _applyConfigDefaults(config) {
693
- const dialogConfig = this._injector.get(DIALOG_CONFIG);
694
- return Object.assign(Object.assign({}, new dialogConfig()), config);
606
+ _removeOpenDialog(dialogRef) {
607
+ const index = this.openDialogs.indexOf(dialogRef);
608
+ if (index > -1) {
609
+ this.openDialogs.splice(index, 1);
610
+ // If all the dialogs were closed, remove/restore the `aria-hidden`
611
+ // to a the siblings and emit to the `afterAllClosed` stream.
612
+ if (!this.openDialogs.length) {
613
+ this._ariaHiddenElements.forEach((previousValue, element) => {
614
+ if (previousValue) {
615
+ element.setAttribute('aria-hidden', previousValue);
616
+ }
617
+ else {
618
+ element.removeAttribute('aria-hidden');
619
+ }
620
+ });
621
+ this._ariaHiddenElements.clear();
622
+ this._getAfterAllClosed().next();
623
+ }
624
+ }
625
+ }
626
+ /** Hides all of the content that isn't an overlay from assistive technology. */
627
+ _hideNonDialogContentFromAssistiveTechnology() {
628
+ const overlayContainer = this._overlayContainer.getContainerElement();
629
+ // Ensure that the overlay container is attached to the DOM.
630
+ if (overlayContainer.parentElement) {
631
+ const siblings = overlayContainer.parentElement.children;
632
+ for (let i = siblings.length - 1; i > -1; i--) {
633
+ const sibling = siblings[i];
634
+ if (sibling !== overlayContainer &&
635
+ sibling.nodeName !== 'SCRIPT' &&
636
+ sibling.nodeName !== 'STYLE' &&
637
+ !sibling.hasAttribute('aria-live')) {
638
+ this._ariaHiddenElements.set(sibling, sibling.getAttribute('aria-hidden'));
639
+ sibling.setAttribute('aria-hidden', 'true');
640
+ }
641
+ }
642
+ }
643
+ }
644
+ /** Closes all of the dialogs in an array. */
645
+ _closeDialogs(dialogs) {
646
+ let i = dialogs.length;
647
+ while (i--) {
648
+ // The `_openDialogs` property isn't updated after close until the rxjs subscription
649
+ // runs on the next microtask, in addition to modifying the array as we're going
650
+ // through it. We loop through all of them and call close without assuming that
651
+ // they'll be removed from the list instantaneously.
652
+ dialogs[i].close();
653
+ }
654
+ }
655
+ _getAfterAllClosed() {
656
+ const parent = this._parentDialog;
657
+ return parent ? parent._getAfterAllClosed() : this._afterAllClosedAtThisLevel;
695
658
  }
696
659
  }
697
- Dialog.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: Dialog, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }, { token: DIALOG_REF }, { token: DIALOG_SCROLL_STRATEGY }, { token: Dialog, optional: true, skipSelf: true }, { token: i2.Location, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
698
- Dialog.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: Dialog });
699
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: Dialog, decorators: [{
660
+ Dialog.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: Dialog, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }, { token: DEFAULT_DIALOG_CONFIG, optional: true }, { token: Dialog, optional: true, skipSelf: true }, { token: i1$1.OverlayContainer }, { token: DIALOG_SCROLL_STRATEGY }], target: i0.ɵɵFactoryTarget.Injectable });
661
+ Dialog.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: Dialog });
662
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: Dialog, decorators: [{
700
663
  type: Injectable
701
664
  }], ctorParameters: function () {
702
- return [{ type: i1$1.Overlay }, { type: i0.Injector }, { type: i0.Type, decorators: [{
703
- type: Inject,
704
- args: [DIALOG_REF]
705
- }] }, { type: undefined, decorators: [{
665
+ return [{ type: i1$1.Overlay }, { type: i0.Injector }, { type: DialogConfig, decorators: [{
666
+ type: Optional
667
+ }, {
706
668
  type: Inject,
707
- args: [DIALOG_SCROLL_STRATEGY]
669
+ args: [DEFAULT_DIALOG_CONFIG]
708
670
  }] }, { type: Dialog, decorators: [{
709
671
  type: Optional
710
672
  }, {
711
673
  type: SkipSelf
712
- }] }, { type: i2.Location, decorators: [{
713
- type: Optional
674
+ }] }, { type: i1$1.OverlayContainer }, { type: undefined, decorators: [{
675
+ type: Inject,
676
+ args: [DIALOG_SCROLL_STRATEGY]
714
677
  }] }];
715
678
  } });
716
679
 
@@ -723,24 +686,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ng
723
686
  */
724
687
  class DialogModule {
725
688
  }
726
- DialogModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: DialogModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
727
- DialogModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: DialogModule, declarations: [CdkDialogContainer], imports: [OverlayModule, PortalModule, A11yModule], exports: [
689
+ DialogModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: DialogModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
690
+ DialogModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: DialogModule, declarations: [CdkDialogContainer], imports: [OverlayModule, PortalModule, A11yModule], exports: [
728
691
  // Re-export the PortalModule so that people extending the `CdkDialogContainer`
729
692
  // don't have to remember to import it or be faced with an unhelpful error.
730
693
  PortalModule,
731
694
  CdkDialogContainer
732
695
  ] });
733
- DialogModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: DialogModule, providers: [
734
- Dialog,
735
- MAT_DIALOG_SCROLL_STRATEGY_PROVIDER,
736
- { provide: DIALOG_REF, useValue: DialogRef },
737
- { provide: DIALOG_CONTAINER, useValue: CdkDialogContainer },
738
- { provide: DIALOG_CONFIG, useValue: DialogConfig },
739
- ], imports: [[OverlayModule, PortalModule, A11yModule],
696
+ DialogModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: DialogModule, providers: [Dialog, DIALOG_SCROLL_STRATEGY_PROVIDER], imports: [[OverlayModule, PortalModule, A11yModule],
740
697
  // Re-export the PortalModule so that people extending the `CdkDialogContainer`
741
698
  // don't have to remember to import it or be faced with an unhelpful error.
742
699
  PortalModule] });
743
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ngImport: i0, type: DialogModule, decorators: [{
700
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.13", ngImport: i0, type: DialogModule, decorators: [{
744
701
  type: NgModule,
745
702
  args: [{
746
703
  imports: [OverlayModule, PortalModule, A11yModule],
@@ -751,13 +708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ng
751
708
  CdkDialogContainer,
752
709
  ],
753
710
  declarations: [CdkDialogContainer],
754
- providers: [
755
- Dialog,
756
- MAT_DIALOG_SCROLL_STRATEGY_PROVIDER,
757
- { provide: DIALOG_REF, useValue: DialogRef },
758
- { provide: DIALOG_CONTAINER, useValue: CdkDialogContainer },
759
- { provide: DIALOG_CONFIG, useValue: DialogConfig },
760
- ],
711
+ providers: [Dialog, DIALOG_SCROLL_STRATEGY_PROVIDER],
761
712
  }]
762
713
  }] });
763
714
 
@@ -781,5 +732,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.0-rc.1", ng
781
732
  * Generated bundle index. Do not edit.
782
733
  */
783
734
 
784
- export { CdkDialogContainer, DIALOG_CONFIG, DIALOG_CONTAINER, DIALOG_DATA, DIALOG_REF, DIALOG_SCROLL_STRATEGY, Dialog, DialogConfig, DialogModule, DialogRef, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY, throwDialogContentAlreadyAttachedError };
735
+ export { CdkDialogContainer, DEFAULT_DIALOG_CONFIG, DIALOG_DATA, DIALOG_SCROLL_STRATEGY, DIALOG_SCROLL_STRATEGY_PROVIDER, DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY, Dialog, DialogConfig, DialogModule, DialogRef, throwDialogContentAlreadyAttachedError };
785
736
  //# sourceMappingURL=dialog.mjs.map