diff --git a/README.md b/README.md
index 77c284d..276b894 100644
--- a/README.md
+++ b/README.md
@@ -136,6 +136,12 @@ export default () => (
top |
the gap position: can be `top`, `bottom`, `left`, or `right`. |
+
+ | loading |
+ Boolean |
+ false |
+ If it is true the indeterminate progress will be enabled. |
+
diff --git a/docs/demo/loading.md b/docs/demo/loading.md
new file mode 100644
index 0000000..73d6cae
--- /dev/null
+++ b/docs/demo/loading.md
@@ -0,0 +1,8 @@
+---
+title: loading
+nav:
+ title: Demo
+ path: /demo
+---
+
+
diff --git a/docs/examples/loading.tsx b/docs/examples/loading.tsx
new file mode 100644
index 0000000..b4948d0
--- /dev/null
+++ b/docs/examples/loading.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import { Line, Circle } from 'rc-progress';
+
+const Loading = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default Loading;
diff --git a/src/Circle/index.tsx b/src/Circle/index.tsx
index db090f7..1d4cdd5 100644
--- a/src/Circle/index.tsx
+++ b/src/Circle/index.tsx
@@ -5,6 +5,7 @@ import type { ProgressProps } from '../interface';
import useId from '../hooks/useId';
import PtgCircle from './PtgCircle';
import { VIEW_BOX_SIZE, getCircleStyle } from './util';
+import getIndeterminateCircle from '../utils/getIndeterminateCircle';
function toArray(value: T | T[]): T[] {
const mergedValue = value ?? [];
@@ -26,6 +27,7 @@ const Circle: React.FC = (props) => {
className,
strokeColor,
percent,
+ loading,
...restProps
} = {
...defaultProps,
@@ -51,6 +53,10 @@ const Circle: React.FC = (props) => {
>;
const isConicGradient = gradient && typeof gradient === 'object';
const mergedStrokeLinecap = isConicGradient ? 'butt' : strokeLinecap;
+ const { indeterminateStyleProps, indeterminateStyleAnimation } = getIndeterminateCircle({
+ id: mergedId,
+ loading,
+ });
const circleStyle = getCircleStyle(
perimeter,
@@ -94,7 +100,7 @@ const Circle: React.FC = (props) => {
radius={radius}
prefixCls={prefixCls}
gradientId={gradientId}
- style={circleStyleForStack}
+ style={{ ...circleStyleForStack, ...indeterminateStyleProps }}
strokeLinecap={mergedStrokeLinecap}
strokeWidth={strokeWidth}
gapDegree={gapDegree}
@@ -180,6 +186,7 @@ const Circle: React.FC = (props) => {
/>
)}
{stepCount ? getStepStokeList() : getStokeList()}
+ {indeterminateStyleAnimation}
);
};
diff --git a/src/Line.tsx b/src/Line.tsx
index 259aa3a..605c779 100644
--- a/src/Line.tsx
+++ b/src/Line.tsx
@@ -2,9 +2,12 @@ import * as React from 'react';
import classNames from 'classnames';
import { useTransitionDuration, defaultProps } from './common';
import type { ProgressProps } from './interface';
+import getIndeterminateLine from './utils/getIndeterminateLine';
+import useId from './hooks/useId';
const Line: React.FC = (props) => {
const {
+ id,
className,
percent,
prefixCls,
@@ -15,12 +18,15 @@ const Line: React.FC = (props) => {
trailColor,
trailWidth,
transition,
+ loading,
...restProps
} = {
...defaultProps,
...props,
};
+ const mergedId = useId(id);
+
// eslint-disable-next-line no-param-reassign
delete restProps.gapPosition;
const percentList = Array.isArray(percent) ? percent : [percent];
@@ -34,6 +40,14 @@ const Line: React.FC = (props) => {
L ${strokeLinecap === 'round' ? right : 100},${center}`;
const viewBoxString = `0 0 100 ${strokeWidth}`;
let stackPtg = 0;
+ const { indeterminateStyleProps, indeterminateStyleAnimation } = getIndeterminateLine({
+ id: mergedId,
+ loading,
+ percent: percentList[0],
+ strokeLinecap,
+ strokeWidth,
+ });
+
return (
);
};
diff --git a/src/common.ts b/src/common.ts
index 5bd0a57..5d320fa 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -10,6 +10,7 @@ export const defaultProps: Partial = {
trailColor: '#D9D9D9',
trailWidth: 1,
gapPosition: 'bottom',
+ loading: false,
};
export const useTransitionDuration = (): SVGPathElement[] => {
diff --git a/src/interface.ts b/src/interface.ts
index a5c3770..e4b9b90 100644
--- a/src/interface.ts
+++ b/src/interface.ts
@@ -14,6 +14,7 @@ export interface ProgressProps {
transition?: string;
onClick?: React.MouseEventHandler;
steps?: number | { count: number; gap: number };
+ loading?: boolean;
}
export type StrokeColorObject = Record;
diff --git a/src/utils/getIndeterminateCircle.tsx b/src/utils/getIndeterminateCircle.tsx
new file mode 100644
index 0000000..704bec0
--- /dev/null
+++ b/src/utils/getIndeterminateCircle.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+
+interface IndeterminateOption {
+ id: string;
+ loading: boolean;
+}
+
+export default ({ id, loading }: IndeterminateOption) => {
+ if (!loading) {
+ return {
+ indeterminateStyleProps: {},
+ indeterminateStyleAnimation: null,
+ };
+ }
+
+ const animationName = `${id}-indeterminate-animate`;
+
+ return {
+ indeterminateStyleProps: {
+ transform: 'rotate(0deg)',
+ animation: `${animationName} 1s linear infinite`,
+ },
+ indeterminateStyleAnimation: (
+
+ ),
+ };
+};
diff --git a/src/utils/getIndeterminateLine.tsx b/src/utils/getIndeterminateLine.tsx
new file mode 100644
index 0000000..cb5be60
--- /dev/null
+++ b/src/utils/getIndeterminateLine.tsx
@@ -0,0 +1,38 @@
+import type { StrokeLinecapType } from '@/interface';
+import React from 'react';
+
+interface IndeterminateOption {
+ id: string;
+ loading: boolean;
+ percent: number;
+ strokeLinecap: StrokeLinecapType;
+ strokeWidth: number;
+}
+
+export default (options: IndeterminateOption) => {
+ const { id, percent, strokeLinecap, strokeWidth, loading } = options;
+ if (!loading) {
+ return {
+ indeterminateStyleProps: {},
+ indeterminateStyleAnimation: null,
+ };
+ }
+ const animationName = `${id}-indeterminate-animate`;
+ const strokeDashOffset = 100 - (percent + (strokeLinecap === 'round' ? strokeWidth : 0));
+
+ return {
+ indeterminateStyleProps: {
+ strokeDasharray: `${percent} 100`,
+ animation: `${animationName} .6s linear alternate infinite`,
+ strokeDashoffset: 0,
+ },
+ indeterminateStyleAnimation: (
+
+ ),
+ };
+};
diff --git a/tests/__snapshots__/index.spec.js.snap b/tests/__snapshots__/index.spec.js.snap
index 6d96e51..fd65bcd 100644
--- a/tests/__snapshots__/index.spec.js.snap
+++ b/tests/__snapshots__/index.spec.js.snap
@@ -24,7 +24,7 @@ exports[` 1`] = `
stroke="#2db7f5"
stroke-linecap="butt"
stroke-width="1"
- style="stroke-dasharray: 20px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: .3s, .3s, .3s, .06s;"
+ style="stroke-dasharray: 20px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: 0s, 0s;"
/>
@@ -50,7 +50,7 @@ exports[` 1`] = `
stroke="#2db7f5"
stroke-linecap="round"
stroke-width="1"
- style="stroke-dasharray: 19.8px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: .3s, .3s, .3s, .06s;"
+ style="stroke-dasharray: 19.8px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: 0s, 0s;"
/>
@@ -76,7 +76,7 @@ exports[` 1`] = `
stroke="#2db7f5"
stroke-linecap="square"
stroke-width="1"
- style="stroke-dasharray: 19.9px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: .3s, .3s, .3s, .06s;"
+ style="stroke-dasharray: 19.9px, 100px; stroke-dashoffset: -0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear; transition-duration: 0s, 0s;"
/>
diff --git a/tests/indeterminate.spec.tsx b/tests/indeterminate.spec.tsx
new file mode 100644
index 0000000..380a098
--- /dev/null
+++ b/tests/indeterminate.spec.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { Circle, Line } from '../src';
+import { render } from '@testing-library/react';
+
+describe('(Circle | Line).indeterminate', () => {
+ describe('Line', () => {
+ it('should render indeterminate style', () => {
+ const { container, rerender } = render();
+ const line: HTMLElement = container.querySelector('.rc-progress-line-path');
+
+ expect(line.style.animation).toContain('indeterminate-animate');
+
+ rerender();
+
+ expect(line.style.animation).not.toContain('indeterminate-animate');
+ });
+
+ it('should render indeterminate with percent and rerennder without it', () => {
+ const { container, rerender } = render();
+ const line: HTMLElement = container.querySelector('.rc-progress-line-path');
+
+ expect(line.style.animation).toContain('indeterminate-animate');
+ expect(line.style.strokeDasharray).toEqual('20 100');
+
+ rerender();
+
+ expect(line.style.animation).not.toContain('indeterminate-animate');
+ expect(line.style.strokeDasharray).not.toEqual('20 100');
+ });
+ });
+
+ describe('Circle', () => {
+ it('should render indeterminate style', () => {
+ const { container, rerender } = render();
+ const circle: HTMLElement = container.querySelector('.rc-progress-circle-path');
+
+ expect(circle.style.animation).toContain('indeterminate-animate');
+
+ rerender();
+
+ expect(circle.style.animation).not.toContain('indeterminate-animate');
+ });
+
+ it('should rerender indeterminate with percent Circle', () => {
+ const { container, rerender } = render();
+ const circle: HTMLElement = container.querySelector('.rc-progress-circle-path');
+
+ expect(circle.style.animation).toContain('indeterminate-animate');
+ expect(circle.style.transform).toEqual('rotate(0deg)');
+
+ rerender();
+
+ expect(circle.style.animation).not.toContain('indeterminate-animate');
+ expect(circle.style.transform).not.toEqual('rotate(0deg)');
+ });
+ });
+});