import classNames from "classnames" ; import * as React from "react" ; import { polyfill } from "react-lifecycles-compat" ; import { AbstractPureComponent2 , Classes } from "../../common" ; import { SPINNER_WARN_CLASSES_SIZE } from "../../common/errors" ; import { DISPLAYNAME_PREFIX , IntentProps , Props } from "../../common/props" ; import { clamp } from "../../common/utils" ; export enum SpinnerSize { SMALL = 20 , STANDARD = 50 , LARGE = 100 } const R = 45 ; const SPINNER_TRACK = `M 50,50 m 0,-${ R } a ${ R },${ R } 0 1 1 0,${ R * 2 } a ${ R },${ R } 0 1 1 0,-${ R * 2 }` ; const PATH_LENGTH = 280 ; const MIN_SIZE = 10 ; const STROKE_WIDTH = 4 ; const MIN_STROKE_WIDTH = 16 ; export type SpinnerProps = ISpinnerProps ; deprecated export interface ISpinnerProps extends Props , IntentProps { default size ? : number ; default tagName ? : keyof JSX . IntrinsicElements ; /**
     * A value between 0 and 1 (inclusive) representing how far along the operation is.
     * Values below 0 or above 1 will be interpreted as 0 or 1 respectively.
     * Omitting this prop will result in an "indeterminate" spinner where the head spins indefinitely.
     */ value ? : number ; } @ polyfill export class Spinner extends AbstractPureComponent2 < SpinnerProps > { public static displayName = `${ DISPLAYNAME_PREFIX }.Spinner` ; deprecated public static readonly SIZE_SMALL = SpinnerSize . SMALL ; deprecated public static readonly SIZE_STANDARD = SpinnerSize . STANDARD ; deprecated public static readonly SIZE_LARGE = SpinnerSize . LARGE ; public componentDidUpdate ( prevProps ) { if ( prevProps . value !== this . props . value ) { this . forceUpdate ( ) ; } } public render ( ) { const { className , intent , value , tagName = "div" } = this . props ; const size = this . getSize ( ) ; const classes = classNames ( Classes . SPINNER , Classes . intentClass ( intent ) , { [ Classes . SPINNER_NO_SPIN ] : value != null } , className ) ; const strokeWidth = Math . min ( MIN_STROKE_WIDTH , ( STROKE_WIDTH * SpinnerSize . LARGE ) / size ) ; const strokeOffset = PATH_LENGTH - PATH_LENGTH * ( value == null ? 0.25 : clamp ( value , 0 , 1 ) ) ; return React . createElement ( tagName , { className : classes , role : "progressbar" , } , React . createElement ( tagName , { className : Classes . SPINNER_ANIMATION } , < svg width = { size } height = { size } strokeWidth = { strokeWidth . toFixed ( 2 ) } viewBox = { this . getViewBox ( strokeWidth ) } > < path className = { Classes . SPINNER_TRACK } d = { SPINNER_TRACK } / > < path className = { Classes . SPINNER_HEAD } d = { SPINNER_TRACK } pathLength = { PATH_LENGTH } strokeDasharray = { `${ PATH_LENGTH } ${ PATH_LENGTH }` } strokeDashoffset = { strokeOffset } / > < /svg>)); } protected validateProps ( { className = "" , size } ) { if ( size != null && ( className . indexOf ( Classes . SMALL ) >= 0 || className . indexOf ( Classes . LARGE ) >= 0 ) ) { console . warn ( SPINNER_WARN_CLASSES_SIZE ) ; } } private getSize ( ) { const { className = "" , size } = this . props ; if ( size == null ) { if ( className . indexOf ( Classes . SMALL ) >= 0 ) { return SpinnerSize . SMALL ; } else if ( className . indexOf ( Classes . LARGE ) >= 0 ) { return SpinnerSize . LARGE ; } return SpinnerSize . STANDARD ; } return Math . max ( MIN_SIZE , size ) ; } private getViewBox ( strokeWidth ) { const radius = R + strokeWidth / 2 ; const viewBoxX = ( 50 - radius ) . toFixed ( 2 ) ; const viewBoxWidth = ( radius * 2 ) . toFixed ( 2 ) ; return `${ viewBoxX } ${ viewBoxX } ${ viewBoxWidth } ${ viewBoxWidth }` ; } }