@canutin/svelte-currency-input 0.13.0 → 1.0.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +231 -110
  2. package/dist/currency-input.svelte +361 -0
  3. package/dist/currency-input.svelte.d.ts +29 -0
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.js +4 -2
  6. package/dist/types.d.ts +36 -9
  7. package/dist/utils/addSeparators.d.ts +1 -0
  8. package/dist/utils/addSeparators.js +3 -0
  9. package/dist/utils/cleanValue.d.ts +2 -0
  10. package/dist/utils/cleanValue.js +43 -0
  11. package/dist/utils/escapeRegExp.d.ts +1 -0
  12. package/dist/utils/escapeRegExp.js +3 -0
  13. package/dist/utils/fixedDecimalValue.d.ts +1 -0
  14. package/dist/utils/fixedDecimalValue.js +25 -0
  15. package/dist/utils/formatValue.d.ts +2 -0
  16. package/dist/utils/formatValue.js +101 -0
  17. package/dist/utils/getLocaleConfig.d.ts +2 -0
  18. package/dist/utils/getLocaleConfig.js +33 -0
  19. package/dist/utils/getSuffix.d.ts +6 -0
  20. package/dist/utils/getSuffix.js +6 -0
  21. package/dist/utils/index.d.ts +13 -0
  22. package/dist/utils/index.js +13 -0
  23. package/dist/utils/isNumber.d.ts +1 -0
  24. package/dist/utils/isNumber.js +1 -0
  25. package/dist/utils/padTrimValue.d.ts +1 -0
  26. package/dist/utils/padTrimValue.js +26 -0
  27. package/dist/utils/parseAbbrValue.d.ts +2 -0
  28. package/dist/utils/parseAbbrValue.js +23 -0
  29. package/dist/utils/removeInvalidChars.d.ts +1 -0
  30. package/dist/utils/removeInvalidChars.js +6 -0
  31. package/dist/utils/removeSeparators.d.ts +1 -0
  32. package/dist/utils/removeSeparators.js +5 -0
  33. package/dist/utils/repositionCursor.d.ts +12 -0
  34. package/dist/utils/repositionCursor.js +18 -0
  35. package/package.json +77 -58
  36. package/dist/CurrencyInput.svelte +0 -181
  37. package/dist/CurrencyInput.svelte.d.ts +0 -30
  38. package/dist/constants.d.ts +0 -11
  39. package/dist/constants.js +0 -11
