license import { animate , AnimationEvent , state , style , transition , trigger } from '@angular/animations' ; import { FocusTrapFactory } from '@angular/cdk/a11y' ; import { BasePortalOutlet , ComponentPortal , PortalHostDirective , TemplatePortal } from '@angular/cdk/portal' ; import { DOCUMENT } from '@angular/common' ; import { ChangeDetectionStrategy , ChangeDetectorRef , Component , ComponentRef , ElementRef , EmbeddedViewRef , HostBinding , Inject , OnDestroy , Optional , ViewChild , ViewEncapsulation , } from '@angular/core' ; import { Subject } from 'rxjs' ; import { distinctUntilChanged } from 'rxjs/operators' ; import { DialogConfig } from './dialog-config' ; export function throwDialogContentAlreadyAttachedError ( ) { throw Error ( 'Attempting to attach dialog content after content is already attached' ) ; } docs-private @ Component ( { moduleId : module . id , selector : 'cdk-dialog-container' , templateUrl : './dialog-container.html' , styleUrls : [ 'dialog-container.css' ] , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . Default , animations : [ trigger ( 'dialog' , [ state ( 'enter' , style ( { opacity : 1 } ) ) , state ( 'exit, void' , style ( { opacity : 0 } ) ) , transition ( '* => enter' , animate ( '{{enterAnimationDuration}}' ) ) , transition ( '* => exit, * => void' , animate ( '{{exitAnimationDuration}}' ) ) , ] ) ] , host : { '[@dialog]' : `{
      value: _state,
      params: {
        enterAnimationDuration: _config.enterAnimationDuration,
        exitAnimationDuration: _config.exitAnimationDuration
      }
    }` , '(@dialog.start)' : '_onAnimationStart($event)' , '(@dialog.done)' : '_animationDone.next($event)' , } , } ) export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy { /** State of the dialog animation. */ _state : 'void' | 'enter' | 'exit' = 'enter' ; /** Element that was focused before the dialog was opened. Save this to restore upon close. */ private _elementFocusedBeforeDialogWasOpened : HTMLElement | null = null ; /** The class that traps and manages focus within the dialog. */ private _focusTrap = this . _focusTrapFactory . create ( this . _elementRef . nativeElement , false ) ; @ HostBinding ( 'attr.aria-label' ) get _ariaLabel ( ) { return this . _config . ariaLabel || null ; } @ HostBinding ( 'attr.aria-describedby' ) get _ariaDescribedBy ( ) { return this . _config . ariaDescribedBy ; } @ HostBinding ( 'attr.role' ) get _role ( ) { return this . _config . role ; } @ HostBinding ( 'attr.tabindex' ) get _tabindex ( ) { return - 1 ; } /** The portal host inside of this container into which the dialog content will be loaded. */ @ ViewChild ( PortalHostDirective ) _portalHost : PortalHostDirective ; /** A subject emitting before the dialog enters the view. */ _beforeEnter : Subject < void > = new Subject ( ) ; /** A subject emitting after the dialog enters the view. */ _afterEnter : Subject < void > = new Subject ( ) ; /** A subject emitting before the dialog exits the view. */ _beforeExit : Subject < void > = new Subject ( ) ; /** A subject emitting after the dialog exits the view. */ _afterExit : Subject < void > = new Subject ( ) ; /** Stream of animation `done` events. */ _animationDone = new Subject < AnimationEvent > ( ) ; constructor ( private _elementRef , private _focusTrapFactory , private _changeDetectorRef , @ Optional ( ) @ Inject ( DOCUMENT ) private _document , /** The dialog configuration. */ public _config ) { super ( ) ; this . _animationDone . pipe ( distinctUntilChanged ( ( x , y ) => { return x . fromState === y . fromState && x . toState === y . toState ; } ) ) . subscribe ( event => { if ( event . toState === 'enter' ) { this . _autoFocusFirstTabbableElement ( ) ; this . _afterEnter . next ( ) ; this . _afterEnter . complete ( ) ; } if ( event . fromState === 'enter' && ( event . toState === 'void' || event . toState === 'exit' ) ) { this . _returnFocusAfterDialog ( ) ; this . _afterExit . next ( ) ; this . _afterExit . complete ( ) ; } } ) ; } /** Destroy focus trap to place focus back to the element focused before the dialog opened. */ ngOnDestroy ( ) { this . _focusTrap . destroy ( ) ; this . _animationDone . complete ( ) ; } param portal attachComponentPortal < T > ( portal ) { if ( this . _portalHost . hasAttached ( ) ) { throwDialogContentAlreadyAttachedError ( ) ; } this . _savePreviouslyFocusedElement ( ) ; return this . _portalHost . attachComponentPortal ( portal ) ; } param portal attachTemplatePortal < C > ( portal ) { if ( this . _portalHost . hasAttached ( ) ) { throwDialogContentAlreadyAttachedError ( ) ; } this . _savePreviouslyFocusedElement ( ) ; return this . _portalHost . attachTemplatePortal ( portal ) ; } /** Emit lifecycle events based on animation `start` callback. */ _onAnimationStart ( event ) { if ( event . toState === 'enter' ) { this . _beforeEnter . next ( ) ; this . _beforeEnter . complete ( ) ; } if ( event . fromState === 'enter' && ( event . toState === 'void' || event . toState === 'exit' ) ) { this . _beforeExit . next ( ) ; this . _beforeExit . complete ( ) ; } } /** Starts the dialog exit animation. */ _startExiting ( ) { this . _state = 'exit' ; this . _changeDetectorRef . markForCheck ( ) ; } /** 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 ; Promise . resolve ( ) . then ( ( ) => this . _elementRef . nativeElement . focus ( ) ) ; } } /**
     * Autofocus the first tabbable element inside of the dialog, if there is not a tabbable element,
     * focus the dialog instead.
     */ private _autoFocusFirstTabbableElement ( ) { if ( this . _config . autoFocus ) { this . _focusTrap . focusInitialElementWhenReady ( ) . then ( hasMovedFocus => { if ( ! hasMovedFocus ) { this . _elementRef . nativeElement . focus ( ) ; } } ) ; } } /** Returns the focus to the element focused before the dialog was open. */ private _returnFocusAfterDialog ( ) { const toFocus = this . _elementFocusedBeforeDialogWasOpened ; if ( toFocus && typeof toFocus . focus === 'function' ) { toFocus . focus ( ) ; } } }