import { forwardRef, useMemo } from 'react' import PropTypes from 'prop-types' import { theme } from 'antd' import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled' import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled' const validProgress = (progress) => { if (!progress || progress < 0) return 0 if (progress > 100) return 100 return progress } const getSuccessPercent = ({ success, successPercent }) => { if (success && 'progress' in success) return success.progress if (success && 'percent' in success) return success.percent return successPercent } const sortGradient = (gradients) => { const entries = Object.keys(gradients) .map((key) => ({ key: Number.parseFloat(key.replace(/%/g, '')), value: gradients[key] })) .filter(({ key }) => !Number.isNaN(key)) .sort((a, b) => a.key - b.key) return entries.map(({ key, value }) => `${value} ${key}%`).join(', ') } const resolveStrokeBackground = (strokeColor, direction = 'to right') => { if (!strokeColor) return undefined if (typeof strokeColor === 'string') { return strokeColor } if (Array.isArray(strokeColor)) { return strokeColor[0] } const { from, to, direction: gradientDirection, ...rest } = strokeColor if (Object.keys(rest).length > 0) { return `linear-gradient(${gradientDirection || direction}, ${sortGradient(rest)})` } return `linear-gradient(${gradientDirection || direction}, ${from}, ${to})` } const getBarHeight = ({ size, strokeWidth }) => { if (typeof strokeWidth === 'number') return strokeWidth if (size === 'small') return 6 if (typeof size === 'number') return size if (Array.isArray(size)) return size[1] ?? 8 if (typeof size === 'object' && size?.height != null) return size.height return 8 } const getOuterWidth = ({ size, strokeWidth }) => { if (typeof size === 'number') return size if (Array.isArray(size)) return size[0] ?? -1 if (typeof size === 'object' && size?.width != null) return size.width if (strokeWidth) return -1 return -1 } const HProgress = forwardRef(function HProgress( { className, rootClassName, style, percent = 0, success, successPercent, status, showInfo = true, strokeColor, trailColor, strokeLinecap = 'round', strokeWidth, size = 'default', format, percentPosition = {} }, ref ) { const { token } = theme.useToken() const { align: infoAlign = 'end', type: infoPosition = 'outer' } = percentPosition const resolvedPercent = validProgress(percent) const resolvedSuccessPercent = getSuccessPercent({ success, successPercent }) const successValue = resolvedSuccessPercent == null ? undefined : validProgress(resolvedSuccessPercent) const percentNumber = Number.parseInt( String(successValue ?? resolvedPercent), 10 ) const progressStatus = useMemo(() => { if (status === 'exception' || status === 'success' || status === 'active') { return status } if (percentNumber >= 100) return 'success' return status || 'normal' }, [status, percentNumber]) const barHeight = getBarHeight({ size, strokeWidth }) const outerWidth = getOuterWidth({ size, strokeWidth }) const borderRadius = strokeLinecap === 'square' || strokeLinecap === 'butt' ? 0 : barHeight const trailBackground = trailColor || token.colorFillSecondary const defaultFillColor = progressStatus === 'exception' ? token.colorError : progressStatus === 'success' ? token.colorSuccess : token.colorPrimary const fillBackground = resolveStrokeBackground(strokeColor) || defaultFillColor const successBackground = success?.strokeColor || token.colorSuccess const textFormatter = format || ((value) => `${value}%`) const progressTextValue = textFormatter( resolvedPercent, successValue ?? undefined ) const progressInfo = !showInfo ? null : ( {progressStatus === 'exception' ? ( ) : progressStatus === 'success' ? ( ) : ( progressTextValue )} ) const innerInfo = infoPosition === 'inner' && showInfo ? ( {progressTextValue} ) : null const lineInner = (
{innerInfo}
{progressStatus === 'active' && (
)} {successValue != null && (
)}
) const isOuterStart = infoPosition === 'outer' && infoAlign === 'start' const isOuterEnd = infoPosition === 'outer' && infoAlign === 'end' const isLayoutBottom = infoPosition === 'outer' && infoAlign === 'center' const content = isLayoutBottom ? (
{lineInner} {progressInfo}
) : (
{isOuterStart && progressInfo} {lineInner} {isOuterEnd && progressInfo}
) return (
{content}
) }) HProgress.propTypes = { className: PropTypes.string, rootClassName: PropTypes.string, style: PropTypes.object, percent: PropTypes.number, success: PropTypes.shape({ percent: PropTypes.number, progress: PropTypes.number, strokeColor: PropTypes.string }), successPercent: PropTypes.number, status: PropTypes.oneOf(['normal', 'exception', 'active', 'success']), showInfo: PropTypes.bool, strokeColor: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf(PropTypes.string), PropTypes.object ]), trailColor: PropTypes.string, strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']), strokeWidth: PropTypes.number, size: PropTypes.oneOfType([ PropTypes.oneOf(['default', 'small']), PropTypes.number, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), PropTypes.shape({ width: PropTypes.number, height: PropTypes.number }) ]), format: PropTypes.func, percentPosition: PropTypes.shape({ align: PropTypes.oneOf(['start', 'center', 'end']), type: PropTypes.oneOf(['inner', 'outer']) }) } export default HProgress