mango-v4-ui/utils/numbers.ts

186 lines
5.6 KiB
TypeScript
Raw Normal View History

import Decimal from 'decimal.js'
2023-01-23 17:26:14 -08:00
export const formatNumericValue = (
value: number | string | Decimal,
decimals?: number,
2023-07-21 11:47:53 -07:00
roundUp?: boolean,
2023-01-23 17:26:14 -08:00
): string => {
const numberValue = Number(value)
let formattedValue
2023-10-17 17:25:05 -07:00
if (decimals !== undefined) {
2023-01-24 16:14:51 -08:00
formattedValue = roundUp
2023-01-24 01:15:27 -08:00
? roundValue(numberValue, decimals, true)
: roundValue(numberValue, decimals)
2023-11-02 15:52:17 -07:00
} else if (numberValue === 0) {
formattedValue = numberValue.toFixed(decimals || 0)
2023-05-22 03:19:23 -07:00
} else if (numberValue > -0.0000000001 && numberValue < 0.000000001) {
2023-11-01 20:18:00 -07:00
formattedValue = numberValue.toExponential(3)
2023-01-23 17:26:14 -08:00
} else if (Math.abs(numberValue) >= 1000) {
2023-01-24 16:14:51 -08:00
formattedValue = roundUp
2023-01-24 01:15:27 -08:00
? roundValue(numberValue, 0, true)
: roundValue(numberValue, 0)
2023-01-23 17:26:14 -08:00
} else if (Math.abs(numberValue) >= 0.1) {
2023-01-24 16:14:51 -08:00
formattedValue = roundUp
2023-01-24 01:15:27 -08:00
? roundValue(numberValue, 3, true)
: roundValue(numberValue, 3)
2023-01-23 17:26:14 -08:00
} else {
2023-01-24 16:14:51 -08:00
formattedValue = roundUp
2023-11-01 20:18:00 -07:00
? roundValue(numberValue, countLeadingZeros(numberValue) + 3, true)
: roundValue(numberValue, countLeadingZeros(numberValue) + 3)
2023-01-23 17:26:14 -08:00
}
2023-01-24 16:14:51 -08:00
return formattedValue
}
export const formatCurrencyValue = (
value: number | string | Decimal,
2023-07-21 11:47:53 -07:00
decimals?: number,
2023-01-24 16:14:51 -08:00
): string => {
const numberValue = Number(value)
let formattedValue
if (numberValue > -0.0000000001 && numberValue < 0.000000001) {
formattedValue = '$0.00'
2023-10-17 17:25:05 -07:00
} else if (decimals !== undefined) {
2023-01-24 16:14:51 -08:00
formattedValue = Intl.NumberFormat('en', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
style: 'currency',
currency: 'USD',
}).format(numberValue)
} else if (Math.abs(numberValue) >= 1000) {
formattedValue = usdFormatter0.format(numberValue)
} else if (Math.abs(numberValue) >= 0.1) {
formattedValue = usdFormatter2.format(numberValue)
} else {
formattedValue = usdFormatter3Sig.format(numberValue)
}
2023-01-23 17:26:14 -08:00
if (formattedValue === '-$0.00') return '$0.00'
return formattedValue
}
2023-01-24 01:15:27 -08:00
const roundValue = (
2023-01-23 17:26:14 -08:00
value: number | string | Decimal,
2023-01-24 01:15:27 -08:00
decimals: number,
2023-07-21 11:47:53 -07:00
roundUp?: boolean,
2023-01-23 17:26:14 -08:00
): string => {
2023-01-24 16:14:51 -08:00
const decimalValue = value instanceof Decimal ? value : new Decimal(value)
2023-01-24 01:15:27 -08:00
const roundMode = roundUp ? Decimal.ROUND_UP : Decimal.ROUND_FLOOR
2023-01-24 16:14:51 -08:00
const roundedValue = decimalValue
.toDecimalPlaces(decimals, roundMode)
.toNumber()
if (decimals === 2) return digits2.format(roundedValue)
if (decimals === 3) return digits3.format(roundedValue)
if (decimals === 4) return digits4.format(roundedValue)
if (decimals === 5) return digits5.format(roundedValue)
if (decimals === 6) return digits6.format(roundedValue)
if (decimals === 7) return digits7.format(roundedValue)
if (decimals === 8) return digits8.format(roundedValue)
if (decimals === 9) return digits9.format(roundedValue)
return roundedValue.toLocaleString(undefined, {
maximumFractionDigits: decimals,
})
2023-01-23 17:26:14 -08:00
}
2023-10-29 03:57:08 -07:00
const digits2 = new Intl.NumberFormat('en', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
2023-01-24 16:14:51 -08:00
const digits3 = new Intl.NumberFormat('en', { maximumFractionDigits: 3 })
const digits4 = new Intl.NumberFormat('en', { maximumFractionDigits: 4 })
const digits5 = new Intl.NumberFormat('en', { maximumFractionDigits: 5 })
2022-07-05 20:37:49 -07:00
const digits6 = new Intl.NumberFormat('en', { maximumFractionDigits: 6 })
2023-01-24 16:14:51 -08:00
const digits7 = new Intl.NumberFormat('en', { maximumFractionDigits: 7 })
2022-08-23 15:33:09 -07:00
const digits8 = new Intl.NumberFormat('en', { maximumFractionDigits: 8 })
2022-07-05 20:37:49 -07:00
const digits9 = new Intl.NumberFormat('en', { maximumFractionDigits: 9 })
2022-07-10 19:01:16 -07:00
export const numberFormat = new Intl.NumberFormat('en', {
maximumSignificantDigits: 7,
})
2022-07-22 14:39:02 -07:00
export const floorToDecimal = (
value: number | string | Decimal,
2023-07-21 11:47:53 -07:00
decimals: number,
): Decimal => {
2022-11-20 20:52:03 -08:00
const decimal = value instanceof Decimal ? value : new Decimal(value)
return decimal.toDecimalPlaces(decimals, Decimal.ROUND_FLOOR)
2022-08-02 11:04:00 -07:00
}
// Significant digits before the dot count against the number of decimals
// to show. When maxSignificantDecimals is 2:
// 0.012345 -> 0.012
// 0.12345 -> 0.12
// 1.12345 -> 1.1
// 12.345 -> 12.3
// 123.456 -> 123
// 1234.567 -> 1234
export const floorToDecimalSignificance = (
value: number | string | Decimal,
maxSignificantDecimals: number,
): Decimal => {
const number = Number(value)
const log = Math.log10(Math.abs(number))
const decimal = new Decimal(value)
return decimal.toDecimalPlaces(
Math.max(0, Math.floor(-log + maxSignificantDecimals - Number.EPSILON)),
)
}
2022-08-13 22:07:53 -07:00
const usdFormatter0 = Intl.NumberFormat('en', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
style: 'currency',
currency: 'USD',
})
const usdFormatter2 = Intl.NumberFormat('en', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
style: 'currency',
currency: 'USD',
})
2023-01-23 17:26:14 -08:00
const usdFormatter3Sig = Intl.NumberFormat('en', {
minimumSignificantDigits: 3,
maximumSignificantDigits: 3,
2022-08-13 22:07:53 -07:00
style: 'currency',
currency: 'USD',
})
2022-08-24 17:14:27 -07:00
export const countLeadingZeros = (x: number) => {
2023-11-02 05:01:34 -07:00
const absoluteX = Math.abs(x)
if (absoluteX % 1 == 0) {
2022-08-24 17:14:27 -07:00
return 0
} else {
2023-11-02 05:01:34 -07:00
return -1 - Math.floor(Math.log10(absoluteX % 1))
2022-08-24 17:14:27 -07:00
}
}
2022-09-13 23:24:26 -07:00
export const getDecimalCount = (value: number): number => {
if (
!isNaN(value) &&
Math.floor(value) !== value &&
value.toString().includes('.')
)
return value.toString().split('.')[1].length || 0
if (
!isNaN(value) &&
Math.floor(value) !== value &&
value.toString().includes('e')
)
return parseInt(value.toString().split('e-')[1] || '0')
return 0
}
export const numberCompacter = Intl.NumberFormat('en', {
maximumFractionDigits: 2,
notation: 'compact',
})
2023-07-16 20:41:13 -07:00
export function stringToNumber(s: string): number | undefined {
const n = parseFloat(s)
if (isNaN(n)) {
return undefined
}
return n
}