Skip to content

refactor(progress): use composition API #4355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
112 changes: 112 additions & 0 deletions components/progress/Circle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { CSSProperties, ExtractPropTypes } from 'vue';
import { computed, defineComponent } from 'vue';
import { Circle as VCCircle } from '../vc-progress';
import { getSuccessPercent, validProgress } from './utils';
import { progressProps } from './props';
import PropTypes from '../_util/vue-types';

const circleProps = {
...progressProps,
prefixCls: PropTypes.string,
// progressStatus: PropTypes.string,
};
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];
}

export default defineComponent({
props: progressProps,
inheritAttrs: false,
setup(props, { slots }) {
const gapDeg = computed(() => {
// Support gapDeg = 0 when type = 'dashboard'
if (props.gapDegree || props.gapDegree === 0) {
return props.gapDegree;
}
if (props.type === 'dashboard') {
return 75;
}
return undefined;
});

const circleStyle = computed<CSSProperties>(() => {
const circleSize = props.width || 120;
return {
width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize,
fontSize: `${circleSize * 0.15 + 6}px`,
};
});

const circleWidth = computed(() => props.strokeWidth || 6);
const gapPos = computed(
() => props.gapPosition || (props.type === 'dashboard' && 'bottom') || 'top',
);

// 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 isGradient = computed(
() => Object.prototype.toString.call(strokeColor.value) === '[object Object]',
);

const wrapperClassName = computed(() => ({
[`${props.prefixCls}-inner`]: true,
[`${props.prefixCls}-circle-gradient`]: isGradient.value,
}));

return () => (
<div class={wrapperClassName.value} style={circleStyle.value}>
<VCCircle
percent={percent.value}
strokeWidth={circleWidth.value}
trailWidth={circleWidth.value}
strokeColor={strokeColor.value}
strokeLinecap={props.strokeLinecap}
trailColor={props.trailColor}
prefixCls={props.prefixCls}
gapDegree={gapDeg.value}
gapPosition={gapPos.value}
/>
{slots.default?.()}
</div>
);
},
});
131 changes: 131 additions & 0 deletions components/progress/Line.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
import { computed, defineComponent } from 'vue';
import type { Direction } from '../config-provider';
import PropTypes from '../_util/vue-types';
import type { StringGradients, ProgressGradient } from './props';
import { progressProps } from './props';
import { getSuccessPercent, validProgress } from './utils';

const lineProps = {
...progressProps,
prefixCls: PropTypes.string,
direction: {
type: String as PropType<Direction>,
},
};

export type LineProps = Partial<ExtractPropTypes<typeof lineProps>>;

/**
* {
* '0%': '#afc163',
* '75%': '#009900',
* '50%': 'green', ====> '#afc163 0%, #66FF00 25%, #00CC00 50%, #009900 75%, #ffffff 100%'
* '25%': '#66FF00',
* '100%': '#ffffff'
* }
*/
export const sortGradient = (gradients: StringGradients) => {
let tempArr = [];
Object.keys(gradients).forEach(key => {
const formattedKey = parseFloat(key.replace(/%/g, ''));
if (!isNaN(formattedKey)) {
tempArr.push({
key: formattedKey,
value: gradients[key],
});
}
});
tempArr = tempArr.sort((a, b) => a.key - b.key);
return tempArr.map(({ key, value }) => `${value} ${key}%`).join(', ');
};

/**
* Then this man came to realize the truth: Besides six pence, there is the moon. Besides bread and
* butter, there is the bug. And... Besides women, there is the code.
*
* @example
* {
* "0%": "#afc163",
* "25%": "#66FF00",
* "50%": "#00CC00", // ====> linear-gradient(to right, #afc163 0%, #66FF00 25%,
* "75%": "#009900", // #00CC00 50%, #009900 75%, #ffffff 100%)
* "100%": "#ffffff"
* }
*/
export const handleGradient = (strokeColor: ProgressGradient, directionConfig: Direction) => {
const {
from = '#1890ff',
to = '#1890ff',
direction = directionConfig === 'rtl' ? 'to left' : 'to right',
...rest
} = strokeColor;
if (Object.keys(rest).length !== 0) {
const sortedGradients = sortGradient(rest as StringGradients);
return { backgroundImage: `linear-gradient(${direction}, ${sortedGradients})` };
}
return { backgroundImage: `linear-gradient(${direction}, ${from}, ${to})` };
};

export default defineComponent({
props: lineProps,
setup(props, { slots }) {
const backgroundProps = computed(() => {
const { strokeColor, direction } = props;
return strokeColor && typeof strokeColor !== 'string'
? handleGradient(strokeColor, direction)
: {
background: strokeColor,
};
});

const trailStyle = computed(() =>
props.trailColor
? {
backgroundColor: props.trailColor,
}
: undefined,
);

const percentStyle = computed<CSSProperties>(() => {
const { percent, strokeWidth, strokeLinecap, size } = props;
return {
width: `${validProgress(percent)}%`,
height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`,
borderRadius: strokeLinecap === 'square' ? 0 : '',
...(backgroundProps.value as any),
};
});

const successPercent = computed(() => {
return getSuccessPercent(props.success, props.successPercent);
});
const successPercentStyle = computed<CSSProperties>(() => {
const { strokeWidth, size, strokeLinecap, success } = props;
return {
width: `${validProgress(successPercent.value)}%`,
height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`,
borderRadius: strokeLinecap === 'square' ? 0 : '',
backgroundColor: success?.strokeColor,
};
});

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}
</div>
</div>
{slots.default?.()}
</>
);
},
});
56 changes: 56 additions & 0 deletions components/progress/Steps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { computed, ExtractPropTypes, PropType, VNodeChild } from 'vue';
import { defineComponent } from 'vue';
import PropTypes from '../_util/vue-types';
import type { ProgressSize } from './props';
import { progressProps } from './props';

const stepsProps = {
...progressProps,
steps: PropTypes.number,
size: {
type: String as PropType<ProgressSize>,
},
strokeColor: PropTypes.string,
trailColor: PropTypes.string,
};

export type StepsProps = Partial<ExtractPropTypes<typeof stepsProps>>;

export default defineComponent({
props: stepsProps,
setup(props, { slots }) {
const current = computed(() => Math.round(props.steps * ((props.percent || 0) / 100)));
const stepWidth = computed(() => (props.size === 'small' ? 2 : 14));

const styledSteps = computed(() => {
const { steps, strokeWidth = 8, strokeColor, trailColor, prefixCls } = props;

const temp: VNodeChild[] = [];
for (let i = 0; i < steps; i += 1) {
const cls = {
[`${prefixCls}-steps-item`]: true,
[`${prefixCls}-steps-item-active`]: i <= current.value - 1,
};
temp.push(
<div
key={i}
class={cls}
style={{
backgroundColor: i <= current.value - 1 ? strokeColor : trailColor,
width: `${stepWidth.value}px`,
height: `${strokeWidth}px`,
}}
/>,
);
}
return temp;
});

return () => (
<div class={`${props.prefixCls}-steps-outer`}>
{styledSteps.value}
{slots.default?.()}
</div>
);
},
});
Loading