@@ -3,12 +3,13 @@ import { forwardRef } from 'react';
33export interface AmountProps {
44 /**
55 * The monetary value to display
6+ * For large numbers (> 2^53), pass the value as string to maintain precision
67 * @default 0
78 * @example
89 * valueInMinorUnits=true: 1299 => "$12.99"
910 * valueInMinorUnits=false: 12.99 => "$12.99"
1011 */
11- value : number ;
12+ value : number | string ;
1213
1314 /**
1415 * ISO 4217 currency code
@@ -155,27 +156,58 @@ export const Amount = forwardRef<HTMLSpanElement, AmountProps>(
155156 } ,
156157 ref
157158 ) => {
158- const validCurrency = isValidCurrency ( currency ) ? currency : 'USD' ;
159- if ( validCurrency !== currency ) {
160- console . warn ( `Invalid currency code: ${ currency } . Falling back to USD.` ) ;
159+ try {
160+ if (
161+ typeof value === 'number' &&
162+ Math . abs ( value ) > Number . MAX_SAFE_INTEGER
163+ ) {
164+ console . warn (
165+ `Warning: The number ${ value } exceeds JavaScript's safe integer limit (${ Number . MAX_SAFE_INTEGER } ). ` +
166+ 'For large numbers, pass the value as a string to maintain precision.'
167+ ) ;
168+ }
169+
170+ const validCurrency = isValidCurrency ( currency ) ? currency : 'USD' ;
171+ if ( validCurrency !== currency ) {
172+ console . warn (
173+ `Invalid currency code: ${ currency } . Falling back to USD.`
174+ ) ;
175+ }
176+
177+ const decimals = getCurrencyDecimals ( validCurrency ) ;
178+
179+ // Handle minor units - use string manipulation for strings and Math.pow for numbers
180+ const baseValue =
181+ valueInMinorUnits && decimals > 0
182+ ? typeof value === 'string'
183+ ? value . slice ( 0 , - decimals ) + '.' + value . slice ( - decimals )
184+ : value / Math . pow ( 10 , decimals )
185+ : value ;
186+
187+ // Remove decimals if hideDecimals is true - handle string and number separately
188+ // Note: Not all numbers passed is converted to string as methods like Math.trunc
189+ // or toString cannot handle large numbers thus, we need to handle it separately (large numbers passed in value throws console warning).
190+ const finalBaseValue = hideDecimals
191+ ? typeof baseValue === 'string'
192+ ? baseValue . split ( '.' ) [ 0 ]
193+ : Math . trunc ( baseValue )
194+ : baseValue ;
195+
196+ const formattedValue = new Intl . NumberFormat ( locale , {
197+ style : 'currency' as const ,
198+ currency : validCurrency . toUpperCase ( ) ,
199+ currencyDisplay,
200+ minimumFractionDigits : hideDecimals ? 0 : minimumFractionDigits ,
201+ maximumFractionDigits : hideDecimals ? 0 : maximumFractionDigits ,
202+ useGrouping : groupDigits
203+ // @ts -ignore - Handling large numbers as string or number, so we need to pass the value as string or number.
204+ } ) . format ( finalBaseValue ) ;
205+
206+ return < span ref = { ref } > { formattedValue } </ span > ;
207+ } catch ( error ) {
208+ console . error ( 'Error formatting amount:' , error ) ;
209+ return < span ref = { ref } > { value } </ span > ;
161210 }
162-
163- const decimals = getCurrencyDecimals ( validCurrency ) ;
164- const baseValue = valueInMinorUnits
165- ? value / Math . pow ( 10 , decimals )
166- : value ;
167- const finalBaseValue = hideDecimals ? Math . trunc ( baseValue ) : baseValue ;
168-
169- const formattedValue = new Intl . NumberFormat ( locale , {
170- style : 'currency' as const ,
171- currency : validCurrency . toUpperCase ( ) ,
172- currencyDisplay,
173- minimumFractionDigits : hideDecimals ? 0 : minimumFractionDigits ,
174- maximumFractionDigits : hideDecimals ? 0 : maximumFractionDigits ,
175- useGrouping : groupDigits
176- } ) . format ( finalBaseValue ) ;
177-
178- return < span ref = { ref } > { formattedValue } </ span > ;
179211 }
180212) ;
181213
0 commit comments