import { coerceArray } from '@angular/cdk/coercion' ; import { Overlay , OverlayConfig , OverlayContainer , OverlayRef , ScrollStrategy , } from '@angular/cdk/overlay' ; import { ComponentPortal , ComponentType , TemplatePortal } from '@angular/cdk/portal' ; import { Directive , Inject , Injectable , InjectionToken , Injector , OnDestroy , Optional , SkipSelf , StaticProvider , TemplateRef , Type , } from '@angular/core' ; import { defer , Observable , Subject } from 'rxjs' ; import { startWith } from 'rxjs/operators' ; import { SbbDialogConfig } from './dialog-config' ; import { SbbDialogContainer , _SbbDialogContainerBase } from './dialog-container' ; import { SbbDialogRef } from './dialog-ref' ; /** Injection token that can be used to access the data that was passed in to a dialog. */ export const SBB_DIALOG_DATA = new InjectionToken < any > ( 'SbbDialogData' ) ; /** Injection token that can be used to specify default dialog options. */ export const SBB_DIALOG_DEFAULT_OPTIONS = new InjectionToken < SbbDialogConfig > ( 'sbb-dialog-default-options' ) ; /** Injection token that determines the scroll handling while the dialog is open. */ export const SBB_DIALOG_SCROLL_STRATEGY = new InjectionToken < ( ) => ScrollStrategy > ( 'sbb-dialog-scroll-strategy' ) ; docs-private export function SBB_DIALOG_SCROLL_STRATEGY_FACTORY ( overlay ) { return ( ) => overlay . scrollStrategies . block ( ) ; } docs-private export function SBB_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY ( overlay ) { return ( ) => overlay . scrollStrategies . block ( ) ; } docs-private export const SBB_DIALOG_SCROLL_STRATEGY_PROVIDER = { provide : SBB_DIALOG_SCROLL_STRATEGY , deps : [ Overlay ] , useFactory : SBB_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY , } ; /**
 * Base class for dialog services. The base dialog service allows
 * for arbitrary dialog refs and dialog container components.
 */ @ Directive ( ) export abstract class _SbbDialogBase < C extends _SbbDialogContainerBase , F extends SbbDialogRef < any > = SbbDialogRef < any > > implements OnDestroy { private _openDialogsAtThisLevel : F [ ] = [ ] ; private readonly _afterAllClosedAtThisLevel = new Subject < void > ( ) ; private readonly _afterOpenedAtThisLevel = new Subject < F > ( ) ; private _ariaHiddenElements = new Map < Element , string | null > ( ) ; private _scrollStrategy : ( ) => ScrollStrategy ; /** Keeps track of the currently-open dialogs. */ get openDialogs ( ) : F [ ] { return this . _parentDialog ? this . _parentDialog . openDialogs : this . _openDialogsAtThisLevel ; } /** Stream that emits when a dialog has been opened. */ get afterOpened ( ) : Subject < F > { return this . _parentDialog ? this . _parentDialog . afterOpened : this . _afterOpenedAtThisLevel ; } _getAfterAllClosed ( ) { const parent = this . _parentDialog ; return parent ? parent . _getAfterAllClosed ( ) : 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 . _getAfterAllClosed ( ) : this . _getAfterAllClosed ( ) . pipe ( startWith ( undefined ) ) ) as Observable < any > ; constructor ( private _overlay , private _injector , private _defaultOptions , private _parentDialog , private _overlayContainer , scrollStrategy , private _dialogRefConstructor , private _dialogContainerType , private _dialogDataToken ) { this . _scrollStrategy = scrollStrategy ; } param componentOrTemplateRef param config returns open < T , D = any , R = any > ( componentOrTemplateRef , config ? ) { config = _applyConfigDefaults ( config , this . _defaultOptions || new SbbDialogConfig ( ) ) ; if ( config . id && this . getDialogById ( config . id ) && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) { throw Error ( `Dialog with id "${ config . id }" exists already. The dialog id must be unique.` ) ; } const overlayRef = this . _createOverlay ( config ) ; const dialogContainer = this . _attachDialogContainer ( overlayRef , config ) ; const dialogRef = this . _attachDialogContent < T , R > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) ; if ( ! this . openDialogs . length ) { this . _hideNonDialogContentFromAssistiveTechnology ( ) ; } this . openDialogs . push ( dialogRef ) ; dialogRef . afterClosed ( ) . subscribe ( ( ) => this . _removeOpenDialog ( dialogRef ) ) ; this . afterOpened . next ( dialogRef ) ; dialogContainer . _initializeWithAttachedContent ( ) ; 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 ( ) ; } param config returns private _createOverlay ( config ) { const overlayConfig = this . _getOverlayConfig ( config ) ; return this . _overlay . create ( overlayConfig ) ; } param dialogConfig returns private _getOverlayConfig ( dialogConfig ) { const state = new OverlayConfig ( { positionStrategy : this . _overlay . position ( ) . global ( ) , scrollStrategy : dialogConfig . scrollStrategy || this . _scrollStrategy ( ) , panelClass : dialogConfig . panelClass , hasBackdrop : dialogConfig . hasBackdrop , backdropClass : [ 'sbb-overlay-background' ] , minWidth : dialogConfig . minWidth , minHeight : dialogConfig . minHeight , maxWidth : dialogConfig . maxWidth , maxHeight : dialogConfig . maxHeight , disposeOnNavigation : dialogConfig . closeOnNavigation , } ) ; if ( dialogConfig . backdropClass ) { state . backdropClass = coerceArray ( dialogConfig . backdropClass ) . concat ( state . backdropClass ! ) ; } return state ; } param overlay param config returns private _attachDialogContainer ( overlay , config ) { const userInjector = config && config . viewContainerRef && config . viewContainerRef . injector ; const injector = Injector . create ( { parent : userInjector || this . _injector , providers : [ { provide : SbbDialogConfig , useValue : config } ] , } ) ; const containerPortal = new ComponentPortal ( this . _dialogContainerType , config . viewContainerRef , injector , config . componentFactoryResolver ) ; const containerRef = overlay . attach < C > ( containerPortal ) ; return containerRef . instance ; } param componentOrTemplateRef param dialogContainer param overlayRef param config returns private _attachDialogContent < T , R > ( componentOrTemplateRef , dialogContainer , overlayRef , config ) { const dialogRef = new this . _dialogRefConstructor ( overlayRef , dialogContainer , config . id ) ; if ( componentOrTemplateRef instanceof TemplateRef ) { dialogContainer . attachTemplatePortal ( new TemplatePortal < T > ( componentOrTemplateRef , null ! , < any > { $implicit : config . data , dialogRef , lightboxRef : 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 ; } dialogRef . updateSize ( config . width , config . height ) . updatePosition ( config . position ) ; return dialogRef ; } param config param dialogRef param dialogContainer returns private _createInjector < T > ( config , dialogRef , dialogContainer ) { const userInjector = config && config . viewContainerRef && config . viewContainerRef . injector ; const providers = [ { provide : this . _dialogContainerType , useValue : dialogContainer } , { provide : this . _dialogDataToken , useValue : config . data } , { provide : this . _dialogRefConstructor , useValue : dialogRef } , ] ; return Injector . create ( { parent : userInjector || this . _injector , providers } ) ; } param dialogRef private _removeOpenDialog ( dialogRef ) { const index = this . openDialogs . indexOf ( dialogRef ) ; if ( index > - 1 ) { 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 . _getAfterAllClosed ( ) . next ( ) ; } } } /**
     * Hides all of the content that isn't an overlay from assistive technology.
     */ private _hideNonDialogContentFromAssistiveTechnology ( ) { const overlayContainer = this . _overlayContainer . getContainerElement ( ) ; if ( overlayContainer . parentElement ) { const siblings = overlayContainer . parentElement . children ; for ( let i = siblings . length - 1 ; i > - 1 ; i -- ) { const sibling = siblings [ i ] ; if ( sibling !== overlayContainer && sibling . nodeName !== 'SCRIPT' && sibling . nodeName !== 'STYLE' && ! sibling . hasAttribute ( 'aria-live' ) ) { this . _ariaHiddenElements . set ( sibling , sibling . getAttribute ( 'aria-hidden' ) ) ; sibling . setAttribute ( 'aria-hidden' , 'true' ) ; } } } } /** Closes all of the dialogs in an array. */ private _closeDialogs ( dialogs ) { let i = dialogs . length ; while ( i -- ) { dialogs [ i ] . close ( ) ; } } } /**
 * Service to open modal dialogs.
 */ @ Injectable ( ) export class SbbDialog extends _SbbDialogBase < SbbDialogContainer > { constructor ( overlay , injector , @ Optional ( ) @ Inject ( SBB_DIALOG_DEFAULT_OPTIONS ) defaultOptions , @ Inject ( SBB_DIALOG_SCROLL_STRATEGY ) scrollStrategy , @ Optional ( ) @ SkipSelf ( ) parentDialog , overlayContainer ) { super ( overlay , injector , defaultOptions , parentDialog , overlayContainer , scrollStrategy , SbbDialogRef , SbbDialogContainer , SBB_DIALOG_DATA ) ; } } param config param defaultOptions returns function _applyConfigDefaults ( config ? , defaultOptions ? ) { return { ... defaultOptions , ... config } ; }