import { ElementRef, EventEmitter, Injector } from '@angular/core'; import { Overlay, OverlayRef, OverlayConfig, ComponentType, PositionStrategy, } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { MwOverlayPositionX } from './overlay-position.enum'; import { MwOverlayOptions } from './overlay.models'; import { Subject } from 'rxjs'; export interface MwBaseOverlayComponent { close(): void; } export class MwOverlayComponent implements MwBaseOverlayComponent { opened = new EventEmitter(); closed = new EventEmitter(); isOpen = false; instance$ = new Subject(); private portal: ComponentPortal; private overlayRef: OverlayRef; private hostRef: ElementRef; private positionX = MwOverlayPositionX.Right; private readonly defaultOptions: MwOverlayOptions = { hasBackdrop: true, position: MwOverlayPositionX.Right, }; constructor( private component: ComponentType, private overlay: Overlay, private injector?: Injector ) { this.portal = new ComponentPortal(this.component, null, this.injector); } setup(hostRef: ElementRef, options?: MwOverlayOptions): void { options = { ...this.defaultOptions, ...options, }; this.hostRef = hostRef; this.positionX = options.position; this.setupOverlay(options); } show(options?: MwOverlayOptions): T { let instance: T = null; if (this.overlayRef && !this.overlayRef.hasAttached()) { const containerRef = this.overlayRef.attach(this.portal); this.opened.emit(containerRef.instance); this.instance$.next(containerRef.instance); this.isOpen = true; instance = containerRef.instance; } else { this.setupOverlay(); instance = this.show(); } if (options && options.width) { this.overlayRef.updateSize({ width: options.width, }); } return instance; } close(): void { if (this.overlayRef && this.overlayRef.hasAttached()) { this.overlayRef.detach(); this.closed.emit(); this.instance$.next(null); this.isOpen = false; } } dispose(): void { if (this.overlayRef) { this.overlayRef.dispose(); this.overlayRef = null; } } private setupOverlay(options?: MwOverlayOptions): void { this.dispose(); if (!this.overlayRef) { this.createOverlay(options); } } private createOverlay(options?: MwOverlayOptions): void { this.overlayRef = this.overlay.create(this.getOverlayConfig(options)); if (this.overlayRef) { this.overlayRef.backdropClick().subscribe(() => this.close()); } } private getOverlayConfig(options?: MwOverlayOptions): OverlayConfig { const overlayConfig = new OverlayConfig(); overlayConfig.positionStrategy = this.getOverlayPosition(); overlayConfig.panelClass = options.panelClass; if (options && options.hasBackdrop) { overlayConfig.hasBackdrop = true; overlayConfig.backdropClass = 'cdk-overlay-transparent-backdrop'; } return overlayConfig; } private getOverlayPosition(): PositionStrategy { return this.overlay .position() .flexibleConnectedTo(this.hostRef) .withFlexibleDimensions(false) .withViewportMargin(8) .withLockedPosition() .withPositions([ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', }, { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', }, { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', }, { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', }, ]); } }