sveltacular 0.0.70 → 0.0.72
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/dist/forms/money-box/money-box.svelte +273 -9
- package/dist/forms/money-box/money-box.svelte.d.ts +8 -4
- package/dist/forms/number-box/number-box.svelte +26 -1
- package/dist/forms/number-box/number-box.svelte.d.ts +2 -0
- package/dist/forms/phone-box/phone-box.svelte +0 -1
- package/dist/generic/phone/phone.svelte.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,16 +1,280 @@
|
|
|
1
|
-
<script>import
|
|
1
|
+
<script>import { uniqueId } from "../../index.js";
|
|
2
|
+
import { createEventDispatcher } from "svelte";
|
|
3
|
+
import FormField from "../form-field.svelte";
|
|
4
|
+
import FormLabel from "../form-label.svelte";
|
|
5
|
+
const id = uniqueId();
|
|
6
|
+
const dipatch = createEventDispatcher();
|
|
2
7
|
export let value;
|
|
3
|
-
export let
|
|
4
|
-
export let
|
|
8
|
+
export let prefix = "$";
|
|
9
|
+
export let suffix = "";
|
|
10
|
+
export let currency = "USD";
|
|
11
|
+
export let allowCents = true;
|
|
5
12
|
export let placeholder = "";
|
|
6
13
|
export let size = "full";
|
|
7
|
-
export let
|
|
14
|
+
export let units = "ones";
|
|
8
15
|
export let min = 0;
|
|
9
|
-
export let max =
|
|
16
|
+
export let max = null;
|
|
17
|
+
const getDollarsFromValue = () => {
|
|
18
|
+
if (!value)
|
|
19
|
+
return "0";
|
|
20
|
+
if (isValueInCents)
|
|
21
|
+
return String(Math.abs(Math.floor(value / 100)));
|
|
22
|
+
return String(Math.abs(Math.floor(value)));
|
|
23
|
+
};
|
|
24
|
+
const getCentsFromValue = () => {
|
|
25
|
+
if (!value)
|
|
26
|
+
return "00";
|
|
27
|
+
if (isValueInCents)
|
|
28
|
+
return String(Math.abs(Math.round(value % 100))).padStart(2, "0");
|
|
29
|
+
return String(Math.abs(Math.round(value % 1 * 100))).padStart(2, "0");
|
|
30
|
+
};
|
|
31
|
+
let isValueInCents = units === "cents";
|
|
32
|
+
const fieldOrder = ["dollars", "cents"];
|
|
33
|
+
let dollars = getDollarsFromValue();
|
|
34
|
+
let cents = getCentsFromValue();
|
|
35
|
+
let lastState = [
|
|
36
|
+
{ value: String(dollars), selectionStart: 0, selectionEnd: 0 },
|
|
37
|
+
{ value: String(cents), selectionStart: 0, selectionEnd: 0 }
|
|
38
|
+
];
|
|
10
39
|
$:
|
|
11
|
-
|
|
40
|
+
if (value !== null && value >= 0) {
|
|
41
|
+
dollars = getDollarsFromValue();
|
|
42
|
+
cents = getCentsFromValue();
|
|
43
|
+
}
|
|
44
|
+
const getTargetProperties = (e) => {
|
|
45
|
+
const target = e.target;
|
|
46
|
+
const name = target.getAttribute("name");
|
|
47
|
+
const index = fieldOrder.indexOf(name ?? "") || 0;
|
|
48
|
+
const nextName = index < fieldOrder.length - 1 ? fieldOrder[index + 1] : null;
|
|
49
|
+
const prevName = index > 0 ? fieldOrder[index - 1] : null;
|
|
50
|
+
const selection = [target.selectionStart ?? 0, target.selectionEnd ?? 0];
|
|
51
|
+
const key = e instanceof KeyboardEvent ? e.key : "";
|
|
52
|
+
const isNumber = !isNaN(Number(key));
|
|
53
|
+
const isDecimal = key === ".";
|
|
54
|
+
const isBackspace = key === "Backspace";
|
|
55
|
+
const isAllowed = isNumber || isDecimal || ["Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(key);
|
|
56
|
+
return {
|
|
57
|
+
element: target,
|
|
58
|
+
name,
|
|
59
|
+
index,
|
|
60
|
+
selectionStart: selection[0],
|
|
61
|
+
selectionEnd: selection[1],
|
|
62
|
+
isHighligted: selection[0] !== selection[1],
|
|
63
|
+
key,
|
|
64
|
+
isNumber,
|
|
65
|
+
isDecimal,
|
|
66
|
+
isBackspace,
|
|
67
|
+
isAllowed,
|
|
68
|
+
lastState: lastState[index],
|
|
69
|
+
value: target.value,
|
|
70
|
+
next: nextName ? document.getElementById(`${id}-${nextName}`) : null,
|
|
71
|
+
previous: prevName ? document.getElementById(`${id}-${prevName}`) : null
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const focusAndHighlightText = (target) => {
|
|
75
|
+
target.focus();
|
|
76
|
+
target.setSelectionRange(0, target.value.length);
|
|
77
|
+
};
|
|
78
|
+
const updateLastState = (e) => {
|
|
79
|
+
const target = getTargetProperties(e);
|
|
80
|
+
lastState[target.index] = {
|
|
81
|
+
value: target.value,
|
|
82
|
+
selectionStart: target.selectionStart,
|
|
83
|
+
selectionEnd: target.selectionEnd
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
const isNumericString = (value2) => {
|
|
87
|
+
return !isNaN(Number(value2));
|
|
88
|
+
};
|
|
89
|
+
const moveExtraCentsToDollars = (centsValue, append = true) => {
|
|
90
|
+
if (centsValue.length > 2 && isNumericString(centsValue) && Number(centsValue) > 0) {
|
|
91
|
+
const whole = centsValue.substring(0, centsValue.length - 2);
|
|
92
|
+
const decimal = centsValue.substring(centsValue.length - 2);
|
|
93
|
+
dollars = append ? `${dollars}${whole}` : whole;
|
|
94
|
+
cents = decimal;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const onKeyPress = (e) => {
|
|
98
|
+
const target = getTargetProperties(e);
|
|
99
|
+
if (!target.isAllowed)
|
|
100
|
+
return e.preventDefault();
|
|
101
|
+
if (target.isDecimal) {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
if (target.next && allowCents)
|
|
104
|
+
focusAndHighlightText(target.next);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
;
|
|
108
|
+
if (target.name === "cents" && target.value.length >= 2 && !target.isHighligted) {
|
|
109
|
+
if (target.isNumber)
|
|
110
|
+
moveExtraCentsToDollars(`${target.value}${e.key}`);
|
|
111
|
+
return e.preventDefault();
|
|
112
|
+
}
|
|
113
|
+
updateLastState(e);
|
|
114
|
+
};
|
|
115
|
+
const onKeyUp = (e) => {
|
|
116
|
+
const target = getTargetProperties(e);
|
|
117
|
+
if (target.key === "ArrowLeft" && !target.isHighligted && target.previous && target.lastState.selectionStart === 0) {
|
|
118
|
+
const preservedValue = String(target.previous.value);
|
|
119
|
+
focusAndHighlightText(target.previous);
|
|
120
|
+
target.previous.value = preservedValue;
|
|
121
|
+
return e.preventDefault();
|
|
122
|
+
}
|
|
123
|
+
if (target.key === "ArrowRight" && !target.isHighligted && target.next && target.lastState.selectionStart === target.value.length) {
|
|
124
|
+
focusAndHighlightText(target.next);
|
|
125
|
+
return e.preventDefault();
|
|
126
|
+
}
|
|
127
|
+
if (target.isBackspace) {
|
|
128
|
+
if (target.lastState.value.length === 0 && target.previous) {
|
|
129
|
+
target.previous.focus();
|
|
130
|
+
e.preventDefault();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
updateLastState(e);
|
|
134
|
+
};
|
|
135
|
+
const onInput = (e) => {
|
|
136
|
+
const target = getTargetProperties(e);
|
|
137
|
+
if (!isNumericString(target.value)) {
|
|
138
|
+
target.element.value = target.lastState.value;
|
|
139
|
+
return e.preventDefault();
|
|
140
|
+
}
|
|
141
|
+
if (target.value.includes("-")) {
|
|
142
|
+
target.value = target.value.replace("-", "");
|
|
143
|
+
}
|
|
144
|
+
if (target.value.includes(".")) {
|
|
145
|
+
const parts = target.value.split(".");
|
|
146
|
+
dollars = parts[0];
|
|
147
|
+
cents = parts[1].padEnd(2, "0").substring(0, 2);
|
|
148
|
+
return e.preventDefault();
|
|
149
|
+
}
|
|
150
|
+
if (target.name === "cents" && target.value.length > 2) {
|
|
151
|
+
moveExtraCentsToDollars(target.value, false);
|
|
152
|
+
return e.preventDefault();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const onSaveStateEvent = (e) => {
|
|
156
|
+
if (dollars.length === 0)
|
|
157
|
+
dollars = "0";
|
|
158
|
+
if (cents.length === 0)
|
|
159
|
+
cents = "00";
|
|
160
|
+
updateLastState(e);
|
|
161
|
+
};
|
|
162
|
+
const onChange = () => {
|
|
163
|
+
let centValue = Math.abs(isNumericString(cents) ? Number(cents) : 0);
|
|
164
|
+
let dollarValue = Math.abs(isNumericString(dollars) ? Number(dollars) : 0);
|
|
165
|
+
if (isValueInCents)
|
|
166
|
+
value = dollarValue * 100 + centValue;
|
|
167
|
+
else
|
|
168
|
+
value = dollarValue + centValue / 100;
|
|
169
|
+
if (min && value < min)
|
|
170
|
+
value = min;
|
|
171
|
+
if (max && value > max)
|
|
172
|
+
value = max;
|
|
173
|
+
cents = String(centValue).padStart(2, "0");
|
|
174
|
+
};
|
|
12
175
|
</script>
|
|
13
176
|
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
>
|
|
177
|
+
<FormField {size}>
|
|
178
|
+
{#if $$slots.default}
|
|
179
|
+
<FormLabel {id}><slot /></FormLabel>
|
|
180
|
+
{/if}
|
|
181
|
+
<div class="input {currency}" class:allowCents {id}>
|
|
182
|
+
{#if prefix}
|
|
183
|
+
<span class="prefix">{prefix}</span>
|
|
184
|
+
{/if}
|
|
185
|
+
|
|
186
|
+
<input
|
|
187
|
+
class="dollars"
|
|
188
|
+
{placeholder}
|
|
189
|
+
bind:value={dollars}
|
|
190
|
+
type="text"
|
|
191
|
+
on:keypress={onKeyPress}
|
|
192
|
+
on:keyup={onKeyUp}
|
|
193
|
+
on:input={onInput}
|
|
194
|
+
on:change={onChange}
|
|
195
|
+
on:mouseup={onSaveStateEvent}
|
|
196
|
+
on:focus={onSaveStateEvent}
|
|
197
|
+
on:blur={onSaveStateEvent}
|
|
198
|
+
name="dollars"
|
|
199
|
+
id="{id}-dollars"
|
|
200
|
+
inputmode="numeric"
|
|
201
|
+
/>
|
|
202
|
+
{#if allowCents}
|
|
203
|
+
<span class="separator">.</span>
|
|
204
|
+
<input
|
|
205
|
+
{placeholder}
|
|
206
|
+
bind:value={cents}
|
|
207
|
+
type="text"
|
|
208
|
+
class="cents"
|
|
209
|
+
on:keypress={onKeyPress}
|
|
210
|
+
on:keyup={onKeyUp}
|
|
211
|
+
on:input={onInput}
|
|
212
|
+
on:change={onChange}
|
|
213
|
+
on:mouseup={onSaveStateEvent}
|
|
214
|
+
on:focus={onSaveStateEvent}
|
|
215
|
+
on:blur={onSaveStateEvent}
|
|
216
|
+
name="cents"
|
|
217
|
+
id="{id}-cents"
|
|
218
|
+
inputmode="numeric"
|
|
219
|
+
/>
|
|
220
|
+
{/if}
|
|
221
|
+
|
|
222
|
+
{#if suffix}
|
|
223
|
+
<span class="suffix">{suffix}</span>
|
|
224
|
+
{/if}
|
|
225
|
+
</div>
|
|
226
|
+
</FormField>
|
|
227
|
+
|
|
228
|
+
<style>.input {
|
|
229
|
+
display: flex;
|
|
230
|
+
align-items: center;
|
|
231
|
+
justify-content: flex-start;
|
|
232
|
+
position: relative;
|
|
233
|
+
width: 100%;
|
|
234
|
+
height: 100%;
|
|
235
|
+
border-radius: 0.25rem;
|
|
236
|
+
border: 1px solid var(--form-input-border, black);
|
|
237
|
+
background-color: var(--form-input-bg, white);
|
|
238
|
+
color: var(--form-input-fg, black);
|
|
239
|
+
font-size: 1rem;
|
|
240
|
+
font-weight: 500;
|
|
241
|
+
line-height: 2rem;
|
|
242
|
+
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out, fill 0.2s ease-in-out, stroke 0.2s ease-in-out;
|
|
243
|
+
user-select: none;
|
|
244
|
+
white-space: nowrap;
|
|
245
|
+
}
|
|
246
|
+
.input input {
|
|
247
|
+
background-color: transparent;
|
|
248
|
+
border: none;
|
|
249
|
+
line-height: 2rem;
|
|
250
|
+
font-size: 1rem;
|
|
251
|
+
padding-left: 1rem;
|
|
252
|
+
}
|
|
253
|
+
.input input:focus {
|
|
254
|
+
outline: none;
|
|
255
|
+
}
|
|
256
|
+
.input .dollars {
|
|
257
|
+
flex-grow: 1;
|
|
258
|
+
}
|
|
259
|
+
.input .separator {
|
|
260
|
+
width: 1rem;
|
|
261
|
+
text-align: center;
|
|
262
|
+
}
|
|
263
|
+
.input .cents {
|
|
264
|
+
width: 4rem;
|
|
265
|
+
}
|
|
266
|
+
.input .prefix,
|
|
267
|
+
.input .suffix {
|
|
268
|
+
font-size: 1rem;
|
|
269
|
+
line-height: 2rem;
|
|
270
|
+
padding-left: 1rem;
|
|
271
|
+
padding-right: 1rem;
|
|
272
|
+
background-color: var(--form-input-accent-bg, #ccc);
|
|
273
|
+
color: var(--form-input-accent-fg, black);
|
|
274
|
+
}
|
|
275
|
+
.input .prefix {
|
|
276
|
+
border-right: 1px solid var(--form-input-border, black);
|
|
277
|
+
}
|
|
278
|
+
.input .suffix {
|
|
279
|
+
border-left: 1px solid var(--form-input-border, black);
|
|
280
|
+
}</style>
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
-
import type
|
|
2
|
+
import { type FormFieldSizeOptions } from '../../index.js';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
5
|
value: number | null;
|
|
6
|
-
|
|
6
|
+
prefix?: string | undefined;
|
|
7
|
+
suffix?: string | undefined;
|
|
8
|
+
currency?: string | undefined;
|
|
7
9
|
allowCents?: boolean | undefined;
|
|
8
10
|
placeholder?: string | undefined;
|
|
9
11
|
size?: FormFieldSizeOptions | undefined;
|
|
10
|
-
|
|
12
|
+
units?: "ones" | "cents" | undefined;
|
|
11
13
|
min?: number | undefined;
|
|
12
|
-
max?: number | undefined;
|
|
14
|
+
max?: number | null | undefined;
|
|
13
15
|
};
|
|
14
16
|
events: {
|
|
17
|
+
change: CustomEvent<number | null>;
|
|
18
|
+
} & {
|
|
15
19
|
[evt: string]: CustomEvent<any>;
|
|
16
20
|
};
|
|
17
21
|
slots: {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
<script>import {
|
|
1
|
+
<script>import { roundToDecimals } from "../../helpers/round-to-decimals.js";
|
|
2
2
|
import { uniqueId } from "../../helpers/unique-id.js";
|
|
3
3
|
import FormField from "../form-field.svelte";
|
|
4
4
|
import FormLabel from "../form-label.svelte";
|
|
5
|
+
import { createEventDispatcher } from "svelte";
|
|
5
6
|
const id = uniqueId();
|
|
7
|
+
const dipatch = createEventDispatcher();
|
|
6
8
|
export let value = 0;
|
|
7
9
|
export let placeholder = "";
|
|
8
10
|
export let size = "full";
|
|
@@ -21,6 +23,27 @@ const valueChanged = () => {
|
|
|
21
23
|
if (value > max)
|
|
22
24
|
value = max;
|
|
23
25
|
value = roundToDecimals(value, decimals);
|
|
26
|
+
dipatch("change", value);
|
|
27
|
+
};
|
|
28
|
+
const onInput = (e) => {
|
|
29
|
+
const input = e.target;
|
|
30
|
+
const newValue = parseFloat(input.value);
|
|
31
|
+
if (isNaN(newValue))
|
|
32
|
+
return;
|
|
33
|
+
value = newValue;
|
|
34
|
+
};
|
|
35
|
+
const onKeyPress = (e) => {
|
|
36
|
+
const isNumber = !isNaN(Number(e.key));
|
|
37
|
+
const isDecimal = e.key === ".";
|
|
38
|
+
const isAllowed = isNumber || isDecimal || ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(e.key);
|
|
39
|
+
if (!isAllowed)
|
|
40
|
+
return e.preventDefault();
|
|
41
|
+
if (isDecimal && decimals === 0)
|
|
42
|
+
return e.preventDefault();
|
|
43
|
+
const newValue = `${value}${e.key}`;
|
|
44
|
+
const decimalPart = newValue.split(".")[1];
|
|
45
|
+
if (decimalPart && decimalPart.length > decimals)
|
|
46
|
+
return e.preventDefault();
|
|
24
47
|
};
|
|
25
48
|
</script>
|
|
26
49
|
|
|
@@ -42,6 +65,8 @@ const valueChanged = () => {
|
|
|
42
65
|
{min}
|
|
43
66
|
{max}
|
|
44
67
|
on:change={valueChanged}
|
|
68
|
+
on:input={onInput}
|
|
69
|
+
on:keypress={onKeyPress}
|
|
45
70
|
/>
|
|
46
71
|
|
|
47
72
|
{#if suffix}
|
|
@@ -2,7 +2,7 @@ import { SvelteComponent } from "svelte";
|
|
|
2
2
|
declare const __propDef: {
|
|
3
3
|
props: {
|
|
4
4
|
value: string;
|
|
5
|
-
type?: "
|
|
5
|
+
type?: "home" | "mobile" | "work" | "sms" | "fax" | "other" | undefined;
|
|
6
6
|
};
|
|
7
7
|
events: {
|
|
8
8
|
[evt: string]: CustomEvent<any>;
|