UNPKG

@orchestrator/core

Version:
1,045 lines (1,005 loc) 41.6 kB
import { NgClass, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { InjectionToken, Injectable, Injector, Inject, InjectFlags, SkipSelf, Optional, EventEmitter, ChangeDetectorRef, Component, ChangeDetectionStrategy, Input, Output, NgModule } from '@angular/core'; import * as i6 from 'ng-dynamic-component'; import { dynamicDirectiveDef, DynamicModule, DynamicAttributesModule, DynamicDirectivesModule } from 'ng-dynamic-component'; import { Property, genIoType, anyOf } from '@orchestrator/gen-io-ts'; import * as genIoTs from '@orchestrator/gen-io-ts'; export { genIoTs as getIoTs }; import { chain, map, fold, left, isLeft, isRight } from 'fp-ts/lib/Either'; import { fold as fold$1, none, some } from 'fp-ts/lib/Option'; import { pipe } from 'fp-ts/function'; import { PathReporter } from 'io-ts/lib/PathReporter'; import * as t from 'io-ts'; import { Int, brand, union, null as null$1, undefined as undefined$1 } from 'io-ts'; import { Subject, combineLatest } from 'rxjs'; import { takeUntil, map as map$1 } from 'rxjs/operators'; import { __decorate, __metadata } from 'tslib'; const COMPONENTS = new InjectionToken('COMPONENTS'); function createMetadataGetSet(key) { const k = Symbol(key); return { set: defineMetadata.bind(null, k), get: (type) => type[k], }; } function defineMetadata(key, value, target) { if (key in target === false) { Object.defineProperty(target, key, { enumerable: false, configurable: true, writable: true, value, }); } else { target[key] = value; } return target; } const dynamicComponentMeta = createMetadataGetSet('DynamicComponentMeta'); function DynamicComponent(options) { return target => dynamicComponentMeta.set(options, target); } function getDynamicComponentMeta(type) { return dynamicComponentMeta.get(type); } class ComponentLocatorService { constructor(injector, cfr) { this.injector = injector; this.cfr = cfr; this.componentRegistry = this.injector.get(COMPONENTS); this.componentArray = this.componentRegistry .filter(isComponentArray) .reduce((arr, reg) => [...arr, ...reg], []); this.componentArrayMap = this.componentArray .map(type => this.cfr.resolveComponentFactory(type)) .reduce((map, compFactory) => ({ ...map, [compFactory.selector]: compFactory.componentType, }), Object.create(null)); this.componentMaps = this.componentRegistry.filter(isComponentMap); this.componentMap = this.componentMaps.reduce((obj, map) => ({ ...obj, ...map }), this.componentArrayMap); } resolve(component) { if (typeof component === 'function') { return component; } return this.componentMap[component]; } getDefaultConfig(component) { const configType = this.getConfigType(component); if (!configType) { return null; } return this.injector.get(configType, null); } getConfigType(component) { if (!component) { return null; } const meta = getDynamicComponentMeta(component); if (!meta) { return null; } return meta.config; } } /** @nocollapse */ /** @nocollapse */ ComponentLocatorService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService, deps: [{ token: i0.Injector }, { token: i0.ComponentFactoryResolver }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ ComponentLocatorService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ComponentFactoryResolver }]; } }); function isComponentArray(reg) { return Array.isArray(reg); } function isComponentMap(reg) { return !!reg && !Array.isArray(reg); } class ErrorStrategy { } const configurationMeta = createMetadataGetSet('ConfigurationMeta'); /** * @internal */ function addConfig(target, meta) { const configs = getConfigs(target); configurationMeta.set([...configs, meta], target); } /** * @internal */ function getConfigs(type) { return configurationMeta.get(type) || []; } /** * @internal */ function execRegex(regex, val) { const arr = []; let group; while ((group = regex.exec(val)) !== null) { if (group.index === regex.lastIndex) { regex.lastIndex++; } arr.push(...group); } return !arr.length ? null : arr; } /** * @internal */ function parseFunction(fnStr) { const fnRegex = /^function\s*(?:[A-z0-9]+)?\s*\(([\w\W]*?)\)\s*\{([\w\W]*)\}$/gm; const arrowFnRegex = /^\(?([\w\W]*?)\)?\s*=>\s*\{([\w\W]*)\}$/gm; const returnArrowFnRegex = /^\(?([\w\W]*?)\)?\s*=>\s*([^}{]*)$/gm; fnStr = fnStr.trim(); const fnInfo = execRegex(fnRegex, fnStr) || execRegex(arrowFnRegex, fnStr) || execRegex(returnArrowFnRegex, fnStr); if (!fnInfo || fnInfo.length < 2) { return null; } const _args = fnInfo.length > 2 ? fnInfo[1] || '' : ''; const args = _args .split(',') .map(arg => arg.trim()) .filter(arg => !!arg); const isReturnFunction = returnArrowFnRegex.test(fnStr); const _body = fnInfo[fnInfo.length - 1]; if (!_body && isReturnFunction) { return null; } const body = isReturnFunction ? `return ${_body}` : _body || ''; return { args, body }; } /** * @internal */ function isArgOptional(argExpr) { return /^[^=]+\s*=.+/.test(argExpr); } /** * @internal */ function getArgName(argExpr) { return argExpr.match(/^([^=\s]+)(?:\s*=.+)?/)[1]; } class FunctionError extends Error { constructor(config, error, fnName, fnBody, args) { super(`During function execution ${fnName} from config ${config.name}: ${error} Stack: ${error.stack} Function Body: ${fnBody} Function Arguments: [${args.join('\n')}]`); this.config = config; this.error = error; this.fnName = fnName; this.fnBody = fnBody; this.args = args; } } class InvalidConfigurationError extends Error { constructor(component, validation, config) { const paths = PathReporter.report(validation).join('\n'); super(`Invalid configuration for component ${component.name}'s config: ${paths} Actual config: ${config ? JSON.stringify(config, null, 2) : config}`); this.component = component; this.validation = validation; this.config = config; } } const CUSTOM_FUNCTION_ARGUMENT_PREFIX = '$'; const FunctionFromMeta = new t.Type('FunctionFromMeta', isFunctionWithMeta, (m, c) => pipe(t.UnknownRecord.validate(m, c), chain((obj) => { if (!hasFunctionMeta(obj)) { return t.failure(m, c); } // Move custom arguments to the end obj.args.sort((arg1, arg2) => { const is1Custom = arg1.startsWith(CUSTOM_FUNCTION_ARGUMENT_PREFIX); const is2Custom = arg2.startsWith(CUSTOM_FUNCTION_ARGUMENT_PREFIX); if (is1Custom === is2Custom) { return 0; } return is1Custom ? 1 : -1; }); const fn = new Function(...obj.args, obj.body); fn.args = obj.args; fn.body = obj.body; return t.success(fn); })), (fn) => ({ args: fn.args, body: fn.body })); const FunctionFromString = new t.Type('FunctionFromString', isFunctionWithMeta, (m, c) => pipe(t.string.validate(m, c), chain((str) => { try { return FunctionFromMeta.validate(parseFunction(str), c); } catch { return t.failure(str, c); } })), (fn) => fn.toString()); // eslint-disable-next-line @typescript-eslint/ban-types const FunctionWithMeta = new t.Type('FunctionWithMeta', isFunctionWithMeta, (m, c) => pipe(t.Function.validate(m, c), chain((fn) => { try { // Reconstruct function from string to reorder arguments return FunctionFromString.validate(fn.toString(), c); } catch { return t.failure(fn, c); } })), (fn) => fn); function OptionFunction(customInjector) { const decorator = Property({ typeFactory: () => t.union([FunctionFromString, FunctionFromMeta, FunctionWithMeta]), }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionFunction, args: [customInjector], }); }; } function hasFunctionMeta(obj) { return obj && Array.isArray(obj.args) && typeof obj.body === 'string'; } function isFunctionWithMeta(fn) { return typeof fn === 'function' && hasFunctionMeta(fn); } class ConfigurationService { constructor(errorStrategy, injector) { this.errorStrategy = errorStrategy; this.injector = injector; this.codecMap = new Map(); } decode(type, config, injector) { return pipe(this.validate(type, config), map((c) => this.processFunctions(type, c, config, injector)), fold(() => config, (decodedConfig) => decodedConfig)); } validate(type, config) { const validation = pipe(this.getCodecFor(type), fold$1(() => left([]), (codec) => codec.decode(config))); if (isLeft(validation) && type) { this.errorStrategy.handle(new InvalidConfigurationError(type, validation, config)); } return validation; } getMetaOf(type) { return getConfigs(type.prototype); } getCodecFor(type) { if (!type) { return none; } const codec = this.codecMap.get(type) || genIoType(type); this.codecMap.set(type, codec); // Set codec back to cache return some(codec); } processFunctions(type, config, originalConfig, injector = this.injector) { const meta = this.getMetaOf(type); meta .filter((m) => m.decorator === OptionFunction && config[m.prop]) .forEach((m) => { const customInjectorFactory = m.args[0]; const customInjector = customInjectorFactory ? customInjectorFactory(injector) : injector; const { args, fn } = this.bindFunction(config[m.prop], customInjector); config[m.prop] = fn; config[m.prop] = this.guardFunction(config[m.prop], type, String(m.prop), originalConfig[m.prop], args); }); return config; } bindFunction(fn, injector) { const { args, body } = fn; const resolvedArgs = args .filter((arg) => !arg.startsWith('$')) .map((arg) => this.resolveArg(arg, injector)); const boundFn = fn.bind(null, ...resolvedArgs); boundFn.args = args; boundFn.body = body; return { fn: boundFn, args: resolvedArgs }; } guardFunction(fn, configType, fnName, fnBody, boundArgs) { const guardedFn = ((...args) => { try { return fn(...args); } catch (e) { this.errorStrategy.handle(new FunctionError(configType, e, fnName, fnBody, [ ...boundArgs, ...args, ])); } }); guardedFn.args = fn.args; guardedFn.body = fn.body; return guardedFn; } resolveArg(argExpr, injector) { const arg = getArgName(argExpr); const isOptional = isArgOptional(argExpr); // Dynamically resolve function arguments - no type info available const res = injector.get(arg, isOptional ? null : Injector.THROW_IF_NOT_FOUND); return res === null && isOptional ? undefined : res; } } /** @nocollapse */ /** @nocollapse */ ConfigurationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService, deps: [{ token: ErrorStrategy }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ ConfigurationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: ErrorStrategy }, { type: i0.Injector }]; } }); class ThrowErrorStrategy extends ErrorStrategy { handle(error) { throw error; } } /** * Abstract component type that is responsible to render dynamic component */ class RenderComponent { } const LOCAL_GET_INJECTOR = new InjectionToken('LOCAL_GET_INJECTOR'); const LOCAL_GET_COMPONENT = new InjectionToken('LOCAL_GET_COMPONENT'); const LOCAL_GET_CONFIG = new InjectionToken('LOCAL_GET_CONFIGURATION'); const LOCAL_UPDATE_CONFIG = new InjectionToken('LOCAL_GET_CONFIGURATION'); const LOCAL_GET_CONFIG_VALID = new InjectionToken('LOCAL_GET_CONFIGURATION_VALID'); const LOCAL_GET_CONTEXT = new InjectionToken('LOCAL_GET_CONTEXT'); const LOCAL_INJECTOR_MAP = { getInjector: LOCAL_GET_INJECTOR, getComponent: LOCAL_GET_COMPONENT, getConfig: LOCAL_GET_CONFIG, updateConfig: LOCAL_UPDATE_CONFIG, isConfigValid: LOCAL_GET_CONFIG_VALID, renderComponent: RenderComponent, getContext: LOCAL_GET_CONTEXT, }; function getLocalProviders(data) { return [ { provide: LOCAL_GET_INJECTOR, useValue: data.getInjector, }, { provide: LOCAL_GET_COMPONENT, useValue: data.getComponent, }, { provide: LOCAL_GET_CONFIG, useValue: data.getConfig, }, { provide: LOCAL_UPDATE_CONFIG, useValue: data.updateConfig, }, { provide: LOCAL_GET_CONFIG_VALID, useValue: data.isConfigValid, }, { provide: LOCAL_GET_CONTEXT, useValue: data.getContext, }, ]; } /** * Multi-provider of {@link InjectorMap} */ const INJECTOR_MAP_TOKEN = new InjectionToken('INJECTOR_MAP'); /** * Helper to provide {@link INJECTOR_MAP_TOKEN} */ function provideInjectorMap(map) { return { provide: INJECTOR_MAP_TOKEN, useValue: map, multi: true }; } /** * Maps tokens to other tokens and then executes parent injector. * * NOT a Service! * Use via {@link MappedInjectorFactory} */ class MappedInjector { constructor(parent, injectorMaps) { this.parent = parent; this.injectorMaps = injectorMaps; } get(token, notFoundValue, flags) { return this.parent.get(this.mapToken(token), notFoundValue, flags); } mapToken(token) { if (typeof token !== 'string') { return token; } this.maybeInitInjectorMap(); token = this.processToken(token); return token in this.injectorMap ? this.injectorMap[token] : token; } maybeInitInjectorMap() { if (!this.injectorMap) { this.injectorMap = this.injectorMaps.reduce((acc, m) => Object.keys(m).reduce((obj, k) => ({ ...obj, [this.processToken(k)]: m[k] }), acc), Object.create(null)); } } processToken(t) { return t.toLowerCase(); } } /** * Factory for {@link MappedInjector} */ class MappedInjectorFactory { constructor(injectorMap) { this.injectorMap = injectorMap; } /** * Creates MappedInjector with parent injector and {@link INJECTOR_MAP_TOKEN} from DI */ create(parent) { return new MappedInjector(parent, this.injectorMap); } } /** @nocollapse */ /** @nocollapse */ MappedInjectorFactory.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory, deps: [{ token: INJECTOR_MAP_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ MappedInjectorFactory.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [INJECTOR_MAP_TOKEN] }] }]; } }); const STATIC_INJECT_FLAGS = new InjectionToken('STATIC_INJECT_FLAGS'); const STATIC_INJECTOR_MAP = { InjectFlags: STATIC_INJECT_FLAGS, }; function getStaticProviders() { return [{ provide: STATIC_INJECT_FLAGS, useValue: InjectFlags }]; } /** * Provides a map for injectors with providers * * @internal */ const INJECTOR_MAP_PROVIDERS = [ ...getStaticProviders(), provideInjectorMap(STATIC_INJECTOR_MAP), provideInjectorMap(LOCAL_INJECTOR_MAP), ]; function OptionAllowedValues(...values) { const decorator = Property({ type: anyOf(...values) }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionAllowedValues, args: [values] }); }; } function OptionInteger() { const decorator = Property({ typeFactory: () => Int }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionInteger, args: [] }); }; } function OptionRange(min, max, step = 1) { const typeFactory = (type) => brand(type, (n) => n >= min && n <= max, 'InRange'); const decorator = Property({ type: Number, typeFactory }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionRange, args: [min, max, step] }); }; } function OptionRequired() { const decorator = Property({ isRequired: true }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionRequired, args: [] }); }; } function OptionType(type) { const decorator = Property({ type }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionType, args: [type] }); }; } /* eslint-disable @typescript-eslint/no-non-null-assertion */ function Option(config = {}) { const decorator = Property(); const decorators = Object.keys(config) .map((key) => { switch (key) { case 'required': return config.required ? OptionRequired() : null; case 'type': return OptionType(config.type); case 'range': return OptionRange(config.range.min, config.range.max, config.range.step); case 'integer': return config.integer ? OptionInteger() : null; case 'allowedValues': return OptionAllowedValues(...config.allowedValues); } }) .filter(Boolean); return (target, prop) => { decorator(target, prop); decorators.forEach((d) => d(target, prop)); }; } function OptionTypeFactory(typeFactory) { const decorator = Property({ typeFactory }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionTypeFactory, args: [typeFactory], }); }; } function classToType(cls) { return genIoType(cls); } /** * Will set type of property to `null | undefined`. * * Useful for cases when you have to explicitly exclude * specific property from type. * * **Example:** * ```ts * class A { * @Option() * prop1: string; * @Option() * prop2: string; * @OptionNotPresent() * prop3?: null | undefined; // This prop should be excluded! * } * * class B { * @Option() * prop1: string; * @Option() * prop2: string; * @Option() * prop3: string; * } * * type AorB = A | B; * ``` */ function OptionNotPresent() { const decorator = Property({ typeFactory: () => union([null$1, undefined$1]), }); return (target, prop) => { decorator(target, prop); addConfig(target, { prop, decorator: OptionNotPresent, args: [] }); }; } class InjectorRegistryService { constructor(parentInjector) { this.parentInjector = parentInjector; this.injector = this.parentInjector; } get(token, notFoundValue, flags) { return this.injector.get(token, notFoundValue, flags); } addProviders(providers) { this.injector = Injector.create({ providers, parent: this.injector, }); } reset(parentInjector) { this.injector = parentInjector || this.parentInjector; } } /** @nocollapse */ /** @nocollapse */ InjectorRegistryService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ InjectorRegistryService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i0.Injector }]; } }); /** * @internal */ function createLocalInjector(params) { const injector = Injector.create({ providers: getLocalProviders({ ...params, getInjector: () => injector, }), parent: params.parentInjector, }); return injector; } class ComponentsRegistryService { constructor(parentComponentsRegistryService) { this.parentComponentsRegistryService = parentComponentsRegistryService; this._componentsReady$ = new Subject(); this.componentsReady$ = this._componentsReady$.asObservable(); this.childComponents = []; this.subChildComponents = []; } ngOnDestroy() { this.childComponents = []; this.subChildComponents = []; } waitFor(count) { this.count = count; this.childComponents = []; this.subChildComponents = []; } add(compRef) { if (this.parentComponentsRegistryService) { this.parentComponentsRegistryService.addChild(compRef); if (this.count === 0) { this.parentComponentsRegistryService.addSubChildren([]); } } } addChildren(compRefs) { if (this.parentComponentsRegistryService) { this.parentComponentsRegistryService.addSubChildren(compRefs); } } addChild(compRef) { this.childComponents.push(compRef); } addSubChildren(compRefs) { this.subChildComponents = this.subChildComponents.concat(compRefs); if (this.childComponents.length >= this.count) { this._componentsReady$.next(this.childComponents.concat(this.subChildComponents).filter(Boolean)); } } } /** @nocollapse */ /** @nocollapse */ ComponentsRegistryService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService, deps: [{ token: ComponentsRegistryService, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ ComponentsRegistryService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: ComponentsRegistryService, decorators: [{ type: SkipSelf }, { type: Optional }] }]; } }); class Handler { } __decorate([ OptionFunction(), __metadata("design:type", Object) ], Handler.prototype, "handler", void 0); class RenderItemComponent extends RenderComponent { constructor(cdr, renderer, cfr, componentLocatorService, componentsRegistryService, configurationService, mappedInjectorFactory, injectorRegistryService) { super(); this.cdr = cdr; this.renderer = renderer; this.cfr = cfr; this.componentLocatorService = componentLocatorService; this.componentsRegistryService = componentsRegistryService; this.configurationService = configurationService; this.mappedInjectorFactory = mappedInjectorFactory; this.injectorRegistryService = injectorRegistryService; this.componentCreated = new EventEmitter(); this.childComponentsCreated = new EventEmitter(); this.destroyed$ = new Subject(); this.inputs = { items: undefined, config: undefined, context: undefined, }; this.directives = []; this.attributes = null; this.disposableHandlers = []; } get itemsLength() { return this.item && this.item.items ? this.item.items.length : 0; } ngOnInit() { this.componentsRegistryService.componentsReady$ .pipe(takeUntil(this.destroyed$)) .subscribe((compRefs) => { this.childComponentsCreated.emit(compRefs); this.componentsRegistryService.addChildren(compRefs); }); this.update(); } ngOnChanges(changes) { if ('item' in changes && !changes.item.firstChange) { this.update(); } else if ('context' in changes && !changes.context.firstChange) { this.updateContextInput(); } } ngOnDestroy() { this.destroyed$.next(); this.disposeHandlers(); this.compRef = this.compCdr = this.compFactory = this.config = null; } onComponentCreated(compRef) { this.compRef = compRef; this.compFactory = this.cfr.resolveComponentFactory(this.componentType); this.componentCreated.emit(compRef); this.componentsRegistryService.add(compRef); this.updateHandlers(); } getInjectorRegistryService() { return this.injectorRegistryService; } markForCheck() { if (!this.compCdr && this.compRef) { this.compCdr = this.compRef.injector.get(ChangeDetectorRef); } if (this.compCdr) { this.compCdr.markForCheck(); } } addItem(item) { if (this.inputs.items) { this.inputs.items = [...this.inputs.items, item]; } else { this.inputs.items = [item]; } this.cdr.markForCheck(); } removeItem(item) { const idx = this.inputs.items ? this.inputs.items.indexOf(item) : -1; if (idx === -1) { return; } this.inputs.items = this.inputs.items.filter((_, i) => i !== idx); this.cdr.markForCheck(); } clearItems() { this.inputs.items = []; this.cdr.markForCheck(); } update() { this.updateComponent(); this.updateConfig(); this.updateInjector(); this.updateInputs(); this.updateContextInput(); this.updateAttributes(); this.updateDirectives(); } updateComponent() { // Invalidate late-component-refs immediately this.compRef = this.compCdr = this.compFactory = null; if (this.item) { this.componentType = this.componentLocatorService.resolve(this.item.component); this.componentsRegistryService.waitFor(this.itemsLength); } else { this.componentType = null; this.componentsRegistryService.waitFor(0); } } updateConfig() { if (this.componentType) { this.config = { ...this.componentLocatorService.getDefaultConfig(this.componentType), ...this.item.config, }; } else { this.config = null; } } updateInjector() { if (this.componentType) { this.injector = this.createInjector(); } else { this.injector = null; } } updateInputs() { if (this.componentType) { this.inputs.items = this.item.items; this.inputs.config = this.getConfig(); } else { this.inputs.items = this.inputs.config = null; } } updateAttributes() { if (this.componentType) { this.attributes = this.item.attributes || null; if (this.item.id) { this.attributes = { ...this.attributes, id: this.item.id }; } } } updateDirectives() { if (this.componentType && this.item.classes) { this.directives = [ dynamicDirectiveDef(NgClass, { ngClass: this.item.classes }), ]; } else { this.directives = []; } } getConfig() { return (this.configurationService.decode(this.componentLocatorService.getConfigType(this.componentType), this.config, this.injector) || {}); } createInjector() { return this.mappedInjectorFactory.create(this.createLocalInjector()); } createLocalInjector() { return createLocalInjector({ parentInjector: this.injectorRegistryService, getComponent: () => this.compRef.instance, getConfig: () => this.inputs.config, updateConfig: (config) => { this.markForCheck(); return (this.inputs.config = { ...this.inputs.config, ...config }); }, isConfigValid: () => isRight(this.configurationService.validate(this.componentLocatorService.getConfigType(this.componentType), this.inputs.config)), getContext: () => this.context, }); } updateHandlers() { this.disposeHandlers(); if (!this.item.handlers || !this.compRef || !this.compFactory) { return; } const { handlers } = this.item; this.disposableHandlers = Object.keys(handlers) .map((event) => ({ event, handler: this.decodeHandler(handlers[event]), })) .filter(({ handler }) => handler) .map(({ event, handler }) => this.attachHandler(event, handler)); } decodeHandler(handler) { const fn = this.configurationService.decode(Handler, { handler }, this.injector).handler; return typeof fn === 'function' ? fn : null; } attachHandler(event, handler) { const outputInfo = this.compFactory.outputs.find((output) => output.templateName === event); if (outputInfo) { const output = this.compRef.instance[outputInfo.propName]; const sub = output.subscribe(handler); return () => sub.unsubscribe(); } return this.renderer.listen(this.compRef.location.nativeElement, event, handler); } disposeHandlers() { this.disposableHandlers.forEach((disposeHandler) => disposeHandler()); this.disposableHandlers = []; } updateContextInput() { if (this.componentType) { this.inputs.context = this.context; } else { this.inputs.context = null; } } } /** @nocollapse */ /** @nocollapse */ RenderItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: RenderItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i0.ComponentFactoryResolver }, { token: ComponentLocatorService }, { token: ComponentsRegistryService }, { token: ConfigurationService }, { token: MappedInjectorFactory }, { token: InjectorRegistryService }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ /** @nocollapse */ RenderItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.0", type: RenderItemComponent, selector: "orc-render-item", inputs: { item: "item", context: "context" }, outputs: { componentCreated: "componentCreated", childComponentsCreated: "childComponentsCreated" }, providers: [ { provide: RenderComponent, useExisting: RenderItemComponent }, ComponentsRegistryService, InjectorRegistryService, MappedInjectorFactory, ], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ndc-dynamic\n [ndcDynamicComponent]=\"componentType\"\n [ndcDynamicInputs]=\"inputs\"\n [ndcDynamicInjector]=\"injector\"\n [ndcDynamicAttributes]=\"attributes\"\n [ndcDynamicDirectives]=\"directives\"\n (ndcDynamicCreated)=\"onComponentCreated($event)\"\n></ndc-dynamic>\n", components: [{ type: i6.DynamicComponent, selector: "ndc-dynamic", inputs: ["ndcDynamicComponent", "ndcDynamicInjector", "ndcDynamicProviders", "ndcDynamicContent"], outputs: ["ndcDynamicCreated"] }], directives: [{ type: i6.DynamicIoDirective, selector: "[ndcDynamicInputs],[ndcDynamicOutputs],[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]", inputs: ["ndcDynamicInputs", "ngComponentOutletNdcDynamicInputs", "ndcDynamicOutputs", "ngComponentOutletNdcDynamicOutputs"] }, { type: i6.DynamicAttributesDirective, selector: "[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]", inputs: ["ndcDynamicAttributes", "ngComponentOutletNdcDynamicAttributes"], exportAs: ["ndcDynamicAttributes"] }, { type: i6.DynamicDirectivesDirective, selector: "[ndcDynamicDirectives],[ngComponentOutletNdcDynamicDirectives]", inputs: ["ndcDynamicDirectives", "ngComponentOutletNdcDynamicDirectives"], outputs: ["ndcDynamicDirectivesCreated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: RenderItemComponent, decorators: [{ type: Component, args: [{ selector: 'orc-render-item', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: RenderComponent, useExisting: RenderItemComponent }, ComponentsRegistryService, InjectorRegistryService, MappedInjectorFactory, ], template: "<ndc-dynamic\n [ndcDynamicComponent]=\"componentType\"\n [ndcDynamicInputs]=\"inputs\"\n [ndcDynamicInjector]=\"injector\"\n [ndcDynamicAttributes]=\"attributes\"\n [ndcDynamicDirectives]=\"directives\"\n (ndcDynamicCreated)=\"onComponentCreated($event)\"\n></ndc-dynamic>\n" }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i0.ComponentFactoryResolver }, { type: ComponentLocatorService }, { type: ComponentsRegistryService }, { type: ConfigurationService }, { type: MappedInjectorFactory }, { type: InjectorRegistryService }]; }, propDecorators: { item: [{ type: Input }], context: [{ type: Input }], componentCreated: [{ type: Output }], childComponentsCreated: [{ type: Output }] } }); class OrchestratorComponent { constructor() { this.compCreated$ = new Subject(); this.childCompsCreated$ = new Subject(); this.componentsCreated = combineLatest([ this.compCreated$, this.childCompsCreated$, ]).pipe(map$1(([comp, comps]) => [comp, ...comps])); } compCreated(compRef) { this.compCreated$.next(compRef); } childCompsCreated(compRefs) { this.childCompsCreated$.next(compRefs); } } /** @nocollapse */ /** @nocollapse */ OrchestratorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ /** @nocollapse */ OrchestratorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.0", type: OrchestratorComponent, selector: "orc-orchestrator", inputs: { config: "config", context: "context" }, outputs: { componentsCreated: "componentsCreated" }, ngImport: i0, template: "<orc-render-item\n [item]=\"config\"\n [context]=\"context\"\n (componentCreated)=\"compCreated($event)\"\n (childComponentsCreated)=\"childCompsCreated($event)\"\n></orc-render-item>\n", components: [{ type: RenderItemComponent, selector: "orc-render-item", inputs: ["item", "context"], outputs: ["componentCreated", "childComponentsCreated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorComponent, decorators: [{ type: Component, args: [{ selector: 'orc-orchestrator', changeDetection: ChangeDetectionStrategy.OnPush, template: "<orc-render-item\n [item]=\"config\"\n [context]=\"context\"\n (componentCreated)=\"compCreated($event)\"\n (childComponentsCreated)=\"childCompsCreated($event)\"\n></orc-render-item>\n" }] }], propDecorators: { config: [{ type: Input }], context: [{ type: Input }], componentsCreated: [{ type: Output }] } }); class OrchestratorCoreModule { /** * Use this to import module in root application only once */ static forRoot() { return { ngModule: OrchestratorCoreModule, providers: [...OrchestratorCoreModule.getRootProviders()], }; } /** * Use this to import module with components in root application only once */ static withComponents(components) { return { ngModule: OrchestratorCoreModule, providers: [ ...OrchestratorCoreModule.getRootProviders(), ...OrchestratorCoreModule.registerComponents(components), ], }; } /** * Use this to provide custom components for {@link OrchestratorCoreModule} */ static registerComponents(components) { return [{ provide: COMPONENTS, useValue: components, multi: true }]; } static getRootProviders() { return [ { provide: ErrorStrategy, useClass: ThrowErrorStrategy }, ...INJECTOR_MAP_PROVIDERS, ComponentLocatorService, ConfigurationService, ]; } } /** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); /** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, declarations: [OrchestratorComponent, RenderItemComponent], imports: [CommonModule, DynamicModule, DynamicAttributesModule, DynamicDirectivesModule], exports: [OrchestratorComponent, RenderItemComponent] }); /** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, imports: [[ CommonModule, DynamicModule, DynamicAttributesModule, DynamicDirectivesModule, ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, decorators: [{ type: NgModule, args: [{ imports: [ CommonModule, DynamicModule, DynamicAttributesModule, DynamicDirectivesModule, ], declarations: [OrchestratorComponent, RenderItemComponent], exports: [OrchestratorComponent, RenderItemComponent], }] }] }); class SuppressErrorStrategy extends ErrorStrategy { // eslint-disable-next-line @typescript-eslint/no-unused-vars handle(error) { // Not doing anything here... } } // Core /** * Generated bundle index. Do not edit. */ export { COMPONENTS, CUSTOM_FUNCTION_ARGUMENT_PREFIX, ConfigurationService, DynamicComponent, ErrorStrategy, FunctionError, FunctionFromMeta, FunctionFromString, FunctionWithMeta, INJECTOR_MAP_TOKEN, InjectorRegistryService, InvalidConfigurationError, LOCAL_GET_COMPONENT, LOCAL_GET_CONFIG, LOCAL_GET_CONFIG_VALID, LOCAL_GET_CONTEXT, LOCAL_GET_INJECTOR, LOCAL_INJECTOR_MAP, LOCAL_UPDATE_CONFIG, MappedInjector, MappedInjectorFactory, Option, OptionAllowedValues, OptionFunction, OptionInteger, OptionNotPresent, OptionRange, OptionRequired, OptionType, OptionTypeFactory, OrchestratorComponent, OrchestratorCoreModule, RenderComponent, RenderItemComponent, STATIC_INJECTOR_MAP, STATIC_INJECT_FLAGS, SuppressErrorStrategy, ThrowErrorStrategy, classToType, getLocalProviders, getStaticProviders, provideInjectorMap }; //# sourceMappingURL=orchestrator-core.mjs.map