@tarktech/ngx-context
Version:
Angular Context: Easy property binding for router outlet and nested component trees.
363 lines (353 loc) • 16 kB
JavaScript
import * as i0 from '@angular/core';
import { Component, ChangeDetectionStrategy, ViewEncapsulation, Optional, SkipSelf, Input, Directive, Host, NgModule } from '@angular/core';
import { Observable, Subject, ReplaySubject } from 'rxjs';
import { takeUntil, startWith, filter } from 'rxjs/operators';
function isSubscribableOrPromise(obj) {
return obj instanceof Observable || obj instanceof Subject || obj instanceof Promise;
}
function parseKeys(input) {
return (Array.isArray(input) ? input : input.split(/\s+/)).filter(key => key);
}
class ContextProviderComponent {
source;
initialized = false;
_contextMap = {};
_provide = '';
provided = new Map();
set contextMap(map) {
this._contextMap = map || {};
}
get contextMap() {
return this._contextMap;
}
set provide(value) {
this._provide = value || '';
}
get provide() {
return this._provide;
}
get component() {
return this.source['_view']?.component || this.source['context'];
}
change$ = new ReplaySubject(1);
reset$ = new Subject();
constructor(source) {
this.source = source;
}
init() {
setTimeout(() => {
const THIS = this;
const context = new Map();
const provided = parseKeys(this.provide).filter(key => key && key in this.component);
provided.forEach(key => {
if (isSubscribableOrPromise(this.component[key])) {
this.change$.next(key);
return;
}
const propertyDescriptor = Object.getOwnPropertyDescriptor(this.component, key) ||
Object.getOwnPropertyDescriptor(this.component.__proto__, key);
this.provided.set(key, propertyDescriptor);
const { value, writable, get: getter, set: setter, ...prop } = propertyDescriptor;
Object.defineProperty(this.component, key, {
...prop,
get: getter
? function () {
return getter.call(this);
}
: function () {
return context.get(key);
},
set: setter
? function () {
setter.apply(this, arguments);
THIS.change$.next(key);
}
: function (newValue) {
context.set(key, newValue);
THIS.change$.next(key);
},
});
this.component[key] = value || this.component[key];
});
}, 0);
}
reset() {
this.provided.forEach((propertyDescriptor, key) => {
const value = this.component[key];
Object.defineProperty(this.component, key, propertyDescriptor);
this.component[key] = value;
});
this.provided.clear();
this.change$.next('');
this.reset$.next();
}
ngOnChanges() {
if (this.initialized) {
this.reset();
if (this.source)
this.init();
}
}
ngOnInit() {
this.initialized = true;
this.ngOnChanges();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextProviderComponent, deps: [{ token: i0.ChangeDetectorRef, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.7", type: ContextProviderComponent, selector: "context-provider", inputs: { contextMap: "contextMap", provide: "provide" }, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextProviderComponent, decorators: [{
type: Component,
args: [{
selector: 'context-provider',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
}]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }]; }, propDecorators: { contextMap: [{
type: Input
}], provide: [{
type: Input
}] } });
class AbstractContextConsumer {
provider;
target;
destroy$ = new Subject();
initialized;
_contextMap = {};
_consume = '';
consumed = new Map();
set contextMap(map) {
this._contextMap = map || {};
}
get contextMap() {
return this._contextMap;
}
set consume(consume) {
this._consume = consume || '';
}
get consume() {
return this._consume;
}
get component() {
return this.target['_view']?.component || this.target['context'];
}
constructor(provider, target) {
this.provider = provider;
this.target = target;
}
init() {
const consumed = parseKeys(this.consume);
this.provider.reset$
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.ngOnChanges());
if (this.provider.provide.length)
this.provider.change$
.pipe(takeUntil(this.destroy$), startWith(...Array.from(this.provider.provided.keys())), filter(key => !!key))
.subscribe(providerKey => this.syncProperties(consumed, providerKey));
}
reset() {
this.consumed.forEach((value, key) => {
this.component[key] = value;
});
this.consumed.clear();
}
syncProperties(consumed, providerKey) {
let key = this.provider.contextMap[providerKey] || providerKey;
key = this.contextMap[key] || key;
if (consumed.length && consumed.indexOf(key) < 0)
return;
if (!this.consumed.has(key))
this.consumed.set(key, this.component[key]);
this.component[key] = this.provider.component[providerKey];
this.target.markForCheck();
}
ngOnChanges() {
if (this.initialized) {
this.destroy$.next();
this.reset();
if (this.target && this.provider)
this.init();
}
}
ngOnDestroy() {
this.destroy$.next();
}
ngOnInit() {
this.initialized = true;
this.ngOnChanges();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: AbstractContextConsumer, deps: [{ token: ContextProviderComponent }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.7", type: AbstractContextConsumer, inputs: { contextMap: "contextMap", consume: "consume" }, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: AbstractContextConsumer, decorators: [{
type: Directive
}], ctorParameters: function () { return [{ type: ContextProviderComponent }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { contextMap: [{
type: Input
}], consume: [{
type: Input
}] } });
class ContextConsumerComponent extends AbstractContextConsumer {
constructor(providerComponent, parent) {
super(providerComponent, parent);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextConsumerComponent, deps: [{ token: ContextProviderComponent, optional: true, skipSelf: true }, { token: i0.ChangeDetectorRef, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.7", type: ContextConsumerComponent, selector: "context-consumer", usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextConsumerComponent, decorators: [{
type: Component,
args: [{
selector: 'context-consumer',
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
}]
}], ctorParameters: function () { return [{ type: ContextProviderComponent, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }, { type: i0.ChangeDetectorRef, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }]; } });
class ContextConsumerDirective extends AbstractContextConsumer {
set consume(consume) {
this._consume = consume || '';
}
get consume() {
return this._consume;
}
constructor(providerComponent, host) {
super(providerComponent, host);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextConsumerDirective, deps: [{ token: ContextProviderComponent, optional: true, skipSelf: true }, { token: i0.ChangeDetectorRef, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.7", type: ContextConsumerDirective, selector: "[contextConsumer]", inputs: { consume: ["contextConsumer", "consume"] }, usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextConsumerDirective, decorators: [{
type: Directive,
args: [{
selector: '[contextConsumer]',
}]
}], ctorParameters: function () { return [{ type: ContextProviderComponent, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }, { type: i0.ChangeDetectorRef, decorators: [{
type: Optional
}, {
type: Host
}] }]; }, propDecorators: { consume: [{
type: Input,
args: ['contextConsumer']
}] } });
class Context {
$implicit = {};
}
class ContextDisposerDirective {
tempRef;
vcRef;
provider;
destroy$ = new Subject();
_dispose = '';
view;
set dispose(dispose) {
this._dispose = dispose || '';
}
get dispose() {
return this._dispose;
}
constructor(tempRef, vcRef, provider) {
this.tempRef = tempRef;
this.vcRef = vcRef;
this.provider = provider;
}
init() {
const disposed = parseKeys(this.dispose);
this.provider.reset$
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.ngOnChanges());
if (this.provider.provide.length)
this.provider.change$
.pipe(takeUntil(this.destroy$), startWith(...Array.from(this.provider.provided.keys())), filter(key => !!key))
.subscribe(providerKey => this.syncProperties(disposed, providerKey));
}
reset() {
this.view = this.vcRef.createEmbeddedView(this.tempRef, new Context());
}
syncProperties(disposed, providerKey) {
const key = this.provider.contextMap[providerKey] || providerKey;
if (disposed.length && disposed.indexOf(key) < 0)
return;
const value = this.provider.component[providerKey];
this.view.context.$implicit[key] = value;
this.view.context[key] = value;
this.view.markForCheck();
}
ngOnChanges() {
this.ngOnDestroy();
this.reset();
if (this.provider && this.tempRef && this.vcRef)
this.init();
}
ngOnDestroy() {
this.destroy$.next();
if (this.view)
this.vcRef.clear();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextDisposerDirective, deps: [{ token: i0.TemplateRef, optional: true }, { token: i0.ViewContainerRef, optional: true }, { token: ContextProviderComponent, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.7", type: ContextDisposerDirective, selector: "[contextDisposer]", inputs: { dispose: ["contextDisposer", "dispose"] }, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: ContextDisposerDirective, decorators: [{
type: Directive,
args: [{
selector: '[contextDisposer]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }, { type: i0.ViewContainerRef, decorators: [{
type: Optional
}] }, { type: ContextProviderComponent, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }]; }, propDecorators: { dispose: [{
type: Input,
args: ['contextDisposer']
}] } });
class NgxContextModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: NgxContextModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.7", ngImport: i0, type: NgxContextModule, declarations: [ContextConsumerComponent,
ContextConsumerDirective,
ContextDisposerDirective,
ContextProviderComponent], exports: [ContextConsumerComponent,
ContextConsumerDirective,
ContextDisposerDirective,
ContextProviderComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: NgxContextModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.7", ngImport: i0, type: NgxContextModule, decorators: [{
type: NgModule,
args: [{
declarations: [
ContextConsumerComponent,
ContextConsumerDirective,
ContextDisposerDirective,
ContextProviderComponent,
],
exports: [
ContextConsumerComponent,
ContextConsumerDirective,
ContextDisposerDirective,
ContextProviderComponent,
],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ContextConsumerComponent, ContextConsumerDirective, ContextDisposerDirective, ContextProviderComponent, NgxContextModule };
//# sourceMappingURL=tarktech-ngx-context.mjs.map