import { concatArray , FunctionProp } from 'ngx-tethys/util' ; import { Subject } from 'rxjs' ; import { ComponentType , Overlay , OverlayConfig , OverlayRef , ScrollStrategy } from '@angular/cdk/overlay' ; import { ComponentPortal , TemplatePortal } from '@angular/cdk/portal' ; import { Injector , TemplateRef } from '@angular/core' ; import { ThyAbstractOverlayContainer } from './abstract-overlay-container' ; import { ThyAbstractOverlayRef } from './abstract-overlay-ref' ; import { ThyAbstractOverlayConfig , ThyAbstractOverlayOptions } from './abstract-overlay.config' ; export type ComponentTypeOrTemplateRef < T > = ComponentType < T > | TemplateRef < T > ; export abstract class ThyAbstractOverlayService < TConfig extends ThyAbstractOverlayConfig , TContainer extends ThyAbstractOverlayContainer > { private openedOverlays : ThyAbstractOverlayRef < unknown , TContainer > [ ] = [ ] ; private readonly _afterAllClosed = new Subject < void > ( ) ; private readonly _afterOpened = new Subject < ThyAbstractOverlayRef < any , TContainer > > ( ) ; constructor ( protected options , protected overlay , protected injector , protected defaultConfig , public scrollStrategy ? ) { } /** Build cdk overlay config by config */ protected abstract buildOverlayConfig ( config ) ; /** Attach overlay container to overlay*/ protected abstract attachOverlayContainer ( overlay , config ) ; /** Create abstract overlay ref by cdk overlay, container and config  */ protected abstract createAbstractOverlayRef < T , TResult > ( overlayRef , containerInstance , config ) ; /** Create injector for component content */ protected abstract createInjector < T > ( config , overlayRef , containerInstance ) ; abstract open < T , TData = undefined , TResult = undefined > ( componentOrTemplateRef , config ? ) ; /** Attach component or template ref to overlay container */ protected attachOverlayContent < T , TResult > ( componentOrTemplateRef , containerInstance , overlayRef , config ) { const abstractOverlayRef = this . createAbstractOverlayRef < T , TResult > ( overlayRef , containerInstance , config ) ; if ( config . hasBackdrop ) { overlayRef . backdropClick ( ) . subscribe ( ( ) => { if ( abstractOverlayRef . backdropClosable ) { abstractOverlayRef . close ( ) ; } } ) ; } if ( componentOrTemplateRef instanceof TemplateRef ) { containerInstance . attachTemplatePortal ( new TemplatePortal < T > ( componentOrTemplateRef , null , < any > { $implicit : config . initialState , [ `${ this . options . name }Ref` ] : abstractOverlayRef } ) ) ; } else { const injector = this . createInjector < T > ( config , abstractOverlayRef , containerInstance ) ; const contentRef = containerInstance . attachComponentPortal < T > ( new ComponentPortal ( componentOrTemplateRef , undefined , injector ) ) ; if ( config . initialState ) { Object . assign ( contentRef . instance , config . initialState ) ; } abstractOverlayRef . componentInstance = contentRef . instance ; } return abstractOverlayRef ; } protected removeOpenedOverlay ( overlayRef ) { const index = this . openedOverlays . indexOf ( overlayRef ) ; if ( index > - 1 ) { this . openedOverlays . splice ( index , 1 ) ; if ( ! this . openedOverlays . length ) { this . _afterAllClosed . next ( ) ; } } } protected getAbstractOverlayById ( id ) { return this . openedOverlays . find ( overlay => overlay . id === id ) ; } protected getAbstractOverlays ( ) { return this . openedOverlays ; } protected buildBaseOverlayConfig ( config , defaultPanelClass ? ) { const overlayConfig = new OverlayConfig ( { positionStrategy : this . overlay . position ( ) . global ( ) , hasBackdrop : config . hasBackdrop , direction : config . direction , width : config . width , height : config . height , minWidth : config . minWidth , minHeight : config . minHeight , maxWidth : config . maxWidth , maxHeight : config . maxHeight , disposeOnNavigation : config . closeOnNavigation } ) ; if ( config . backdropClass ) { overlayConfig . backdropClass = config . backdropClass ; } overlayConfig . panelClass = concatArray ( config . panelClass , defaultPanelClass ) ; return overlayConfig ; } protected openOverlay < T , TResult = unknown > ( componentOrTemplateRef , config ? ) { config = { ... this . defaultConfig , ... config } ; if ( config . id && this . getAbstractOverlayById ( config . id ) ) { throw Error ( `${ this . options . name } with id ${ config . id } exists already. The ${ this . options . name } id must be unique.` ) ; } const overlayConfig = this . buildOverlayConfig ( config ) ; const overlayRef = this . overlay . create ( overlayConfig ) ; const overlayContainer = this . attachOverlayContainer ( overlayRef , config ) ; const abstractOverlayRef = this . attachOverlayContent < T , TResult > ( componentOrTemplateRef , overlayContainer , overlayRef , config ) ; this . openedOverlays . push ( abstractOverlayRef ) ; abstractOverlayRef . afterClosed ( ) . subscribe ( ( ) => { this . removeOpenedOverlay ( abstractOverlayRef ) ; } ) ; this . _afterOpened . next ( abstractOverlayRef ) ; return abstractOverlayRef ; } afterAllClosed ( ) { return this . _afterAllClosed ; } afterOpened ( ) { return this . _afterOpened ; } close < T > ( result ? ) { if ( this . openedOverlays . length > 0 ) { const lastOverlayRef = this . openedOverlays [ this . openedOverlays . length - 1 ] ; if ( lastOverlayRef ) { lastOverlayRef . close ( result ) ; } } } closeAll ( ) { let i = this . openedOverlays . length ; while ( i -- ) { this . openedOverlays [ i ] . close ( ) ; } } dispose ( ) { this . closeAll ( ) ; this . _afterAllClosed . complete ( ) ; this . _afterOpened . complete ( ) ; } }