@@ -77,24 +77,81 @@ export function DoughnutMetrics({
7777 className,
7878 size = 'medium' ,
7979} : DoughnutProps ) {
80- let filledDegrees = fillWidth * 3.6 ;
81- let doughnutFillVar = 'var(--doughnut-color)' ;
82- let doughnutBackdropVar = 'var(--doughnut-backdrop-color)' ;
83-
84- if ( filledDegrees > 360 ) {
85- filledDegrees -= 360 ;
86- doughnutBackdropVar = 'var(--doughnut-color)' ;
87- doughnutFillVar = 'var(--doughnut-overlap-color)' ;
80+ // Size configurations
81+ const sizeConfig = {
82+ small : { width : 65 , strokeWidth : 12 } ,
83+ medium : { width : 100 , strokeWidth : 16 } ,
84+ large : { width : 130 , strokeWidth : 20 } ,
85+ } ;
86+
87+ const config = sizeConfig [ size ] ;
88+ const radius = ( config . width - config . strokeWidth ) / 2 ;
89+ const circumference = 2 * Math . PI * radius ;
90+
91+ // Calculate stroke dash for filled portion
92+ let strokeDasharray : string ;
93+ // Start from bottom (270 degrees = 0.75 of circumference)
94+ const strokeDashoffset = circumference * 0.75 ;
95+
96+ if ( fillWidth <= 100 ) {
97+ const filledLength = ( fillWidth / 100 ) * circumference ;
98+ // Use negative dash to go counter-clockwise
99+ strokeDasharray = `0 ${ circumference - filledLength } ${ filledLength } 0` ;
100+ } else {
101+ // For values over 100%, we need to show overlap
102+ strokeDasharray = `0 0 ${ circumference } 0` ;
103+ // We'll use a second circle for the overlap
88104 }
89105
90- const doughnutStyle : React . CSSProperties = {
91- background : `conic-gradient(${ doughnutFillVar } 0deg ${ filledDegrees } deg, ${ doughnutBackdropVar } ${ filledDegrees } deg 360deg)` ,
92- } ;
106+ const needsOverlapCircle = fillWidth > 100 ;
107+ const overlapDasharray = needsOverlapCircle
108+ ? `0 ${ circumference - ( ( fillWidth - 100 ) / 100 ) * circumference } ${ ( ( fillWidth - 100 ) / 100 ) * circumference } 0`
109+ : '0 0' ;
93110
94111 return (
95112 < SizeContext . Provider value = { size } >
96113 < div className = { b ( { status} , className ) } style = { { position : 'relative' } } >
97- < div style = { doughnutStyle } className = { b ( 'doughnut' , { size} ) } > </ div >
114+ < svg width = { config . width } height = { config . width } className = { b ( 'doughnut' , { size} ) } >
115+ { /* Background circle */ }
116+ < circle
117+ cx = { config . width / 2 }
118+ cy = { config . width / 2 }
119+ r = { radius }
120+ fill = "none"
121+ stroke = "var(--doughnut-backdrop-color)"
122+ strokeWidth = { config . strokeWidth }
123+ />
124+
125+ { /* Progress circle */ }
126+ < circle
127+ cx = { config . width / 2 }
128+ cy = { config . width / 2 }
129+ r = { radius }
130+ fill = "none"
131+ stroke = "var(--doughnut-color)"
132+ strokeWidth = { config . strokeWidth }
133+ strokeDasharray = { strokeDasharray }
134+ strokeDashoffset = { strokeDashoffset }
135+ strokeLinecap = "butt"
136+ className = { b ( 'progress-circle' ) }
137+ />
138+
139+ { /* Overlap circle for values > 100% */ }
140+ { needsOverlapCircle && (
141+ < circle
142+ cx = { config . width / 2 }
143+ cy = { config . width / 2 }
144+ r = { radius }
145+ fill = "none"
146+ stroke = "var(--doughnut-overlap-color)"
147+ strokeWidth = { config . strokeWidth }
148+ strokeDasharray = { overlapDasharray }
149+ strokeDashoffset = { strokeDashoffset }
150+ strokeLinecap = "butt"
151+ className = { b ( 'overlap-circle' ) }
152+ />
153+ ) }
154+ </ svg >
98155 < div className = { b ( 'text-wrapper' ) } > { children } </ div >
99156 </ div >
100157 </ SizeContext . Provider >
0 commit comments