-
Notifications
You must be signed in to change notification settings - Fork 1
/
number-styling.ts
101 lines (91 loc) · 3.83 KB
/
number-styling.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
export const isNumber = (number: string | number): boolean => {
if (typeof number === 'number') return true
if (number === '') return false
if (isNaN(parseFloat(number))) return false
return Boolean(number.match(/^-?[\d,]*\.?[\d]*$/u))
}
export const addThousandsSeparators = (input: string | number): string => {
if (!isNumber(input)) return ''
let number = String(input)
number = number.replace(/,/giu, '')
const parts = number.split('.')
// eslint-disable-next-line unicorn/no-unsafe-regex -- laziness
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/gu, ',')
return parts.join('.')
}
export const truncateDecimals = (input: string | number, decimals: number): string => {
if (!isNumber(input)) return ''
const parts = String(input).split('.')
if (!parts[1]) return String(input)
parts[1] = parts[1].slice(0, Math.max(0, decimals))
let number = parts.join('.')
number = number.replace(/[.]+$/u, '')
return number
}
export const roundDecimals = (input: string | number, decimals: number): string => {
if (!isNumber(input)) return ''
const inputNumber = typeof input === 'string' ? input : String(input)
if (inputNumber.startsWith('-')) return truncateDecimals(input, decimals)
const parts = inputNumber.split('.')
if (!parts[1]) return String(input)
const roundingDigit = parts[1].slice(decimals, decimals + 1)
parts[1] = parts[1].slice(0, Math.max(0, decimals))
if (parseInt(roundingDigit) >= 5) {
if (decimals === 0) {
const lastDigit = parseInt(parts[0].slice(-1))
parts[0] = parts[0].slice(0, -1) + (lastDigit + 1)
} else {
const lastDigit = parseInt(parts[1].slice(-1))
parts[1] = parts[1].slice(0, -1) + (lastDigit + 1)
}
}
let number = parts.join('.')
number = number.replace(/[.]+$/u, '')
return number
}
export const fixedDecimals = (input: string | number, decimals: number): string => {
const number = truncateDecimals(input, decimals)
if (number === '') return number
const parts = number.split('.')
if (decimals === 0) return parts[0]
let finalDecimals = parts[1] ?? ''
for (let index = finalDecimals.length; index < decimals; index++) finalDecimals += '0'
return `${parts[0]}.${finalDecimals}`
}
export const formatNumber = (input: string | number): string => {
if (!isNumber(input)) return ''
let number = String(input)
number = number.replace(/,/giu, '')
number = number.replace(/^0+/u, '')
if (number === '') number = '0'
number = number.replace(/^[.]+/u, '0.')
number = number.replace(/[.]+$/u, '')
number = addThousandsSeparators(number)
if (number === '-0') return '0'
return number
}
export const toPercentage = (input: string | number, decimals?: number): string => {
if (!isNumber(input)) return ''
let number = typeof input === 'string' ? parseFloat(input) : input
number *= 100
let percentage = formatNumber(number)
if (typeof decimals !== 'undefined') percentage = roundDecimals(percentage, decimals)
return `${percentage}%`
}
export const stringToNumber = (input: string): number => {
if (!isNumber(input)) return NaN
const number = input.replace(/,/gu, '')
return parseFloat(number)
}
export const padZeros = (number: string | number, digits: number): string => {
if (!isNumber(number)) return ''
const stringNumber = String(number)
const parts = stringNumber.split('.')
const isNegative = stringNumber.startsWith('-')
const currentDigitsCount = parts[0].length - (isNegative ? 1 : 0)
if (digits <= currentDigitsCount) return stringNumber
let padding = ''
for (let index = 0; index < digits - currentDigitsCount; index++) padding += '0'
parts[0] = `${isNegative ? '-' : ''}${padding}${parts[0].slice(isNegative ? 1 : 0)}`
return parts.join('.')
}