import * as React from 'react' import { useProgress } from '../core/Progress' import { CSSProperties } from 'react' interface LoaderOptions { containerStyles: CSSProperties innerStyles: CSSProperties barStyles: CSSProperties dataStyles: CSSProperties dataInterpolation: (p: number) => string initialState: (active: boolean) => boolean } const defaultDataInterpolation = (p: number) => `Loading ${p.toFixed(2)}%` export function Loader({ containerStyles, innerStyles, barStyles, dataStyles, dataInterpolation = defaultDataInterpolation, initialState = (active: boolean) => active, }: Partial) { const { active, progress } = useProgress() const progressRef = React.useRef(0) const rafRef = React.useRef(0) const progressSpanRef = React.useRef(null) const [shown, setShown] = React.useState(initialState(active)) React.useEffect(() => { let t if (active !== shown) t = setTimeout(() => setShown(active), 300) return () => clearTimeout(t) }, [shown, active]) const updateProgress = React.useCallback(() => { if (!progressSpanRef.current) return progressRef.current += (progress - progressRef.current) / 2 if (progressRef.current > 0.95 * progress || progress === 100) progressRef.current = progress progressSpanRef.current.innerText = dataInterpolation(progressRef.current) if (progressRef.current < progress) rafRef.current = requestAnimationFrame(updateProgress) }, [dataInterpolation, progress]) React.useEffect(() => { updateProgress() return () => cancelAnimationFrame(rafRef.current) }, [updateProgress]) return shown ? (
) : null } const styles: { [key: string]: CSSProperties } = { container: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', background: '#171717', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'opacity 300ms ease', zIndex: 1000, }, inner: { width: 100, height: 3, background: '#272727', textAlign: 'center', }, bar: { height: 3, width: '100%', background: 'white', transition: 'transform 200ms', transformOrigin: 'left center', }, data: { display: 'inline-block', position: 'relative', fontVariantNumeric: 'tabular-nums', marginTop: '0.8em', color: '#f0f0f0', fontSize: '0.6em', fontFamily: `-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Helvetica Neue", Helvetica, Arial, Roboto, Ubuntu, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`, whiteSpace: 'nowrap', }, }