mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-02-20 08:42:45 +01:00
fix(suite-common): allow long decimals with localizeNumber
This commit is contained in:
@@ -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);
|
||||
|
||||
9
packages/utils/src/getLocaleSeparators.ts
Normal file
9
packages/utils/src/getLocaleSeparators.ts
Normal 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 };
|
||||
};
|
||||
@@ -29,3 +29,4 @@ export * from './truncateMiddle';
|
||||
export * from './topologicalSort';
|
||||
export * as versionUtils from './versionUtils';
|
||||
export * as xssFilters from './xssFilters';
|
||||
export * from './getLocaleSeparators';
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user