/* Angular Imports */ import { ComponentType, Overlay, OriginConnectionPosition, OverlayConnectionPosition, ConnectionPositionPair } from '@angular/cdk/overlay'; import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal'; import { Injectable, InjectionToken, Injector, TemplateRef, ElementRef } from '@angular/core'; /* Custom Imports */ import { PopoverConfig } from './popover-config'; import { PopoverRef } from './popover-ref'; import { PopoverComponent } from './popover.component'; /** * Injection token that can be used to access the data that was passed in to a popover. * */ export const POPOVER_DATA = new InjectionToken('popover.data'); const defaultConfig: PopoverConfig = { backdropClass: '', disableClose: false, panelClass: '', arrowOffset: 30, arrowSize: 20 }; /** * Service to open modal and manage popovers. */ @Injectable({ providedIn: 'root' }) export class PopoverService { /** * @param {Injector} injector Injector. * @param {overlay} overlay Overlay. */ constructor(private overlay: Overlay, private injector: Injector) { } /** * Opens the popover at target element * @param {ComponentType | TemplateRef} componentOrTemplate ComponentType | TemplateRef. * @param {ElementRef | HTMLElement} target ElementRef | HTMLElement. * @param {string} position String. * @param {Partial = {}} config Partial = {}. */ open(componentOrTemplate: ComponentType | TemplateRef, target: ElementRef | HTMLElement, position: string, backdrop: boolean, config: Partial = {}): PopoverRef { const popoverConfig: PopoverConfig = Object.assign({}, defaultConfig, config); const arrowSize = popoverConfig.arrowSize; const arrowOffset = popoverConfig.arrowOffset; const panelOffset = arrowSize / 2; let showbackdrop: boolean; let positions: ConnectionPositionPair[]; // preferred positions, in order of priority if (position === 'bottom') { positions = [ // bottom center { overlayX: 'center', overlayY: 'top', originX: 'center', originY: 'bottom', panelClass: ['top', 'center'], offsetY: panelOffset }, // bottom left { overlayX: 'start', overlayY: 'top', originX: 'center', originY: 'bottom', panelClass: ['top', 'left'], offsetX: -1 * arrowOffset, offsetY: panelOffset }, // bottom right { overlayX: 'end', overlayY: 'top', originX: 'center', originY: 'bottom', panelClass: ['top', 'right'], offsetX: arrowOffset, offsetY: panelOffset } ]; } else if (position === 'top') { positions = [ // top center { overlayX: 'center', overlayY: 'bottom', originX: 'center', originY: 'top', panelClass: ['bottom', 'center'], offsetY: -1 * panelOffset }, // top left { overlayX: 'start', overlayY: 'bottom', originX: 'center', originY: 'top', panelClass: ['bottom', 'left'], offsetX: -1 * arrowOffset, offsetY: -1 * panelOffset }, // top right { overlayX: 'end', overlayY: 'bottom', originX: 'center', originY: 'top', panelClass: ['bottom', 'right'], offsetX: arrowOffset, offsetY: -1 * panelOffset } ]; } else if (position === 'right') { positions = [ // right { originX : 'end', originY : 'center', overlayX: 'start', overlayY: 'center', panelClass: ['center', 'left'], offsetX: 0.5 * arrowOffset, offsetY: -1 * panelOffset }, ]; } else if (position === 'left') { positions = [ // left { originX : 'start', originY : 'center', overlayX: 'end', overlayY: 'center', panelClass: ['center', 'right'], offsetX: -0.5 * arrowOffset, offsetY: -1 * panelOffset }, ]; } if (backdrop === true) { showbackdrop = true; } else { showbackdrop = false; } const positionStrategy = this.overlay .position() .flexibleConnectedTo(target) .withPush(false) .withFlexibleDimensions(false) .withPositions(positions); const overlayRef = this.overlay.create({ hasBackdrop: showbackdrop, backdropClass: config.backdropClass, panelClass: config.panelClass, positionStrategy, scrollStrategy: this.overlay.scrollStrategies.reposition() }); const popoverRef = new PopoverRef(overlayRef, positionStrategy, popoverConfig); const popover = overlayRef.attach(new ComponentPortal( PopoverComponent, null, Injector.create({ parent: this.injector, providers: [ { provide: PopoverRef, useValue: popoverRef } ] }) // new PortalInjector( // this.injector, // new WeakMap([ // [PopoverRef, popoverRef] // ]) // ) )).instance; if (componentOrTemplate instanceof TemplateRef) { // rendering a provided template dynamically popover.attachTemplatePortal( new TemplatePortal( componentOrTemplate, null, { $implicit: config.data, popover: popoverRef } ) ); } else { // rendering a provided component dynamically popover.attachComponentPortal( new ComponentPortal( componentOrTemplate, null, Injector.create({ parent: this.injector, providers: [ { provide: POPOVER_DATA, useValue: config.data }, { provide: PopoverRef, useValue: popoverRef }, ] }) // new PortalInjector( // this.injector, // new WeakMap([ // [POPOVER_DATA, config.data], // [PopoverRef, popoverRef] // ]) // ) ) ); } return popoverRef; } }