import { ChangeDetectionStrategy , Component , ComponentRef , ElementRef , EmbeddedViewRef , Optional , Inject , ViewChild , ViewEncapsulation } from '@angular/core' ; import { DOCUMENT } from '@angular/common' ; import { FocusTrap , FocusTrapFactory } from '@angular/cdk/a11y' ; import { BasePortalOutlet , ComponentPortal , CdkPortalOutlet , TemplatePortal } from '@angular/cdk/portal' ; import { Subject } from 'rxjs' ; import { MdcDialogConfig } from './dialog-config' ; @ Component ( { selector : 'mdc-dialog-portal' , host : { '[attr.id]' : '_id' } , template : '<ng-template cdkPortalOutlet></ng-template>' , changeDetection : ChangeDetectionStrategy . OnPush , encapsulation : ViewEncapsulation . None } ) export class MdcDialogPortal extends BasePortalOutlet { @ ViewChild ( CdkPortalOutlet , { static : true } ) _portalOutlet ! : CdkPortalOutlet ; /** The class that traps and manages focus within the dialog. */ private _focusTrap ! : FocusTrap ; /** Element that was focused before the dialog was opened. Save this to restore upon close. */ private _elementFocusedBeforeDialogWasOpened : HTMLElement | null = null ; /** A subject emitting after the dialog exits the view. */ _afterExit : Subject < void > = new Subject ( ) ; /** ID for the container DOM element. */ _id ! : string ; param portal attachComponentPortal < T > ( portal ) { this . _savePreviouslyFocusedElement ( ) ; return this . _portalOutlet . attachComponentPortal ( portal ) ; } param portal attachTemplatePortal < C > ( portal ) { this . _savePreviouslyFocusedElement ( ) ; return this . _portalOutlet . attachTemplatePortal ( portal ) ; } constructor ( private _elementRef , private _focusTrapFactory , @ Optional ( ) @ Inject ( DOCUMENT ) private _document , public _config ) { super ( ) ; } /** Moves the focus inside the focus trap. */ trapFocus ( ) { const element = this . _elementRef . nativeElement ; if ( ! this . _focusTrap ) { this . _focusTrap = this . _focusTrapFactory . create ( element ) ; } if ( this . _config . autoFocus ) { this . _focusTrap . focusInitialElementWhenReady ( ) ; } else { const activeElement = this . _document . activeElement ; if ( activeElement !== element && ! element . contains ( activeElement ) ) { element . focus ( ) ; } } } /** Restores focus to the element that was focused before the dialog opened. */ restoreFocus ( ) { const toFocus = this . _elementFocusedBeforeDialogWasOpened ; if ( this . _config . restoreFocus && toFocus && typeof toFocus . focus === 'function' ) { toFocus . focus ( ) ; } if ( this . _focusTrap ) { this . _focusTrap . destroy ( ) ; } } /** Saves a reference to the element that was focused before the dialog was opened. */ private _savePreviouslyFocusedElement ( ) { if ( this . _document ) { this . _elementFocusedBeforeDialogWasOpened = this . _document . activeElement as HTMLElement ; if ( this . _elementRef . nativeElement . focus ) { Promise . resolve ( ) . then ( ( ) => this . _elementRef . nativeElement . focus ( ) ) ; } } } }