mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
Refactor/spin cssinjs (#34653)
* TODO: fix test case * TODO: 样式、test文件的import * TODO: 伪类不生效 * temp changed * fix: disable eslint * chore: update snapshots * chore: 解决伪类不生效 * chore: update token prop name * TODO: mark FIXME * fix: recover snapshot * fix: recover snapshot again * chore: update snapshot Co-authored-by: Zack Chang <zackchangjx@foxmail.com>
This commit is contained in:
parent
1627207255
commit
c8b2b28c95
@ -40,6 +40,7 @@ import Rate from '../../rate';
|
||||
import Select from '../../select';
|
||||
import Skeleton from '../../skeleton';
|
||||
import Slider from '../../slider';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin from '../../spin';
|
||||
import Statistic from '../../statistic';
|
||||
import Steps from '../../steps';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin, { SpinProps } from '../spin';
|
||||
import useBreakpoint from '../grid/hooks/useBreakpoint';
|
||||
import { Breakpoint, responsiveArray } from '../_util/responsiveObserve';
|
||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import RcMentions from 'rc-mentions';
|
||||
import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin from '../spin';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Spin from '..';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin, { Spin as SpinClass } from '..';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('delay spinning', () => {
|
||||
@ -24,8 +25,8 @@ describe('delay spinning', () => {
|
||||
|
||||
it('should cancel debounce function when unmount', async () => {
|
||||
const wrapper = mount(<Spin spinning delay={100} />);
|
||||
const spy = jest.spyOn(wrapper.instance().updateSpinning, 'cancel');
|
||||
expect(wrapper.instance().updateSpinning.cancel).toEqual(expect.any(Function));
|
||||
const spy = jest.spyOn(wrapper.find(SpinClass).instance().updateSpinning, 'cancel');
|
||||
expect(wrapper.find(SpinClass).instance().updateSpinning.cancel).toEqual(expect.any(Function));
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
wrapper.unmount();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { render, mount } from 'enzyme';
|
||||
import Spin from '..';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin, { Spin as SpinClass } from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
@ -26,9 +27,9 @@ describe('Spin', () => {
|
||||
|
||||
it('should be controlled by spinning', () => {
|
||||
const wrapper = mount(<Spin spinning={false} />);
|
||||
expect(wrapper.instance().state.spinning).toBe(false);
|
||||
expect(wrapper.find(SpinClass).instance().state.spinning).toBe(false);
|
||||
wrapper.setProps({ spinning: true });
|
||||
expect(wrapper.instance().state.spinning).toBe(true);
|
||||
expect(wrapper.find(SpinClass).instance().state.spinning).toBe(true);
|
||||
});
|
||||
|
||||
it('if indicator set null should not be render default indicator', () => {
|
||||
|
@ -2,9 +2,10 @@ 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 { ConfigConsumer, ConfigConsumerProps, ConfigContext } from '../config-provider';
|
||||
import { tuple } from '../_util/type';
|
||||
import { isValidElement, cloneElement } from '../_util/reactNode';
|
||||
import useStyle from './style/index';
|
||||
|
||||
const SpinSizes = tuple('small', 'default', 'large');
|
||||
export type SpinSize = typeof SpinSizes[number];
|
||||
@ -22,6 +23,15 @@ export interface SpinProps {
|
||||
indicator?: SpinIndicator;
|
||||
}
|
||||
|
||||
export interface SpinClassProps extends SpinProps {
|
||||
hashId: string;
|
||||
spinPrefixCls: string;
|
||||
}
|
||||
|
||||
export type SpinFCType = React.FC<SpinProps> & {
|
||||
setDefaultIndicator: (indicator: React.ReactNode) => void;
|
||||
};
|
||||
|
||||
export interface SpinState {
|
||||
spinning?: boolean;
|
||||
notCssAnimationSupported?: boolean;
|
||||
@ -30,7 +40,7 @@ export interface SpinState {
|
||||
// Render indicator
|
||||
let defaultIndicator: React.ReactNode = null;
|
||||
|
||||
function renderIndicator(prefixCls: string, props: SpinProps): React.ReactNode {
|
||||
function renderIndicator(prefixCls: string, props: SpinClassProps): React.ReactNode {
|
||||
const { indicator } = props;
|
||||
const dotClassName = `${prefixCls}-dot`;
|
||||
|
||||
@ -65,20 +75,16 @@ function shouldDelay(spinning?: boolean, delay?: number): boolean {
|
||||
return !!spinning && !!delay && !isNaN(Number(delay));
|
||||
}
|
||||
|
||||
class Spin extends React.Component<SpinProps, SpinState> {
|
||||
export class Spin extends React.Component<SpinClassProps, SpinState> {
|
||||
static defaultProps = {
|
||||
spinning: true,
|
||||
size: 'default' as SpinSize,
|
||||
wrapperClassName: '',
|
||||
};
|
||||
|
||||
static setDefaultIndicator(indicator: React.ReactNode) {
|
||||
defaultIndicator = indicator;
|
||||
}
|
||||
|
||||
originalUpdateSpinning: () => void;
|
||||
|
||||
constructor(props: SpinProps) {
|
||||
constructor(props: SpinClassProps) {
|
||||
super(props);
|
||||
|
||||
const { spinning, delay } = props;
|
||||
@ -103,7 +109,7 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
this.cancelExistingSpin();
|
||||
}
|
||||
|
||||
debouncifyUpdateSpinning = (props?: SpinProps) => {
|
||||
debouncifyUpdateSpinning = (props?: SpinClassProps) => {
|
||||
const { delay } = props || this.props;
|
||||
if (delay) {
|
||||
this.cancelExistingSpin();
|
||||
@ -130,9 +136,10 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
return !!(this.props && typeof this.props.children !== 'undefined');
|
||||
}
|
||||
|
||||
renderSpin = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
renderSpin = ({ direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
spinPrefixCls: prefixCls,
|
||||
hashId,
|
||||
className,
|
||||
size,
|
||||
tip,
|
||||
@ -142,7 +149,6 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
} = this.props;
|
||||
const { spinning } = this.state;
|
||||
|
||||
const prefixCls = getPrefixCls('spin', customizePrefixCls);
|
||||
const spinClassName = classNames(
|
||||
prefixCls,
|
||||
{
|
||||
@ -153,10 +159,11 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
|
||||
// fix https://fb.me/react-unknown-prop
|
||||
const divProps = omit(restProps, ['spinning', 'delay', 'indicator']);
|
||||
const divProps = omit(restProps, ['spinning', 'delay', 'indicator', 'prefixCls']);
|
||||
|
||||
const spinElement = (
|
||||
<div
|
||||
@ -175,7 +182,10 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
[`${prefixCls}-blur`]: spinning,
|
||||
});
|
||||
return (
|
||||
<div {...divProps} className={classNames(`${prefixCls}-nested-loading`, wrapperClassName)}>
|
||||
<div
|
||||
{...divProps}
|
||||
className={classNames(`${prefixCls}-nested-loading`, wrapperClassName, hashId)}
|
||||
>
|
||||
{spinning && <div key="loading">{spinElement}</div>}
|
||||
<div className={containerClassName} key="container">
|
||||
{this.props.children}
|
||||
@ -191,4 +201,28 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default Spin;
|
||||
const SpinFC: SpinFCType = (props: SpinProps) => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
|
||||
const spinPrefixCls = getPrefixCls('spin', customizePrefixCls);
|
||||
|
||||
const [wrapSSR, hashId] = useStyle(spinPrefixCls);
|
||||
|
||||
const spinClassProps: SpinClassProps = {
|
||||
...props,
|
||||
spinPrefixCls,
|
||||
hashId,
|
||||
};
|
||||
return wrapSSR(<Spin {...spinClassProps} />);
|
||||
};
|
||||
|
||||
SpinFC.setDefaultIndicator = (indicator: React.ReactNode) => {
|
||||
defaultIndicator = indicator;
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
SpinFC.displayName = 'Spin';
|
||||
}
|
||||
|
||||
export default SpinFC;
|
||||
|
@ -1,2 +1,244 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// deps-lint-skip-all
|
||||
import { CSSObject, Keyframes } from '@ant-design/cssinjs';
|
||||
import {
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
resetComponent,
|
||||
GenerateStyle,
|
||||
UseComponentStyleResult,
|
||||
} from '../../_util/theme';
|
||||
import type { DerivativeToken } from '../../_util/theme';
|
||||
|
||||
interface SpinToken extends DerivativeToken {
|
||||
spinCls: string;
|
||||
spinDotDefault: string;
|
||||
spinDotSize: number;
|
||||
spinDotSizeSM: number;
|
||||
spinDotSizeLG: number;
|
||||
}
|
||||
|
||||
const antSpinMove = new Keyframes('antSpinMove', {
|
||||
to: { opacity: 1 },
|
||||
});
|
||||
|
||||
const antRotate = new Keyframes('antRotate', {
|
||||
to: { transform: 'rotate(405deg)' },
|
||||
});
|
||||
|
||||
const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken, hashId: string): CSSObject => ({
|
||||
[`${token.spinCls}`]: {
|
||||
...resetComponent(token),
|
||||
position: 'absolute',
|
||||
display: 'none',
|
||||
color: token.colorPrimary,
|
||||
textAlign: 'center',
|
||||
verticalAlign: 'middle',
|
||||
opacity: 0,
|
||||
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOutCirc}`,
|
||||
|
||||
'&-spinning': {
|
||||
position: 'static',
|
||||
display: 'inline-block',
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
'&-nested-loading': {
|
||||
position: 'relative',
|
||||
[`> div > ${token.spinCls}`]: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
zIndex: 4,
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxHeight: 400, // FIXME: hard code in v4
|
||||
|
||||
[`${token.spinCls}-dot`]: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
insetInlineStart: '50%',
|
||||
margin: -token.spinDotSize / 2,
|
||||
},
|
||||
|
||||
[`${token.spinCls}-text`]: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
width: '100%',
|
||||
paddingTop: (token.spinDotSize - token.fontSize) / 2 + 2,
|
||||
textShadow: `0 1px 2px ${token.colorBgComponent}`,
|
||||
},
|
||||
|
||||
[`&${token.spinCls}-show-text ${token.spinCls}-dot`]: {
|
||||
marginTop: -(token.spinDotSize / 2) - 10,
|
||||
},
|
||||
|
||||
[`> div > ${token.spinCls}-sm`]: {
|
||||
[`${token.spinCls}-dot`]: {
|
||||
margin: -token.spinDotSizeSM / 2,
|
||||
},
|
||||
[`${token.spinCls}-text`]: {
|
||||
paddingTop: (token.spinDotSizeSM - token.fontSize) / 2 + 2,
|
||||
},
|
||||
[`&${token.spinCls}-show-text ${token.spinCls}-dot`]: {
|
||||
marginTop: -(token.spinDotSizeSM / 2) - 10,
|
||||
},
|
||||
},
|
||||
|
||||
[`> div > ${token.spinCls}-lg`]: {
|
||||
[`${token.spinCls}-dot`]: {
|
||||
margin: -(token.spinDotSizeLG / 2),
|
||||
},
|
||||
[`${token.spinCls}-text`]: {
|
||||
paddingTop: (token.spinDotSizeLG - token.fontSize) / 2 + 2,
|
||||
},
|
||||
[`&${token.spinCls}-show-text ${token.spinCls}-dot`]: {
|
||||
marginTop: -(token.spinDotSizeLG / 2) - 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${token.spinCls}-container`]: {
|
||||
position: 'relative',
|
||||
transition: `opacity ${token.motionDurationSlow}`,
|
||||
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
zIndex: 10,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: token.colorBgComponent,
|
||||
opacity: 0,
|
||||
transition: `all ${token.motionDurationSlow}`,
|
||||
content: '""',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
[`${token.spinCls}-blur`]: {
|
||||
clear: 'both',
|
||||
opacity: 0.5,
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none',
|
||||
|
||||
[`&::after`]: {
|
||||
opacity: 0.4,
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// tip
|
||||
// ------------------------------
|
||||
[`&-tip`]: {
|
||||
color: token.spinDotDefault,
|
||||
},
|
||||
|
||||
// dots
|
||||
// ------------------------------
|
||||
[`${token.spinCls}-dot`]: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
fontSize: token.spinDotSize,
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
|
||||
'&-item': {
|
||||
position: 'absolute',
|
||||
display: 'block',
|
||||
width: 9, // FIXME: hard code in v4
|
||||
height: 9, // FIXME: hard code in v4
|
||||
backgroundColor: token.colorPrimary,
|
||||
borderRadius: '100%',
|
||||
transform: 'scale(0.75)',
|
||||
transformOrigin: '50% 50%',
|
||||
opacity: 0.3,
|
||||
animation: `${antSpinMove.getName(hashId)} 1s infinite linear alternate`,
|
||||
|
||||
'&:nth-child(1)': {
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
},
|
||||
|
||||
'&:nth-child(2)': {
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
animationDelay: '0.4s',
|
||||
},
|
||||
|
||||
'&:nth-child(3)': {
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
animationDelay: '0.8s',
|
||||
},
|
||||
|
||||
'&:nth-child(4)': {
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
animationDelay: '1.2s',
|
||||
},
|
||||
},
|
||||
|
||||
'&-spin': {
|
||||
transform: 'rotate(45deg)',
|
||||
animation: `${antRotate.getName(hashId)} 1.2s infinite linear`,
|
||||
},
|
||||
},
|
||||
|
||||
// Sizes
|
||||
// ------------------------------
|
||||
|
||||
// small
|
||||
[`&-sm ${token.spinCls}-dot`]: {
|
||||
fontSize: token.spinDotSizeSM,
|
||||
|
||||
i: {
|
||||
width: 6, // FIXME: hard code in v4
|
||||
height: 6, // FIXME: hard code in v4
|
||||
},
|
||||
},
|
||||
|
||||
// large
|
||||
[`&-lg ${token.spinCls}-dot`]: {
|
||||
fontSize: token.spinDotSizeLG,
|
||||
|
||||
i: {
|
||||
width: 14, // FIXME: hard code in v4
|
||||
height: 14, // FIXME: hard code in v4
|
||||
},
|
||||
},
|
||||
|
||||
[`&${token.spinCls}-show-text ${token.spinCls}-text`]: {
|
||||
display: 'block',
|
||||
},
|
||||
|
||||
// animation
|
||||
antSpinMove,
|
||||
antRotate,
|
||||
},
|
||||
});
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(prefixCls: string): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
const spinToken: SpinToken = {
|
||||
...token,
|
||||
spinCls: `.${prefixCls}`,
|
||||
spinDotDefault: token.colorTextSecondary,
|
||||
spinDotSize: 20, // FIXME: hard code in v4
|
||||
spinDotSizeSM: 14, // FIXME: hard code in v4
|
||||
spinDotSizeLG: 32, // FIXME: hard code in v4
|
||||
};
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
genSpinStyle(spinToken, hashId),
|
||||
]),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import omit from 'rc-util/lib/omit';
|
||||
import RcTable, { Summary } from 'rc-table';
|
||||
import { TableProps as RcTableProps, INTERNAL_HOOKS } from 'rc-table/lib/Table';
|
||||
import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Spin, { SpinProps } from '../spin';
|
||||
import Pagination from '../pagination';
|
||||
import { TooltipProps } from '../tooltip';
|
||||
|
Loading…
Reference in New Issue
Block a user