UNPKG

ems-web-app-view-container

Version:

This angular.io module includes a component and service that can be used to render nested view containers that transition between screens (e.g., a tab container).

209 lines (201 loc) 10.4 kB
import * as i0 from '@angular/core'; import { Injectable, EventEmitter, Component, HostBinding, Input, Output, TemplateRef, ContentChildren, NgModule } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { delay, sleep } from 'ems-web-app-utils'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; class ViewContainerService { constructor() { this.viewSource = new BehaviorSubject(null); this.view = this.viewSource.asObservable(); this.manifest = {}; } registerView(containerId, viewId, template) { this.manifest[`${containerId}${ViewContainerService.DELIMITER}${viewId}`] = { viewId, containerId, template }; } setCurrentView(viewId, containerId) { const qualifiedId = this.getQualifiedId(viewId, containerId); let view = this.manifest[qualifiedId ?? ""]; if (!view && containerId) view = { containerId, viewId, template: null }; //clears the view this.viewSource.next(view ?? null); } getQualifiedId(viewId, containerId) { if (containerId) return `${containerId}${ViewContainerService.DELIMITER}${viewId}`; const regex = new RegExp(`^.*?${ViewContainerService.DELIMITER}(.*?)$`, "gim"); for (let prop in this.manifest) { const parsedViewId = prop.replace(regex, "$1"); if (parsedViewId === viewId) return prop; } return null; } } ViewContainerService.DELIMITER = "---"; //used to separate container id from view id in lookup; should be something that wouldn't appear in a supplied id ViewContainerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); ViewContainerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class ViewContainerComponent { constructor(service) { this.service = service; this.generatedId = ""; this.currentViewClass = ""; this.transitioning = true; this.speed = 250; this.onReady = new EventEmitter(); this.onBegin = new EventEmitter(); this.onExecute = new EventEmitter(); this.onComplete = new EventEmitter(); this.requestedView = null; this.currentView = null; } ngOnInit() { if (!this.id) { //auto-create ids if none supplied this.id = `generated-vcid-${ViewContainerComponent.counter}`; ViewContainerComponent.counter++; } this.generatedId = this.id; } ngAfterViewInit() { delay(() => this.initialize()); } ngOnDestroy() { if (this.subscription) this.subscription.unsubscribe(); } setCurrentView(view, callback) { this.requestedView = view; this.callback = callback; this.beginViewTransition(); } async beginViewTransition() { this.transitioning = true; //begin fadeout this.onBegin.emit(); await sleep(this.speed); this.executeViewTransition(); } async executeViewTransition() { this.currentView = null; //completely destroy current view/logic/listeners await sleep(0); this.currentView = this.requestedView; await sleep(0); //allow a tick to paint the transparent state of the view this.onExecute.emit(); if (this.callback) this.callback(); this.transitioning = false; //begin fade in await sleep(this.speed); this.completeViewTransition(); } completeViewTransition() { this.onComplete.emit(); } initialize() { this.subscription = this.service.view.subscribe(view => { if (view?.containerId !== this.id) return; this.setCurrentView(view); }); this.onReady.emit(); //component is subscribed and ready to respond to view requests } } ViewContainerComponent.counter = 1; ViewContainerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerComponent, deps: [{ token: ViewContainerService }], target: i0.ɵɵFactoryTarget.Component }); ViewContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: ViewContainerComponent, selector: "view-container", inputs: { id: "id", speed: ["transition-speed", "speed"] }, outputs: { onReady: "ready", onBegin: "begin", onExecute: "execute", onComplete: "complete" }, host: { properties: { "attr.id": "this.generatedId", "class": "this.currentViewClass", "class.transitioning": "this.transitioning" } }, ngImport: i0, template: `<ng-container *ngIf="currentView"><ng-container *ngTemplateOutlet="currentView.template"></ng-container></ng-container><ng-content></ng-content>`, isInline: true, styles: [":host{transition:opacity .25s;transition-property:opacity;display:flex;flex-direction:column;flex:1 1 auto;width:100%;overflow:auto}:host.transitioning{transition-property:opacity;opacity:0;transition:opacity .25s;pointer-events:none}\n"], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerComponent, decorators: [{ type: Component, args: [{ selector: 'view-container', template: `<ng-container *ngIf="currentView"><ng-container *ngTemplateOutlet="currentView.template"></ng-container></ng-container><ng-content></ng-content>`, styles: [":host{transition:opacity .25s;transition-property:opacity;display:flex;flex-direction:column;flex:1 1 auto;width:100%;overflow:auto}:host.transitioning{transition-property:opacity;opacity:0;transition:opacity .25s;pointer-events:none}\n"] }] }], ctorParameters: function () { return [{ type: ViewContainerService }]; }, propDecorators: { generatedId: [{ type: HostBinding, args: ["attr.id"] }], currentViewClass: [{ type: HostBinding, args: ["class"] }], transitioning: [{ type: HostBinding, args: ["class.transitioning"] }], id: [{ type: Input, args: ["id"] }], speed: [{ type: Input, args: ["transition-speed"] }], onReady: [{ type: Output, args: ["ready"] }], onBegin: [{ type: Output, args: ["begin"] }], onExecute: [{ type: Output, args: ["execute"] }], onComplete: [{ type: Output, args: ["complete"] }] } }); class ViewComponent { constructor(service, element) { this.service = service; this.element = element; } ngAfterViewInit() { if (!this.id) throw new Error("All <view/> elements must have an id attribute."); const parentId = this.element.nativeElement.parentNode.getAttribute("id"); this.service.registerView(parentId, this.id, this.template.first); } } ViewComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewComponent, deps: [{ token: ViewContainerService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); ViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: ViewComponent, selector: "view", inputs: { id: "id" }, queries: [{ propertyName: "template", predicate: TemplateRef }], ngImport: i0, template: `<ng-content></ng-content>`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewComponent, decorators: [{ type: Component, args: [{ selector: 'view', template: `<ng-content></ng-content>` }] }], ctorParameters: function () { return [{ type: ViewContainerService }, { type: i0.ElementRef }]; }, propDecorators: { id: [{ type: Input, args: ["id"] }], template: [{ type: ContentChildren, args: [TemplateRef] }] } }); class ViewContainerModule { } ViewContainerModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); ViewContainerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerModule, declarations: [ViewContainerComponent, ViewComponent], imports: [CommonModule], exports: [ViewContainerComponent, ViewComponent] }); ViewContainerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ViewContainerModule, decorators: [{ type: NgModule, args: [{ declarations: [ ViewContainerComponent, ViewComponent ], imports: [ CommonModule ], exports: [ ViewContainerComponent, ViewComponent ] }] }] }); /* * Public API Surface of view-container */ /** * Generated bundle index. Do not edit. */ export { ViewComponent, ViewContainerComponent, ViewContainerModule, ViewContainerService }; //# sourceMappingURL=ems-web-app-view-container.mjs.map