@canutin/svelte-currency-input 0.11.4 → 0.12.0
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.
- package/README.md +4 -3
- package/dist/CurrencyInput.svelte +55 -68
- package/dist/CurrencyInput.svelte.d.ts +5 -10
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +11 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +1 -0
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# svelte-currency-input
|
|
2
2
|
|
|
3
|
-
A form input that converts numbers to localized currency formats as you type
|
|
3
|
+
A masked form input that converts numbers to localized currency formats as you type
|
|
4
4
|
|
|
5
5
|
[<img width="962" alt="image" src="https://user-images.githubusercontent.com/1434675/190873948-c0385747-6fa9-4077-8bd5-717e4d1124a0.png">](https://svelte.dev/repl/d8f7d22e5b384555b430f62b157ac503?version=3.50.1)
|
|
6
6
|
|
|
@@ -64,8 +64,9 @@ value | `number` | `undefined` | Initial value. If left `unde
|
|
|
64
64
|
locale | `string` | `en-US` | Overrides default locale. [Examples](https://gist.github.com/ncreated/9934896) |
|
|
65
65
|
currency | `string` | `USD` | Overrides default currency. [Examples](https://www.xe.com/symbols/) |
|
|
66
66
|
name | `string` | `total` | Applies the name to the [input fields](#how-it-works) for _unformatted_ (e.g `[name=total]`) and _formatted_ (e.g. `[name=formatted-total]`) values |
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
id | `string` | `undefined` | Sets the `id` attribute on the input |
|
|
68
|
+
required | `boolean` | `false` | Marks the input as required |
|
|
69
|
+
disabled | `boolean` | `false` | Marks the input as disabled |
|
|
69
70
|
placeholder | `string` `number` `null` | `0` | A `string` will override the default placeholder. A `number` will override it by formatting it to the set currency. Setting it to `null` will not show a placeholder |
|
|
70
71
|
isZeroNullish | `boolean` | `false` | If `true` and when the value is `0`, it will override the default placeholder and render the formatted value in the field like any other value. _Note: this option might become the default in future versions_ |
|
|
71
72
|
autocomplete | `string` | `undefined` | Sets the autocomplete attribute. Accepts any valid HTML [autocomplete attribute values](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) |
|
|
@@ -1,89 +1,93 @@
|
|
|
1
|
-
<script>import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<script>import {
|
|
2
|
+
DEFAULT_LOCALE,
|
|
3
|
+
DEFAULT_CURRENCY,
|
|
4
|
+
DEFAULT_NAME,
|
|
5
|
+
DEFAULT_VALUE,
|
|
6
|
+
DEFAULT_FRACTION_DIGITS,
|
|
7
|
+
DEFAULT_CLASS_WRAPPER,
|
|
8
|
+
DEFAULT_CLASS_UNFORMATTED,
|
|
9
|
+
DEFAULT_CLASS_FORMATTED,
|
|
10
|
+
DEFAULT_CLASS_FORMATTED_POSITIVE,
|
|
11
|
+
DEFAULT_CLASS_FORMATTED_NEGATIVE,
|
|
12
|
+
DEFAULT_CLASS_FORMATTED_ZERO
|
|
13
|
+
} from "./constants";
|
|
14
|
+
import { onMount } from "svelte";
|
|
13
15
|
export let value = DEFAULT_VALUE;
|
|
14
16
|
export let locale = DEFAULT_LOCALE;
|
|
15
17
|
export let currency = DEFAULT_CURRENCY;
|
|
18
|
+
export let fractionDigits = DEFAULT_FRACTION_DIGITS;
|
|
16
19
|
export let name = DEFAULT_NAME;
|
|
20
|
+
export let id = void 0;
|
|
17
21
|
export let required = false;
|
|
18
22
|
export let disabled = false;
|
|
19
23
|
export let placeholder = DEFAULT_VALUE;
|
|
20
24
|
export let autocomplete = void 0;
|
|
21
25
|
export let isNegativeAllowed = true;
|
|
22
26
|
export let isZeroNullish = false;
|
|
23
|
-
export let fractionDigits = DEFAULT_FRACTION_DIGITS;
|
|
24
27
|
export let inputClasses = null;
|
|
25
28
|
export let onValueChange = () => {
|
|
26
29
|
};
|
|
27
|
-
|
|
30
|
+
let inputElement;
|
|
31
|
+
let inputTarget;
|
|
32
|
+
let formattedValue = "";
|
|
33
|
+
$: isNegative = value < 0;
|
|
34
|
+
$: isPositive = value > 0;
|
|
35
|
+
$: isZero = !isNegative && !isPositive;
|
|
36
|
+
$: value, setFormattedValue();
|
|
37
|
+
onMount(() => {
|
|
38
|
+
setFormattedValue();
|
|
39
|
+
});
|
|
40
|
+
function formatCurrency(value2, maximumFractionDigits, minimumFractionDigits) {
|
|
28
41
|
return new Intl.NumberFormat(locale, {
|
|
29
42
|
currency,
|
|
30
43
|
style: "currency",
|
|
31
44
|
maximumFractionDigits: maximumFractionDigits || 0,
|
|
32
45
|
minimumFractionDigits: minimumFractionDigits || 0
|
|
33
46
|
}).format(value2);
|
|
34
|
-
}
|
|
35
|
-
|
|
47
|
+
}
|
|
48
|
+
function handleKeyDown(event) {
|
|
36
49
|
const isDeletion = event.key === "Backspace" || event.key === "Delete";
|
|
37
50
|
const isModifier = event.metaKey || event.altKey || event.ctrlKey;
|
|
38
51
|
const isArrowKey = event.key === "ArrowLeft" || event.key === "ArrowRight";
|
|
39
52
|
const isTab = event.key === "Tab";
|
|
40
53
|
const isInvalidCharacter = !/^\d|,|\.|-$/g.test(event.key);
|
|
41
|
-
|
|
42
|
-
if (event.key !== "," && event.key !== ".")
|
|
43
|
-
|
|
44
|
-
if (isDecimalComma)
|
|
45
|
-
return formattedValue.split(",").length >= 2;
|
|
46
|
-
if (!isDecimalComma)
|
|
47
|
-
return formattedValue.split(".").length >= 2;
|
|
54
|
+
function isPunctuationDuplicated() {
|
|
55
|
+
if (event.key !== "," && event.key !== ".") return false;
|
|
56
|
+
if (isDecimalComma) return formattedValue.split(",").length >= 2;
|
|
57
|
+
if (!isDecimalComma) return formattedValue.split(".").length >= 2;
|
|
48
58
|
return false;
|
|
49
|
-
}
|
|
59
|
+
}
|
|
50
60
|
if (isPunctuationDuplicated() || !isDeletion && !isModifier && !isArrowKey && isInvalidCharacter && !isTab)
|
|
51
61
|
event.preventDefault();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
});
|
|
60
|
-
let inputTarget;
|
|
62
|
+
}
|
|
63
|
+
function handlePlaceholder(placeholder2) {
|
|
64
|
+
if (typeof placeholder2 === "number")
|
|
65
|
+
return formatCurrency(placeholder2, fractionDigits, fractionDigits);
|
|
66
|
+
if (placeholder2 === null) return "";
|
|
67
|
+
return placeholder2;
|
|
68
|
+
}
|
|
61
69
|
const currencyDecimal = new Intl.NumberFormat(locale).format(1.1).charAt(1);
|
|
62
70
|
const isDecimalComma = currencyDecimal === ",";
|
|
63
71
|
const currencySymbol = formatCurrency(0, 0).replace("0", "").replace(/\u00A0/, "");
|
|
64
|
-
|
|
72
|
+
function setUnformattedValue(event) {
|
|
65
73
|
if (event) {
|
|
66
|
-
if (event.key === currencyDecimal)
|
|
67
|
-
return;
|
|
74
|
+
if (event.key === currencyDecimal) return;
|
|
68
75
|
if (isDecimalComma && event.key === ".")
|
|
69
76
|
formattedValue = formattedValue.replace(/\.([^.]*)$/, currencyDecimal + "$1");
|
|
70
77
|
if (!isDecimalComma && event.key === ",")
|
|
71
78
|
formattedValue = formattedValue.replace(/\,([^,]*)$/, currencyDecimal + "$1");
|
|
72
79
|
const ignoreSymbols = [currencySymbol, `-${currencySymbol}`, "-"];
|
|
73
80
|
const strippedUnformattedValue = formattedValue.replace(" ", "");
|
|
74
|
-
if (ignoreSymbols.includes(strippedUnformattedValue))
|
|
75
|
-
return;
|
|
81
|
+
if (ignoreSymbols.includes(strippedUnformattedValue)) return;
|
|
76
82
|
inputTarget = event.target;
|
|
77
|
-
if (isNegativeAllowed && event.key === "-")
|
|
78
|
-
value = value * -1;
|
|
83
|
+
if (isNegativeAllowed && event.key === "-") value = value * -1;
|
|
79
84
|
}
|
|
80
85
|
let unformattedValue = isNegativeAllowed ? formattedValue.replace(/[^0-9,.-]/g, "") : formattedValue.replace(/[^0-9,.]/g, "");
|
|
81
86
|
if (Number.isNaN(parseFloat(unformattedValue))) {
|
|
82
87
|
value = 0;
|
|
83
88
|
} else {
|
|
84
89
|
unformattedValue = unformattedValue.replace(isDecimalComma ? /\./g : /\,/g, "");
|
|
85
|
-
if (isDecimalComma)
|
|
86
|
-
unformattedValue = unformattedValue.replace(",", ".");
|
|
90
|
+
if (isDecimalComma) unformattedValue = unformattedValue.replace(",", ".");
|
|
87
91
|
const previousValue = value;
|
|
88
92
|
value = parseFloat(unformattedValue);
|
|
89
93
|
if (event && previousValue === value) {
|
|
@@ -92,43 +96,25 @@ const setUnformattedValue = (event) => {
|
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
const startCaretPosition = inputElement?.selectionStart || 0;
|
|
99
|
+
}
|
|
100
|
+
function setFormattedValue() {
|
|
101
|
+
if (!inputElement) return;
|
|
102
|
+
const startCaretPosition = inputElement.selectionStart || 0;
|
|
100
103
|
const previousFormattedValueLength = formattedValue.length;
|
|
101
104
|
formattedValue = isZero && !isZeroNullish ? "" : formatCurrency(
|
|
102
105
|
value,
|
|
103
106
|
fractionDigits,
|
|
104
|
-
|
|
107
|
+
document.activeElement === inputElement ? 0 : fractionDigits
|
|
105
108
|
);
|
|
106
109
|
setUnformattedValue();
|
|
107
110
|
let retries = 0;
|
|
108
|
-
while (previousFormattedValueLength === formattedValue.length && retries < 10)
|
|
109
|
-
retries++;
|
|
111
|
+
while (previousFormattedValueLength === formattedValue.length && retries < 10) retries++;
|
|
110
112
|
if (previousFormattedValueLength !== formattedValue.length) {
|
|
111
113
|
const endCaretPosition = startCaretPosition + formattedValue.length - previousFormattedValueLength;
|
|
112
114
|
inputElement?.setSelectionRange(endCaretPosition, endCaretPosition);
|
|
113
115
|
}
|
|
114
116
|
onValueChange(value);
|
|
115
|
-
}
|
|
116
|
-
const handlePlaceholder = (placeholder2) => {
|
|
117
|
-
if (typeof placeholder2 === "number")
|
|
118
|
-
return formatCurrency(placeholder2, fractionDigits, fractionDigits);
|
|
119
|
-
if (placeholder2 === null)
|
|
120
|
-
return "";
|
|
121
|
-
return placeholder2;
|
|
122
|
-
};
|
|
123
|
-
let formattedValue = "";
|
|
124
|
-
$:
|
|
125
|
-
isNegative = value < 0;
|
|
126
|
-
$:
|
|
127
|
-
isPositive = value > 0;
|
|
128
|
-
$:
|
|
129
|
-
isZero = !isNegative && !isPositive;
|
|
130
|
-
$:
|
|
131
|
-
value, setFormattedValue();
|
|
117
|
+
}
|
|
132
118
|
</script>
|
|
133
119
|
|
|
134
120
|
<div class={inputClasses?.wrapper ?? DEFAULT_CLASS_WRAPPER}>
|
|
@@ -157,11 +143,12 @@ $:
|
|
|
157
143
|
placeholder={handlePlaceholder(placeholder)}
|
|
158
144
|
{autocomplete}
|
|
159
145
|
{disabled}
|
|
146
|
+
{id}
|
|
160
147
|
bind:this={inputElement}
|
|
161
148
|
bind:value={formattedValue}
|
|
162
149
|
on:keydown={handleKeyDown}
|
|
163
150
|
on:keyup={setUnformattedValue}
|
|
164
|
-
on:blur={
|
|
151
|
+
on:blur={setFormattedValue}
|
|
165
152
|
/>
|
|
166
153
|
</div>
|
|
167
154
|
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import type { InputClasses, Callback } from './types';
|
|
2
3
|
declare const __propDef: {
|
|
3
4
|
props: {
|
|
4
5
|
value?: number | undefined;
|
|
5
6
|
locale?: string | undefined;
|
|
6
7
|
currency?: string | undefined;
|
|
8
|
+
fractionDigits?: number | undefined;
|
|
7
9
|
name?: string | undefined;
|
|
10
|
+
id?: string | undefined;
|
|
8
11
|
required?: boolean | undefined;
|
|
9
12
|
disabled?: boolean | undefined;
|
|
10
13
|
placeholder?: string | number | null | undefined;
|
|
11
14
|
autocomplete?: string | null | undefined;
|
|
12
15
|
isNegativeAllowed?: boolean | undefined;
|
|
13
16
|
isZeroNullish?: boolean | undefined;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
wrapper?: string | undefined;
|
|
17
|
-
unformatted?: string | undefined;
|
|
18
|
-
formatted?: string | undefined;
|
|
19
|
-
formattedPositive?: string | undefined;
|
|
20
|
-
formattedNegative?: string | undefined;
|
|
21
|
-
formattedZero?: string | undefined;
|
|
22
|
-
} | null | undefined;
|
|
23
|
-
onValueChange?: ((value: number) => any) | undefined;
|
|
17
|
+
inputClasses?: InputClasses | null | undefined;
|
|
18
|
+
onValueChange?: Callback | undefined;
|
|
24
19
|
};
|
|
25
20
|
events: {
|
|
26
21
|
[evt: string]: CustomEvent<any>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const DEFAULT_LOCALE = "en-US";
|
|
2
|
+
export declare const DEFAULT_CURRENCY = "USD";
|
|
3
|
+
export declare const DEFAULT_NAME = "total";
|
|
4
|
+
export declare const DEFAULT_VALUE = 0;
|
|
5
|
+
export declare const DEFAULT_FRACTION_DIGITS = 2;
|
|
6
|
+
export declare const DEFAULT_CLASS_WRAPPER = "currencyInput";
|
|
7
|
+
export declare const DEFAULT_CLASS_UNFORMATTED = "currencyInput__unformatted";
|
|
8
|
+
export declare const DEFAULT_CLASS_FORMATTED = "currencyInput__formatted";
|
|
9
|
+
export declare const DEFAULT_CLASS_FORMATTED_POSITIVE = "currencyInput__formatted--positive";
|
|
10
|
+
export declare const DEFAULT_CLASS_FORMATTED_NEGATIVE = "currencyInput__formatted--negative";
|
|
11
|
+
export declare const DEFAULT_CLASS_FORMATTED_ZERO = "currencyInput__formatted--zero";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const DEFAULT_LOCALE = 'en-US';
|
|
2
|
+
export const DEFAULT_CURRENCY = 'USD';
|
|
3
|
+
export const DEFAULT_NAME = 'total';
|
|
4
|
+
export const DEFAULT_VALUE = 0;
|
|
5
|
+
export const DEFAULT_FRACTION_DIGITS = 2;
|
|
6
|
+
export const DEFAULT_CLASS_WRAPPER = 'currencyInput';
|
|
7
|
+
export const DEFAULT_CLASS_UNFORMATTED = 'currencyInput__unformatted';
|
|
8
|
+
export const DEFAULT_CLASS_FORMATTED = 'currencyInput__formatted';
|
|
9
|
+
export const DEFAULT_CLASS_FORMATTED_POSITIVE = 'currencyInput__formatted--positive';
|
|
10
|
+
export const DEFAULT_CLASS_FORMATTED_NEGATIVE = 'currencyInput__formatted--negative';
|
|
11
|
+
export const DEFAULT_CLASS_FORMATTED_ZERO = 'currencyInput__formatted--zero';
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canutin/svelte-currency-input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build",
|
|
@@ -25,10 +25,11 @@
|
|
|
25
25
|
],
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@playwright/test": "^1.39.0",
|
|
28
|
-
"@sveltejs/adapter-auto": "^2.
|
|
29
|
-
"@sveltejs/adapter-cloudflare": "^
|
|
30
|
-
"@sveltejs/kit": "^
|
|
28
|
+
"@sveltejs/adapter-auto": "^3.2.5",
|
|
29
|
+
"@sveltejs/adapter-cloudflare": "^4.7.3",
|
|
30
|
+
"@sveltejs/kit": "^2.7.1",
|
|
31
31
|
"@sveltejs/package": "^2.2.2",
|
|
32
|
+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
|
32
33
|
"@types/node": "^20.8.7",
|
|
33
34
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
34
35
|
"@typescript-eslint/parser": "^6.0.0",
|
|
@@ -40,11 +41,16 @@
|
|
|
40
41
|
"prettier-plugin-svelte": "^2.10.1",
|
|
41
42
|
"publint": "^0.2.5",
|
|
42
43
|
"sass": "^1.69.0",
|
|
43
|
-
"semantic-release": "^
|
|
44
|
+
"semantic-release": "^24.1.1",
|
|
45
|
+
"svelte": "^4.2.19",
|
|
44
46
|
"svelte-check": "^3.4.3",
|
|
45
47
|
"tslib": "^2.4.1",
|
|
46
48
|
"typescript": "^5.0.0",
|
|
47
|
-
"vite": "^
|
|
49
|
+
"vite": "^5.0.0"
|
|
50
|
+
},
|
|
51
|
+
"optionalDependencies": {
|
|
52
|
+
"@cloudflare/workerd-linux-64": "^1.20240909.0",
|
|
53
|
+
"@rollup/rollup-linux-x64-gnu": "^4.21.3"
|
|
48
54
|
},
|
|
49
55
|
"svelte": "./dist/index.js",
|
|
50
56
|
"types": "./dist/index.d.ts",
|