UNPKG

@wizdm/teleport

Version:
203 lines (196 loc) 7.98 kB
import { InjectionToken, ɵɵdefineInjectable, ɵɵinject, Injectable, Optional, Inject, EventEmitter, SimpleChange, Directive, TemplateRef, ViewContainerRef, Input, Output, NgModule } from '@angular/core'; import { NgTemplateOutlet, CommonModule } from '@angular/common'; import { shareReplay, filter, scan, map, distinctUntilChanged, switchMap, delay } from 'rxjs/operators'; import { BehaviorSubject } from 'rxjs'; const TeleportConfigToken = new InjectionToken('wizdm.teleport.config'); class TeleportService { constructor(config) { this.inner$ = new BehaviorSubject(null); this.beam$ = this.inner$.pipe(shareReplay((config === null || config === void 0 ? void 0 : config.bufferSize) || 1)); } /** Beam observable streaming the template to render as they occur */ beam(name) { return this.beam$.pipe( // Filters only those instances targetting the requested portal filter(payload => !payload || payload.target === name), // Buffers the requests scan((history, payload) => { // Clears all the history if (!payload || payload.action === 'clearAll') { return []; } // Seeks for the given template in the history first const index = history.findIndex(value => value.template === payload.template); // Removes the template from the history if (index >= 0 && payload.action === 'clear') { return history.splice(index, 1), history; } // Activates the new template if (payload.action === 'activate') { // If already existing, replaces the payload in place if (index >= 0) { return history.splice(index, 1, payload), history; } // Pushes the new template otherwise history.push(payload); } return history; }, []), // Emits the last template in history or null map(history => history[history.length - 1] || null), distinctUntilChanged()); } /** Activates the template at the given target portal */ activate(target, template, data) { this.inner$.next({ action: 'activate', target, template, data }); } /** Clears the template from the target portal */ clear(target, template) { this.inner$.next({ action: 'clear', target, template }); } /** Clear all the portals */ clearAll() { this.inner$.next({ action: 'clearAll' }); } } TeleportService.ɵprov = ɵɵdefineInjectable({ factory: function TeleportService_Factory() { return new TeleportService(ɵɵinject(TeleportConfigToken, 8)); }, token: TeleportService, providedIn: "root" }); TeleportService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; TeleportService.ctorParameters = () => [ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [TeleportConfigToken,] }] } ]; class PortalDirective extends NgTemplateOutlet { constructor(teleport, host, container) { super(container); this.teleport = teleport; this.host = host; this.name$ = new BehaviorSubject(''); this.firstChange = true; /** The actual template in use. NOTE for this event to bind correclty the directive must be used in its de-sugared form */ this.template = new EventEmitter(); /** True then the portal is active */ this.active = new EventEmitter(); /** Optional data passed along from the TeleportDirective back to the Portal */ this.data = new EventEmitter(); // Builds the template observable this.sub = this.name$.pipe( // Beams the content with the given name switchMap(name => this.teleport.beam(name)), // Wait the next round to avoid expressionChangedAfterItHasBeenChecked() exception delay(0)).subscribe(payload => this.changeTemplate(payload || {})); } /** The portal name */ set name(name) { this.name$.next(name); } changeTemplate({ template, data }) { // Emits the new optional data, if any if (typeof (data) !== undefined) { this.data.emit(data); } /** Skips on no template changes */ if (template === this.ngTemplateOutlet) { return; } // Creates a simple change object const ngTemplateOutlet = new SimpleChange(this.ngTemplateOutlet, template || this.host, this.firstChange); // Updates the parent's template variable accordingly this.ngTemplateOutlet = ngTemplateOutlet.currentValue; // Tracks for the first change this.firstChange = false; // Forces the change to occur super.ngOnChanges({ ngTemplateOutlet }); // Emits the new template this.template.emit(this.ngTemplateOutlet); // Emits true whenever the template isn't the host one this.active.emit(this.ngTemplateOutlet !== this.host); } // Intercepts the input changes ngOnChanges(changes) { // Gets rid of the name's SimpleChange object if (changes.name) { delete changes.name; } // Skips when no changes remains if (Object.keys(changes).length === 0) { return; } // Lets the context changes go through super.ngOnChanges(changes); } // Disposes of the subscription on deletion ngOnDestroy() { this.sub.unsubscribe(); } } PortalDirective.decorators = [ { type: Directive, args: [{ selector: 'ng-template[wmPortal]', exportAs: 'wmPortal' },] } ]; PortalDirective.ctorParameters = () => [ { type: TeleportService }, { type: TemplateRef }, { type: ViewContainerRef } ]; PortalDirective.propDecorators = { name: [{ type: Input, args: ['wmPortal',] }], ngTemplateOutletContext: [{ type: Input, args: ['wmPortalContext',] }], template: [{ type: Output, args: ['wmPortalTemplate',] }], active: [{ type: Output, args: ['wmPortalActive',] }], data: [{ type: Output, args: ['wmPortalData',] }] }; class TeleportDirective { constructor(teleport, template) { this.teleport = teleport; this.template = template; } ngOnChanges(changes) { const target = changes.target; if (!target) { return; } // Clears the previous target, if any target.previousValue && this.teleport.clear(target.previousValue, this.template); // Teleports the template to the new target portal target.currentValue && this.teleport.activate(target.currentValue, this.template, this.data); } ngOnDestroy() { // Clears the portal on destroy this.target && this.teleport.clear(this.target, this.template); } } TeleportDirective.decorators = [ { type: Directive, args: [{ selector: 'ng-template[wmTeleport]' },] } ]; TeleportDirective.ctorParameters = () => [ { type: TeleportService }, { type: TemplateRef } ]; TeleportDirective.propDecorators = { target: [{ type: Input, args: ['wmTeleport',] }], data: [{ type: Input, args: ['wmTeleportData',] }] }; class TeleportModule { static init(config) { return { ngModule: TeleportModule, providers: [ { provide: TeleportConfigToken, useValue: config } ] }; } } TeleportModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [PortalDirective, TeleportDirective], exports: [PortalDirective, TeleportDirective] },] } ]; /** * Generated bundle index. Do not edit. */ export { PortalDirective, TeleportConfigToken, TeleportDirective, TeleportModule, TeleportService }; //# sourceMappingURL=wizdm-teleport.js.map