/** * https://github.com/ant-design/ant-design/blob/HEAD/components/spin/index.tsx */ import React, { cloneElement, isValidElement } from 'react' import classNames from 'classnames' import debounce from 'lodash/debounce' import omit from 'lodash/omit' import './Spin.less' const SpinSizes = ['small', 'default', 'large'] export type SpinSize = typeof SpinSizes[number] export type SpinIndicator = React.ReactElement export interface SpinProps { prefixCls?: string className?: string spinning?: boolean style?: React.CSSProperties size?: SpinSize tip?: React.ReactNode delay?: number wrapperClassName?: string indicator?: SpinIndicator } export interface SpinState { spinning?: boolean notCssAnimationSupported?: boolean } // Render indicator let defaultIndicator: React.ReactNode = null function renderIndicator(prefixCls: string, props: SpinProps): React.ReactNode { const { indicator } = props const dotClassName = `${prefixCls}-dot` // should not be render default indicator when indicator value is null if (indicator === null) { return null } if (isValidElement(indicator)) { return cloneElement(indicator, { className: classNames(indicator.props.className, dotClassName), }) } if (isValidElement(defaultIndicator)) { return cloneElement(defaultIndicator as SpinIndicator, { className: classNames( (defaultIndicator as SpinIndicator).props.className, dotClassName ), }) } return ( ) } function shouldDelay(spinning?: boolean, delay?: number): boolean { return !!spinning && !!delay && !isNaN(Number(delay)) } class Spin extends React.Component { static defaultProps = { spinning: true, size: 'default' as SpinSize, wrapperClassName: '', } static setDefaultIndicator(indicator: React.ReactNode) { defaultIndicator = indicator } originalUpdateSpinning: () => void constructor(props: SpinProps) { super(props) const { spinning, delay } = props const shouldBeDelayed = shouldDelay(spinning, delay) this.state = { spinning: spinning && !shouldBeDelayed, } this.originalUpdateSpinning = this.updateSpinning this.debouncifyUpdateSpinning(props) } componentDidMount() { this.updateSpinning() } componentDidUpdate() { this.debouncifyUpdateSpinning() this.updateSpinning() } componentWillUnmount() { this.cancelExistingSpin() } debouncifyUpdateSpinning = (props?: SpinProps) => { const { delay } = props || this.props if (delay) { this.cancelExistingSpin() this.updateSpinning = debounce(this.originalUpdateSpinning, delay) } } updateSpinning = () => { const { spinning } = this.props const { spinning: currentSpinning } = this.state if (currentSpinning !== spinning) { this.setState({ spinning }) } } cancelExistingSpin() { const { updateSpinning } = this if (updateSpinning && (updateSpinning as any).cancel) { ;(updateSpinning as any).cancel() } } isNestedPattern() { return !!(this.props && typeof this.props.children !== 'undefined') } renderSpin = () => { const { prefixCls: customizePrefixCls, className, size, tip, wrapperClassName, style, ...restProps } = this.props const { spinning } = this.state const prefixCls = '__dumi-default-spin' const spinClassName = classNames( prefixCls, { [`${prefixCls}-sm`]: size === 'small', [`${prefixCls}-lg`]: size === 'large', [`${prefixCls}-spinning`]: spinning, [`${prefixCls}-show-text`]: !!tip, }, className ) // fix https://fb.me/react-unknown-prop const divProps = omit(restProps, ['spinning', 'delay', 'indicator']) const spinElement = (
{renderIndicator(prefixCls, this.props)} {tip ?
{tip}
: null}
) if (this.isNestedPattern()) { const containerClassName = classNames(`${prefixCls}-container`, { [`${prefixCls}-blur`]: spinning, }) return (
{spinning &&
{spinElement}
}
{this.props.children}
) } return spinElement } render() { return this.renderSpin() } } export default Spin