source: trip-planner-front/node_modules/@angular/material/fesm2015/dialog.js

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 44.3 KB
Line 
1import { Overlay, OverlayConfig, OverlayContainer, OverlayModule } from '@angular/cdk/overlay';
2import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal, PortalModule } from '@angular/cdk/portal';
3import { EventEmitter, Directive, ElementRef, ChangeDetectorRef, Optional, Inject, ViewChild, Component, ViewEncapsulation, ChangeDetectionStrategy, InjectionToken, Injector, TemplateRef, InjectFlags, Type, Injectable, SkipSelf, Input, NgModule } from '@angular/core';
4import { MatCommonModule } from '@angular/material/core';
5import { Directionality } from '@angular/cdk/bidi';
6import { DOCUMENT, Location } from '@angular/common';
7import { Subject, defer, of } from 'rxjs';
8import { filter, take, startWith } from 'rxjs/operators';
9import { FocusTrapFactory, FocusMonitor } from '@angular/cdk/a11y';
10import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
11import { trigger, state, style, transition, animate } from '@angular/animations';
12import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
13
14/**
15 * @license
16 * Copyright Google LLC All Rights Reserved.
17 *
18 * Use of this source code is governed by an MIT-style license that can be
19 * found in the LICENSE file at https://angular.io/license
20 */
21/**
22 * Configuration for opening a modal dialog with the MatDialog service.
23 */
24class MatDialogConfig {
25 constructor() {
26 /** The ARIA role of the dialog element. */
27 this.role = 'dialog';
28 /** Custom class for the overlay pane. */
29 this.panelClass = '';
30 /** Whether the dialog has a backdrop. */
31 this.hasBackdrop = true;
32 /** Custom class for the backdrop. */
33 this.backdropClass = '';
34 /** Whether the user can use escape or clicking on the backdrop to close the modal. */
35 this.disableClose = false;
36 /** Width of the dialog. */
37 this.width = '';
38 /** Height of the dialog. */
39 this.height = '';
40 /** Max-width of the dialog. If a number is provided, assumes pixel units. Defaults to 80vw. */
41 this.maxWidth = '80vw';
42 /** Data being injected into the child component. */
43 this.data = null;
44 /** ID of the element that describes the dialog. */
45 this.ariaDescribedBy = null;
46 /** ID of the element that labels the dialog. */
47 this.ariaLabelledBy = null;
48 /** Aria label to assign to the dialog element. */
49 this.ariaLabel = null;
50 /** Whether the dialog should focus the first focusable element on open. */
51 this.autoFocus = true;
52 /**
53 * Whether the dialog should restore focus to the
54 * previously-focused element, after it's closed.
55 */
56 this.restoreFocus = true;
57 /**
58 * Whether the dialog should close when the user goes backwards/forwards in history.
59 * Note that this usually doesn't include clicking on links (unless the user is using
60 * the `HashLocationStrategy`).
61 */
62 this.closeOnNavigation = true;
63 // TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling.
64 }
65}
66
67/**
68 * @license
69 * Copyright Google LLC All Rights Reserved.
70 *
71 * Use of this source code is governed by an MIT-style license that can be
72 * found in the LICENSE file at https://angular.io/license
73 */
74/**
75 * Animations used by MatDialog.
76 * @docs-private
77 */
78const matDialogAnimations = {
79 /** Animation that is applied on the dialog container by default. */
80 dialogContainer: trigger('dialogContainer', [
81 // Note: The `enter` animation transitions to `transform: none`, because for some reason
82 // specifying the transform explicitly, causes IE both to blur the dialog content and
83 // decimate the animation performance. Leaving it as `none` solves both issues.
84 state('void, exit', style({ opacity: 0, transform: 'scale(0.7)' })),
85 state('enter', style({ transform: 'none' })),
86 transition('* => enter', animate('150ms cubic-bezier(0, 0, 0.2, 1)', style({ transform: 'none', opacity: 1 }))),
87 transition('* => void, * => exit', animate('75ms cubic-bezier(0.4, 0.0, 0.2, 1)', style({ opacity: 0 }))),
88 ])
89};
90
91/**
92 * @license
93 * Copyright Google LLC All Rights Reserved.
94 *
95 * Use of this source code is governed by an MIT-style license that can be
96 * found in the LICENSE file at https://angular.io/license
97 */
98/**
99 * Throws an exception for the case when a ComponentPortal is
100 * attached to a DomPortalOutlet without an origin.
101 * @docs-private
102 */
103function throwMatDialogContentAlreadyAttachedError() {
104 throw Error('Attempting to attach dialog content after content is already attached');
105}
106/**
107 * Base class for the `MatDialogContainer`. The base class does not implement
108 * animations as these are left to implementers of the dialog container.
109 */
110class _MatDialogContainerBase extends BasePortalOutlet {
111 constructor(_elementRef, _focusTrapFactory, _changeDetectorRef, _document,
112 /** The dialog configuration. */
113 _config, _focusMonitor) {
114 super();
115 this._elementRef = _elementRef;
116 this._focusTrapFactory = _focusTrapFactory;
117 this._changeDetectorRef = _changeDetectorRef;
118 this._config = _config;
119 this._focusMonitor = _focusMonitor;
120 /** Emits when an animation state changes. */
121 this._animationStateChanged = new EventEmitter();
122 /** Element that was focused before the dialog was opened. Save this to restore upon close. */
123 this._elementFocusedBeforeDialogWasOpened = null;
124 /**
125 * Type of interaction that led to the dialog being closed. This is used to determine
126 * whether the focus style will be applied when returning focus to its original location
127 * after the dialog is closed.
128 */
129 this._closeInteractionType = null;
130 /**
131 * Attaches a DOM portal to the dialog container.
132 * @param portal Portal to be attached.
133 * @deprecated To be turned into a method.
134 * @breaking-change 10.0.0
135 */
136 this.attachDomPortal = (portal) => {
137 if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
138 throwMatDialogContentAlreadyAttachedError();
139 }
140 return this._portalOutlet.attachDomPortal(portal);
141 };
142 this._ariaLabelledBy = _config.ariaLabelledBy || null;
143 this._document = _document;
144 }
145 /** Initializes the dialog container with the attached content. */
146 _initializeWithAttachedContent() {
147 this._setupFocusTrap();
148 // Save the previously focused element. This element will be re-focused
149 // when the dialog closes.
150 this._capturePreviouslyFocusedElement();
151 // Move focus onto the dialog immediately in order to prevent the user
152 // from accidentally opening multiple dialogs at the same time.
153 this._focusDialogContainer();
154 }
155 /**
156 * Attach a ComponentPortal as content to this dialog container.
157 * @param portal Portal to be attached as the dialog content.
158 */
159 attachComponentPortal(portal) {
160 if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
161 throwMatDialogContentAlreadyAttachedError();
162 }
163 return this._portalOutlet.attachComponentPortal(portal);
164 }
165 /**
166 * Attach a TemplatePortal as content to this dialog container.
167 * @param portal Portal to be attached as the dialog content.
168 */
169 attachTemplatePortal(portal) {
170 if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
171 throwMatDialogContentAlreadyAttachedError();
172 }
173 return this._portalOutlet.attachTemplatePortal(portal);
174 }
175 /** Moves focus back into the dialog if it was moved out. */
176 _recaptureFocus() {
177 if (!this._containsFocus()) {
178 const focusContainer = !this._config.autoFocus || !this._focusTrap.focusInitialElement();
179 if (focusContainer) {
180 this._elementRef.nativeElement.focus();
181 }
182 }
183 }
184 /** Moves the focus inside the focus trap. */
185 _trapFocus() {
186 // If we were to attempt to focus immediately, then the content of the dialog would not yet be
187 // ready in instances where change detection has to run first. To deal with this, we simply
188 // wait for the microtask queue to be empty.
189 if (this._config.autoFocus) {
190 this._focusTrap.focusInitialElementWhenReady();
191 }
192 else if (!this._containsFocus()) {
193 // Otherwise ensure that focus is on the dialog container. It's possible that a different
194 // component tried to move focus while the open animation was running. See:
195 // https://github.com/angular/components/issues/16215. Note that we only want to do this
196 // if the focus isn't inside the dialog already, because it's possible that the consumer
197 // turned off `autoFocus` in order to move focus themselves.
198 this._elementRef.nativeElement.focus();
199 }
200 }
201 /** Restores focus to the element that was focused before the dialog opened. */
202 _restoreFocus() {
203 const previousElement = this._elementFocusedBeforeDialogWasOpened;
204 // We need the extra check, because IE can set the `activeElement` to null in some cases.
205 if (this._config.restoreFocus && previousElement &&
206 typeof previousElement.focus === 'function') {
207 const activeElement = _getFocusedElementPierceShadowDom();
208 const element = this._elementRef.nativeElement;
209 // Make sure that focus is still inside the dialog or is on the body (usually because a
210 // non-focusable element like the backdrop was clicked) before moving it. It's possible that
211 // the consumer moved it themselves before the animation was done, in which case we shouldn't
212 // do anything.
213 if (!activeElement || activeElement === this._document.body || activeElement === element ||
214 element.contains(activeElement)) {
215 if (this._focusMonitor) {
216 this._focusMonitor.focusVia(previousElement, this._closeInteractionType);
217 this._closeInteractionType = null;
218 }
219 else {
220 previousElement.focus();
221 }
222 }
223 }
224 if (this._focusTrap) {
225 this._focusTrap.destroy();
226 }
227 }
228 /** Sets up the focus trap. */
229 _setupFocusTrap() {
230 this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
231 }
232 /** Captures the element that was focused before the dialog was opened. */
233 _capturePreviouslyFocusedElement() {
234 if (this._document) {
235 this._elementFocusedBeforeDialogWasOpened = _getFocusedElementPierceShadowDom();
236 }
237 }
238 /** Focuses the dialog container. */
239 _focusDialogContainer() {
240 // Note that there is no focus method when rendering on the server.
241 if (this._elementRef.nativeElement.focus) {
242 this._elementRef.nativeElement.focus();
243 }
244 }
245 /** Returns whether focus is inside the dialog. */
246 _containsFocus() {
247 const element = this._elementRef.nativeElement;
248 const activeElement = _getFocusedElementPierceShadowDom();
249 return element === activeElement || element.contains(activeElement);
250 }
251}
252_MatDialogContainerBase.decorators = [
253 { type: Directive }
254];
255_MatDialogContainerBase.ctorParameters = () => [
256 { type: ElementRef },
257 { type: FocusTrapFactory },
258 { type: ChangeDetectorRef },
259 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] },
260 { type: MatDialogConfig },
261 { type: FocusMonitor }
262];
263_MatDialogContainerBase.propDecorators = {
264 _portalOutlet: [{ type: ViewChild, args: [CdkPortalOutlet, { static: true },] }]
265};
266/**
267 * Internal component that wraps user-provided dialog content.
268 * Animation is based on https://material.io/guidelines/motion/choreography.html.
269 * @docs-private
270 */
271class MatDialogContainer extends _MatDialogContainerBase {
272 constructor() {
273 super(...arguments);
274 /** State of the dialog animation. */
275 this._state = 'enter';
276 }
277 /** Callback, invoked whenever an animation on the host completes. */
278 _onAnimationDone({ toState, totalTime }) {
279 if (toState === 'enter') {
280 this._trapFocus();
281 this._animationStateChanged.next({ state: 'opened', totalTime });
282 }
283 else if (toState === 'exit') {
284 this._restoreFocus();
285 this._animationStateChanged.next({ state: 'closed', totalTime });
286 }
287 }
288 /** Callback, invoked when an animation on the host starts. */
289 _onAnimationStart({ toState, totalTime }) {
290 if (toState === 'enter') {
291 this._animationStateChanged.next({ state: 'opening', totalTime });
292 }
293 else if (toState === 'exit' || toState === 'void') {
294 this._animationStateChanged.next({ state: 'closing', totalTime });
295 }
296 }
297 /** Starts the dialog exit animation. */
298 _startExitAnimation() {
299 this._state = 'exit';
300 // Mark the container for check so it can react if the
301 // view container is using OnPush change detection.
302 this._changeDetectorRef.markForCheck();
303 }
304}
305MatDialogContainer.decorators = [
306 { type: Component, args: [{
307 selector: 'mat-dialog-container',
308 template: "<ng-template cdkPortalOutlet></ng-template>\n",
309 encapsulation: ViewEncapsulation.None,
310 // Using OnPush for dialogs caused some G3 sync issues. Disabled until we can track them down.
311 // tslint:disable-next-line:validate-decorators
312 changeDetection: ChangeDetectionStrategy.Default,
313 animations: [matDialogAnimations.dialogContainer],
314 host: {
315 'class': 'mat-dialog-container',
316 'tabindex': '-1',
317 'aria-modal': 'true',
318 '[id]': '_id',
319 '[attr.role]': '_config.role',
320 '[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',
321 '[attr.aria-label]': '_config.ariaLabel',
322 '[attr.aria-describedby]': '_config.ariaDescribedBy || null',
323 '[@dialogContainer]': '_state',
324 '(@dialogContainer.start)': '_onAnimationStart($event)',
325 '(@dialogContainer.done)': '_onAnimationDone($event)',
326 },
327 styles: [".mat-dialog-container{display:block;padding:24px;border-radius:4px;box-sizing:border-box;overflow:auto;outline:0;width:100%;height:100%;min-height:inherit;max-height:inherit}.cdk-high-contrast-active .mat-dialog-container{outline:solid 1px}.mat-dialog-content{display:block;margin:0 -24px;padding:0 24px;max-height:65vh;overflow:auto;-webkit-overflow-scrolling:touch}.mat-dialog-title{margin:0 0 20px;display:block}.mat-dialog-actions{padding:8px 0;display:flex;flex-wrap:wrap;min-height:52px;align-items:center;box-sizing:content-box;margin-bottom:-24px}.mat-dialog-actions[align=end]{justify-content:flex-end}.mat-dialog-actions[align=center]{justify-content:center}.mat-dialog-actions .mat-button-base+.mat-button-base,.mat-dialog-actions .mat-mdc-button-base+.mat-mdc-button-base{margin-left:8px}[dir=rtl] .mat-dialog-actions .mat-button-base+.mat-button-base,[dir=rtl] .mat-dialog-actions .mat-mdc-button-base+.mat-mdc-button-base{margin-left:0;margin-right:8px}\n"]
328 },] }
329];
330
331/**
332 * @license
333 * Copyright Google LLC All Rights Reserved.
334 *
335 * Use of this source code is governed by an MIT-style license that can be
336 * found in the LICENSE file at https://angular.io/license
337 */
338// TODO(jelbourn): resizing
339// Counter for unique dialog ids.
340let uniqueId = 0;
341/**
342 * Reference to a dialog opened via the MatDialog service.
343 */
344class MatDialogRef {
345 constructor(_overlayRef, _containerInstance,
346 /** Id of the dialog. */
347 id = `mat-dialog-${uniqueId++}`) {
348 this._overlayRef = _overlayRef;
349 this._containerInstance = _containerInstance;
350 this.id = id;
351 /** Whether the user is allowed to close the dialog. */
352 this.disableClose = this._containerInstance._config.disableClose;
353 /** Subject for notifying the user that the dialog has finished opening. */
354 this._afterOpened = new Subject();
355 /** Subject for notifying the user that the dialog has finished closing. */
356 this._afterClosed = new Subject();
357 /** Subject for notifying the user that the dialog has started closing. */
358 this._beforeClosed = new Subject();
359 /** Current state of the dialog. */
360 this._state = 0 /* OPEN */;
361 // Pass the id along to the container.
362 _containerInstance._id = id;
363 // Emit when opening animation completes
364 _containerInstance._animationStateChanged.pipe(filter(event => event.state === 'opened'), take(1))
365 .subscribe(() => {
366 this._afterOpened.next();
367 this._afterOpened.complete();
368 });
369 // Dispose overlay when closing animation is complete
370 _containerInstance._animationStateChanged.pipe(filter(event => event.state === 'closed'), take(1)).subscribe(() => {
371 clearTimeout(this._closeFallbackTimeout);
372 this._finishDialogClose();
373 });
374 _overlayRef.detachments().subscribe(() => {
375 this._beforeClosed.next(this._result);
376 this._beforeClosed.complete();
377 this._afterClosed.next(this._result);
378 this._afterClosed.complete();
379 this.componentInstance = null;
380 this._overlayRef.dispose();
381 });
382 _overlayRef.keydownEvents()
383 .pipe(filter(event => {
384 return event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event);
385 }))
386 .subscribe(event => {
387 event.preventDefault();
388 _closeDialogVia(this, 'keyboard');
389 });
390 _overlayRef.backdropClick().subscribe(() => {
391 if (this.disableClose) {
392 this._containerInstance._recaptureFocus();
393 }
394 else {
395 _closeDialogVia(this, 'mouse');
396 }
397 });
398 }
399 /**
400 * Close the dialog.
401 * @param dialogResult Optional result to return to the dialog opener.
402 */
403 close(dialogResult) {
404 this._result = dialogResult;
405 // Transition the backdrop in parallel to the dialog.
406 this._containerInstance._animationStateChanged.pipe(filter(event => event.state === 'closing'), take(1))
407 .subscribe(event => {
408 this._beforeClosed.next(dialogResult);
409 this._beforeClosed.complete();
410 this._overlayRef.detachBackdrop();
411 // The logic that disposes of the overlay depends on the exit animation completing, however
412 // it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback
413 // timeout which will clean everything up if the animation hasn't fired within the specified
414 // amount of time plus 100ms. We don't need to run this outside the NgZone, because for the
415 // vast majority of cases the timeout will have been cleared before it has the chance to fire.
416 this._closeFallbackTimeout = setTimeout(() => this._finishDialogClose(), event.totalTime + 100);
417 });
418 this._state = 1 /* CLOSING */;
419 this._containerInstance._startExitAnimation();
420 }
421 /**
422 * Gets an observable that is notified when the dialog is finished opening.
423 */
424 afterOpened() {
425 return this._afterOpened;
426 }
427 /**
428 * Gets an observable that is notified when the dialog is finished closing.
429 */
430 afterClosed() {
431 return this._afterClosed;
432 }
433 /**
434 * Gets an observable that is notified when the dialog has started closing.
435 */
436 beforeClosed() {
437 return this._beforeClosed;
438 }
439 /**
440 * Gets an observable that emits when the overlay's backdrop has been clicked.
441 */
442 backdropClick() {
443 return this._overlayRef.backdropClick();
444 }
445 /**
446 * Gets an observable that emits when keydown events are targeted on the overlay.
447 */
448 keydownEvents() {
449 return this._overlayRef.keydownEvents();
450 }
451 /**
452 * Updates the dialog's position.
453 * @param position New dialog position.
454 */
455 updatePosition(position) {
456 let strategy = this._getPositionStrategy();
457 if (position && (position.left || position.right)) {
458 position.left ? strategy.left(position.left) : strategy.right(position.right);
459 }
460 else {
461 strategy.centerHorizontally();
462 }
463 if (position && (position.top || position.bottom)) {
464 position.top ? strategy.top(position.top) : strategy.bottom(position.bottom);
465 }
466 else {
467 strategy.centerVertically();
468 }
469 this._overlayRef.updatePosition();
470 return this;
471 }
472 /**
473 * Updates the dialog's width and height.
474 * @param width New width of the dialog.
475 * @param height New height of the dialog.
476 */
477 updateSize(width = '', height = '') {
478 this._overlayRef.updateSize({ width, height });
479 this._overlayRef.updatePosition();
480 return this;
481 }
482 /** Add a CSS class or an array of classes to the overlay pane. */
483 addPanelClass(classes) {
484 this._overlayRef.addPanelClass(classes);
485 return this;
486 }
487 /** Remove a CSS class or an array of classes from the overlay pane. */
488 removePanelClass(classes) {
489 this._overlayRef.removePanelClass(classes);
490 return this;
491 }
492 /** Gets the current state of the dialog's lifecycle. */
493 getState() {
494 return this._state;
495 }
496 /**
497 * Finishes the dialog close by updating the state of the dialog
498 * and disposing the overlay.
499 */
500 _finishDialogClose() {
501 this._state = 2 /* CLOSED */;
502 this._overlayRef.dispose();
503 }
504 /** Fetches the position strategy object from the overlay ref. */
505 _getPositionStrategy() {
506 return this._overlayRef.getConfig().positionStrategy;
507 }
508}
509/**
510 * Closes the dialog with the specified interaction type. This is currently not part of
511 * `MatDialogRef` as that would conflict with custom dialog ref mocks provided in tests.
512 * More details. See: https://github.com/angular/components/pull/9257#issuecomment-651342226.
513 */
514// TODO: TODO: Move this back into `MatDialogRef` when we provide an official mock dialog ref.
515function _closeDialogVia(ref, interactionType, result) {
516 // Some mock dialog ref instances in tests do not have the `_containerInstance` property.
517 // For those, we keep the behavior as is and do not deal with the interaction type.
518 if (ref._containerInstance !== undefined) {
519 ref._containerInstance._closeInteractionType = interactionType;
520 }
521 return ref.close(result);
522}
523
524/**
525 * @license
526 * Copyright Google LLC All Rights Reserved.
527 *
528 * Use of this source code is governed by an MIT-style license that can be
529 * found in the LICENSE file at https://angular.io/license
530 */
531/** Injection token that can be used to access the data that was passed in to a dialog. */
532const MAT_DIALOG_DATA = new InjectionToken('MatDialogData');
533/** Injection token that can be used to specify default dialog options. */
534const MAT_DIALOG_DEFAULT_OPTIONS = new InjectionToken('mat-dialog-default-options');
535/** Injection token that determines the scroll handling while the dialog is open. */
536const MAT_DIALOG_SCROLL_STRATEGY = new InjectionToken('mat-dialog-scroll-strategy');
537/** @docs-private */
538function MAT_DIALOG_SCROLL_STRATEGY_FACTORY(overlay) {
539 return () => overlay.scrollStrategies.block();
540}
541/** @docs-private */
542function MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
543 return () => overlay.scrollStrategies.block();
544}
545/** @docs-private */
546const MAT_DIALOG_SCROLL_STRATEGY_PROVIDER = {
547 provide: MAT_DIALOG_SCROLL_STRATEGY,
548 deps: [Overlay],
549 useFactory: MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,
550};
551/**
552 * Base class for dialog services. The base dialog service allows
553 * for arbitrary dialog refs and dialog container components.
554 */
555class _MatDialogBase {
556 constructor(_overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, scrollStrategy, _dialogRefConstructor, _dialogContainerType, _dialogDataToken) {
557 this._overlay = _overlay;
558 this._injector = _injector;
559 this._defaultOptions = _defaultOptions;
560 this._parentDialog = _parentDialog;
561 this._overlayContainer = _overlayContainer;
562 this._dialogRefConstructor = _dialogRefConstructor;
563 this._dialogContainerType = _dialogContainerType;
564 this._dialogDataToken = _dialogDataToken;
565 this._openDialogsAtThisLevel = [];
566 this._afterAllClosedAtThisLevel = new Subject();
567 this._afterOpenedAtThisLevel = new Subject();
568 this._ariaHiddenElements = new Map();
569 // TODO (jelbourn): tighten the typing right-hand side of this expression.
570 /**
571 * Stream that emits when all open dialog have finished closing.
572 * Will emit on subscribe if there are no open dialogs to begin with.
573 */
574 this.afterAllClosed = defer(() => this.openDialogs.length ?
575 this._getAfterAllClosed() :
576 this._getAfterAllClosed().pipe(startWith(undefined)));
577 this._scrollStrategy = scrollStrategy;
578 }
579 /** Keeps track of the currently-open dialogs. */
580 get openDialogs() {
581 return this._parentDialog ? this._parentDialog.openDialogs : this._openDialogsAtThisLevel;
582 }
583 /** Stream that emits when a dialog has been opened. */
584 get afterOpened() {
585 return this._parentDialog ? this._parentDialog.afterOpened : this._afterOpenedAtThisLevel;
586 }
587 _getAfterAllClosed() {
588 const parent = this._parentDialog;
589 return parent ? parent._getAfterAllClosed() : this._afterAllClosedAtThisLevel;
590 }
591 open(componentOrTemplateRef, config) {
592 config = _applyConfigDefaults(config, this._defaultOptions || new MatDialogConfig());
593 if (config.id && this.getDialogById(config.id) &&
594 (typeof ngDevMode === 'undefined' || ngDevMode)) {
595 throw Error(`Dialog with id "${config.id}" exists already. The dialog id must be unique.`);
596 }
597 const overlayRef = this._createOverlay(config);
598 const dialogContainer = this._attachDialogContainer(overlayRef, config);
599 const dialogRef = this._attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config);
600 // If this is the first dialog that we're opening, hide all the non-overlay content.
601 if (!this.openDialogs.length) {
602 this._hideNonDialogContentFromAssistiveTechnology();
603 }
604 this.openDialogs.push(dialogRef);
605 dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
606 this.afterOpened.next(dialogRef);
607 // Notify the dialog container that the content has been attached.
608 dialogContainer._initializeWithAttachedContent();
609 return dialogRef;
610 }
611 /**
612 * Closes all of the currently-open dialogs.
613 */
614 closeAll() {
615 this._closeDialogs(this.openDialogs);
616 }
617 /**
618 * Finds an open dialog by its id.
619 * @param id ID to use when looking up the dialog.
620 */
621 getDialogById(id) {
622 return this.openDialogs.find(dialog => dialog.id === id);
623 }
624 ngOnDestroy() {
625 // Only close the dialogs at this level on destroy
626 // since the parent service may still be active.
627 this._closeDialogs(this._openDialogsAtThisLevel);
628 this._afterAllClosedAtThisLevel.complete();
629 this._afterOpenedAtThisLevel.complete();
630 }
631 /**
632 * Creates the overlay into which the dialog will be loaded.
633 * @param config The dialog configuration.
634 * @returns A promise resolving to the OverlayRef for the created overlay.
635 */
636 _createOverlay(config) {
637 const overlayConfig = this._getOverlayConfig(config);
638 return this._overlay.create(overlayConfig);
639 }
640 /**
641 * Creates an overlay config from a dialog config.
642 * @param dialogConfig The dialog configuration.
643 * @returns The overlay configuration.
644 */
645 _getOverlayConfig(dialogConfig) {
646 const state = new OverlayConfig({
647 positionStrategy: this._overlay.position().global(),
648 scrollStrategy: dialogConfig.scrollStrategy || this._scrollStrategy(),
649 panelClass: dialogConfig.panelClass,
650 hasBackdrop: dialogConfig.hasBackdrop,
651 direction: dialogConfig.direction,
652 minWidth: dialogConfig.minWidth,
653 minHeight: dialogConfig.minHeight,
654 maxWidth: dialogConfig.maxWidth,
655 maxHeight: dialogConfig.maxHeight,
656 disposeOnNavigation: dialogConfig.closeOnNavigation
657 });
658 if (dialogConfig.backdropClass) {
659 state.backdropClass = dialogConfig.backdropClass;
660 }
661 return state;
662 }
663 /**
664 * Attaches a dialog container to a dialog's already-created overlay.
665 * @param overlay Reference to the dialog's underlying overlay.
666 * @param config The dialog configuration.
667 * @returns A promise resolving to a ComponentRef for the attached container.
668 */
669 _attachDialogContainer(overlay, config) {
670 const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
671 const injector = Injector.create({
672 parent: userInjector || this._injector,
673 providers: [{ provide: MatDialogConfig, useValue: config }]
674 });
675 const containerPortal = new ComponentPortal(this._dialogContainerType, config.viewContainerRef, injector, config.componentFactoryResolver);
676 const containerRef = overlay.attach(containerPortal);
677 return containerRef.instance;
678 }
679 /**
680 * Attaches the user-provided component to the already-created dialog container.
681 * @param componentOrTemplateRef The type of component being loaded into the dialog,
682 * or a TemplateRef to instantiate as the content.
683 * @param dialogContainer Reference to the wrapping dialog container.
684 * @param overlayRef Reference to the overlay in which the dialog resides.
685 * @param config The dialog configuration.
686 * @returns A promise resolving to the MatDialogRef that should be returned to the user.
687 */
688 _attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config) {
689 // Create a reference to the dialog we're creating in order to give the user a handle
690 // to modify and close it.
691 const dialogRef = new this._dialogRefConstructor(overlayRef, dialogContainer, config.id);
692 if (componentOrTemplateRef instanceof TemplateRef) {
693 dialogContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null, { $implicit: config.data, dialogRef }));
694 }
695 else {
696 const injector = this._createInjector(config, dialogRef, dialogContainer);
697 const contentRef = dialogContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.viewContainerRef, injector));
698 dialogRef.componentInstance = contentRef.instance;
699 }
700 dialogRef
701 .updateSize(config.width, config.height)
702 .updatePosition(config.position);
703 return dialogRef;
704 }
705 /**
706 * Creates a custom injector to be used inside the dialog. This allows a component loaded inside
707 * of a dialog to close itself and, optionally, to return a value.
708 * @param config Config object that is used to construct the dialog.
709 * @param dialogRef Reference to the dialog.
710 * @param dialogContainer Dialog container element that wraps all of the contents.
711 * @returns The custom injector that can be used inside the dialog.
712 */
713 _createInjector(config, dialogRef, dialogContainer) {
714 const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
715 // The dialog container should be provided as the dialog container and the dialog's
716 // content are created out of the same `ViewContainerRef` and as such, are siblings
717 // for injector purposes. To allow the hierarchy that is expected, the dialog
718 // container is explicitly provided in the injector.
719 const providers = [
720 { provide: this._dialogContainerType, useValue: dialogContainer },
721 { provide: this._dialogDataToken, useValue: config.data },
722 { provide: this._dialogRefConstructor, useValue: dialogRef }
723 ];
724 if (config.direction && (!userInjector ||
725 !userInjector.get(Directionality, null, InjectFlags.Optional))) {
726 providers.push({
727 provide: Directionality,
728 useValue: { value: config.direction, change: of() }
729 });
730 }
731 return Injector.create({ parent: userInjector || this._injector, providers });
732 }
733 /**
734 * Removes a dialog from the array of open dialogs.
735 * @param dialogRef Dialog to be removed.
736 */
737 _removeOpenDialog(dialogRef) {
738 const index = this.openDialogs.indexOf(dialogRef);
739 if (index > -1) {
740 this.openDialogs.splice(index, 1);
741 // If all the dialogs were closed, remove/restore the `aria-hidden`
742 // to a the siblings and emit to the `afterAllClosed` stream.
743 if (!this.openDialogs.length) {
744 this._ariaHiddenElements.forEach((previousValue, element) => {
745 if (previousValue) {
746 element.setAttribute('aria-hidden', previousValue);
747 }
748 else {
749 element.removeAttribute('aria-hidden');
750 }
751 });
752 this._ariaHiddenElements.clear();
753 this._getAfterAllClosed().next();
754 }
755 }
756 }
757 /**
758 * Hides all of the content that isn't an overlay from assistive technology.
759 */
760 _hideNonDialogContentFromAssistiveTechnology() {
761 const overlayContainer = this._overlayContainer.getContainerElement();
762 // Ensure that the overlay container is attached to the DOM.
763 if (overlayContainer.parentElement) {
764 const siblings = overlayContainer.parentElement.children;
765 for (let i = siblings.length - 1; i > -1; i--) {
766 let sibling = siblings[i];
767 if (sibling !== overlayContainer &&
768 sibling.nodeName !== 'SCRIPT' &&
769 sibling.nodeName !== 'STYLE' &&
770 !sibling.hasAttribute('aria-live')) {
771 this._ariaHiddenElements.set(sibling, sibling.getAttribute('aria-hidden'));
772 sibling.setAttribute('aria-hidden', 'true');
773 }
774 }
775 }
776 }
777 /** Closes all of the dialogs in an array. */
778 _closeDialogs(dialogs) {
779 let i = dialogs.length;
780 while (i--) {
781 // The `_openDialogs` property isn't updated after close until the rxjs subscription
782 // runs on the next microtask, in addition to modifying the array as we're going
783 // through it. We loop through all of them and call close without assuming that
784 // they'll be removed from the list instantaneously.
785 dialogs[i].close();
786 }
787 }
788}
789_MatDialogBase.decorators = [
790 { type: Directive }
791];
792_MatDialogBase.ctorParameters = () => [
793 { type: Overlay },
794 { type: Injector },
795 { type: undefined },
796 { type: undefined },
797 { type: OverlayContainer },
798 { type: undefined },
799 { type: Type },
800 { type: Type },
801 { type: InjectionToken }
802];
803/**
804 * Service to open Material Design modal dialogs.
805 */
806class MatDialog extends _MatDialogBase {
807 constructor(overlay, injector,
808 /**
809 * @deprecated `_location` parameter to be removed.
810 * @breaking-change 10.0.0
811 */
812 location, defaultOptions, scrollStrategy, parentDialog, overlayContainer) {
813 super(overlay, injector, defaultOptions, parentDialog, overlayContainer, scrollStrategy, MatDialogRef, MatDialogContainer, MAT_DIALOG_DATA);
814 }
815}
816MatDialog.decorators = [
817 { type: Injectable }
818];
819MatDialog.ctorParameters = () => [
820 { type: Overlay },
821 { type: Injector },
822 { type: Location, decorators: [{ type: Optional }] },
823 { type: MatDialogConfig, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DIALOG_DEFAULT_OPTIONS,] }] },
824 { type: undefined, decorators: [{ type: Inject, args: [MAT_DIALOG_SCROLL_STRATEGY,] }] },
825 { type: MatDialog, decorators: [{ type: Optional }, { type: SkipSelf }] },
826 { type: OverlayContainer }
827];
828/**
829 * Applies default options to the dialog config.
830 * @param config Config to be modified.
831 * @param defaultOptions Default options provided.
832 * @returns The new configuration object.
833 */
834function _applyConfigDefaults(config, defaultOptions) {
835 return Object.assign(Object.assign({}, defaultOptions), config);
836}
837
838/**
839 * @license
840 * Copyright Google LLC All Rights Reserved.
841 *
842 * Use of this source code is governed by an MIT-style license that can be
843 * found in the LICENSE file at https://angular.io/license
844 */
845/** Counter used to generate unique IDs for dialog elements. */
846let dialogElementUid = 0;
847/**
848 * Button that will close the current dialog.
849 */
850class MatDialogClose {
851 constructor(
852 /**
853 * Reference to the containing dialog.
854 * @deprecated `dialogRef` property to become private.
855 * @breaking-change 13.0.0
856 */
857 // The dialog title directive is always used in combination with a `MatDialogRef`.
858 // tslint:disable-next-line: lightweight-tokens
859 dialogRef, _elementRef, _dialog) {
860 this.dialogRef = dialogRef;
861 this._elementRef = _elementRef;
862 this._dialog = _dialog;
863 /** Default to "button" to prevents accidental form submits. */
864 this.type = 'button';
865 }
866 ngOnInit() {
867 if (!this.dialogRef) {
868 // When this directive is included in a dialog via TemplateRef (rather than being
869 // in a Component), the DialogRef isn't available via injection because embedded
870 // views cannot be given a custom injector. Instead, we look up the DialogRef by
871 // ID. This must occur in `onInit`, as the ID binding for the dialog container won't
872 // be resolved at constructor time.
873 this.dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs);
874 }
875 }
876 ngOnChanges(changes) {
877 const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
878 if (proxiedChange) {
879 this.dialogResult = proxiedChange.currentValue;
880 }
881 }
882 _onButtonClick(event) {
883 // Determinate the focus origin using the click event, because using the FocusMonitor will
884 // result in incorrect origins. Most of the time, close buttons will be auto focused in the
885 // dialog, and therefore clicking the button won't result in a focus change. This means that
886 // the FocusMonitor won't detect any origin change, and will always output `program`.
887 _closeDialogVia(this.dialogRef, event.screenX === 0 && event.screenY === 0 ? 'keyboard' : 'mouse', this.dialogResult);
888 }
889}
890MatDialogClose.decorators = [
891 { type: Directive, args: [{
892 selector: '[mat-dialog-close], [matDialogClose]',
893 exportAs: 'matDialogClose',
894 host: {
895 '(click)': '_onButtonClick($event)',
896 '[attr.aria-label]': 'ariaLabel || null',
897 '[attr.type]': 'type',
898 }
899 },] }
900];
901MatDialogClose.ctorParameters = () => [
902 { type: MatDialogRef, decorators: [{ type: Optional }] },
903 { type: ElementRef },
904 { type: MatDialog }
905];
906MatDialogClose.propDecorators = {
907 ariaLabel: [{ type: Input, args: ['aria-label',] }],
908 type: [{ type: Input }],
909 dialogResult: [{ type: Input, args: ['mat-dialog-close',] }],
910 _matDialogClose: [{ type: Input, args: ['matDialogClose',] }]
911};
912/**
913 * Title of a dialog element. Stays fixed to the top of the dialog when scrolling.
914 */
915class MatDialogTitle {
916 constructor(
917 // The dialog title directive is always used in combination with a `MatDialogRef`.
918 // tslint:disable-next-line: lightweight-tokens
919 _dialogRef, _elementRef, _dialog) {
920 this._dialogRef = _dialogRef;
921 this._elementRef = _elementRef;
922 this._dialog = _dialog;
923 /** Unique id for the dialog title. If none is supplied, it will be auto-generated. */
924 this.id = `mat-dialog-title-${dialogElementUid++}`;
925 }
926 ngOnInit() {
927 if (!this._dialogRef) {
928 this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs);
929 }
930 if (this._dialogRef) {
931 Promise.resolve().then(() => {
932 const container = this._dialogRef._containerInstance;
933 if (container && !container._ariaLabelledBy) {
934 container._ariaLabelledBy = this.id;
935 }
936 });
937 }
938 }
939}
940MatDialogTitle.decorators = [
941 { type: Directive, args: [{
942 selector: '[mat-dialog-title], [matDialogTitle]',
943 exportAs: 'matDialogTitle',
944 host: {
945 'class': 'mat-dialog-title',
946 '[id]': 'id',
947 },
948 },] }
949];
950MatDialogTitle.ctorParameters = () => [
951 { type: MatDialogRef, decorators: [{ type: Optional }] },
952 { type: ElementRef },
953 { type: MatDialog }
954];
955MatDialogTitle.propDecorators = {
956 id: [{ type: Input }]
957};
958/**
959 * Scrollable content container of a dialog.
960 */
961class MatDialogContent {
962}
963MatDialogContent.decorators = [
964 { type: Directive, args: [{
965 selector: `[mat-dialog-content], mat-dialog-content, [matDialogContent]`,
966 host: { 'class': 'mat-dialog-content' }
967 },] }
968];
969/**
970 * Container for the bottom action buttons in a dialog.
971 * Stays fixed to the bottom when scrolling.
972 */
973class MatDialogActions {
974}
975MatDialogActions.decorators = [
976 { type: Directive, args: [{
977 selector: `[mat-dialog-actions], mat-dialog-actions, [matDialogActions]`,
978 host: { 'class': 'mat-dialog-actions' }
979 },] }
980];
981/**
982 * Finds the closest MatDialogRef to an element by looking at the DOM.
983 * @param element Element relative to which to look for a dialog.
984 * @param openDialogs References to the currently-open dialogs.
985 */
986function getClosestDialog(element, openDialogs) {
987 let parent = element.nativeElement.parentElement;
988 while (parent && !parent.classList.contains('mat-dialog-container')) {
989 parent = parent.parentElement;
990 }
991 return parent ? openDialogs.find(dialog => dialog.id === parent.id) : null;
992}
993
994/**
995 * @license
996 * Copyright Google LLC All Rights Reserved.
997 *
998 * Use of this source code is governed by an MIT-style license that can be
999 * found in the LICENSE file at https://angular.io/license
1000 */
1001class MatDialogModule {
1002}
1003MatDialogModule.decorators = [
1004 { type: NgModule, args: [{
1005 imports: [
1006 OverlayModule,
1007 PortalModule,
1008 MatCommonModule,
1009 ],
1010 exports: [
1011 MatDialogContainer,
1012 MatDialogClose,
1013 MatDialogTitle,
1014 MatDialogContent,
1015 MatDialogActions,
1016 MatCommonModule,
1017 ],
1018 declarations: [
1019 MatDialogContainer,
1020 MatDialogClose,
1021 MatDialogTitle,
1022 MatDialogActions,
1023 MatDialogContent,
1024 ],
1025 providers: [
1026 MatDialog,
1027 MAT_DIALOG_SCROLL_STRATEGY_PROVIDER,
1028 ],
1029 entryComponents: [MatDialogContainer],
1030 },] }
1031];
1032
1033/**
1034 * @license
1035 * Copyright Google LLC All Rights Reserved.
1036 *
1037 * Use of this source code is governed by an MIT-style license that can be
1038 * found in the LICENSE file at https://angular.io/license
1039 */
1040
1041/**
1042 * Generated bundle index. Do not edit.
1043 */
1044
1045export { MAT_DIALOG_DATA, MAT_DIALOG_DEFAULT_OPTIONS, MAT_DIALOG_SCROLL_STRATEGY, MAT_DIALOG_SCROLL_STRATEGY_FACTORY, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY, MatDialog, MatDialogActions, MatDialogClose, MatDialogConfig, MatDialogContainer, MatDialogContent, MatDialogModule, MatDialogRef, MatDialogTitle, _MatDialogBase, _MatDialogContainerBase, _closeDialogVia, matDialogAnimations, throwMatDialogContentAlreadyAttachedError };
1046//# sourceMappingURL=dialog.js.map
Note: See TracBrowser for help on using the repository browser.