diff --git a/.changeset/cuddly-facts-pick.md b/.changeset/cuddly-facts-pick.md new file mode 100644 index 000000000..82ff45386 --- /dev/null +++ b/.changeset/cuddly-facts-pick.md @@ -0,0 +1,5 @@ +--- +'@reservoir0x/relay-kit-ui': minor +--- + +Add real-time formatting to input diff --git a/packages/ui/src/components/common/AmountInput.tsx b/packages/ui/src/components/common/AmountInput.tsx index 3413a0472..4221d97aa 100644 --- a/packages/ui/src/components/common/AmountInput.tsx +++ b/packages/ui/src/components/common/AmountInput.tsx @@ -59,15 +59,17 @@ const AmountInput: FC = ({ // The prefix will be re-applied by the `value` prop on re-render } + const cleanValue = newNumericValue.replace(/,/g, '') + // Validate and set the numeric part const regex = /^[0-9]+(\.[0-9]*)?$/ - if (newNumericValue === '.' || newNumericValue.includes(',')) { - setValue('0.') - } else if ( - regex.test(newNumericValue) || - newNumericValue === '' + if ( + cleanValue === '.' || + (newNumericValue.includes(',') && cleanValue === '') ) { - setValue(newNumericValue) + setValue('0.') + } else if (regex.test(cleanValue) || cleanValue === '') { + setValue(cleanValue) } } } diff --git a/packages/ui/src/components/widgets/SwapWidget/index.tsx b/packages/ui/src/components/widgets/SwapWidget/index.tsx index 2bac65c96..5de7ece1c 100644 --- a/packages/ui/src/components/widgets/SwapWidget/index.tsx +++ b/packages/ui/src/components/widgets/SwapWidget/index.tsx @@ -15,7 +15,8 @@ import type { LinkedWallet, Token } from '../../../types/index.js' import { formatFixedLength, formatDollar, - formatNumber + formatNumber, + formatNumberInput } from '../../../utils/numbers.js' import AmountInput from '../../common/AmountInput.js' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' @@ -801,11 +802,11 @@ const SwapWidget: FC = ({ prefixSymbol={isUsdInputMode ? '$' : undefined} value={ isUsdInputMode - ? usdInputValue + ? formatNumberInput(usdInputValue) : tradeType === 'EXACT_INPUT' - ? amountInputValue + ? formatNumberInput(amountInputValue) : amountInputValue - ? formatFixedLength(amountInputValue, 8) + ? formatNumberInput(amountInputValue) : amountInputValue } setValue={(e) => { @@ -1337,11 +1338,11 @@ const SwapWidget: FC = ({ prefixSymbol={isUsdInputMode ? '$' : undefined} value={ isUsdInputMode - ? usdOutputValue + ? formatNumberInput(usdOutputValue) : tradeType === 'EXPECTED_OUTPUT' - ? amountOutputValue + ? formatNumberInput(amountOutputValue) : amountOutputValue - ? formatFixedLength(amountOutputValue, 8) + ? formatNumberInput(amountOutputValue) : amountOutputValue } setValue={(e) => { diff --git a/packages/ui/src/utils/numbers.ts b/packages/ui/src/utils/numbers.ts index f8b117fba..909b42be2 100644 --- a/packages/ui/src/utils/numbers.ts +++ b/packages/ui/src/utils/numbers.ts @@ -240,10 +240,53 @@ function formatFixedLength(amount: string, maxLength: number) { return result } +/** + * Formats a numeric string for display with comma separators while preserving decimal precision + * @param value The input value as string + * @returns Formatted string with commas, or original value if invalid + */ +function formatNumberInput(value: string): string { + if (!value || value === '') return value + + if (value === '0' || value === '0.' || value.startsWith('0.')) return value + + // Handle decimal input case (e.g., "123.", etc.) + if (value.endsWith('.')) { + const integerPart = value.slice(0, -1) + if (integerPart === '0' || integerPart === '') return value + + const formatted = new Intl.NumberFormat('en-US', { + useGrouping: true + }).format(Number(integerPart)) + return formatted + '.' + } + + const numValue = Number(value) + if (isNaN(numValue)) return value + + if (numValue === 0) return value + + if (numValue >= 1000000000) return '>1B' + + const parts = value.split('.') + const integerPart = parts[0] + const decimalPart = parts[1] + + const formattedInteger = new Intl.NumberFormat('en-US', { + useGrouping: true + }).format(Number(integerPart)) + + // Reconstruct with decimal if present, preserving trailing zeros in decimal part + return decimalPart !== undefined + ? `${formattedInteger}.${decimalPart}` + : formattedInteger +} + export { formatDollar, formatBN, formatFixedLength, formatNumber, + formatNumberInput, truncateBalance }