@@ -19,6 +19,19 @@ function getDecimalPlaces(value: number): number {
1919 return 4 ;
2020}
2121
22+ /**
23+ * Sanitizes a decimal places value to ensure it's valid for toLocaleString.
24+ * - Coerces to a finite number (handles NaN, Infinity, -Infinity)
25+ * - Rounds to an integer
26+ * - Clamps to the valid 0-20 range for toLocaleString options
27+ */
28+ function sanitizeDecimals ( decimals : number ) : number {
29+ if ( ! Number . isFinite ( decimals ) ) {
30+ return 0 ;
31+ }
32+ return Math . min ( 20 , Math . max ( 0 , Math . round ( decimals ) ) ) ;
33+ }
34+
2235export function AnimatedNumber ( {
2336 value,
2437 duration = 0.5 ,
@@ -31,19 +44,19 @@ export function AnimatedNumber({
3144} ) {
3245 const motionValue = useMotionValue ( value ) ;
3346
34- // Determine decimal places - use provided value or auto-detect
35- const decimals = useMemo (
36- ( ) => ( decimalPlaces !== undefined ? decimalPlaces : getDecimalPlaces ( value ) ) ,
37- [ decimalPlaces , value ]
38- ) ;
47+ // Determine decimal places - use provided value or auto-detect, then sanitize
48+ const safeDecimals = useMemo ( ( ) => {
49+ const rawDecimals = decimalPlaces !== undefined ? decimalPlaces : getDecimalPlaces ( value ) ;
50+ return sanitizeDecimals ( rawDecimals ) ;
51+ } , [ decimalPlaces , value ] ) ;
3952
4053 const display = useTransform ( motionValue , ( current ) => {
41- if ( decimals === 0 ) {
54+ if ( safeDecimals === 0 ) {
4255 return Math . round ( current ) . toLocaleString ( ) ;
4356 }
4457 return current . toLocaleString ( undefined , {
45- minimumFractionDigits : decimals ,
46- maximumFractionDigits : decimals ,
58+ minimumFractionDigits : safeDecimals ,
59+ maximumFractionDigits : safeDecimals ,
4760 } ) ;
4861 } ) ;
4962
0 commit comments