@rx-angular/cdk
Version:
@rx-angular/cdk is a Component Development Kit for ergonomic and highly performant angular applications. It helps to to build Large scale applications, UI libs, state management, rendering systems and much more. Furthermore the unique way of mixing reacti
143 lines (136 loc) • 5.11 kB
JavaScript
import { coalesceWith } from '@rx-angular/cdk/coalescing';
import { from, combineLatest, Observable, ReplaySubject } from 'rxjs';
import { filter, distinctUntilChanged, map, shareReplay, concatMap, mapTo } from 'rxjs/operators';
import { ɵglobal as _global, assertInInjectionContext, inject, Injector, effect, untracked, DestroyRef } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
function getZoneUnPatchedApi(targetOrName, name) {
// If the user has provided the API name as the first argument, for instance:
// `const addEventListener = getZoneUnPatchedApi('addEventListener');`
// Then we just swap arguments and make `global` or `window` as the default target.
if (typeof targetOrName === 'string') {
name = targetOrName;
targetOrName = _global;
}
return targetOrName['__zone_symbol__' + String(name)] || targetOrName[name];
}
const resolvedPromise = getZoneUnPatchedApi('Promise').resolve();
const resolvedPromise$ = from(resolvedPromise);
/**
* @internal
*
* Used for typing
*/
function getEntriesToObjectReducerFn(keys) {
return (accumulator, currentValue, currentIndex) => {
return {
...accumulator,
[keys[currentIndex]]: currentValue,
};
};
}
/**
* This Observable creation function helps to accumulate an object of key & Observable of values to
* an Observable of objects of key & value.
* This comes in handy if you quickly want to create subsets as objects/state-slices of different Observables.
*
* The resulting Observable filters out undefined values forwards only distinct values and shared the aggregated output.
*
* @example
*
* Default usage:
*
* const object$: Observable<{
* prop1: number,
* prop2: string,
* prop3: string
* }> = accumulateObservables({
* prop1: interval(42),
* prop2: of('lorem'),
* prop3: 'test'
* });
*
* Usage with custom duration selector:
*
* const object$: Observable<{
* prop1: number,
* prop2: string,
* prop3: string
* }> = accumulateObservables({
* prop1: interval(42),
* prop2: of('lorem'),
* prop3: 'test'
* }, timer(0, 20));
*
* @param obj - An object of key & Observable values pairs
* @param durationSelector - An Observable determining the duration for the internal coalescing method
*/
function accumulateObservables(
// @TODO type static or Observable to enable mixing of imperative and reatctive values
obj, durationSelector = resolvedPromise$) {
const keys = Object.keys(obj);
// @TODO better typing to enable static values => coerceObservable(obj[key])
const observables = keys.map((key) => obj[key].pipe(
// we avoid using the nullish operator later ;)
filter((v) => v !== undefined),
// state "changes" differ from each other, this operator ensures distinct values
distinctUntilChanged()));
return combineLatest(observables).pipe(
// As combineLatest will emit multiple times for a change in multiple properties we coalesce those emissions
// together
coalesceWith(durationSelector),
// mapping array of values to object
map((values) => values.reduce(getEntriesToObjectReducerFn(keys), {})),
// by using shareReplay we share the last composition work done to create the accumulated object
shareReplay({ refCount: true, bufferSize: 1 }));
}
/**
* Creates an Observable that emits after a setTimeout.
* The timeout it unpatched to not avoid zone pollution
* @param setTimeoutFn
*/
function timeout(delay = 0) {
return new Observable((subscriber) => {
const asyncID = getZoneUnPatchedApi('setTimeout')(() => subscriber.next(0), delay);
return () => {
getZoneUnPatchedApi('clearTimeout')(asyncID);
};
});
}
/**
*
*/
function timeoutSwitchMapWith() {
return (o$) => o$.pipe(concatMap((v) => timeout().pipe(mapTo(v))));
}
// Copied from angular/core/rxjs-interop/src/to_observable.ts -> because it's a private API
// https://github.com/angular/angular/blob/46f00f951842dd117653df6cca3bfd5ee5baa0f1/packages/core/rxjs-interop/src/to_observable.ts#L72
function toObservableMicrotaskInternal(source, options) {
if (!options?.injector) {
assertInInjectionContext(toObservable);
}
const injector = options?.injector ?? inject(Injector);
const subject = new ReplaySubject(1);
const watcher = effect(() => {
let value;
try {
value = source();
}
catch (err) {
untracked(() => subject.error(err));
return;
}
untracked(() => subject.next(value));
},
// forceRoot will ensure that the effect will be scheduled as a microtask
{ injector, manualCleanup: true, forceRoot: true });
injector.get(DestroyRef).onDestroy(() => {
watcher.destroy();
subject.complete();
});
return subject.asObservable();
}
/**
* Generated bundle index. Do not edit.
*/
export { accumulateObservables, getZoneUnPatchedApi, timeoutSwitchMapWith, toObservableMicrotaskInternal };
//# sourceMappingURL=cdk-internals-core.mjs.map