|
|
|
import * as React from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import omit from 'rc-util/lib/omit';
|
|
|
|
import debounce from 'lodash/debounce';
|
|
|
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
|
|
|
import { tuple } from '../_util/type';
|
|
|
|
import { isValidElement, cloneElement } from '../_util/reactNode';
|
|
|
|
|
|
|
|
const SpinSizes = tuple('small', 'default', 'large');
|
|
|
|
export type SpinSize = typeof SpinSizes[number];
|
|
|
|
export type SpinIndicator = React.ReactElement<HTMLElement>;
|
|
|
|
|
|
|
|
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 (
|
|
|
|
<span className={classNames(dotClassName, `${prefixCls}-dot-spin`)}>
|
|
|
|
<i className={`${prefixCls}-dot-item`} />
|
|
|
|
<i className={`${prefixCls}-dot-item`} />
|
|
|
|
<i className={`${prefixCls}-dot-item`} />
|
|
|
|
<i className={`${prefixCls}-dot-item`} />
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function shouldDelay(spinning?: boolean, delay?: number): boolean {
|
|
|
|
return !!spinning && !!delay && !isNaN(Number(delay));
|
|
|
|
}
|
|
|
|
|
|
|
|
class Spin extends React.Component<SpinProps, SpinState> {
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|