@rx-angular/template
Version:
**Fully** Reactive Component Template Rendering in Angular. @rx-angular/template aims to be a reflection of Angular's built in renderings just reactive.
146 lines (141 loc) • 6.78 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Inject, Optional, Input, NgModule } from '@angular/core';
import { coerceObservableWith } from '@rx-angular/cdk/coercing';
import * as i1 from '@rx-angular/cdk/render-strategies';
import * as i2 from '@rx-angular/template/let';
import { RxLet } from '@rx-angular/template/let';
import { Subject, Observable, BehaviorSubject, of, combineLatest } from 'rxjs';
import { mergeAll, map, filter, withLatestFrom } from 'rxjs/operators';
function intersectionObserver(options) {
const subject = new Subject();
const observer = observerSupported()
? new IntersectionObserver((entries) => {
entries.forEach((entry) => subject.next(entry));
}, options)
: null;
const entries$ = new Observable((subscriber) => {
subject.subscribe(subscriber);
return () => {
if (observer) {
observer.disconnect();
}
};
});
return {
entries$,
observe: observer.observe,
unobserve: observer.unobserve,
};
}
const observerSupported = () => typeof window !== 'undefined'
? !!window.IntersectionObserver
: false;
class ViewportPrioDirective {
set viewportPrio(prio) {
this._viewportPrioObservables.next(prio);
}
constructor(el, strategyProvider, letDirective) {
this.el = el;
this.strategyProvider = strategyProvider;
this.letDirective = letDirective;
// Note that we're picking only the `intersectionRatio` property
// since this is the only property that we're intersted in.
this.entriesSubject = new Subject();
this.entries$ = this.entriesSubject.pipe(mergeAll());
this._viewportPrioObservables = new BehaviorSubject(of('noop'));
this._viewportPrio = this._viewportPrioObservables.pipe(coerceObservableWith(), mergeAll(), map((v) => (!v ? 'noop' : v)));
this.observer = observerSupported()
? new IntersectionObserver((entries) => {
this.entriesSubject.next(entries);
}, {
threshold: 0,
})
: null;
this.visibilityEvents$ = this.entries$.pipe(map((entry) => {
if (entry.intersectionRatio > 0) {
return 'visible';
}
else {
return 'invisible';
}
}));
}
ngOnInit() {
const letStrategyName$ = this.strategyProvider.primaryStrategy$.pipe(map(({ name }) => name));
let lastValue = undefined;
// @TODO add a connect here to get rid of the subscribe
this.letDirective.values$
.pipe(filter((n) => n.kind === 'next'))
.subscribe((v) => {
lastValue = v;
});
this.visibilityEvents$
.pipe(withLatestFrom(combineLatest(letStrategyName$, this._viewportPrio).pipe(filter(([newN, oldN]) => newN !== oldN))), map(([visibility, strategyNames]) => {
const [inStrategyName, outStrategyName] = strategyNames;
return visibility === 'visible'
? [visibility, inStrategyName]
: [visibility, outStrategyName];
}))
.subscribe(([visibility, strategyName]) => {
if (this.letDirective !== null) {
this.letDirective.strategy = strategyName;
}
if (visibility === 'visible') {
// render actual state on viewport enter
// this.letDirective.templateNotification$.next(lastValue);
}
});
// If the browser doesn't support the `IntersectionObserver` or we're inside
// the Node.js environment, then this will throw an exception that property
// `observe` doesn't exist on `null`.
if (this.observer !== null) {
this.observer.observe(this.el.nativeElement);
}
else {
// If we're inside the Node.js environment then this should be
// rendered (e.g. for SEO purposes), and when running this code in browser
// it will decide itself to render it or not.
this.entriesSubject.next([{ intersectionRatio: 1 }]);
}
}
ngOnDestroy() {
if (this.observer) {
this.observer.disconnect();
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioDirective, deps: [{ token: i0.ElementRef }, { token: i1.RxStrategyProvider }, { token: RxLet, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.0", type: ViewportPrioDirective, isStandalone: true, selector: "[viewport-prio]", inputs: { viewportPrio: ["viewport-prio", "viewportPrio"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioDirective, decorators: [{
type: Directive,
args: [{
selector: '[viewport-prio]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.RxStrategyProvider }, { type: i2.RxLet, decorators: [{
type: Inject,
args: [RxLet]
}, {
type: Optional
}] }], propDecorators: { viewportPrio: [{
type: Input,
args: ['viewport-prio']
}] } });
/** @deprecated use the standalone import, will be removed with v16 */
class ViewportPrioModule {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioModule, imports: [ViewportPrioDirective], exports: [ViewportPrioDirective] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: ViewportPrioModule, decorators: [{
type: NgModule,
args: [{
imports: [ViewportPrioDirective],
exports: [ViewportPrioDirective],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ViewportPrioDirective, ViewportPrioModule };
//# sourceMappingURL=template-experimental-viewport-prio.mjs.map