Reference import { ChangeDetectorRef , ComponentFactoryResolver , ComponentRef , Directive , EventEmitter , HostListener , Input , OnChanges , Output , SimpleChange , ViewContainerRef , ApplicationRef , Injector , Type , EmbeddedViewRef } from '@angular/core' ; import { PopoverContentComponent } from './popover-content.component' ; import { PopoverPlacement } from './popover.placement' ; group component @ Directive ( { selector : '[popover]' , exportAs : 'popover' } ) export class PopoverDirective implements OnChanges { protected popoverContentComponent = PopoverContentComponent ; protected popover : ComponentRef < PopoverContentComponent > ; protected visible : boolean ; constructor ( protected viewContainerRef , protected cdr , protected resolver , protected appRef , private injector ) { } @ Input ( 'popover' ) public content : string | PopoverContentComponent ; @ Input ( ) public popoverSize : 'small' | 'medium-small' | 'medium' | 'large' | 'auto' ; @ Input ( ) public popoverDisabled : boolean ; @ Input ( ) public popoverAnimation : boolean ; @ Input ( ) public popoverPlacement : PopoverPlacement ; @ Input ( ) public popoverTitle : string ; @ Input ( ) public popoverOnHover = true ; @ Input ( ) public popoverCloseOnClickOutside : boolean ; @ Input ( ) public popoverCloseOnMouseOutside : boolean ; @ Input ( ) public popoverDismissTimeout = 0 ; @ Input ( ) public appendToBody : boolean ; @ Output ( ) public onShown = new EventEmitter < PopoverDirective > ( ) ; @ Output ( ) public onHidden = new EventEmitter < PopoverDirective > ( ) ; @ HostListener ( 'click' , [ '$event' ] ) public showOrHideOnClick ( evt ) { if ( this . popoverOnHover ) { return ; } if ( this . popoverDisabled ) { return ; } evt . stopImmediatePropagation ( ) ; this . toggle ( ) ; } @ HostListener ( 'touchend' , [ '$event' ] ) public showOrHideOnTouch ( evt ) { evt . stopImmediatePropagation ( ) ; if ( ! this . popoverOnHover ) { return ; } if ( this . popoverDisabled ) { return ; } this . toggle ( ) ; } @ HostListener ( 'focusin' ) @ HostListener ( 'mouseenter' ) public showOnHover ( ) { if ( ! this . popoverOnHover ) { return ; } if ( this . popoverDisabled ) { return ; } this . show ( ) ; } @ HostListener ( 'focusout' ) @ HostListener ( 'mouseleave' ) public hideOnHover ( ) { if ( this . popoverCloseOnMouseOutside ) { return ; } if ( ! this . popoverOnHover ) { return ; } if ( this . popoverDisabled ) { return ; } this . hide ( ) ; } public ngOnChanges ( changes ) { if ( changes [ 'popoverDisabled' ] ) { if ( changes [ 'popoverDisabled' ] . currentValue ) { this . hide ( ) ; } } } protected createComponent ( component ) { const factory = this . resolver . resolveComponentFactory ( component ) ; const componentRef = this . appendToBody ? factory . create ( this . injector ) : this . viewContainerRef . createComponent ( factory ) ; if ( this . appendToBody ) { this . appRef . attachView ( componentRef . hostView ) ; const domElem = ( componentRef . hostView as EmbeddedViewRef < any > ) . rootNodes [ 0 ] as HTMLElement ; document . body . appendChild ( domElem ) ; } return componentRef ; } protected removeComponent ( componentRef ) { if ( this . popover ) { if ( this . appendToBody ) { this . appRef . detachView ( componentRef . hostView ) ; } componentRef . destroy ( ) ; } } public toggle ( ) { if ( ! this . visible ) { this . show ( ) ; } else { this . hide ( ) ; } } public show ( ) { if ( this . visible ) { return ; } this . visible = true ; if ( typeof this . content === 'string' ) { if ( ! this . visible ) { return ; } this . popover = this . createComponent ( this . popoverContentComponent ) ; const popover = this . popover . instance as PopoverContentComponent ; popover . popover = this ; popover . content = this . content as string ; if ( this . popoverPlacement !== undefined ) { popover . placement = this . popoverPlacement ; } if ( this . popoverAnimation !== undefined ) { popover . animation = this . popoverAnimation ; } if ( this . popoverTitle !== undefined ) { popover . title = this . popoverTitle ; } if ( this . popoverCloseOnClickOutside !== undefined ) { popover . closeOnClickOutside = this . popoverCloseOnClickOutside ; } if ( this . popoverCloseOnMouseOutside !== undefined ) { popover . closeOnMouseOutside = this . popoverCloseOnMouseOutside ; } if ( this . popoverSize ) { popover . size = this . popoverSize ; } popover . appendToBody = this . appendToBody ; popover . onCloseFromOutside . subscribe ( ( ) => this . hide ( ) ) ; if ( this . popoverDismissTimeout > 0 ) { setTimeout ( ( ) => this . hide ( ) , this . popoverDismissTimeout ) ; } } else { const popover = this . content as PopoverContentComponent ; popover . popover = this ; if ( this . popoverPlacement !== undefined ) { popover . placement = this . popoverPlacement ; } if ( this . popoverAnimation !== undefined ) { popover . animation = this . popoverAnimation ; } if ( this . popoverTitle !== undefined ) { popover . title = this . popoverTitle ; } if ( this . popoverCloseOnClickOutside !== undefined ) { popover . closeOnClickOutside = this . popoverCloseOnClickOutside ; } if ( this . popoverCloseOnMouseOutside !== undefined ) { popover . closeOnMouseOutside = this . popoverCloseOnMouseOutside ; } if ( this . popoverSize ) { popover . size = this . popoverSize ; } popover . appendToBody = this . appendToBody ; popover . onCloseFromOutside . subscribe ( ( ) => this . hide ( ) ) ; if ( this . popoverDismissTimeout > 0 ) { setTimeout ( ( ) => this . hide ( ) , this . popoverDismissTimeout ) ; } popover . show ( ) ; } this . cdr . detectChanges ( ) ; this . onShown . emit ( this ) ; } public hide ( ) { if ( ! this . visible ) { return ; } this . visible = false ; this . removeComponent ( this . popover ) ; if ( this . content instanceof PopoverContentComponent ) { ( this . content as PopoverContentComponent ) . hideFromPopover ( ) ; } this . cdr . detectChanges ( ) ; this . onHidden . emit ( this ) ; } public getElement ( ) { return this . viewContainerRef . element . nativeElement ; } }