import { Inject , Injectable , InjectionToken , Injector , OnDestroy , Optional , SkipSelf , TemplateRef } from '@angular/core' ; import { Observable , Subject , defer } from 'rxjs' ; import { startWith } from 'rxjs/operators' ; import { Overlay , OverlayRef } from '@angular-mdc/web/overlay' ; import { ComponentPortal , ComponentType , PortalInjector , TemplatePortal } from '@angular/cdk/portal' ; import { MdcDialogConfig } from './dialog-config' ; import { MdcDialogPortal } from './dialog-portal' ; import { MdcDialogRef } from './dialog-ref' ; /** Injection token that can be used to access the data that was passed in to a dialog. */ export const MDC_DIALOG_DATA = new InjectionToken < any > ( 'MdcDialogData' ) ; /** Injection token that can be used to specify default dialog options. */ export const MDC_DIALOG_DEFAULT_OPTIONS = new InjectionToken < MdcDialogConfig > ( 'mdc-dialog-default-options' ) ; @ Injectable ( ) export class MdcDialog implements OnDestroy { private _openDialogsAtThisLevel : MdcDialogRef < any > [ ] = [ ] ; private readonly _afterAllClosedAtThisLevel = new Subject < void > ( ) ; private readonly _afterOpenedAtThisLevel = new Subject < MdcDialogRef < any > > ( ) ; private _ariaHiddenElements = new Map < Element , string | null > ( ) ; /** Keeps track of the currently-open dialogs. */ get openDialogs ( ) : MdcDialogRef < any > [ ] { return this . _parentDialog ? this . _parentDialog . openDialogs : this . _openDialogsAtThisLevel ; } /** Stream that emits when a dialog has been opened. */ get afterOpened ( ) : Subject < MdcDialogRef < any > > { return this . _parentDialog ? this . _parentDialog . afterOpened : this . _afterOpenedAtThisLevel ; } get _afterAllClosed ( ) : Subject < void > { const parent = this . _parentDialog ; return parent ? parent . _afterAllClosed : this . _afterAllClosedAtThisLevel ; } /**
     * Stream that emits when all open dialog have finished closing.
     * Will emit on subscribe if there are no open dialogs to begin with.
     */ readonly afterAllClosed : Observable < void > = defer ( ( ) => this . openDialogs . length ? this . _afterAllClosed : this . _afterAllClosed . pipe ( startWith ( undefined ) ) ) as Observable < any > ; constructor ( private _overlay , private _injector , @ Optional ( ) @ Inject ( MDC_DIALOG_DEFAULT_OPTIONS ) private _defaultOptions , @ Optional ( ) @ SkipSelf ( ) private _parentDialog ) { } param componentOrTemplateRef param config returns open < T , D = any , R = any > ( componentOrTemplateRef , config ? ) { config = _applyConfigDefaults ( config , this . _defaultOptions || new MdcDialogConfig ( ) ) ; if ( config . id && this . getDialogById ( config . id ) ) { throw Error ( `Dialog with id "${ config . id }" exists already. The dialog id must be unique.` ) ; } const overlayRef = this . _createOverlay ( ) ; const dialogContainer = this . _attachDialogContainer ( overlayRef , config ) ; const dialogRef = this . _attachDialogContent < T , R > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) ; this . openDialogs . push ( dialogRef ) ; dialogRef . afterClosed ( ) . subscribe ( ( ) => this . _removeOpenDialog ( dialogRef , dialogContainer ) ) ; this . afterOpened . next ( dialogRef ) ; return dialogRef ; } /** Closes all of the currently-open dialogs. */ closeAll ( ) { this . _closeDialogs ( this . openDialogs ) ; } param id getDialogById ( id ) { return this . openDialogs . find ( dialog => dialog . id === id ) ; } ngOnDestroy ( ) { this . _closeDialogs ( this . _openDialogsAtThisLevel ) ; this . _afterAllClosedAtThisLevel . complete ( ) ; this . _afterOpenedAtThisLevel . complete ( ) ; } returns private _createOverlay ( ) { return this . _overlay . create ( ) ; } param overlay param config returns private _attachDialogContainer ( overlay , config ) { const userInjector = config && config . viewContainerRef && config . viewContainerRef . injector ; const injector = new PortalInjector ( userInjector || this . _injector , new WeakMap ( [ [ MdcDialogConfig , config ] ] ) ) ; const containerPortal = new ComponentPortal ( MdcDialogPortal , config . viewContainerRef , injector , config . componentFactoryResolver ) ; const containerRef = overlay . attach < MdcDialogPortal > ( containerPortal ) ; return containerRef . instance ; } param componentOrTemplateRef param dialogContainer param overlayRef param config returns private _attachDialogContent < T , R > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) { const dialogRef = new MdcDialogRef < T , R > ( overlayRef , dialogContainer , config . id ) ; if ( componentOrTemplateRef instanceof TemplateRef ) { dialogContainer . attachTemplatePortal ( new TemplatePortal < T > ( componentOrTemplateRef , null ! , < any > { $implicit : config . data , dialogRef } ) ) ; } else { const injector = this . _createInjector < T > ( config , dialogRef , dialogContainer ) ; const contentRef = dialogContainer . attachComponentPortal < T > ( new ComponentPortal ( componentOrTemplateRef , config . viewContainerRef , injector ) ) ; dialogRef . componentInstance = contentRef . instance ; } return dialogRef ; } param config param dialogRef param container returns private _createInjector < T > ( config , dialogRef , portalContainer ) { const userInjector = config && config . viewContainerRef && config . viewContainerRef . injector ; portalContainer . trapFocus ( ) ; const injectionTokens = new WeakMap < any , any > ( [ [ MdcDialogPortal , portalContainer ] , [ MDC_DIALOG_DATA , config . data ] , [ MdcDialogRef , dialogRef ] ] ) ; return new PortalInjector ( userInjector || this . _injector , injectionTokens ) ; } param dialogRef private _removeOpenDialog ( dialogRef , dialogContainer ) { const index = this . openDialogs . indexOf ( dialogRef ) ; if ( index > - 1 ) { dialogContainer . restoreFocus ( ) ; this . openDialogs . splice ( index , 1 ) ; if ( ! this . openDialogs . length ) { this . _ariaHiddenElements . forEach ( ( previousValue , element ) => { if ( previousValue ) { element . setAttribute ( 'aria-hidden' , previousValue ) ; } else { element . removeAttribute ( 'aria-hidden' ) ; } } ) ; this . _ariaHiddenElements . clear ( ) ; this . _afterAllClosed . next ( ) ; } } } /** Closes all of the dialogs in an array. */ private _closeDialogs ( dialogs ) { let i = dialogs . length ; while ( i -- ) { dialogs [ i ] . close ( ) ; } } } param config param defaultOptions returns function _applyConfigDefaults ( config ? , defaultOptions ? ) { return { ... defaultOptions , ... config } ; }