refactor: progress

refactor-progress
tangjinzhou 2021-07-12 16:39:54 +08:00
parent 05c0d2e489
commit a0d9372f46
16 changed files with 133 additions and 136 deletions

View File

@ -2,9 +2,9 @@ import type { Ref } from 'vue';
import { onBeforeUpdate, ref } from 'vue';
export type UseRef = [(el: any, key: string | number) => void, Ref<any>];
export type Refs = Record<string | number, any>;
export const useRef = (): UseRef => {
const refs = ref<any>({});
const refs = ref<Refs>({});
const setRef = (el: any, key: string | number) => {
refs.value[key] = el;
};
@ -13,3 +13,5 @@ export const useRef = (): UseRef => {
});
return [setRef, refs];
};
export default useRef;

View File

@ -1,5 +1,6 @@
import type { CSSProperties, ExtractPropTypes } from 'vue';
import { computed, defineComponent } from 'vue';
import { presetPrimaryColors } from '@ant-design/colors';
import { Circle as VCCircle } from '../vc-progress';
import { getSuccessPercent, validProgress } from './utils';
import { progressProps } from './props';
@ -12,39 +13,9 @@ const circleProps = {
};
export type CircleProps = Partial<ExtractPropTypes<typeof circleProps>>;
const statusColorMap = {
normal: '#108ee9',
exception: '#ff5500',
success: '#87d068',
};
function getPercentage(
percent: CircleProps['percent'],
success: CircleProps['success'],
successPercent: CircleProps['successPercent'],
) {
const ptg = validProgress(percent);
const realSuccessPercent = getSuccessPercent(success, successPercent);
if (!realSuccessPercent) {
return ptg;
}
return [
validProgress(realSuccessPercent),
validProgress(ptg - validProgress(realSuccessPercent)),
];
}
function getStrokeColor(
success: CircleProps['success'],
strokeColor: CircleProps['strokeColor'],
successPercent: CircleProps['successPercent'],
) {
const color = strokeColor || null;
const realSuccessPercent = getSuccessPercent(success, successPercent);
if (!realSuccessPercent) {
return color;
}
return [statusColorMap.success, color];
function getPercentage({ percent, success, successPercent }: CircleProps) {
const realSuccessPercent = validProgress(getSuccessPercent({ success, successPercent }));
return [realSuccessPercent, validProgress(validProgress(percent) - realSuccessPercent)];
}
export default defineComponent({
@ -77,14 +48,10 @@ export default defineComponent({
);
// using className to style stroke color
const strokeColor = computed(() =>
getStrokeColor(props.success, props.strokeColor, props.successPercent),
);
const percent = computed(() =>
getPercentage(props.percent, props.success, props.successPercent),
);
const strokeColor = computed(() => [presetPrimaryColors.green, props.strokeColor || null]);
const percent = computed(() => getPercentage(props));
const isGradient = computed(
() => Object.prototype.toString.call(strokeColor.value) === '[object Object]',
() => Object.prototype.toString.call(props.strokeColor) === '[object Object]',
);
const wrapperClassName = computed(() => ({

View File

@ -69,6 +69,7 @@ export const handleGradient = (strokeColor: ProgressGradient, directionConfig: D
export default defineComponent({
props: lineProps,
name: 'Line',
setup(props, { slots }) {
const backgroundProps = computed(() => {
const { strokeColor, direction } = props;
@ -98,7 +99,7 @@ export default defineComponent({
});
const successPercent = computed(() => {
return getSuccessPercent(props.success, props.successPercent);
return getSuccessPercent(props);
});
const successPercentStyle = computed<CSSProperties>(() => {
const { strokeWidth, size, strokeLinecap, success } = props;
@ -110,18 +111,14 @@ export default defineComponent({
};
});
const successSegment = computed(() =>
successPercent.value !== undefined ? (
<div class={`${props.prefixCls}-success-bg`} style={successPercentStyle.value} />
) : null,
);
return () => (
<>
<div class={`${props.prefixCls}-outer`}>
<div class={`${props.prefixCls}-inner`} style={trailStyle.value}>
<div class={`${props.prefixCls}-bg`} style={percentStyle.value} />
{successSegment.value}
{successPercent.value !== undefined ? (
<div class={`${props.prefixCls}-success-bg`} style={successPercentStyle.value} />
) : null}
</div>
</div>
{slots.default?.()}

View File

@ -10,6 +10,9 @@ exports[`Progress render dashboard 295 gapDegree 1`] = `
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -147.5px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke: #52C41A; stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -147.5px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
</svg><span class="ant-progress-text" title="0%">0%</span></div>
</div>
`;
@ -24,6 +27,9 @@ exports[`Progress render dashboard 296 gapDegree 1`] = `
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -148px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke: #52C41A; stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -148px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
</svg><span class="ant-progress-text" title="0%">0%</span></div>
</div>
`;
@ -38,6 +44,9 @@ exports[`Progress render dashboard zero gapDegree 1`] = `
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -0px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
<path d="M 50,50 m 0,47
a 47,47 0 1 1 0,-94
a 47,47 0 1 1 0,94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke: #52C41A; stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -0px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
</svg><span class="ant-progress-text" title="0%">0%</span></div>
</div>
`;
@ -118,6 +127,9 @@ exports[`Progress render strokeColor 1`] = `
<path d="M 50,50 m 0,-47
a 47,47 0 1 1 0,94
a 47,47 0 1 1 0,-94" stroke="" stroke-linecap="round" stroke-width="6" opacity="1" fill-opacity="0" class="ant-progress-circle-path" style="stroke: red; stroke-dasharray: 147.6548547187203px 295.3097094374406px; stroke-dashoffset: -0px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
<path d="M 50,50 m 0,-47
a 47,47 0 1 1 0,94
a 47,47 0 1 1 0,-94" stroke="" stroke-linecap="round" stroke-width="6" opacity="0" fill-opacity="0" class="ant-progress-circle-path" style="stroke: #52C41A; stroke-dasharray: 0px 295.3097094374406px; stroke-dashoffset: -0px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s;"></path>
</svg><span class="ant-progress-text" title="50%">50%</span></div>
</div>
`;

View File

@ -82,7 +82,7 @@ describe('Progress', () => {
const wrapper = mount(Progress, {
props: {
percent: 50,
successPercent: 10,
success: { percent: 10 },
format: (percent, successPercent) => `${percent} ${successPercent}`,
},
sync: false,
@ -213,17 +213,17 @@ describe('Progress', () => {
expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1);
});
// https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969
it('should show success status when status is invalid', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const wrapper = mount({
render() {
return <Progress percent={100} status="invalid" />;
},
});
expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1);
errorSpy.mockRestore();
});
// // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969
// it('should show success status when status is invalid', () => {
// const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
// const wrapper = mount({
// render() {
// return <Progress percent={100} status="invalid" />;
// },
// });
// expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1);
// errorSpy.mockRestore();
// });
it('should support steps', () => {
const wrapper = mount({

View File

@ -11,7 +11,6 @@ import Steps from './Steps';
import { getSuccessPercent, validProgress } from './utils';
import useConfigInject from '../_util/hooks/useConfigInject';
import devWarning from '../vc-util/devWarning';
import type { ProgressStatusesType } from './props';
import { progressProps, progressStatuses } from './props';
export default defineComponent({
@ -26,9 +25,14 @@ export default defineComponent({
gapDegree: 0,
strokeLinecap: 'round',
}),
slots: ['format'],
setup(props, { slots }) {
const { prefixCls, direction } = useConfigInject('progress', props);
devWarning(
props.successPercent == undefined,
'Progress',
'`successPercent` is deprecated. Please use `success.percent` instead.',
);
const classString = computed(() => {
const { type, showInfo, size } = props;
const pre = prefixCls.value;
@ -41,44 +45,44 @@ export default defineComponent({
};
});
const getPercentNumber = () => {
const percentNumber = computed(() => {
const { percent = 0 } = props;
const successPercent = getSuccessPercent(props.success, props.successPercent);
const successPercent = getSuccessPercent(props);
return parseInt(
successPercent !== undefined ? successPercent.toString() : percent.toString(),
10,
);
};
});
const getProgressStatus = () => {
const progressStatus = computed(() => {
const { status } = props;
if (progressStatuses.indexOf(status) < 0 && getPercentNumber() >= 100) {
if (progressStatuses.indexOf(status) < 0 && percentNumber.value >= 100) {
return 'success';
}
return status || 'normal';
};
});
const renderProcessInfo = (prefixCls: string, progressStatus: ProgressStatusesType) => {
const renderProcessInfo = () => {
const { showInfo, format, type, percent } = props;
const successPercent = getSuccessPercent(props.success, props.successPercent);
const successPercent = getSuccessPercent(props);
if (!showInfo) return null;
let text: VNodeChild;
const textFormatter = format || slots?.format || (percentNumber => `${percentNumber}%`);
const textFormatter = format || slots?.format || ((val: number) => `${val}%`);
const isLineType = type === 'line';
if (
format ||
slots?.format ||
(progressStatus !== 'exception' && progressStatus !== 'success')
(progressStatus.value !== 'exception' && progressStatus.value !== 'success')
) {
text = textFormatter(validProgress(percent), validProgress(successPercent));
} else if (progressStatus === 'exception') {
} else if (progressStatus.value === 'exception') {
text = isLineType ? <CloseCircleFilled /> : <CloseOutlined />;
} else if (progressStatus === 'success') {
} else if (progressStatus.value === 'success') {
text = isLineType ? <CheckCircleFilled /> : <CheckOutlined />;
}
return (
<span class={`${prefixCls}-text`} title={typeof text === 'string' ? text : undefined}>
<span class={`${prefixCls.value}-text`} title={typeof text === 'string' ? text : undefined}>
{text}
</span>
);
@ -86,14 +90,7 @@ export default defineComponent({
return () => {
const { type, steps, strokeColor } = props;
const progressStatus = getProgressStatus();
const progressInfo = renderProcessInfo(prefixCls.value, progressStatus);
devWarning(
props.successPercent == undefined,
'Progress',
'`successPercent` is deprecated. Please use `success.percent` instead.',
);
const progressInfo = renderProcessInfo();
let progress: VNodeChild;
// Render progress shape
@ -122,7 +119,7 @@ export default defineComponent({
const classNames = {
...classString.value,
[`${prefixCls.value}-status-${progressStatus}`]: true,
[`${prefixCls.value}-status-${progressStatus.value}`]: true,
};
return <div class={classNames}>{progress}</div>;

View File

@ -14,8 +14,6 @@ export type ProgressGradient = { direction?: string } & (StringGradients | FromT
export interface SuccessProps {
percent?: number;
/** @deprecated Use `percent` instead */
progress?: number;
strokeColor?: string;
}

View File

@ -189,15 +189,15 @@
@keyframes ~"@{ant-prefix}-progress-active" {
0% {
width: 0;
transform: translateX(-100%) scaleX(0);
opacity: 0.1;
}
20% {
width: 0;
transform: translateX(-100%) scaleX(0);
opacity: 0.5;
}
100% {
width: 100%;
transform: translateX(0) scaleX(1);
opacity: 0;
}
}

View File

@ -1,5 +1,4 @@
import devWarning from '../vc-util/devWarning';
import type { ProgressProps } from './props';
export function validProgress(progress: number | undefined) {
if (!progress || progress < 0) {
@ -11,10 +10,16 @@ export function validProgress(progress: number | undefined) {
return progress;
}
export function getSuccessPercent(
success?: ProgressProps['success'],
successPercent?: ProgressProps['successPercent'],
) {
export function getSuccessPercent({
success,
successPercent,
}: {
success?: {
progress?: number;
percent?: number;
};
successPercent?: number;
}) {
let percent = successPercent;
/** @deprecated Use `percent` instead */
if (success && 'progress' in success) {

View File

@ -1,6 +1,7 @@
// based on rc-progress 2.5.2
// based on rc-progress 3.1.4
import Progress, { Line, Circle } from './src';
export { Line, Circle };
import type { ProgressProps } from './src';
export { Line, Circle, ProgressProps };
export default Progress;

View File

@ -2,6 +2,7 @@ import { useTransitionDuration, defaultProps } from './common';
import { propTypes, GapPositionType } from './types';
import { computed, defineComponent, ref } from 'vue';
import initDefaultProps from '../../_util/props-util/initDefaultProps';
import { useRef } from '../../_util/hooks/useRef';
let gradientSeed = 0;
@ -73,7 +74,8 @@ export default defineComponent({
const percentList = computed(() => toArray(props.percent));
const strokeColorList = computed(() => toArray(props.strokeColor));
const paths = useTransitionDuration(percentList);
const [setRef, paths] = useRef();
useTransitionDuration(paths);
const getStokeList = () => {
const { prefixCls, strokeWidth, strokeLinecap, gapDegree, gapPosition } = props;
@ -108,7 +110,7 @@ export default defineComponent({
class: `${prefixCls}-circle-path`,
style: pathStyle,
};
return <path ref={c => (paths.value[index].value = c)} {...pathProps} />;
return <path ref={c => setRef(c, index)} {...pathProps} />;
});
};

View File

@ -1,3 +1,4 @@
import { useRef } from '../../_util/hooks/useRef';
import { computed, defineComponent } from 'vue';
import initDefaultProps from '../../_util/props-util/initDefaultProps';
import { useTransitionDuration, defaultProps } from './common';
@ -11,13 +12,54 @@ export default defineComponent({
const { percent } = props;
return Array.isArray(percent) ? percent : [percent];
});
const percentListProps = computed(() => {
const { prefixCls, strokeLinecap, strokeWidth, transition } = props;
let stackPtg = 0;
return percentList.value.map((ptg, index) => {
let dashPercent = 1;
switch (strokeLinecap) {
case 'round':
dashPercent = 1 - strokeWidth / 100;
break;
case 'square':
dashPercent = 1 - strokeWidth / 2 / 100;
break;
default:
dashPercent = 1;
break;
}
const pathStyle = {
strokeDasharray: `${ptg * dashPercent}px, 100px`,
strokeDashoffset: `-${stackPtg}px`,
transition:
transition ||
'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear',
};
const color =
strokeColorList.value[index] || strokeColorList.value[strokeColorList.value.length - 1];
stackPtg += ptg;
const pathProps = {
key: index,
d: pathString.value,
'stroke-linecap': strokeLinecap,
stroke: color as string,
'stroke-width': strokeWidth,
'fill-opacity': '0',
class: `${prefixCls}-line-path`,
style: pathStyle,
};
return pathProps;
});
});
const strokeColorList = computed(() => {
const { strokeColor } = props;
return Array.isArray(strokeColor) ? strokeColor : [strokeColor];
});
const paths = useTransitionDuration(percentList);
const [setRef, paths] = useRef();
useTransitionDuration(paths);
const center = computed(() => props.strokeWidth / 2);
const right = computed(() => 100 - props.strokeWidth / 2);
@ -52,8 +94,6 @@ export default defineComponent({
delete restProps.gapPosition;
let stackPtg = 0;
return (
<svg
class={`${prefixCls}-line`}
@ -62,32 +102,8 @@ export default defineComponent({
{...restProps}
>
<path {...pathFirst.value} />
{percentList.value.map((ptg, index) => {
const pathStyle = {
strokeDasharray: `${ptg}px, 100px`,
strokeDashoffset: `-${stackPtg}px`,
transition:
transition ||
'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear',
};
const color =
strokeColorList.value[index] ||
strokeColorList.value[strokeColorList.value.length - 1];
stackPtg += ptg;
const pathProps = {
key: index,
d: pathString.value,
'stroke-linecap': strokeLinecap,
stroke: color as string,
'stroke-width': strokeWidth,
'fill-opacity': '0',
class: `${prefixCls}-line-path`,
style: pathStyle,
};
return <path ref={c => (paths.value[index].value = c)} {...pathProps} />;
{percentListProps.value.map((pathProps, index) => {
return <path ref={c => setRef(c, index)} {...pathProps} />;
})}
</svg>
);

View File

@ -1,5 +1,5 @@
import type { Ref } from 'vue';
import { ref, onUpdated, computed } from 'vue';
import type { Refs } from '../../_util/hooks/useRef';
import { ref, onUpdated } from 'vue';
import type { ProgressProps } from './types';
export const defaultProps: Partial<ProgressProps> = {
@ -12,8 +12,7 @@ export const defaultProps: Partial<ProgressProps> = {
trailWidth: 1,
};
export const useTransitionDuration = (percentList: Ref<number[]>) => {
const paths = computed(() => percentList.value.map(() => ref()));
export const useTransitionDuration = (paths: Refs) => {
const prevTimeStamp = ref(null);
onUpdated(() => {
@ -21,7 +20,7 @@ export const useTransitionDuration = (percentList: Ref<number[]>) => {
let updated = false;
Object.keys(paths.value).forEach(key => {
const path = paths.value[key].value;
const path = paths.value[key];
if (!path) {
return;
}

View File

@ -203,6 +203,7 @@
"xhr-mock": "^2.5.1"
},
"dependencies": {
"@ant-design/colors": "^5.1.1",
"@ant-design/icons-vue": "^6.0.0",
"@babel/runtime": "^7.10.5",
"@simonwep/pickr": "~1.8.0",

2
v2-doc

@ -1 +1 @@
Subproject commit d965edbb9045771095acf91489145e13c975cb51
Subproject commit d2c096476ee3e8d27fbb9e1cb7933887501b1fab