diff --git a/README.md b/README.md index 6e63542..f430d6d 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,12 @@ export default () => ( top the gap position, value: top, bottom, left, right. + + dot + Boolean | Object + false + within dot or not, value: true, false, { size: Number }, if the size is not a number, it will be the strokeWidth as default + diff --git a/docs/examples/gap.tsx b/docs/examples/gap.tsx index 913a1ee..b358b61 100644 --- a/docs/examples/gap.tsx +++ b/docs/examples/gap.tsx @@ -30,6 +30,7 @@ class Example extends React.Component { const circleContainerStyle = { width: '200px', height: '200px', + marginBottom: '60px' }; const { percent, colorIndex } = this.state; const color = getColor(colorIndex); @@ -40,6 +41,7 @@ class Example extends React.Component { Change State [{percent}]

+

within dot

{ strokeWidth={6} strokeLinecap="square" strokeColor={color} + dot={{ size: 10 }} />
@@ -59,9 +62,11 @@ class Example extends React.Component { trailWidth={6} strokeLinecap="round" strokeColor={[color, getColor(colorIndex + 1), getColor(colorIndex + 2)]} + dot={true} />
+

without dot

{ return (
-

Circle Progress {90}%

+

Circle Progress {90}% (within dot)

{ '0%': '#108ee9', '100%': '#87d068', }} + dot={true} />

Circle Progress {100}%

diff --git a/src/Circle.tsx b/src/Circle.tsx index b2772f6..8627a42 100644 --- a/src/Circle.tsx +++ b/src/Circle.tsx @@ -5,6 +5,10 @@ import type { ProgressProps, GapPositionType } from './interface'; let gradientSeed = 0; +function pxToNumber(px: string) { + return parseInt(px.slice(0, px.indexOf('p')), 10); +} + function stripPercentToNumber(percent: string) { return +percent.replace('%', ''); } @@ -76,6 +80,7 @@ const Circle: React.FC = ({ className, strokeColor, percent, + dot, ...restProps }) => { const gradientId = React.useMemo(() => { @@ -98,31 +103,66 @@ const Circle: React.FC = ({ const [paths] = useTransitionDuration(percentList); + const getDotList = (pathDoms) => { + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + if (dot) { + return pathDoms.map((pathDom, index) => { + const strokeDasharrayTemp = pathDom.props.style.strokeDasharray; + const strokeLength = + pxToNumber(strokeDasharrayTemp.slice(0, strokeDasharrayTemp.indexOf(' '))) + + Math.abs(pxToNumber(pathDom.props.style.strokeDashoffset)) + + strokeWidth / 2; + + path.setAttribute('d', pathDom.props.d); + const dotPoint = path.getPointAtLength(strokeLength); + + return ( + + ); + }); + } + return []; + }; + const getStokeList = () => { let stackPtg = 0; - return percentList.map((ptg, index) => { - const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; - const stroke = - Object.prototype.toString.call(color) === '[object Object]' - ? `url(#${prefixCls}-gradient-${gradientId})` - : ''; - const pathStyles = getPathStyles(stackPtg, ptg, color, strokeWidth, gapDegree, gapPosition); - stackPtg += ptg; - return ( - - ); - }); + const pathDoms = percentList + .map((ptg, index) => { + const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; + const stroke = + Object.prototype.toString.call(color) === '[object Object]' + ? `url(#${prefixCls}-gradient-${gradientId})` + : ''; + const pathStyles = getPathStyles(stackPtg, ptg, color, strokeWidth, gapDegree, gapPosition); + stackPtg += ptg; + return ( + + ); + }) + .reverse(); + return pathDoms.concat(getDotList(pathDoms)); }; return ( @@ -158,7 +198,7 @@ const Circle: React.FC = ({ fillOpacity="0" style={pathStyle} /> - {getStokeList().reverse()} + {getStokeList()} ); }; diff --git a/src/common.ts b/src/common.ts index 4b7e6fe..54df6de 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,5 +1,5 @@ import { useRef, useEffect } from 'react'; -import { ProgressProps } from './interface'; +import type { ProgressProps } from './interface'; export const defaultProps: Partial = { className: '', @@ -8,7 +8,7 @@ export const defaultProps: Partial = { strokeColor: '#2db7f5', strokeLinecap: 'round', strokeWidth: 1, - style: {}, + style: { overflow: 'visible' }, trailColor: '#D9D9D9', trailWidth: 1, }; @@ -21,7 +21,7 @@ export const useTransitionDuration = (percentList: number[]) => { const now = Date.now(); let updated = false; - Object.keys(paths).forEach(key => { + Object.keys(paths).forEach((key) => { const path = paths[key].current; if (!path) { return; diff --git a/src/interface.ts b/src/interface.ts index 8599290..f841c11 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,4 +1,4 @@ -import * as React from 'react'; +import type * as React from 'react'; export interface ProgressProps { strokeWidth?: number; @@ -13,6 +13,7 @@ export interface ProgressProps { gapDegree?: number; gapPosition?: GapPositionType; transition?: string; + dot?: DotType; } export type StrokeColorType = string | string[] | object; @@ -20,3 +21,5 @@ export type StrokeColorType = string | string[] | object; export type GapPositionType = 'top' | 'right' | 'bottom' | 'left'; export type StrokeLinecapType = 'round' | 'butt' | 'square'; + +export type DotType = boolean | { size: number }; diff --git a/tests/__snapshots__/index.spec.js.snap b/tests/__snapshots__/index.spec.js.snap index a69bdb6..616791f 100644 --- a/tests/__snapshots__/index.spec.js.snap +++ b/tests/__snapshots__/index.spec.js.snap @@ -153,13 +153,21 @@ Array [ strokeColor="#2db7f5" strokeLinecap="square" strokeWidth="6" - style={Object {}} + style={ + Object { + "overflow": "visible", + } + } trailColor="#D9D9D9" trailWidth={1} > { ); }); }); + + describe('dot', () => { + it('the size of dot must be a number', () => { + const wrapper = mount( + , + ); + expect(wrapper.find('.rc-progress-circle-dot').getDOMNode().r).toBe('6'); + + wrapper.setProps({ dot: { size: 10 } }); + expect(wrapper.find('.rc-progress-circle-dot').getDOMNode().r).toBe('10'); + + wrapper.setProps({ dot: true }); + expect(wrapper.find('.rc-progress-circle-dot').getDOMNode().r).toBe('6'); + + wrapper.setProps({ dot: [] }); + expect(wrapper.find('.rc-progress-circle-dot').getDOMNode().r).toBe('6'); + }); + }); });