UNPKG

@ynmstudio/utils

Version:
223 lines (216 loc) 7.98 kB
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: "19.2.13", 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: "19.2.13", ngImport: i0, type: LoadingBarService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", 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