|
1
|
|
|
/* |
|
2
|
|
|
* decimal.js-i18n v0.2.6 |
|
3
|
|
|
* Full internationalization support for decimal.js. |
|
4
|
|
|
* MIT License |
|
5
|
|
|
* Copyright (c) 2022 Pedro José Batista <[email protected]> |
|
6
|
|
|
* https://github.com/pjbatista/decimal.js-i18n |
|
7
|
|
|
*/ |
|
8
|
|
|
import type BaseFormatOptions from "./baseOptions"; |
|
9
|
1 |
|
import { DECIMAL_LIMIT, DEFAULT_OPTIONS, ECMA_LIMIT } from "./constants"; |
|
10
|
|
|
import type FormatLocaleMatcher from "./localeMatcher"; |
|
11
|
|
|
import FormatNotation from "./notation"; |
|
12
|
|
|
import type ResolvedFormatOptions from "./resolvedFormatOptions"; |
|
13
|
|
|
import FormatStyle from "./style"; |
|
14
|
|
|
|
|
15
|
|
|
type DigitsProperty = |
|
16
|
|
|
| "maximumFractionDigits" |
|
17
|
|
|
| "minimumFractionDigits" |
|
18
|
|
|
| "minimumIntegerDigits" |
|
19
|
|
|
| "maximumSignificantDigits" |
|
20
|
|
|
| "minimumSignificantDigits"; |
|
21
|
|
|
type DigitsPropertyCallback<T> = (property: DigitsProperty, factor: number) => T; |
|
22
|
|
|
|
|
23
|
|
|
// A generator function to be used to access or modify all digits properties |
|
24
|
|
|
// -> factor is a simple index shifting value (fractions go from 0 to 1e9-1, other from 1 to 1e9) |
|
25
|
1 |
|
const forEachDigitsPropertyGenerator = function* <T>(callback: DigitsPropertyCallback<T>) { |
|
26
|
3497 |
|
yield callback("maximumFractionDigits", -1); |
|
27
|
3497 |
|
yield callback("minimumFractionDigits", -1); |
|
28
|
3497 |
|
yield callback("minimumIntegerDigits", 0); |
|
29
|
3497 |
|
yield callback("maximumSignificantDigits", 0); |
|
30
|
3497 |
|
yield callback("minimumSignificantDigits", 0); |
|
31
|
|
|
}; |
|
32
|
|
|
|
|
33
|
|
|
// Spreads the generated values of `forEachDigitsPropertyGenerator` to an array |
|
34
|
3497 |
|
const forEachDigitsProperty = <T>(callback: DigitsPropertyCallback<T>) => [...forEachDigitsPropertyGenerator(callback)]; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* Creates and returns a new object with the extension of an existing set of options with any number of modifiers. |
|
38
|
|
|
* |
|
39
|
|
|
* @param options Object to be extended. |
|
40
|
|
|
* @param modifiers Anu number of objects containing the modifiers to the `options`. |
|
41
|
|
|
* @returns An extended format options. |
|
42
|
|
|
*/ |
|
43
|
1 |
|
export const extend = <T extends FormatOptions | Intl.NumberFormatOptions>(options: T, ...modifiers: T[]) => |
|
44
|
2328 |
|
Object.assign({ ...options }, ...modifiers) as T; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Creates and returns an object based on a `Intl.ResolvedNumberFormatOptions`, however re-expanding it to |
|
48
|
|
|
* beyond the limits of the ECMA-specification. |
|
49
|
|
|
* |
|
50
|
|
|
* If the user wants 999999999 fraction digits, they can have it (though it would be larger than 2GBs and take |
|
51
|
|
|
* quite a while to calculate at in an average PC). |
|
52
|
|
|
* |
|
53
|
|
|
* @param options Decimal format options to be merged with the ECMA resolved options. |
|
54
|
|
|
* @param ecma Object resulting from `Intl.NumberFormat.resolvedOptions()`. |
|
55
|
|
|
* @returns A resolved decimal format options. |
|
56
|
|
|
*/ |
|
57
|
1 |
|
export const resolve = <TNotation extends FormatNotation = "standard", TStyle extends FormatStyle = "decimal">( |
|
58
|
|
|
options: FormatOptions<TNotation, TStyle>, |
|
59
|
|
|
ecma: Intl.ResolvedNumberFormatOptions, |
|
60
|
|
|
) => { |
|
61
|
1164 |
|
const result = { ...ecma } as ResolvedFormatOptions< |
|
62
|
|
|
typeof ecma.notation extends FormatNotation ? typeof ecma.notation : TNotation, |
|
63
|
|
|
typeof ecma.style extends FormatStyle ? typeof ecma.style : TStyle |
|
64
|
|
|
>; |
|
65
|
|
|
|
|
66
|
1164 |
|
forEachDigitsProperty(property => { |
|
67
|
5820 |
|
if (!(property in result)) { |
|
68
|
2328 |
|
return; |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
// Gets the maximum in between both objects and the defaults |
|
72
|
3492 |
|
result[property] = Math.max(options[property] ?? 0, result[property] ?? 0, DEFAULT_OPTIONS[property]); |
|
73
|
|
|
}); |
|
74
|
|
|
|
|
75
|
|
|
// Parsed from group 1? |
|
76
|
1164 |
|
if (typeof result.maximumFractionDigits === "number") { |
|
77
|
1160 |
|
result.maximumFractionDigits = Math.max(result.minimumFractionDigits!, result.maximumFractionDigits); |
|
78
|
|
|
} |
|
79
|
|
|
// Or from group 2? |
|
80
|
|
|
else { |
|
81
|
4 |
|
result.maximumSignificantDigits = Math.max(result.minimumSignificantDigits!, result.maximumSignificantDigits!); |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
// Our custom defaults: |
|
85
|
1164 |
|
result.rounding ??= options.rounding ?? DEFAULT_OPTIONS.rounding; |
|
86
|
1164 |
|
result.trailingZeroDisplay ??= options.trailingZeroDisplay ?? DEFAULT_OPTIONS.trailingZeroDisplay; |
|
87
|
|
|
|
|
88
|
1164 |
|
return result; |
|
89
|
|
|
}; |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* Creates and returns an `Intl.NumberFormatOptions` object, copying from a {@link FormatOptions} object with |
|
93
|
|
|
* its digits limited to ECMA-specification's range. |
|
94
|
|
|
* |
|
95
|
|
|
* @template TNotation Numeric notation of formatting. |
|
96
|
|
|
* @template TStyle Numeric style of formatting. |
|
97
|
|
|
* @param options Decimal format options used as a baseline for the new object. |
|
98
|
|
|
* @returns A new `Intl.NumberFormatOptions` object. |
|
99
|
|
|
*/ |
|
100
|
1 |
|
export const toEcma = <TNotation extends FormatNotation = "standard", TStyle extends FormatStyle = "decimal">( |
|
101
|
|
|
options: FormatOptions<TNotation, TStyle>, |
|
102
|
|
|
) => { |
|
103
|
1164 |
|
const result = { ...options }; |
|
104
|
|
|
|
|
105
|
1164 |
|
forEachDigitsProperty((property, factor) => { |
|
106
|
|
|
// Check if it already exists to prevent creating unnecessary properties |
|
107
|
5820 |
|
if (property in result && Number(result[property]) > ECMA_LIMIT + factor) { |
|
108
|
16 |
|
result[property] = ECMA_LIMIT + factor; |
|
109
|
|
|
} |
|
110
|
|
|
}); |
|
111
|
|
|
|
|
112
|
1164 |
|
return result as Intl.NumberFormatOptions; |
|
113
|
|
|
}; |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* Validates whether the given {@link FormatOptions} contains acceptable values. |
|
117
|
|
|
* |
|
118
|
|
|
* It specially checks if the digits properties are within `decimal.js`' range, which is 1e9±1. |
|
119
|
|
|
* |
|
120
|
|
|
* @template TNotation Numeric notation of formatting. |
|
121
|
|
|
* @template TStyle Numeric style of formatting. |
|
122
|
|
|
* @param options Decimal format options to be validated. |
|
123
|
|
|
* @returns `true` if all properties are valid. Otherwise, an array with the invalid properties names. |
|
124
|
|
|
*/ |
|
125
|
1 |
|
export const validate = <TNotation extends FormatNotation = "standard", TStyle extends FormatStyle = "decimal">( |
|
126
|
|
|
options: FormatOptions<TNotation, TStyle>, |
|
127
|
|
|
) => { |
|
128
|
1169 |
|
const result = forEachDigitsProperty((property, factor) => { |
|
129
|
5845 |
|
if (property in options && Number(options[property]) > DECIMAL_LIMIT + factor) { |
|
130
|
6 |
|
return property; |
|
131
|
|
|
} |
|
132
|
5839 |
|
return true; |
|
133
|
|
|
}); |
|
134
|
|
|
|
|
135
|
5835 |
|
if (result.every(entry => entry === true)) { |
|
136
|
1164 |
|
return true; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
25 |
|
return result.filter(entry => entry !== true) as DigitsProperty[]; |
|
140
|
|
|
}; |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* Object used to configure a `Decimal.Format` object; the following properties fall into two groups: |
|
144
|
|
|
* |
|
145
|
|
|
* - {@link minimumIntegerDigits}, {@link minimumFractionDigits}, and {@link maximumFractionDigits} in one group; |
|
146
|
|
|
* - {@link minimumSignificantDigits} and {@link maximumSignificantDigits} in the other. |
|
147
|
|
|
* |
|
148
|
|
|
* If at least one property from the second group is defined, then the first group is ignored. |
|
149
|
|
|
* |
|
150
|
|
|
* @template TNotation Numeric notation of formatting. |
|
151
|
|
|
* @template TStyle Numeric style of formatting. |
|
152
|
|
|
*/ |
|
153
|
|
|
export interface FormatOptions<TNotation extends FormatNotation = "standard", TStyle extends FormatStyle = "decimal"> |
|
154
|
|
|
extends Partial<BaseFormatOptions<TNotation, TStyle>> { |
|
155
|
|
|
/** |
|
156
|
|
|
* The locale matching algorithm to use. Possible values are "`lookup`" and "`best fit`"; the default is |
|
157
|
|
|
* "`best fit`". For information about this option, see the [Intl page on |
|
158
|
|
|
* MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl). |
|
159
|
|
|
*/ |
|
160
|
|
|
localeMatcher?: FormatLocaleMatcher | undefined; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
export default FormatOptions; |
|
164
|
|
|
|