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
JavaScript
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