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.
@@ -1,16 +1,280 @@
1
- <script>import NumberBox from "../number-box/number-box.svelte";
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 symbol = "$";
4
- export let allowCents = false;
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 step = 1;
14
+ export let units = "ones";
8
15
  export let min = 0;
9
- export let max = 1e6;
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
- decimals = allowCents ? 2 : 0;
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
- <NumberBox bind:value prefix={symbol} {decimals} {placeholder} {size} {min} {max} {step}
15
- ><slot /></NumberBox
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 { FormFieldSizeOptions } from '../../index.js';
2
+ import { type FormFieldSizeOptions } from '../../index.js';
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  value: number | null;
6
- symbol?: string | undefined;
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
- step?: number | undefined;
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 { formatNumber, roundToDecimals } from "../../helpers/round-to-decimals.js";
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}
@@ -14,6 +14,8 @@ declare const __propDef: {
14
14
  step?: number | undefined;
15
15
  };
16
16
  events: {
17
+ change: CustomEvent<number | null>;
18
+ } & {
17
19
  [evt: string]: CustomEvent<any>;
18
20
  };
19
21
  slots: {
@@ -96,7 +96,6 @@ const keyUp = (event) => {
96
96
  return props.value;
97
97
  })();
98
98
  if (newValue.length >= props.maxLength) {
99
- console.log(event.key);
100
99
  props.target.value = newValue.slice(0, props.maxLength);
101
100
  if (props.nextInput)
102
101
  props.nextInput.focus();
@@ -2,7 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  value: string;
5
- type?: "sms" | "mobile" | "home" | "work" | "fax" | "other" | undefined;
5
+ type?: "home" | "mobile" | "work" | "sms" | "fax" | "other" | undefined;
6
6
  };
7
7
  events: {
8
8
  [evt: string]: CustomEvent<any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveltacular",
3
- "version": "0.0.70",
3
+ "version": "0.0.72",
4
4
  "description": "A Svelte component library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",