feat: Spin support percent (#48657)

* feat: spin support ptg

* chore: ptg

* chore: update demo

* chore: popout component

* chore: adjust logic

* test: update snapshot

* test: update snapshot

* docs: update docs

* test: update snapshot

* test: update snapshot

* test: update snapshot

* chore: back of snapshot

* chore: clean up

* test: fix test case

* chore: fix types

* test: update snapshot

* chore: opt

* test: update testcase

* test: coverage

* test: update snapshot
This commit is contained in:
二货爱吃白萝卜 2024-06-03 11:30:27 +08:00 committed by GitHub
parent f3000ed746
commit 0352b2fd2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1267 additions and 583 deletions

View File

@ -25791,20 +25791,24 @@ exports[`ConfigProvider components Spin configProvider 1`] = `
class="config-spin config-spin-spinning"
>
<span
class="config-spin-dot config-spin-dot-spin"
class="config-spin-dot-holder"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<span
class="config-spin-dot config-spin-dot-spin"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25816,20 +25820,24 @@ exports[`ConfigProvider components Spin configProvider componentDisabled 1`] = `
class="config-spin config-spin-spinning"
>
<span
class="config-spin-dot config-spin-dot-spin"
class="config-spin-dot-holder"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<span
class="config-spin-dot config-spin-dot-spin"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25841,20 +25849,24 @@ exports[`ConfigProvider components Spin configProvider componentSize large 1`] =
class="config-spin config-spin-spinning"
>
<span
class="config-spin-dot config-spin-dot-spin"
class="config-spin-dot-holder"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<span
class="config-spin-dot config-spin-dot-spin"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25866,20 +25878,24 @@ exports[`ConfigProvider components Spin configProvider componentSize middle 1`]
class="config-spin config-spin-spinning"
>
<span
class="config-spin-dot config-spin-dot-spin"
class="config-spin-dot-holder"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<span
class="config-spin-dot config-spin-dot-spin"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25891,20 +25907,24 @@ exports[`ConfigProvider components Spin configProvider componentSize small 1`] =
class="config-spin config-spin-spinning"
>
<span
class="config-spin-dot config-spin-dot-spin"
class="config-spin-dot-holder"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<span
class="config-spin-dot config-spin-dot-spin"
>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
<i
class="config-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25916,20 +25936,24 @@ exports[`ConfigProvider components Spin normal 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -25941,20 +25965,24 @@ exports[`ConfigProvider components Spin prefixCls 1`] = `
class="prefix-Spin prefix-Spin-spinning"
>
<span
class="prefix-Spin-dot prefix-Spin-dot-spin"
class="prefix-Spin-dot-holder"
>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
<span
class="prefix-Spin-dot prefix-Spin-dot-spin"
>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
<i
class="prefix-Spin-dot-item"
/>
</span>
</span>
</div>
`;

View File

@ -1799,20 +1799,24 @@ exports[`renders components/list/demo/loadmore.tsx extend context correctly 1`]
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>

View File

@ -1794,20 +1794,24 @@ exports[`renders components/list/demo/loadmore.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>

View File

@ -347,20 +347,24 @@ exports[`renders components/qr-code/demo/status.tsx extend context correctly 1`]
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>

View File

@ -299,20 +299,24 @@ exports[`renders components/qr-code/demo/status.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>

View File

@ -0,0 +1,30 @@
import * as React from 'react';
import classNames from 'classnames';
import Progress from './Progress';
export interface IndicatorProps {
prefixCls: string;
percent?: number;
}
export default function Looper(props: IndicatorProps) {
const { prefixCls, percent = 0 } = props;
const dotClassName = `${prefixCls}-dot`;
const holderClassName = `${dotClassName}-holder`;
const hideClassName = `${holderClassName}-hidden`;
// ===================== Render =====================
return (
<>
<span className={classNames(holderClassName, percent > 0 && hideClassName)}>
<span className={classNames(dotClassName, `${prefixCls}-dot-spin`)}>
{[1, 2, 3, 4].map((i) => (
<i className={`${prefixCls}-dot-item`} key={i} />
))}
</span>
</span>
<Progress prefixCls={prefixCls} percent={percent} />
</>
);
}

View File

@ -0,0 +1,73 @@
import * as React from 'react';
import classNames from 'classnames';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
export interface ProgressProps {
prefixCls: string;
percent: number;
}
export default function Progress({ percent, prefixCls }: ProgressProps) {
const dotClassName = `${prefixCls}-dot`;
const holderClassName = `${dotClassName}-holder`;
const hideClassName = `${holderClassName}-hidden`;
const [render, setRender] = React.useState(false);
// ==================== Visible =====================
useLayoutEffect(() => {
if (percent !== 0) {
setRender(true);
}
}, [percent !== 0]);
// ==================== Progress ====================
const safePtg = Math.max(Math.min(percent, 100), 0);
const viewSize = 100;
const borderWidth = viewSize / 5;
const radius = viewSize / 2 - borderWidth / 2;
const circumference = radius * 2 * Math.PI;
const renderCircle = (circleClassName?: string, style?: React.CSSProperties) => (
<circle
className={classNames(circleClassName, `${dotClassName}-circle`)}
r={radius}
cx="50"
cy="50"
strokeWidth={borderWidth}
style={style}
/>
);
// ===================== Render =====================
if (!render) {
return null;
}
return (
<span
className={classNames(
holderClassName,
`${dotClassName}-progress`,
safePtg <= 0 && hideClassName,
)}
>
<svg
viewBox={`0 0 ${viewSize} ${viewSize}`}
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={safePtg}
>
{renderCircle(`${dotClassName}-circle-bg`)}
{renderCircle('', {
strokeDasharray: `${(circumference * safePtg) / 100} ${
(circumference * (100 - safePtg)) / 100
}`,
strokeDashoffset: `${circumference / 4}`,
})}
</svg>
</span>
);
}

View File

@ -0,0 +1,24 @@
import * as React from 'react';
import classNames from 'classnames';
import { cloneElement } from '../../_util/reactNode';
import Looper from './Looper';
export interface IndicatorProps {
prefixCls: string;
indicator?: React.ReactNode;
percent?: number;
}
export default function Indicator(props: IndicatorProps) {
const { prefixCls, indicator, percent } = props;
const dotClassName = `${prefixCls}-dot`;
if (indicator && React.isValidElement(indicator)) {
return cloneElement(indicator, {
className: classNames(indicator.props.className, dotClassName),
});
}
return <Looper prefixCls={prefixCls} percent={percent} />;
}

View File

@ -7,20 +7,24 @@ exports[`renders components/spin/demo/basic.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -125,67 +129,44 @@ Array [
type="button"
>
<span>
Show fullscreen for 3s
Show fullscreen
</span>
</button>,
<div
aria-busy="false"
aria-live="polite"
class="ant-spin ant-spin-fullscreen"
class="ant-spin-fullscreen"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
<div
aria-busy="false"
aria-live="polite"
class="ant-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>,
]
`;
exports[`renders components/spin/demo/fullscreen.tsx extend context correctly 2`] = `[]`;
exports[`renders components/spin/demo/inside.tsx extend context correctly 1`] = `
<div
class="example"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</div>
</div>
`;
exports[`renders components/spin/demo/inside.tsx extend context correctly 2`] = `[]`;
exports[`renders components/spin/demo/nested.tsx extend context correctly 1`] = `
Array [
<div
@ -246,6 +227,214 @@ Array [
exports[`renders components/spin/demo/nested.tsx extend context correctly 2`] = `[]`;
exports[`renders components/spin/demo/percent.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<button
aria-checked="false"
class="ant-switch"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
>
Auto
</span>
<span
class="ant-switch-inner-unchecked"
>
Auto
</span>
</span>
</button>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-sm ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<span
class="ant-spin-dot-holder ant-spin-dot-progress ant-spin-dot-holder-hidden"
>
<svg
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="0"
role="progressbar"
viewBox="0 0 100 100"
>
<circle
class="ant-spin-dot-circle-bg ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
/>
<circle
class="ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
style="stroke-dasharray: 0 251.32741228718342; stroke-dashoffset: 62.83185307179586;"
/>
</svg>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<span
class="ant-spin-dot-holder ant-spin-dot-progress ant-spin-dot-holder-hidden"
>
<svg
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="0"
role="progressbar"
viewBox="0 0 100 100"
>
<circle
class="ant-spin-dot-circle-bg ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
/>
<circle
class="ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
style="stroke-dasharray: 0 251.32741228718342; stroke-dashoffset: 62.83185307179586;"
/>
</svg>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-lg ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<span
class="ant-spin-dot-holder ant-spin-dot-progress ant-spin-dot-holder-hidden"
>
<svg
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="0"
role="progressbar"
viewBox="0 0 100 100"
>
<circle
class="ant-spin-dot-circle-bg ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
/>
<circle
class="ant-spin-dot-circle"
cx="50"
cy="50"
r="40"
stroke-width="20"
style="stroke-dasharray: 0 251.32741228718342; stroke-dashoffset: 62.83185307179586;"
/>
</svg>
</span>
</div>
</div>
</div>
`;
exports[`renders components/spin/demo/percent.tsx extend context correctly 2`] = `[]`;
exports[`renders components/spin/demo/size.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-center ant-flex-gap-middle"
@ -256,20 +445,24 @@ exports[`renders components/spin/demo/size.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-sm ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
<div
@ -278,20 +471,24 @@ exports[`renders components/spin/demo/size.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
<div
@ -300,20 +497,24 @@ exports[`renders components/spin/demo/size.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-lg ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>
@ -338,20 +539,24 @@ exports[`renders components/spin/demo/tip.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-sm ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -378,20 +583,24 @@ exports[`renders components/spin/demo/tip.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -418,20 +627,24 @@ exports[`renders components/spin/demo/tip.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-lg ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -459,20 +672,24 @@ exports[`renders components/spin/demo/tip.tsx extend context correctly 1`] = `
class="ant-spin ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"

View File

@ -7,20 +7,24 @@ exports[`renders components/spin/demo/basic.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
`;
@ -119,63 +123,42 @@ Array [
type="button"
>
<span>
Show fullscreen for 3s
Show fullscreen
</span>
</button>,
<div
aria-busy="false"
aria-live="polite"
class="ant-spin ant-spin-fullscreen"
class="ant-spin-fullscreen"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
<div
aria-busy="false"
aria-live="polite"
class="ant-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>,
]
`;
exports[`renders components/spin/demo/inside.tsx correctly 1`] = `
<div
class="example"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</div>
</div>
`;
exports[`renders components/spin/demo/nested.tsx correctly 1`] = `
Array [
<div
@ -234,6 +217,131 @@ Array [
]
`;
exports[`renders components/spin/demo/percent.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<button
aria-checked="false"
class="ant-switch"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
>
Auto
</span>
<span
class="ant-switch-inner-unchecked"
>
Auto
</span>
</span>
</button>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-sm ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>
<div
class="ant-space-item"
>
<div
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-lg ant-spin-spinning"
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>
</div>
`;
exports[`renders components/spin/demo/size.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-center ant-flex-gap-middle"
@ -244,20 +352,24 @@ exports[`renders components/spin/demo/size.tsx correctly 1`] = `
class="ant-spin ant-spin-sm ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
<div
@ -266,20 +378,24 @@ exports[`renders components/spin/demo/size.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
<div
@ -288,20 +404,24 @@ exports[`renders components/spin/demo/size.tsx correctly 1`] = `
class="ant-spin ant-spin-lg ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>
@ -324,20 +444,24 @@ exports[`renders components/spin/demo/tip.tsx correctly 1`] = `
class="ant-spin ant-spin-sm ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -364,20 +488,24 @@ exports[`renders components/spin/demo/tip.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -404,20 +532,24 @@ exports[`renders components/spin/demo/tip.tsx correctly 1`] = `
class="ant-spin ant-spin-lg ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"
@ -445,20 +577,24 @@ exports[`renders components/spin/demo/tip.tsx correctly 1`] = `
class="ant-spin ant-spin-spinning ant-spin-show-text"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
<div
class="ant-spin-text"

View File

@ -5,7 +5,28 @@ exports[`Spin if indicator set null should not be render default indicator 1`] =
aria-busy="true"
aria-live="polite"
class="ant-spin ant-spin-spinning"
/>
>
<span
class="ant-spin-dot-holder"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
`;
exports[`Spin rtl render component should be rendered correctly in RTL direction 1`] = `
@ -15,20 +36,24 @@ exports[`Spin rtl render component should be rendered correctly in RTL direction
class="ant-spin ant-spin-spinning ant-spin-rtl"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
`;

View File

@ -1,12 +1,20 @@
import React from 'react';
import { render } from '@testing-library/react';
import Spin from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { waitFakeTimer } from '../../../tests/utils';
import { act, render, waitFakeTimer } from '../../../tests/utils';
describe('Spin', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
mountTest(Spin);
rtlTest(Spin);
@ -89,7 +97,21 @@ describe('Spin', () => {
it('right style when fullscreen', () => {
const { container } = render(<Spin fullscreen spinning />);
const element = container.querySelector<HTMLDivElement>('.ant-spin.ant-spin-fullscreen');
const element = container.querySelector<HTMLDivElement>('.ant-spin-fullscreen');
expect(element).not.toHaveStyle({ pointerEvents: 'none' });
});
it('percent support auto', () => {
const { container } = render(<Spin percent="auto" />);
act(() => {
jest.advanceTimersByTime(100000);
});
const nowPTG = Number(
container.querySelector('[role="progressbar"]')?.getAttribute('aria-valuenow'),
);
expect(nowPTG).toBeGreaterThanOrEqual(1);
});
});

View File

@ -2,19 +2,29 @@ import React from 'react';
import { Button, Spin } from 'antd';
const App: React.FC = () => {
const [spinning, setSpinning] = React.useState<boolean>(false);
const [spinning, setSpinning] = React.useState(false);
const [percent, setPercent] = React.useState(0);
const showLoader = () => {
setSpinning(true);
setTimeout(() => {
setSpinning(false);
}, 3000);
let ptg = -10;
const interval = setInterval(() => {
ptg += 5;
setPercent(ptg);
if (ptg > 120) {
clearInterval(interval);
setSpinning(false);
setPercent(0);
}
}, 100);
};
return (
<>
<Button onClick={showLoader}>Show fullscreen for 3s</Button>
<Spin spinning={spinning} fullscreen />
<Button onClick={showLoader}>Show fullscreen</Button>
<Spin spinning={spinning} percent={percent} fullscreen />
</>
);
};

View File

@ -1,24 +0,0 @@
## zh-CN
放入一个容器中。
## en-US
Spin in a container.
```css
.example {
margin: 20px 0;
margin-bottom: 20px;
padding: 30px 50px;
text-align: center;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
```
<style>
.example {
background: rgba(255,255,255,0.08);
}
</style>

View File

@ -1,10 +0,0 @@
import React from 'react';
import { Spin } from 'antd';
const App: React.FC = () => (
<div className="example">
<Spin />
</div>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
展示进度,当设置 `percent="auto"` 时会预估一个永远不会停止的进度条。
## en-US
Show the progress. When `percent="auto"` is set, an indeterminate progress will be displayed.

View File

@ -0,0 +1,40 @@
import React from 'react';
import { Space, Spin, Switch } from 'antd';
const App: React.FC = () => {
const [auto, setAuto] = React.useState(false);
const [percent, setPercent] = React.useState(-50);
React.useEffect(() => {
const timeout = setTimeout(() => {
setPercent((v) => {
const nextPercent = v + 5;
return nextPercent > 150 ? -50 : nextPercent;
});
}, 100);
return () => {
clearTimeout(timeout);
};
}, [percent]);
const mergedPercent = auto ? 'auto' : percent;
return (
<Space>
<Switch
checkedChildren="Auto"
unCheckedChildren="Auto"
checked={auto}
onChange={() => {
setAuto(!auto);
setPercent(-50);
}}
/>
<Spin percent={mergedPercent} size="small" />
<Spin percent={mergedPercent} />
<Spin percent={mergedPercent} size="large" />
</Space>
);
};
export default App;

View File

@ -18,11 +18,11 @@ When part of the page is waiting for asynchronous data or during a rendering pro
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">Basic Usage</code>
<code src="./demo/size.tsx">Size</code>
<code src="./demo/inside.tsx">Inside a container</code>
<code src="./demo/nested.tsx">Embedded mode</code>
<code src="./demo/tip.tsx">Customized description</code>
<code src="./demo/delayAndDebounce.tsx">Delay</code>
<code src="./demo/custom-indicator.tsx">Custom spinning indicator</code>
<code src="./demo/percent.tsx" version="5.18.0">Progress</code>
<code src="./demo/fullscreen.tsx">Fullscreen</code>
## API
@ -32,12 +32,13 @@ Common props ref[Common props](/docs/react/common-props)
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| delay | Specifies a delay in milliseconds for loading state (prevent flush) | number (milliseconds) | - |
| indicator | React node of the spinning indicator | ReactNode | - |
| size | The size of Spin, options: `small`, `default` and `large` | string | `default` |
| spinning | Whether Spin is visible | boolean | true |
| tip | Customize description content when Spin has children | ReactNode | - |
| wrapperClassName | The className of wrapper when Spin has children | string | - |
| fullscreen | Display a backdrop with the `Spin` component | boolean | false | 5.11.0 |
| indicator | React node of the spinning indicator | ReactNode | - | |
| percent | The progress percentage, when set to `auto`, it will be an indeterminate progress | number \| 'auto' | - | 5.18.0 |
| size | The size of Spin, options: `small`, `default` and `large` | string | `default` | |
| spinning | Whether Spin is visible | boolean | true | |
| tip | Customize description content when Spin has children | ReactNode | - | |
| wrapperClassName | The className of wrapper when Spin has children | string | - | |
### Static Method

View File

@ -1,13 +1,13 @@
import * as React from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { debounce } from 'throttle-debounce';
import { cloneElement } from '../_util/reactNode';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import Indicator from './Indicator';
import useStyle from './style/index';
import usePercent from './usePercent';
const SpinSizes = ['small', 'default', 'large'] as const;
export type SpinSize = (typeof SpinSizes)[number];
@ -38,6 +38,7 @@ export interface SpinProps {
children?: React.ReactNode;
/** Display a backdrop with the `Spin` component */
fullscreen?: boolean;
percent?: number | 'auto';
}
export type SpinType = React.FC<SpinProps> & {
@ -45,38 +46,7 @@ export type SpinType = React.FC<SpinProps> & {
};
// 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 (React.isValidElement(indicator)) {
return cloneElement(indicator, {
className: classNames(indicator.props.className, dotClassName),
});
}
if (React.isValidElement(defaultIndicator)) {
return cloneElement(defaultIndicator, {
className: classNames(defaultIndicator.props.className, dotClassName),
});
}
return (
<span className={classNames(dotClassName, `${prefixCls}-dot-spin`)}>
<i className={`${prefixCls}-dot-item`} key={1} />
<i className={`${prefixCls}-dot-item`} key={2} />
<i className={`${prefixCls}-dot-item`} key={3} />
<i className={`${prefixCls}-dot-item`} key={4} />
</span>
);
}
let defaultIndicator: React.ReactNode | undefined;
function shouldDelay(spinning?: boolean, delay?: number): boolean {
return !!spinning && !!delay && !isNaN(Number(delay));
@ -95,6 +65,8 @@ const Spin: SpinType = (props) => {
style,
children,
fullscreen = false,
indicator,
percent,
...restProps
} = props;
@ -108,6 +80,8 @@ const Spin: SpinType = (props) => {
() => customSpinning && !shouldDelay(customSpinning, delay),
);
const mergedPercent = usePercent(spinning, percent);
React.useEffect(() => {
if (customSpinning) {
const showSpinning = debounce(delay, () => {
@ -147,12 +121,10 @@ const Spin: SpinType = (props) => {
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-spinning`]: spinning,
[`${prefixCls}-show-text`]: !!tip,
[`${prefixCls}-fullscreen`]: fullscreen,
[`${prefixCls}-fullscreen-show`]: fullscreen && spinning,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
rootClassName,
!fullscreen && rootClassName,
hashId,
cssVarCls,
);
@ -161,20 +133,21 @@ const Spin: SpinType = (props) => {
[`${prefixCls}-blur`]: spinning,
});
// fix https://fb.me/react-unknown-prop
const divProps = omit(restProps, ['indicator']);
const mergedStyle: React.CSSProperties = { ...spin?.style, ...style };
const spinElement: React.ReactNode = (
<div
{...divProps}
{...restProps}
style={mergedStyle}
className={spinClassName}
aria-live="polite"
aria-busy={spinning}
>
{renderIndicator(prefixCls, props)}
<Indicator
prefixCls={prefixCls}
indicator={indicator ?? defaultIndicator}
percent={mergedPercent}
/>
{tip && (isNestedPattern || fullscreen) ? (
<div className={`${prefixCls}-text`}>{tip}</div>
) : null}
@ -184,7 +157,7 @@ const Spin: SpinType = (props) => {
if (isNestedPattern) {
return wrapCSSVar(
<div
{...divProps}
{...restProps}
className={classNames(`${prefixCls}-nested-loading`, wrapperClassName, hashId, cssVarCls)}
>
{spinning && <div key="loading">{spinElement}</div>}
@ -194,6 +167,25 @@ const Spin: SpinType = (props) => {
</div>,
);
}
if (fullscreen) {
return wrapCSSVar(
<div
className={classNames(
`${prefixCls}-fullscreen`,
{
[`${prefixCls}-fullscreen-show`]: spinning,
},
rootClassName,
hashId,
cssVarCls,
)}
>
{spinElement}
</div>,
);
}
return wrapCSSVar(spinElement);
};

View File

@ -19,11 +19,11 @@ demo:
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">基本用法</code>
<code src="./demo/size.tsx">各种大小</code>
<code src="./demo/inside.tsx">容器</code>
<code src="./demo/nested.tsx">卡片加载中</code>
<code src="./demo/tip.tsx">自定义描述文案</code>
<code src="./demo/delayAndDebounce.tsx">延迟</code>
<code src="./demo/custom-indicator.tsx">自定义指示符</code>
<code src="./demo/percent.tsx" version="5.18.0">进度</code>
<code src="./demo/fullscreen.tsx">全屏</code>
## API
@ -33,12 +33,13 @@ demo:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| delay | 延迟显示加载效果的时间(防止闪烁) | number (毫秒) | - |
| indicator | 加载指示符 | ReactNode | - |
| size | 组件大小,可选值为 `small` `default` `large` | string | `default` |
| spinning | 是否为加载中状态 | boolean | true |
| fullscreen | 显示带有 `Spin` 组件的背景 | boolean | false | 5.11.0 |
| indicator | 加载指示符 | ReactNode | - | |
| percent | 展示进度,当设置 `percent="auto"` 时会预估一个永远不会停止的进度 | number \| 'auto' | - | 5.18.0 |
| size | 组件大小,可选值为 `small` `default` `large` | string | `default` | |
| spinning | 是否为加载中状态 | boolean | true | |
| tip | 当作为包裹元素时,可以自定义描述文案 | ReactNode | - |
| wrapperClassName | 包装器的类属性 | string | - |
| fullscreen | 显示带有 `Spin` 组件的背景 | boolean | false | 5.11.0 |
### 静态方法

View File

@ -55,7 +55,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOutCirc}`,
'&-spinning': {
position: 'static',
position: 'relative',
display: 'inline-block',
opacity: 1,
},
@ -82,11 +82,14 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
opacity: 1,
visibility: 'visible',
},
[`${componentCls}-dot ${componentCls}-dot-item`]: {
backgroundColor: token.colorWhite,
},
[`${componentCls}-text`]: {
color: token.colorTextLightSolid,
[componentCls]: {
[`${componentCls}-dot-holder`]: {
color: token.colorWhite,
},
[`${componentCls}-text`]: {
color: token.colorTextLightSolid,
},
},
},
@ -183,12 +186,42 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
color: token.spinDotDefault,
},
// holder
// ------------------------------
[`${componentCls}-dot-progress`]: {
position: 'absolute',
top: 0,
insetInlineStart: 0,
},
[`${componentCls}-dot-holder`]: {
width: '1em',
height: '1em',
fontSize: token.dotSize,
display: 'inline-block',
transition: `transform ${token.motionDurationSlow} ease, opacity ${token.motionDurationSlow} ease`,
transformOrigin: '50% 50%',
lineHeight: 1,
color: token.colorPrimary,
'&-hidden': {
transform: 'scale(0.3)',
opacity: 0,
},
},
// progress
// ------------------------------
[`${componentCls}-dot-progress`]: {
position: 'absolute',
top: 0,
insetInlineStart: 0,
},
// dots
// ------------------------------
[`${componentCls}-dot`]: {
position: 'relative',
display: 'inline-block',
fontSize: token.dotSize,
width: '1em',
height: '1em',
@ -197,7 +230,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
display: 'block',
width: calc(token.dotSize).sub(calc(token.marginXXS).div(2)).div(2).equal(),
height: calc(token.dotSize).sub(calc(token.marginXXS).div(2)).div(2).equal(),
backgroundColor: token.colorPrimary,
background: 'currentColor',
borderRadius: '100%',
transform: 'scale(0.75)',
transformOrigin: '50% 50%',
@ -240,9 +273,22 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
animationIterationCount: 'infinite',
animationTimingFunction: 'linear',
},
'&-circle': {
strokeLinecap: 'round',
transition: ['stroke-dashoffset', 'stroke-dasharray', 'stroke', 'stroke-width', 'opacity']
.map((item) => `${item} ${token.motionDurationSlow} ease`)
.join(','),
fillOpacity: 0,
stroke: 'currentcolor',
},
'&-circle-bg': {
stroke: token.colorFillSecondary,
},
},
// small
[`&-sm ${componentCls}-dot`]: {
[`&-sm ${componentCls}-dot-holder`]: {
fontSize: token.dotSizeSM,
i: {
width: calc(calc(token.dotSizeSM).sub(calc(token.marginXXS).div(2)))
@ -254,7 +300,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
},
},
// large
[`&-lg ${componentCls}-dot`]: {
[`&-lg ${componentCls}-dot-holder`]: {
fontSize: token.dotSizeLG,
i: {
width: calc(calc(token.dotSizeLG).sub(token.marginXXS)).div(2).equal(),

View File

@ -0,0 +1,46 @@
import * as React from 'react';
const AUTO_INTERVAL = 200;
const STEP_BUCKETS: [limit: number, stepPtg: number][] = [
[30, 0.05],
[70, 0.03],
[96, 0.01],
];
export default function usePercent(
spinning: boolean,
percent?: number | 'auto',
): number | undefined {
const [mockPercent, setMockPercent] = React.useState(0);
const mockIntervalRef = React.useRef<ReturnType<typeof setInterval>>();
const isAuto = percent === 'auto';
React.useEffect(() => {
if (isAuto && spinning) {
setMockPercent(0);
mockIntervalRef.current = setInterval(() => {
setMockPercent((prev) => {
const restPTG = 100 - prev;
for (let i = 0; i < STEP_BUCKETS.length; i += 1) {
const [limit, stepPtg] = STEP_BUCKETS[i];
if (prev <= limit) {
return prev + restPTG * stepPtg;
}
}
return prev;
});
}, AUTO_INTERVAL);
}
return () => {
clearInterval(mockIntervalRef.current);
};
}, [isAuto, spinning]);
return isAuto ? mockPercent : percent;
}

View File

@ -541,20 +541,24 @@ exports[`Table renders empty table without emptyText when loading 1`] = `
class="ant-spin ant-spin-spinning"
>
<span
class="ant-spin-dot ant-spin-dot-spin"
class="ant-spin-dot-holder"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<span
class="ant-spin-dot ant-spin-dot-spin"
>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
<i
class="ant-spin-dot-item"
/>
</span>
</span>
</div>
</div>