@cisstech/nge
Version:
NG Essentials is a collection of libraries for Angular developers.
178 lines (171 loc) • 7.93 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Optional, Inject, Directive, NgModule } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { from } from 'rxjs';
import * as i1 from '@cisstech/nge/services';
import { CommonModule } from '@angular/common';
const NGE_ELEMENTS = new InjectionToken('NGE_ELEMENTS');
class NgeElementService {
constructor(injector, compiler, elements) {
this.injector = injector;
this.compiler = compiler;
this.registry = new Map();
this.defineds = new Set();
this.promises = new Map();
elements?.forEach((route) => {
this.registry.set(route.selector.trim().toLowerCase(), route);
});
}
listUnloadeds() {
return Array.from(this.registry.keys()).filter((s) => !this.defineds.has(s));
}
/**
* Allows to lazy load a element given it's selector (i.e. tagname).
* If the element selector has been registered, it's according module
* will be fetched lazily
* @param selector selector of the element to load.
*/
loadElement(selector) {
if (this.promises.has(selector)) {
return this.promises.get(selector);
}
const definition = this.registry.get(selector);
if (!definition) {
throw new Error(`Unrecognized element "${selector}". Make sure it is registered in the registry`);
}
const promise = new Promise(async (resolve, reject) => {
try {
const type = definition.module
? await definition.module()
: definition.component
? await definition.component()
: null;
if (!type) {
throw new Error(`No module or component found for element "${selector}`);
}
const { component, injector } = await this.compiler.resolveComponent(type, this.injector);
const customElement = createCustomElement(component, { injector });
customElements.define(selector, customElement);
await customElements.whenDefined(selector);
this.defineds.add(selector);
resolve();
}
catch (error) {
reject(error);
}
});
this.promises.set(selector, promise);
return promise;
}
loadElements(selectors) {
selectors = selectors.map((e) => e.trim().toLowerCase()).filter((s) => this.registry.has(s));
return from(Promise.all(selectors.map((s) => this.loadElement(s))));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementService, deps: [{ token: i0.Injector }, { token: i1.CompilerService }, { token: NGE_ELEMENTS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.CompilerService }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [NGE_ELEMENTS]
}] }] });
// TODO make angular universal compatible using Renderer2
class NgeElementDetectorDirective {
constructor(elementService) {
this.elementService = elementService;
}
async ngAfterViewInit() {
let selectors = this.elementService.listUnloadeds();
for (const selector of selectors) {
const tags = document.getElementsByTagName(selector);
if (tags?.length) {
await this.elementService.loadElement(selector);
selectors = selectors.filter((e) => e !== selector);
}
}
this.addMutationObserver();
}
ngOnDestroy() {
this.observer?.disconnect();
if (this.listener) {
this.listener();
}
}
addMutationObserver() {
const target = document.body;
let unloadedTags = this.elementService.listUnloadeds();
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLElement) {
unloadedTags = this.checkElementsInNode(node, unloadedTags);
}
});
});
});
this.observer.observe(target, {
subtree: true,
childList: true,
});
}
checkElementsInNode(node, unloadedTags) {
if (!unloadedTags.length) {
return unloadedTags;
}
const tagName = node.tagName.toLowerCase();
if (unloadedTags.includes(tagName)) {
unloadedTags = unloadedTags.filter((e) => e !== tagName);
this.elementService.loadElement(tagName).catch(console.error);
}
for (const child of Array.from(node.childNodes)) {
if (child instanceof HTMLElement) {
unloadedTags = this.checkElementsInNode(child, unloadedTags);
}
}
return unloadedTags;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementDetectorDirective, deps: [{ token: NgeElementService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.1", type: NgeElementDetectorDirective, selector: "nge-element-detector, [nge-element-detector]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementDetectorDirective, decorators: [{
type: Directive,
args: [{
// tslint:disable-next-line: directive-selector
selector: 'nge-element-detector, [nge-element-detector]',
}]
}], ctorParameters: () => [{ type: NgeElementService }] });
class NgeElementModule {
static forRoot(elements) {
return {
ngModule: NgeElementModule,
providers: [
{
provide: NGE_ELEMENTS,
useValue: elements,
},
],
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.1", ngImport: i0, type: NgeElementModule, declarations: [NgeElementDetectorDirective], imports: [CommonModule], exports: [NgeElementDetectorDirective] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: NgeElementModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [NgeElementDetectorDirective],
exports: [NgeElementDetectorDirective],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { NGE_ELEMENTS, NgeElementDetectorDirective, NgeElementModule, NgeElementService };
//# sourceMappingURL=cisstech-nge-elements.mjs.map