UNPKG

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