Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ export default () => (
<td>top</td>
<td>the gap position: can be `top`, `bottom`, `left`, or `right`. </td>
</tr>
<tr>
<td>loading</td>
<td>Boolean</td>
<td>false</td>
<td>If it is true the indeterminate progress will be enabled.</td>
</tr>
</tbody>
</table>

Expand Down
8 changes: 8 additions & 0 deletions docs/demo/loading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: loading
nav:
title: Demo
path: /demo
---

<code src="../examples/loading.tsx"></code>
13 changes: 13 additions & 0 deletions docs/examples/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';
import { Line, Circle } from 'rc-progress';

const Loading = () => {
return (
<div style={{ margin: 10, width: 200 }}>
<Circle loading percent={10} />
<Line loading percent={50} />
</div>
);
};

export default Loading;
9 changes: 8 additions & 1 deletion src/Circle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(value: T | T[]): T[] {
const mergedValue = value ?? [];
Expand All @@ -26,6 +27,7 @@ const Circle: React.FC<ProgressProps> = (props) => {
className,
strokeColor,
percent,
loading,
...restProps
} = {
...defaultProps,
Expand All @@ -51,6 +53,10 @@ const Circle: React.FC<ProgressProps> = (props) => {
>;
const isConicGradient = gradient && typeof gradient === 'object';
const mergedStrokeLinecap = isConicGradient ? 'butt' : strokeLinecap;
const { indeterminateStyleProps, indeterminateStyleAnimation } = getIndeterminateCircle({
id: mergedId,
loading,
});

const circleStyle = getCircleStyle(
perimeter,
Expand Down Expand Up @@ -94,7 +100,7 @@ const Circle: React.FC<ProgressProps> = (props) => {
radius={radius}
prefixCls={prefixCls}
gradientId={gradientId}
style={circleStyleForStack}
style={{ ...circleStyleForStack, ...indeterminateStyleProps }}
strokeLinecap={mergedStrokeLinecap}
strokeWidth={strokeWidth}
gapDegree={gapDegree}
Expand Down Expand Up @@ -180,6 +186,7 @@ const Circle: React.FC<ProgressProps> = (props) => {
/>
)}
{stepCount ? getStepStokeList() : getStokeList()}
{indeterminateStyleAnimation}
</svg>
);
};
Expand Down
16 changes: 16 additions & 0 deletions src/Line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ProgressProps> = (props) => {
const {
id,
className,
percent,
prefixCls,
Expand All @@ -15,12 +18,15 @@ const Line: React.FC<ProgressProps> = (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];
Expand All @@ -34,6 +40,14 @@ const Line: React.FC<ProgressProps> = (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 (
<svg
className={classNames(`${prefixCls}-line`, className)}
Expand Down Expand Up @@ -69,6 +83,7 @@ const Line: React.FC<ProgressProps> = (props) => {
transition:
transition ||
'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear',
...indeterminateStyleProps,
};
const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1];
stackPtg += ptg;
Expand All @@ -93,6 +108,7 @@ const Line: React.FC<ProgressProps> = (props) => {
/>
);
})}
{indeterminateStyleAnimation}
</svg>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const defaultProps: Partial<ProgressProps> = {
trailColor: '#D9D9D9',
trailWidth: 1,
gapPosition: 'bottom',
loading: false,
};

export const useTransitionDuration = (): SVGPathElement[] => {
Expand Down
1 change: 1 addition & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ProgressProps {
transition?: string;
onClick?: React.MouseEventHandler;
steps?: number | { count: number; gap: number };
loading?: boolean;
}

export type StrokeColorObject = Record<string, string | boolean>;
Expand Down
32 changes: 32 additions & 0 deletions src/utils/getIndeterminateCircle.tsx
Original file line number Diff line number Diff line change
@@ -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: (
<style>
{`@keyframes ${animationName} {
0% { transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}`}
Comment thread
SaidMarar marked this conversation as resolved.
</style>
),
};
};
38 changes: 38 additions & 0 deletions src/utils/getIndeterminateLine.tsx
Original file line number Diff line number Diff line change
@@ -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: (
<style>
{`@keyframes ${animationName} {
0% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -${strokeDashOffset};
}`}
</style>
),
};
};
6 changes: 3 additions & 3 deletions tests/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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;"
/>
</svg>
<br />
Expand All @@ -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;"
/>
</svg>
<br />
Expand All @@ -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;"
/>
</svg>
</div>
Expand Down
57 changes: 57 additions & 0 deletions tests/indeterminate.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import { Circle, Line } from '../src';
import { render, waitFor } from '@testing-library/react';

describe('(Circle | Line).indeterminate', () => {
describe('Line', () => {
it('should render indeterminate style', () => {
const { container, rerender } = render(<Line loading />);
const line: HTMLElement = container.querySelector('.rc-progress-line-path');

expect(line.style.animation).toContain('indeterminate-animate');

rerender(<Line />);

expect(line.style.animation).not.toContain('indeterminate-animate');
});

it('should render indeterminate with percent and rerennder without it', () => {
const { container, rerender } = render(<Line percent={20} loading />);
const line: HTMLElement = container.querySelector('.rc-progress-line-path');

expect(line.style.animation).toContain('indeterminate-animate');
expect(line.style.strokeDasharray).toEqual('20 100');

rerender(<Line percent={20} />);

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(<Circle loading />);
const circle: HTMLElement = container.querySelector('.rc-progress-circle-path');

expect(circle.style.animation).toContain('indeterminate-animate');

rerender(<Circle />);

expect(circle.style.animation).not.toContain('indeterminate-animate');
});

it('should rerender indeterminate with percent Circle', () => {
const { container, rerender } = render(<Circle percent={20} loading />);
const circle: HTMLElement = container.querySelector('.rc-progress-circle-path');

expect(circle.style.animation).toContain('indeterminate-animate');
expect(circle.style.transform).toEqual('rotate(0deg)');

rerender(<Circle percent={20} />);

expect(circle.style.animation).not.toContain('indeterminate-animate');
expect(circle.style.transform).not.toEqual('rotate(0deg)');
});
});
});