@ynmstudio/utils
Version:
YNM Utilities for Angular
223 lines (216 loc) • 7.97 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, PLATFORM_ID, Inject, Optional, Injectable, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { Subject, of, timer, combineLatest, Observable } from 'rxjs';
import { take, map, tap, startWith, switchMap, shareReplay, finalize } from 'rxjs/operators';
import { HttpContextToken } from '@angular/common/http';
const LOADING_BAR_CONFIG = new InjectionToken('LOADING_BAR_CONFIG');
class LoadingBarState {
#requests;
#disabled;
#stream$;
constructor(config = {}) {
this.config = config;
this.state = {
action: undefined,
value: 0,
initialValue: 0,
};
this.#requests = null;
this.#disabled = false;
this.#stream$ = new Subject();
this._value$ = null;
this.timer$ = (s) => {
let state$ = of(s);
switch (s.action) {
case 'start':
case 'increment':
case 'set': {
if (s.action === 'start' && this.config.latencyThreshold === 0 && s.value === 0) {
s.value = s.initialValue;
}
if (this.#requests > 0) {
state$ = timer(this.config.latencyThreshold ?? 0, 250).pipe(map((t) => ({ ...s, value: t === 0 ? this.state.value || s.initialValue : this._increment() })));
}
break;
}
case 'complete':
case 'stop': {
// Attempt to aggregate any start/complete calls within 500ms:
state$ =
s.value === 0
? of({ ...s })
: timer(0, 500).pipe(take(2), map((t) => ({ value: t === 0 ? 100 : 0 })));
break;
}
}
return state$.pipe(map((next) => ({ ...next, action: 'set' })), tap((next) => this.next(next, false)));
};
this.config = {
latencyThreshold: 0,
...config,
};
}
get value$() {
if (this._value$) {
return this._value$;
}
return (this._value$ = this.#stream$.pipe(startWith(this.state), switchMap((s) => this.timer$(s)), shareReplay(), map((s) => s.value)));
}
start(initialValue = 2) {
if (this.#disabled) {
return;
}
this.next({ action: 'start', initialValue });
}
stop() {
this.next({ action: 'stop' });
}
complete() {
this.next({ action: 'complete' });
}
disable() {
this.#disabled = true;
}
set(value) {
this.next({ action: 'set', value });
}
increment(value = 0) {
this.next({ action: 'increment', value });
}
next(state, emitEvent = true) {
switch (state.action) {
case 'start':
this.#requests = (this.#requests || 0) + 1;
break;
case 'complete':
this.#requests = (this.#requests || 1) - 1;
if (this.#requests > 0) {
return;
}
break;
case 'stop':
this.#requests = 0;
break;
case 'increment':
state.value = this._increment(state.value);
break;
}
this.state = { ...this.state, action: undefined, ...state };
if (emitEvent) {
this.#stream$.next(this.state);
}
}
_increment(rnd = 0) {
const stat = this.state.value;
if (stat >= 99) {
rnd = 0;
}
if (rnd === 0) {
if (stat >= 0 && stat < 25) {
// Start out between 3 - 6% increments
rnd = Math.random() * (5 - 3 + 1) + 3;
}
else if (stat >= 25 && stat < 65) {
// increment between 0 - 3%
rnd = Math.random() * 3;
}
else if (stat >= 65 && stat < 90) {
// increment between 0 - 2%
rnd = Math.random() * 2;
}
else if (stat >= 90 && stat < 99) {
// finally, increment it .5 %
rnd = 0.5;
}
else {
// after 99%, don't increment:
rnd = 0;
}
}
return rnd + stat;
}
}
class LoadingBarService {
constructor(platformId, config = {}, zone) {
this.platformId = platformId;
this.config = config;
this.zone = zone;
this.refs = {};
this.streams$ = new Subject();
this.value$ = this.streams$.pipe(startWith(null), switchMap(() => combineLatest(Object.keys(this.refs).map((s) => this.refs[s].value$))), runInZone(this.zone), map((v) => Math.max(0, ...v)));
}
/** @deprecated use `useRef` instead. */
start(initialValue = 2) {
this.useRef().start(initialValue);
}
/** @deprecated use `useRef` instead. */
set(value) {
this.useRef().set(value);
}
/** @deprecated use `useRef` instead. */
increment(value) {
this.useRef().increment(value);
}
/** @deprecated use `useRef` instead. */
complete() {
this.useRef().complete();
}
/** @deprecated use `useRef` instead. */
stop() {
this.useRef().stop();
}
useRef(id = 'default') {
if (!this.refs[id]) {
this.refs[id] = new LoadingBarState(this.config);
this.streams$.next();
if (!isPlatformBrowser(this.platformId)) {
this.refs[id].disable();
}
}
return this.refs[id];
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LoadingBarService, deps: [{ token: PLATFORM_ID }, { token: LOADING_BAR_CONFIG, optional: true }, { token: i0.NgZone, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LoadingBarService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LoadingBarService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: Object, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [LOADING_BAR_CONFIG]
}] }, { type: i0.NgZone, decorators: [{
type: Optional
}] }] });
// https://stackoverflow.com/a/57452361/1406096
function runInZone(zone) {
if (!zone) {
return (source) => source;
}
return (source) => new Observable((observer) => source.subscribe((value) => zone.run(() => observer.next(value)), (e) => zone.run(() => observer.error(e)), () => zone.run(() => observer.complete())));
}
const NGX_LOADING_BAR_IGNORED = new HttpContextToken(() => false);
const loadingBarInterceptor = (req, next) => {
const loader = inject(LoadingBarService);
if (req.context.get(NGX_LOADING_BAR_IGNORED) === true) {
return next(req);
}
let started = false;
const ref = loader.useRef('http');
return next(req).pipe(tap(() => {
if (!started) {
ref.start();
started = true;
}
}), finalize(() => started && ref.complete()));
};
/**
* Generated bundle index. Do not edit.
*/
export { LOADING_BAR_CONFIG, LoadingBarService, LoadingBarState, NGX_LOADING_BAR_IGNORED, loadingBarInterceptor, runInZone };
//# sourceMappingURL=ynmstudio-utils-loading-bar.mjs.map