fix(suite-common): allow long decimals with localizeNumber

This commit is contained in:
Jan Komarek
2023-05-05 19:54:38 +02:00
committed by Jan Komárek
parent 30086ce8a8
commit 63bc156f28
4 changed files with 44 additions and 33 deletions

View File

@@ -5,22 +5,14 @@ import {
useController,
UseControllerOptions,
} from 'react-hook-form';
import BigNumber from 'bignumber.js';
import { Input, InputProps } from '@trezor/components';
import { TypedValidationRules } from '@suite-common/wallet-types';
import { localizeNumber } from '@suite-common/wallet-utils';
import { useSelector } from '@trezor/suite/src/hooks/suite';
import { Locale } from '@suite-config/languages';
import BigNumber from 'bignumber.js';
const getLocaleSeparators = (locale: Locale) => {
const numberFormat = new Intl.NumberFormat(locale);
const parts = numberFormat.formatToParts(10000.1);
const decimalSeparator = parts.find(({ type }) => type === 'decimal')?.value as string;
const thousandsSeparator = parts.find(({ type }) => type === 'group')?.value as string;
return { decimalSeparator, thousandsSeparator };
};
import { useSelector } from '@trezor/suite/src/hooks/suite';
import { getLocaleSeparators } from '@trezor/utils';
const isValidDecimalString = (value: string) => /^([^.]*)\.[^.]+$/.test(value);
const hasLeadingZeroes = (value: string) => /^0+(\d+\.\d*|\d+)$/.test(value);

View File

@@ -0,0 +1,9 @@
export const getLocaleSeparators = (locale: string) => {
const numberFormat = new Intl.NumberFormat(locale);
const parts = numberFormat.formatToParts(10000.1);
const decimalSeparator = parts.find(({ type }) => type === 'decimal')?.value as string;
const thousandsSeparator = parts.find(({ type }) => type === 'group')?.value as string;
return { decimalSeparator, thousandsSeparator };
};

View File

@@ -29,3 +29,4 @@ export * from './truncateMiddle';
export * from './topologicalSort';
export * as versionUtils from './versionUtils';
export * as xssFilters from './xssFilters';
export * from './getLocaleSeparators';

View File

@@ -1,12 +1,14 @@
import BigNumber from 'bignumber.js';
import { getLocaleSeparators } from '@trezor/utils';
export const localizeNumber = (
value: number | string | BigNumber,
locale = 'en',
minDecimals = 0,
maxDecimals = 20,
): string => {
if (maxDecimals < minDecimals) {
maxDecimals?: number,
) => {
if (maxDecimals !== undefined && maxDecimals < minDecimals) {
throw Error(
`maxDecimals (${maxDecimals}) cannot be lower than minDecimals (${minDecimals})`,
);
@@ -18,25 +20,32 @@ export const localizeNumber = (
return '';
}
const amountRoundedDown = amount.toFixed(0, BigNumber.ROUND_DOWN);
const { decimalSeparator, thousandsSeparator } = getLocaleSeparators(locale);
const wholeNumber = BigInt(amountRoundedDown);
const formattedWholeNumber = wholeNumber.toLocaleString(locale);
const getDecimalsLength = () => {
const originalDecimalsLegth = amount.decimalPlaces() ?? 0;
if (originalDecimalsLegth < minDecimals) {
return minDecimals;
}
if (maxDecimals !== undefined && originalDecimalsLegth > maxDecimals) {
// Remove trailing zeroes after formatting:
return new BigNumber(amount.toFixed(maxDecimals)).decimalPlaces() ?? maxDecimals;
}
return originalDecimalsLegth;
};
const decimalNumber = amount.minus(amountRoundedDown).toNumber();
const formattedDecimalNumber = Intl.NumberFormat(locale, {
maximumFractionDigits: maxDecimals,
minimumFractionDigits: minDecimals,
})
.format(decimalNumber)
.slice(amount.isNegative() ? 2 : 1); // remove leading zero and minus sign i.e. -0.123 -> .123, 0.123 -> .123
// In some locales (e.g. Spanish), thousands separator may not be used when the number has four digits.
// Respect the way Intl formats the numbers.
const groupSize =
amount.lt(10000) &&
amount.gte(1000) &&
!Intl.NumberFormat(locale).format(amount.toNumber()).includes(thousandsSeparator)
? 4
: 3;
const isDecimalNumber =
minDecimals > 0 || new BigNumber(formattedDecimalNumber).decimalPlaces() !== 0;
const hasNegativeZero = amount.isNegative() && wholeNumber === BigInt(0);
return `${hasNegativeZero ? '-' : ''}${formattedWholeNumber}${
isDecimalNumber ? formattedDecimalNumber : ''
}`;
return amount.toFormat(getDecimalsLength(), {
decimalSeparator,
groupSize,
groupSeparator: thousandsSeparator,
});
};