@@ -0,0 +1,361 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import type {
4
+ IntlConfig,
5
+ CurrencyInputValues,
6
+ CleanValueOptions,
7
+ FormatValueOptions
8
+ } from './types';
9
+ import {
10
+ isNumber,
11
+ cleanValue,
12
+ fixedDecimalValue,
13
+ formatValue,
14
+ getLocaleConfig,
15
+ padTrimValue,
16
+ getSuffix,
17
+ repositionCursor
18
+ } from './utils/index';
19
+
20
+ type Props = Omit<HTMLInputAttributes, 'value'> & {
21
+ value?: string;
22
+ intlConfig?: IntlConfig;
23
+ prefix?: string;
24
+ suffix?: string;
25
+ decimalSeparator?: string;
26
+ groupSeparator?: string;
27
+ allowDecimals?: boolean;
28
+ decimalsLimit?: number;
29
+ decimalScale?: number;
30
+ fixedDecimalLength?: number;
31
+ allowNegativeValue?: boolean;
32
+ min?: number;
33
+ max?: number;
34
+ maxLength?: number;
35
+ step?: number;
36
+ disableGroupSeparators?: boolean;
37
+ disableAbbreviations?: boolean;
38
+ formatValueOnBlur?: boolean;
39
+ transformRawValue?: (rawValue: string) => string;
40
+ oninputvalue?: (values: CurrencyInputValues) => void;
41
+ onchangevalue?: (values: CurrencyInputValues) => void;
42
+ ref?: HTMLInputElement | null;
43
+ };
44
+
45
+ let {
46
+ value = $bindable(''),
47
+ intlConfig,
48
+ prefix: userPrefix,
49
+ suffix: userSuffix,
50
+ decimalSeparator: _decimalSeparator,
51
+ groupSeparator: _groupSeparator,
52
+ allowDecimals = true,
53
+ decimalsLimit,
54
+ decimalScale,
55
+ fixedDecimalLength,
56
+ allowNegativeValue = true,
57
+ min,
58
+ max,
59
+ maxLength: userMaxLength,
60
+ step,
61
+ disableGroupSeparators = false,
62
+ disableAbbreviations = false,
63
+ formatValueOnBlur = true,
64
+ transformRawValue,
65
+ oninputvalue,
66
+ onchangevalue,
67
+ ref = $bindable(null),
68
+ ...restProps
69
+ }: Props = $props();
70
+
71
+ $effect(() => {
72
+ if (_decimalSeparator && isNumber(_decimalSeparator)) {
73
+ throw new Error('decimalSeparator cannot be a number');
74
+ }
75
+
76
+ if (_groupSeparator && isNumber(_groupSeparator)) {
77
+ throw new Error('groupSeparator cannot be a number');
78
+ }
79
+ });
80
+
81
+ let localeConfig = $derived(getLocaleConfig(intlConfig));
82
+ let decimalSeparator = $derived(_decimalSeparator || localeConfig.decimalSeparator || '');
83
+ let groupSeparator = $derived(_groupSeparator || localeConfig.groupSeparator || '');
84
+ let prefix = $derived(userPrefix ?? localeConfig.prefix);
85
+ let suffix = $derived(userSuffix ?? '');
86
+
87
+ $effect(() => {
88
+ if (
89
+ decimalSeparator &&
90
+ groupSeparator &&
91
+ decimalSeparator === groupSeparator &&
92
+ disableGroupSeparators === false
93
+ ) {
94
+ throw new Error('decimalSeparator cannot be the same as groupSeparator');
95
+ }
96
+ });
97
+
98
+ let formatValueOptions = $derived<Partial<FormatValueOptions>>({
99
+ decimalSeparator,
100
+ groupSeparator,
101
+ disableGroupSeparators,
102
+ intlConfig,
103
+ prefix,
104
+ suffix
105
+ });
106
+
107
+ let cleanValueOptions = $derived<Partial<CleanValueOptions>>({
108
+ decimalSeparator,
109
+ groupSeparator,
110
+ allowDecimals,
111
+ decimalsLimit: decimalsLimit ?? fixedDecimalLength ?? 2,
112
+ allowNegativeValue,
113
+ disableAbbreviations,
114
+ prefix,
115
+ transformRawValue
116
+ });
117
+
118
+ let stateValue = $state('');
119
+ let dirty = $state(false);
120
+ let cursor = $state(0);
121
+ let changeCount = $state(0);
122
+ let lastKeyStroke = $state<string | null>(null);
123
+
124
+ $effect(() => {
125
+ if (stateValue === '-' || (decimalSeparator && stateValue === decimalSeparator)) {
126
+ return;
127
+ }
128
+ if (value !== undefined && value !== null && value !== '') {
129
+ const formatted = formatValue({
130
+ ...formatValueOptions,
131
+ decimalScale: dirty ? undefined : decimalScale,
132
+ value: String(value)
133
+ });
134
+ if (!dirty) {
135
+ stateValue = formatted;
136
+ }
137
+ } else if (!dirty) {
138
+ stateValue = '';
139
+ }
140
+ });
141
+
142
+ $effect(() => {
143
+ if (dirty && stateValue !== '-' && ref && document.activeElement === ref) {
144
+ ref.setSelectionRange(cursor, cursor);
145
+ }
146
+ });
147
+
148
+ function processChange(inputValue: string, selectionStart?: number | null): void {
149
+ dirty = true;
150
+
151
+ const { modifiedValue, cursorPosition } = repositionCursor({
152
+ selectionStart,
153
+ value: inputValue,
154
+ lastKeyStroke,
155
+ stateValue,
156
+ groupSeparator
157
+ });
158
+
159
+ const stringValue = cleanValue({ value: modifiedValue, ...cleanValueOptions });
160
+
161
+ if (userMaxLength && stringValue.replace(/-/g, '').length > userMaxLength) {
162
+ if (ref) {
163
+ ref.value = stateValue;
164
+ const cursorPos = Math.min(selectionStart ?? 0, stateValue.length);
165
+ ref.setSelectionRange(cursorPos, cursorPos);
166
+ }
167
+ return;
168
+ }
169
+
170
+ if (stringValue === '' || stringValue === '-' || stringValue === decimalSeparator) {
171
+ value = '';
172
+ stateValue = stringValue;
173
+ cursor = 1;
174
+
175
+ if (ref) {
176
+ ref.value = stringValue;
177
+ ref.setSelectionRange(cursor, cursor);
178
+ }
179
+
180
+ const values: CurrencyInputValues = { float: null, formatted: '', value: '' };
181
+ oninputvalue?.(values);
182
+ return;
183
+ }
184
+
185
+ const stringValueWithoutSeparator = decimalSeparator
186
+ ? stringValue.replace(decimalSeparator, '.')
187
+ : stringValue;
188
+
189
+ const numberValue = parseFloat(stringValueWithoutSeparator);
190
+
191
+ const formattedValue = formatValue({
192
+ value: stringValue,
193
+ ...formatValueOptions
194
+ });
195
+
196
+ let newCursor = cursorPosition ?? formattedValue.length;
197
+ if (cursorPosition != null) {
198
+ newCursor = cursorPosition + (formattedValue.length - inputValue.length);
199
+ newCursor = newCursor <= 0 ? (prefix ? prefix.length : 0) : newCursor;
200
+ }
201
+
202
+ cursor = newCursor;
203
+ changeCount = changeCount + 1;
204
+ stateValue = formattedValue;
205
+ value = stringValue;
206
+
207
+ if (ref) {
208
+ ref.value = formattedValue;
209
+ ref.setSelectionRange(cursor, cursor);
210
+ }
211
+
212
+ const values: CurrencyInputValues = {
213
+ float: numberValue,
214
+ formatted: formattedValue,
215
+ value: stringValue
216
+ };
217
+ oninputvalue?.(values);
218
+ }
219
+
220
+ function handleInput(event: Event): void {
221
+ const target = event.target as HTMLInputElement;
222
+ const { value: inputValue, selectionStart } = target;
223
+
224
+ processChange(inputValue, selectionStart);
225
+ }
226
+
227
+ function handleBlur(event: FocusEvent): void {
228
+ const target = event.target as HTMLInputElement;
229
+ const { value: inputValue } = target;
230
+
231
+ const valueOnly = cleanValue({ value: inputValue, ...cleanValueOptions });
232
+
233
+ if (valueOnly === '-' || valueOnly === decimalSeparator || !valueOnly) {
234
+ stateValue = '';
235
+ value = '';
236
+ dirty = false;
237
+ return;
238
+ }
239
+
240
+ const fixedDecimals = fixedDecimalValue(valueOnly, decimalSeparator, fixedDecimalLength);
241
+
242
+ const newValue = padTrimValue(
243
+ fixedDecimals,
244
+ decimalSeparator,
245
+ decimalScale !== undefined ? decimalScale : fixedDecimalLength
246
+ );
247
+
248
+ const stringValueWithoutSeparator = decimalSeparator
249
+ ? newValue.replace(decimalSeparator, '.')
250
+ : newValue;
251
+
252
+ const numberValue = parseFloat(stringValueWithoutSeparator);
253
+
254
+ const formattedValue = formatValue({
255
+ ...formatValueOptions,
256
+ value: newValue
257
+ });
258
+
259
+ if (formatValueOnBlur) {
260
+ stateValue = formattedValue;
261
+ value = newValue;
262
+
263
+ const values: CurrencyInputValues = {
264
+ float: numberValue,
265
+ formatted: formattedValue,
266
+ value: newValue
267
+ };
268
+ onchangevalue?.(values);
269
+ }
270
+
271
+ dirty = false;
272
+ }
273
+
274
+ function handleKeyDown(event: KeyboardEvent): void {
275
+ const { key } = event;
276
+
277
+ lastKeyStroke = key;
278
+
279
+ if (step && (key === 'ArrowUp' || key === 'ArrowDown')) {
280
+ event.preventDefault();
281
+ cursor = stateValue.length;
282
+
283
+ const stringValueWithoutSeparator =
284
+ decimalSeparator && value ? value.replace(decimalSeparator, '.') : value;
285
+
286
+ const currentValue =
287
+ parseFloat(
288
+ stringValueWithoutSeparator != null && stringValueWithoutSeparator !== ''
289
+ ? stringValueWithoutSeparator
290
+ : cleanValue({ value: stateValue, ...cleanValueOptions })
291
+ ) || 0;
292
+ const newValue = key === 'ArrowUp' ? currentValue + step : currentValue - step;
293
+
294
+ if ((min !== undefined && newValue < min) || (!allowNegativeValue && newValue < 0)) {
295
+ return;
296
+ }
297
+
298
+ if (max !== undefined && newValue > max) {
299
+ return;
300
+ }
301
+
302
+ const fixedLength = String(step).includes('.')
303
+ ? Number(String(step).split('.')[1].length)
304
+ : undefined;
305
+
306
+ processChange(
307
+ String(fixedLength ? newValue.toFixed(fixedLength) : newValue).replace(
308
+ '.',
309
+ decimalSeparator
310
+ )
311
+ );
312
+ }
313
+ }
314
+
315
+ function handleKeyUp(event: KeyboardEvent): void {
316
+ const { key } = event;
317
+ const target = event.currentTarget as HTMLInputElement;
318
+ const { selectionStart } = target;
319
+
320
+ if (key !== 'ArrowUp' && key !== 'ArrowDown' && stateValue !== '-') {
321
+ const suffixValue = getSuffix(stateValue, { groupSeparator, decimalSeparator });
322
+
323
+ if (
324
+ suffixValue &&
325
+ selectionStart &&
326
+ selectionStart > stateValue.length - suffixValue.length
327
+ ) {
328
+ if (ref) {
329
+ const newCursor = stateValue.length - suffixValue.length;
330
+ ref.setSelectionRange(newCursor, newCursor);
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ let displayValue = $derived.by(() => {
337
+ if (stateValue === '-' || stateValue === decimalSeparator) {
338
+ return stateValue;
339
+ }
340
+ if (value !== undefined && value !== null && value !== '') {
341
+ return formatValue({
342
+ ...formatValueOptions,
343
+ decimalScale: dirty ? undefined : decimalScale,
344
+ value: String(value)
345
+ });
346
+ }
347
+ return stateValue;
348
+ });
349
+ </script>
350
+
351
+ <input
352
+ bind:this={ref}
353
+ type="text"
354
+ inputmode="decimal"
355
+ value={displayValue}
356
+ oninput={handleInput}
357
+ onblur={handleBlur}
358
+ onkeydown={handleKeyDown}
359
+ onkeyup={handleKeyUp}
360
+ {...restProps}
361
+ />
@@ -0,0 +1,29 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { IntlConfig, CurrencyInputValues } from './types';
3
+ type Props = Omit<HTMLInputAttributes, 'value'> & {
4
+ value?: string;
5
+ intlConfig?: IntlConfig;
6
+ prefix?: string;
7
+ suffix?: string;
8
+ decimalSeparator?: string;
9
+ groupSeparator?: string;
10
+ allowDecimals?: boolean;
11
+ decimalsLimit?: number;
12
+ decimalScale?: number;
13
+ fixedDecimalLength?: number;
14
+ allowNegativeValue?: boolean;
15
+ min?: number;
16
+ max?: number;
17
+ maxLength?: number;
18
+ step?: number;
19
+ disableGroupSeparators?: boolean;
20
+ disableAbbreviations?: boolean;
21
+ formatValueOnBlur?: boolean;
22
+ transformRawValue?: (rawValue: string) => string;
23
+ oninputvalue?: (values: CurrencyInputValues) => void;
24
+ onchangevalue?: (values: CurrencyInputValues) => void;
25
+ ref?: HTMLInputElement | null;
26
+ };
27
+ declare const CurrencyInput: import("svelte").Component<Props, {}, "value" | "ref">;
28
+ type CurrencyInput = ReturnType<typeof CurrencyInput>;
29
+ export default CurrencyInput;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import CurrencyInput from './CurrencyInput.svelte';
2
- export default CurrencyInput;
1
+ export { default as CurrencyInput } from './currency-input.svelte';
2
+ export type { IntlConfig, CurrencyInputValues, LocaleConfig, FormatValueOptions, CleanValueOptions } from './types';
3
+ export { addSeparators, cleanValue, escapeRegExp, fixedDecimalValue, formatValue, getLocaleConfig, getSuffix, isNumber, padTrimValue, parseAbbrValue, abbrValue, removeInvalidChars, removeSeparators, repositionCursor } from './utils/index';
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
- import CurrencyInput from './CurrencyInput.svelte';
2
- export default CurrencyInput;
1
+ // Component
2
+ export { default as CurrencyInput } from './currency-input.svelte';
3
+ // Utilities
4
+ export { addSeparators, cleanValue, escapeRegExp, fixedDecimalValue, formatValue, getLocaleConfig, getSuffix, isNumber, padTrimValue, parseAbbrValue, abbrValue, removeInvalidChars, removeSeparators, repositionCursor } from './utils/index';
package/dist/types.d.ts CHANGED
@@ -1,9 +1,36 @@
1
- export interface InputClasses {
2
- wrapper?: string;
3
- unformatted?: string;
4
- formatted?: string;
5
- formattedPositive?: string;
6
- formattedNegative?: string;
7
- formattedZero?: string;
8
- }
9
- export type Callback = (value: number) => any;
1
+ export type IntlConfig = {
2
+ locale: string;
3
+ } & Intl.NumberFormatOptions;
4
+ export type CurrencyInputValues = {
5
+ float: number | null;
6
+ formatted: string;
7
+ value: string;
8
+ };
9
+ export type LocaleConfig = {
10
+ currencySymbol: string;
11
+ groupSeparator: string;
12
+ decimalSeparator: string;
13
+ prefix: string;
14
+ suffix: string;
15
+ };
16
+ export type FormatValueOptions = {
17
+ value: string | undefined;
18
+ decimalSeparator?: string;
19
+ groupSeparator?: string;
20
+ disableGroupSeparators?: boolean;
21
+ intlConfig?: IntlConfig;
22
+ decimalScale?: number;
23
+ prefix?: string;
24
+ suffix?: string;
25
+ };
26
+ export type CleanValueOptions = {
27
+ value: string;
28
+ decimalSeparator?: string;
29
+ groupSeparator?: string;
30
+ allowDecimals?: boolean;
31
+ decimalsLimit?: number;
32
+ allowNegativeValue?: boolean;
33
+ disableAbbreviations?: boolean;
34
+ prefix?: string;
35
+ transformRawValue?: (rawValue: string) => string;
36
+ };
@@ -0,0 +1 @@
1
+ export declare const addSeparators: (value: string, separator?: string) => string;
@@ -0,0 +1,3 @@
1
+ export const addSeparators = (value, separator = ',') => {
2
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
3
+ };
@@ -0,0 +1,2 @@
1
+ import type { CleanValueOptions } from '../types';
2
+ export declare const cleanValue: ({ value, groupSeparator, decimalSeparator, allowDecimals, decimalsLimit, allowNegativeValue, disableAbbreviations, prefix, transformRawValue }: CleanValueOptions) => string;
@@ -0,0 +1,43 @@
1
+ import { escapeRegExp } from './escapeRegExp';
2
+ import { parseAbbrValue } from './parseAbbrValue';
3
+ import { removeInvalidChars } from './removeInvalidChars';
4
+ import { removeSeparators } from './removeSeparators';
5
+ export const cleanValue = ({ value, groupSeparator = ',', decimalSeparator = '.', allowDecimals = true, decimalsLimit = 2, allowNegativeValue = true, disableAbbreviations = false, prefix = '', transformRawValue = (rawValue) => rawValue }) => {
6
+ const transformedValue = transformRawValue(value);
7
+ if (transformedValue === '-') {
8
+ return transformedValue;
9
+ }
10
+ const abbreviations = disableAbbreviations ? [] : ['k', 'm', 'b'];
11
+ const reg = new RegExp(`((^|\\D)-\\d)|(-${escapeRegExp(prefix)})`);
12
+ const isNegative = reg.test(transformedValue);
13
+ const [prefixWithValue, preValue] = RegExp(`(\\d+)-?${escapeRegExp(prefix)}`).exec(value) || [];
14
+ const withoutPrefix = prefix
15
+ ? prefixWithValue
16
+ ? transformedValue.replace(prefixWithValue, '').concat(preValue)
17
+ : transformedValue.replace(prefix, '')
18
+ : transformedValue;
19
+ const withoutSeparators = removeSeparators(withoutPrefix, groupSeparator);
20
+ const withoutInvalidChars = removeInvalidChars(withoutSeparators, [
21
+ groupSeparator,
22
+ decimalSeparator,
23
+ ...abbreviations
24
+ ]);
25
+ let valueOnly = withoutInvalidChars;
26
+ if (!disableAbbreviations) {
27
+ if (abbreviations.some((letter) => letter === withoutInvalidChars.toLowerCase().replace(decimalSeparator, ''))) {
28
+ return '';
29
+ }
30
+ const parsed = parseAbbrValue(withoutInvalidChars, decimalSeparator);
31
+ if (parsed) {
32
+ valueOnly = String(parsed);
33
+ }
34
+ }
35
+ const includeNegative = isNegative && allowNegativeValue ? '-' : '';
36
+ if (decimalSeparator && valueOnly.includes(decimalSeparator)) {
37
+ const [int, decimals] = withoutInvalidChars.split(decimalSeparator);
38
+ const trimmedDecimals = decimalsLimit && decimals ? decimals.slice(0, decimalsLimit) : decimals;
39
+ const includeDecimals = allowDecimals ? `${decimalSeparator}${trimmedDecimals}` : '';
40
+ return `${includeNegative}${int}${includeDecimals}`;
41
+ }
42
+ return `${includeNegative}${valueOnly}`;
43
+ };
@@ -0,0 +1 @@
1
+ export declare const escapeRegExp: (stringToGoIntoTheRegex: string) => string;
@@ -0,0 +1,3 @@
1
+ export const escapeRegExp = (stringToGoIntoTheRegex) => {
2
+ return stringToGoIntoTheRegex.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
3
+ };
@@ -0,0 +1 @@
1
+ export declare const fixedDecimalValue: (value: string, decimalSeparator: string, fixedDecimalLength?: number) => string;
@@ -0,0 +1,25 @@
1
+ export const fixedDecimalValue = (value, decimalSeparator, fixedDecimalLength) => {
2
+ if (fixedDecimalLength !== undefined && value.length > 1) {
3
+ if (fixedDecimalLength === 0) {
4
+ return value.replace(decimalSeparator, '');
5
+ }
6
+ if (value.includes(decimalSeparator)) {
7
+ const [int, decimals] = value.split(decimalSeparator);
8
+ if (decimals.length === fixedDecimalLength) {
9
+ return value;
10
+ }
11
+ if (decimals.length > fixedDecimalLength) {
12
+ return `${int}${decimalSeparator}${decimals.slice(0, fixedDecimalLength)}`;
13
+ }
14
+ }
15
+ const reg = value.length > fixedDecimalLength
16
+ ? new RegExp(`(\\d+)(\\d{${fixedDecimalLength}})`)
17
+ : new RegExp(`(\\d)(\\d+)`);
18
+ const match = value.match(reg);
19
+ if (match) {
20
+ const [, int, decimals] = match;
21
+ return `${int}${decimalSeparator}${decimals}`;
22
+ }
23
+ }
24
+ return value;
25
+ };
@@ -0,0 +1,2 @@
1
+ import type { FormatValueOptions } from '../types';
2
+ export declare const formatValue: (options: FormatValueOptions) => string;
@@ -0,0 +1,101 @@
1
+ import { escapeRegExp } from './escapeRegExp';
2
+ import { getSuffix } from './getSuffix';
3
+ export const formatValue = (options) => {
4
+ const { value: _value, decimalSeparator, intlConfig, decimalScale, prefix = '', suffix = '' } = options;
5
+ if (_value === '' || _value === undefined) {
6
+ return '';
7
+ }
8
+ if (_value === '-') {
9
+ return '-';
10
+ }
11
+ const isNegative = new RegExp(`^\\d?-${prefix ? `${escapeRegExp(prefix)}?` : ''}\\d`).test(_value);
12
+ let value = decimalSeparator !== '.'
13
+ ? replaceDecimalSeparator(_value, decimalSeparator, isNegative)
14
+ : _value;
15
+ if (decimalSeparator && decimalSeparator !== '-' && value.startsWith(decimalSeparator)) {
16
+ value = '0' + value;
17
+ }
18
+ const { locale, currency, ...formatOptions } = intlConfig || {};
19
+ const defaultNumberFormatOptions = {
20
+ ...formatOptions,
21
+ minimumFractionDigits: decimalScale || 0,
22
+ maximumFractionDigits: 20
23
+ };
24
+ const numberFormatter = intlConfig
25
+ ? new Intl.NumberFormat(locale, {
26
+ ...defaultNumberFormatOptions,
27
+ ...(currency && { style: 'currency', currency })
28
+ })
29
+ : new Intl.NumberFormat(undefined, defaultNumberFormatOptions);
30
+ const parts = numberFormatter.formatToParts(Number(value));
31
+ let formatted = replaceParts(parts, options);
32
+ const intlSuffix = getSuffix(formatted, { ...options });
33
+ const includeDecimalSeparator = _value.slice(-1) === decimalSeparator ? decimalSeparator : '';
34
+ const [, decimals] = value.match(RegExp('\\d+\\.(\\d+)')) || [];
35
+ if (decimalScale === undefined && decimals && decimalSeparator) {
36
+ if (formatted.includes(decimalSeparator)) {
37
+ formatted = formatted.replace(RegExp(`(\\d+)(${escapeRegExp(decimalSeparator)})(\\d+)`, 'g'), `$1$2${decimals}`);
38
+ }
39
+ else {
40
+ if (intlSuffix && !suffix) {
41
+ formatted = formatted.replace(intlSuffix, `${decimalSeparator}${decimals}${intlSuffix}`);
42
+ }
43
+ else {
44
+ formatted = `${formatted}${decimalSeparator}${decimals}`;
45
+ }
46
+ }
47
+ }
48
+ if (suffix && includeDecimalSeparator) {
49
+ return `${formatted}${includeDecimalSeparator}${suffix}`;
50
+ }
51
+ if (intlSuffix && includeDecimalSeparator) {
52
+ return formatted.replace(intlSuffix, `${includeDecimalSeparator}${intlSuffix}`);
53
+ }
54
+ if (intlSuffix && suffix) {
55
+ return formatted.replace(intlSuffix, `${includeDecimalSeparator}${suffix}`);
56
+ }
57
+ return [formatted, includeDecimalSeparator, suffix].join('');
58
+ };
59
+ const replaceDecimalSeparator = (value, decimalSeparator, isNegative) => {
60
+ let newValue = value;
61
+ if (decimalSeparator && decimalSeparator !== '.') {
62
+ newValue = newValue.replace(RegExp(escapeRegExp(decimalSeparator), 'g'), '.');
63
+ if (isNegative && decimalSeparator === '-') {
64
+ newValue = `-${newValue.slice(1)}`;
65
+ }
66
+ }
67
+ return newValue;
68
+ };
69
+ const replaceParts = (parts, { prefix, groupSeparator, decimalSeparator, decimalScale, disableGroupSeparators = false }) => {
70
+ return parts
71
+ .reduce((prev, { type, value }, i) => {
72
+ if (i === 0 && prefix) {
73
+ if (type === 'minusSign') {
74
+ return [value, prefix];
75
+ }
76
+ if (type === 'currency') {
77
+ return [...prev, prefix];
78
+ }
79
+ return [prefix, value];
80
+ }
81
+ if (type === 'currency') {
82
+ return prefix ? prev : [...prev, value];
83
+ }
84
+ if (type === 'group') {
85
+ return !disableGroupSeparators
86
+ ? [...prev, groupSeparator !== undefined ? groupSeparator : value]
87
+ : prev;
88
+ }
89
+ if (type === 'decimal') {
90
+ if (decimalScale !== undefined && decimalScale === 0) {
91
+ return prev;
92
+ }
93
+ return [...prev, decimalSeparator !== undefined ? decimalSeparator : value];
94
+ }
95
+ if (type === 'fraction') {
96
+ return [...prev, decimalScale !== undefined ? value.slice(0, decimalScale) : value];
97
+ }
98
+ return [...prev, value];
99
+ }, [''])
100
+ .join('');
101
+ };
@@ -0,0 +1,2 @@
1
+ import type { IntlConfig, LocaleConfig } from '../types';
2
+ export declare const getLocaleConfig: (intlConfig?: IntlConfig) => LocaleConfig;
@@ -0,0 +1,33 @@
1
+ const defaultConfig = {
2
+ currencySymbol: '',
3
+ groupSeparator: '',
4
+ decimalSeparator: '',
5
+ prefix: '',
6
+ suffix: ''
7
+ };
8
+ export const getLocaleConfig = (intlConfig) => {
9
+ const { locale, currency, ...formatOptions } = intlConfig || {};
10
+ const numberFormatter = locale
11
+ ? new Intl.NumberFormat(locale, {
12
+ ...formatOptions,
13
+ ...(currency && { currency, style: 'currency' })
14
+ })
15
+ : new Intl.NumberFormat();
16
+ return numberFormatter.formatToParts(1000.1).reduce((prev, curr, i) => {
17
+ if (curr.type === 'currency') {
18
+ if (i === 0) {
19
+ return { ...prev, currencySymbol: curr.value, prefix: curr.value };
20
+ }
21
+ else {
22
+ return { ...prev, currencySymbol: curr.value, suffix: curr.value };
23
+ }
24
+ }
25
+ if (curr.type === 'group') {
26
+ return { ...prev, groupSeparator: curr.value };
27
+ }
28
+ if (curr.type === 'decimal') {
29
+ return { ...prev, decimalSeparator: curr.value };
30
+ }
31
+ return prev;
32
+ }, defaultConfig);
33
+ };