import { clamp } from "../../common/utils" ; /** Returns the `decimal` number separator based on locale */ function getDecimalSeparator ( locale ) { const testNumber = 1.9 ; const testText = testNumber . toLocaleString ( locale ) ; const one = ( 1 ) . toLocaleString ( locale ) ; const nine = ( 9 ) . toLocaleString ( locale ) ; const pattern = `${ one }(.+)${ nine }` ; const result = new RegExp ( pattern ) . exec ( testText ) ; return ( result && result [ 1 ] ) || "." ; } export function toLocaleString ( num , locale = "en-US" ) { return sanitizeNumericInput ( num . toLocaleString ( locale ) , locale ) ; } export function clampValue ( value , min ? , max ? ) { const adjustedMin = min != null ? min : - Infinity ; const adjustedMax = max != null ? max : Infinity ; return clamp ( value , adjustedMin , adjustedMax ) ; } export function getValueOrEmptyValue ( value = "" ) { return value . toString ( ) ; } /** Transform the localized character (ex. "") to a javascript recognizable string number (ex. "10.99")  */ function transformLocalizedNumberToStringNumber ( character , locale ) { const charactersMap = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] . map ( value => value . toLocaleString ( locale ) ) ; const jsNumber = charactersMap . indexOf ( character ) ; if ( jsNumber !== - 1 ) { return jsNumber ; } else { return character ; } } /** Transforms the localized number (ex. "10,99") to a javascript recognizable string number (ex. "10.99")  */ export function parseStringToStringNumber ( value , locale ) { const valueAsString = "" + value ; if ( parseFloat ( valueAsString ) . toString ( ) === value . toString ( ) ) { return value . toString ( ) ; } if ( locale !== undefined ) { const decimalSeparator = getDecimalSeparator ( locale ) ; const sanitizedString = sanitizeNumericInput ( valueAsString , locale ) ; return sanitizedString . split ( "" ) . map ( character => transformLocalizedNumberToStringNumber ( character , locale ) ) . join ( "" ) . replace ( decimalSeparator , "." ) ; } return value . toString ( ) ; } /** Returns `true` if the string represents a valid numeric value, like "1e6". */ export function isValueNumeric ( value , locale ) { const stringToStringNumber = parseStringToStringNumber ( value , locale ) ; return value != null && ( stringToStringNumber as any ) - parseFloat ( stringToStringNumber ) + 1 >= 0 ; } export function isValidNumericKeyboardEvent ( e , locale ) { if ( e . key == null ) { return true ; } if ( e . ctrlKey || e . altKey || e . metaKey ) { return true ; } const isSingleCharKey = e . key . length === 1 ; if ( ! isSingleCharKey ) { return true ; } return isFloatingPointNumericCharacter ( e . key , locale ) ; } /**
 * A regex that matches a string of length 1 (i.e. a standalone character)
 * if and only if it is a floating-point number character as defined by W3C:
 * https://www.w3.org/TR/2012/WD-html-markup-20120329/datatypes.html#common.data.float
 *
 * Floating-point number characters are the only characters that can be
 * printed within a default input[type="number"]. This component should
 * behave the same way when this.props.allowNumericCharactersOnly = true.
 * See here for the input[type="number"].value spec:
 * https://www.w3.org/TR/2012/WD-html-markup-20120329/input.number.html#input.number.attrs.value
 */ function isFloatingPointNumericCharacter ( character , locale ) { if ( locale !== undefined ) { const decimalSeparator = getDecimalSeparator ( locale ) . replace ( "." , "\\." ) ; const numbers = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] . map ( value => value . toLocaleString ( locale ) ) . join ( "" ) ; const localeFloatingPointNumericCharacterRegex = new RegExp ( "^[Ee" + numbers + "\\+\\-" + decimalSeparator + "]$" ) ; return localeFloatingPointNumericCharacterRegex . test ( character ) ; } else { const floatingPointNumericCharacterRegex = /^[Ee0-9\+\-\.]$/ ; return floatingPointNumericCharacterRegex . test ( character ) ; } } /**
 * Round the value to have _up to_ the specified maximum precision.
 *
 * This differs from `toFixed(5)` in that trailing zeroes are not added on
 * more precise values, resulting in shorter strings.
 */ export function toMaxPrecision ( value , maxPrecision ) { const scaleFactor = Math . pow ( 10 , maxPrecision ) ; return Math . round ( value * scaleFactor ) / scaleFactor ; } /**
 * Convert Japanese full-width numbers, e.g. '５', to ASCII, e.g. '5'
 * This should be called before performing any other numeric string input validation.
 */ function convertFullWidthNumbersToAscii ( value ) { return value . replace ( /[\uFF10-\uFF19]/g , m => String . fromCharCode ( m . charCodeAt ( 0 ) - 0xfee0 ) ) ; } /**
 * Convert full-width (Japanese) numbers to ASCII, and strip all characters that are not valid floating-point numeric characters
 */ export function sanitizeNumericInput ( value , locale ) { const valueChars = convertFullWidthNumbersToAscii ( value ) . split ( "" ) ; const sanitizedValueChars = valueChars . filter ( valueChar => isFloatingPointNumericCharacter ( valueChar , locale ) ) ; return sanitizedValueChars . join ( "" ) ; }