import React , { Component } from 'react' ; export let activeRequests = 0 ; export let progressBar ; type Mode = 'hibernate' | 'init' | 'active' | 'complete' | 'inactive' ; interface Props { style ? : Record < string , any > ; } interface State { mode : Mode ; } export class ProgressBar extends Component < Props , State > { public state : Readonly < State > = { mode : 'hibernate' , } ; public componentDidMount ( ) { progressBar = this ; } public shouldComponentUpdate ( nextProps , nextState ) { return nextState . mode !== this . state . mode ; } memberof public tick ( ) { const mode = this . state . mode ; if ( mode === 'complete' ) { setTimeout ( ( ) => { this . setState ( { mode : 'hibernate' } ) ; } , 1000 ) ; } else if ( mode === 'active' ) { if ( activeRequests === 0 ) { setTimeout ( ( ) => { if ( activeRequests === 0 ) { this . moveToMode ( 'complete' ) ; } else { this . tick ( ) ; } } , 200 ) ; } else { this . tickWithDelay ( ) ; } } else { if ( activeRequests > 0 ) { setTimeout ( ( ) => { if ( activeRequests > 0 ) { this . moveToMode ( 'active' ) ; } else { this . setState ( { mode : 'hibernate' } ) ; } } , 100 ) ; } else { this . setState ( { mode : 'hibernate' } ) ; } } } public moveToInit ( ) { if ( this . state . mode === 'hibernate' ) { this . moveToMode ( 'init' ) ; } } public moveToMode ( mode ) { this . setState ( { mode } , ( ) => { this . tick ( ) ; } ) ; } public tickWithDelay ( ) { setTimeout ( ( ) => { this . tick ( ) ; } , 50 ) ; } public render ( ) { const mode = this . state . mode ; if ( mode === 'hibernate' ) { return null ; } const width = mode === 'complete' ? 100 : mode === 'init' ? 0 : 80 ; const animationSpeed = mode === 'complete' ? 0.8 : 30 ; const transition = mode === 'init' ? '' : `width ${ animationSpeed }s ease-in` ; const style = { position : 'absolute' , top : '0' , zIndex : 9000 , backgroundColor : '#f0ad4e' , height : '4px' , transition , width : `${ width }%` , ... this . props . style , } ; return < div className = "react-fetch-progress-bar" style = { style } / > ; } } type FetchSignature = ( url : string , options ? : object ) => Promise < Response > ; let originalFetch ; export function setOriginalFetch ( nextOriginalFetch ) { originalFetch = nextOriginalFetch ; } export param { string } url param { RequestOptions } options returns { Promise < Response > } export async function progressBarFetch ( url , options ? ) { activeRequests += 1 ; if ( progressBar ) { progressBar . moveToInit ( ) ; } try { const response = await originalFetch ( url , options ) ; activeRequests -= 1 ; return response ; } catch ( error ) { activeRequests -= 1 ; return Promise . reject ( error ) ; } } export param { number } nextActiveRequest export function setActiveRequests ( nextActiveRequest ) { activeRequests = nextActiveRequest ; }