sveltacular 1.0.4 → 1.0.6

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 (36) hide show
  1. package/README.md +21 -2
  2. package/dist/forms/base-input-wrapper.svelte +0 -2
  3. package/dist/forms/check-box/check-box-group.svelte +2 -2
  4. package/dist/forms/date-box/date-box.svelte +15 -4
  5. package/dist/forms/file-box/file-box.svelte +1 -1
  6. package/dist/forms/form-field/form-field.svelte +86 -0
  7. package/dist/forms/form-field/form-field.svelte.d.ts +16 -0
  8. package/dist/forms/form-footer.svelte +7 -5
  9. package/dist/forms/form-label/form-label.svelte +30 -0
  10. package/dist/forms/form-label/form-label.svelte.d.ts +9 -0
  11. package/dist/forms/form-row/form-row.svelte +29 -0
  12. package/dist/forms/form-row/form-row.svelte.d.ts +7 -0
  13. package/dist/forms/form-section/form-section.svelte +36 -0
  14. package/dist/forms/form-section/form-section.svelte.d.ts +10 -0
  15. package/dist/forms/index.d.ts +10 -6
  16. package/dist/forms/index.js +10 -7
  17. package/dist/forms/info-box/info-box.svelte +1 -1
  18. package/dist/forms/list-box/list-box.svelte +3 -3
  19. package/dist/forms/money-box/money-box.svelte +27 -17
  20. package/dist/forms/number-box/number-box.svelte +5 -2
  21. package/dist/forms/number-range-box/number-range-box.svelte +218 -0
  22. package/dist/forms/number-range-box/number-range-box.svelte.d.ts +21 -0
  23. package/dist/forms/phone-box/phone-box.svelte +25 -13
  24. package/dist/forms/radio-group/radio-group.svelte +1 -1
  25. package/dist/forms/slider/slider.svelte +15 -15
  26. package/dist/forms/tag-input-box/tag-input-box.svelte +203 -0
  27. package/dist/forms/tag-input-box/tag-input-box.svelte.d.ts +17 -0
  28. package/dist/forms/text-area/text-area.svelte +1 -1
  29. package/dist/forms/text-box/text-box.svelte +1 -1
  30. package/dist/forms/time-box/time-box.svelte +42 -17
  31. package/dist/generic/avatar/avatar.svelte +3 -0
  32. package/dist/generic/chip/chip.svelte +3 -0
  33. package/dist/navigation/context-menu/README.md +3 -0
  34. package/dist/navigation/context-menu/context-menu-divider.svelte +3 -0
  35. package/dist/sveltacular.css +5 -1
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -12,18 +12,37 @@ npm i sveltacular
12
12
 
13
13
  ## Quick Start
14
14
 
15
- First, import the default styles to get all CSS variables and base styles:
15
+ ### 1. Import the default stylesheet (once, in your app root)
16
+
17
+ **For SvelteKit**, add this to `src/routes/+layout.svelte`:
16
18
 
17
19
  ```svelte
18
20
  <script lang="ts">
19
21
  import 'sveltacular/styles.css';
22
+ </script>
23
+
24
+ <slot />
25
+ ```
26
+
27
+ **For regular Svelte**, add it to your main `App.svelte` or root component:
28
+
29
+ ```svelte
30
+ <script lang="ts">
31
+ import 'sveltacular/styles.css';
32
+ </script>
33
+ ```
34
+
35
+ ### 2. Use components anywhere
36
+
37
+ ```svelte
38
+ <script lang="ts">
20
39
  import { Button } from 'sveltacular';
21
40
  </script>
22
41
 
23
42
  <Button variant="primary" label="Hello World" />
24
43
  ```
25
44
 
26
- **Note**: The styles import is required for components to render with default styling. If you prefer to provide your own theme, you can skip this import and define your own CSS variables (see [Theming](#theming) below).
45
+ **Note**: The stylesheet import is required for components to render with default styling. Import it once at the app level (not in individual components). If you prefer to provide your own theme, you can skip this import and define your own CSS variables (see [Theming](#theming) below).
27
46
 
28
47
  ## Component Catalog
29
48
 
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { FormFieldSizeOptions } from '../types/form.js';
4
3
 
5
4
  let {
6
5
  id,
@@ -96,4 +95,3 @@
96
95
  padding: var(--spacing-xs);
97
96
  color: var(--form-input-error-fg);
98
97
  }</style>
99
-
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { untrack } from 'svelte';
3
3
  import type { DropdownOption, FormFieldSizeOptions } from '../../types/form.js';
4
- import FormField from '../form-field.svelte';
4
+ import FormField from '../form-field/form-field.svelte';
5
5
  import CheckBox from './check-box.svelte';
6
6
  import { uniqueId } from '../../helpers/unique-id.js';
7
7
 
@@ -34,7 +34,7 @@
34
34
  // Track items and group as dependencies
35
35
  const currentItems = items;
36
36
  const currentGroup = group;
37
-
37
+
38
38
  // Use untrack to prevent writing to itemsWithState from triggering this effect again
39
39
  untrack(() => {
40
40
  // Rebuild itemsWithState from items, using group to determine checked state
@@ -1,12 +1,18 @@
1
1
  <script lang="ts">
2
2
  import { untrack } from 'svelte';
3
- import { addUnits, currentDateTime, isDateString, isDateOrDateTimeString, isDateTimeString } from '../../helpers/date.js';
3
+ import {
4
+ addUnits,
5
+ currentDateTime,
6
+ isDateString,
7
+ isDateOrDateTimeString,
8
+ isDateTimeString
9
+ } from '../../helpers/date.js';
4
10
  import { uniqueId } from '../../helpers/unique-id.js';
5
- import FormField from '../form-field.svelte';
11
+ import FormField from '../form-field/form-field.svelte';
6
12
  import type { DateUnit, FormFieldSizeOptions } from '../../index.js';
7
13
  import Button from '../button/button.svelte';
8
14
 
9
- type DateIncrementStep = { label: string; value: number, unit: DateUnit };
15
+ type DateIncrementStep = { label: string; value: number; unit: DateUnit };
10
16
 
11
17
  const id = uniqueId();
12
18
 
@@ -91,7 +97,12 @@
91
97
  {#if steps.length > 0}
92
98
  <span class="steps">
93
99
  {#each steps as step}
94
- <Button noMargin={true} collapse={true} onClick={() => incrementValue(step)} label={step.label} />
100
+ <Button
101
+ noMargin={true}
102
+ collapse={true}
103
+ onClick={() => incrementValue(step)}
104
+ label={step.label}
105
+ />
95
106
  {/each}
96
107
  </span>
97
108
  {/if}
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import { uniqueId } from '../../helpers/unique-id.js';
4
- import FormField from '../form-field.svelte';
4
+ import FormField from '../form-field/form-field.svelte';
5
5
  import type { FormFieldSizeOptions } from '../../types/form.js';
6
6
 
7
7
  const id = uniqueId();
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { ComponentSize } from '../../types/size.js';
4
+ import { getMaxWidth, getDisplayType } from '../../types/size.js';
5
+ import FormLabel from '../form-label/form-label.svelte';
6
+
7
+ let {
8
+ size = 'full',
9
+ label = undefined,
10
+ id = undefined,
11
+ required = false,
12
+ disabled = false,
13
+ helperText = undefined,
14
+ errorText = undefined,
15
+ successText = undefined,
16
+ children
17
+ }: {
18
+ size?: ComponentSize;
19
+ label?: string | undefined;
20
+ id?: string | undefined;
21
+ required?: boolean;
22
+ disabled?: boolean;
23
+ helperText?: string | undefined;
24
+ errorText?: string | undefined;
25
+ successText?: string | undefined;
26
+ children: Snippet;
27
+ } = $props();
28
+
29
+ let displayType = $derived(getDisplayType(size));
30
+ let maxWidth = $derived(getMaxWidth(size));
31
+
32
+ let showHelperText = $derived(!!helperText && !errorText && !successText);
33
+ let showSuccessText = $derived(!!successText && !errorText);
34
+ let showErrorText = $derived(!!errorText);
35
+ </script>
36
+
37
+ <div class="form-field {size} {displayType} {maxWidth}">
38
+ {#if label}
39
+ <FormLabel {id} {required} {disabled} {label} />
40
+ {/if}
41
+ {@render children?.()}
42
+ {#if showHelperText}
43
+ <div class="helper-text" id="{id}-helper">{helperText}</div>
44
+ {/if}
45
+ {#if showSuccessText}
46
+ <div class="success-text" id="{id}-success" role="status" aria-live="polite">
47
+ {successText}
48
+ </div>
49
+ {/if}
50
+ {#if showErrorText}
51
+ <div class="error-text" id="{id}-error" role="alert" aria-live="assertive">
52
+ {errorText}
53
+ </div>
54
+ {/if}
55
+ </div>
56
+
57
+ <style>/* ============================================
58
+ BREAKPOINTS - Responsive Design
59
+ ============================================ */
60
+ .form-field {
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 0.25rem;
64
+ flex: 1;
65
+ }
66
+ @media (max-width: 479.98px) {
67
+ .form-field {
68
+ width: 100%;
69
+ }
70
+ }
71
+
72
+ .success-text {
73
+ font-size: var(--font-sm);
74
+ line-height: 1.25rem;
75
+ padding: var(--spacing-xs);
76
+ color: var(--success, #28a745);
77
+ font-weight: 500;
78
+ }
79
+
80
+ .error-text {
81
+ font-size: var(--font-sm);
82
+ line-height: 1.25rem;
83
+ padding: var(--spacing-xs);
84
+ color: var(--danger, #dc3545);
85
+ font-weight: 500;
86
+ }</style>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ComponentSize } from '../../types/size.js';
3
+ type $$ComponentProps = {
4
+ size?: ComponentSize;
5
+ label?: string | undefined;
6
+ id?: string | undefined;
7
+ required?: boolean;
8
+ disabled?: boolean;
9
+ helperText?: string | undefined;
10
+ errorText?: string | undefined;
11
+ successText?: string | undefined;
12
+ children: Snippet;
13
+ };
14
+ declare const FormField: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type FormField = ReturnType<typeof FormField>;
16
+ export default FormField;
@@ -1,18 +1,20 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import FlexRow from '../layout/flex-row.svelte';
4
3
 
5
4
  let { children }: { children: Snippet } = $props();
6
5
  </script>
7
6
 
8
7
  <div>
9
- <FlexRow justifyContent="stretch">
10
- {@render children?.()}
11
- </FlexRow>
8
+ {@render children?.()}
12
9
  </div>
13
10
 
14
11
  <style>
15
12
  div {
16
- margin-top: 1.5rem;
13
+ display: flex;
14
+ flex-direction: row;
15
+ gap: var(--spacing-base);
16
+ justify-content: flex-end;
17
+ margin-top: var(--spacing-lg);
18
+ margin-top: var(--spacing-xl);
17
19
  }
18
20
  </style>
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ let {
3
+ id = undefined,
4
+ required = false,
5
+ disabled = false,
6
+ label = ''
7
+ }: {
8
+ id?: string | undefined;
9
+ required?: boolean;
10
+ disabled?: boolean;
11
+ label?: string;
12
+ } = $props();
13
+ </script>
14
+
15
+ <label for={id} class:required class:disabled aria-required={required}>{label}</label>
16
+
17
+ <style>label {
18
+ display: block;
19
+ margin-bottom: var(--spacing-sm);
20
+ font-weight: 500;
21
+ font-size: var(--font-base);
22
+ }
23
+ label.required::after {
24
+ content: "*";
25
+ margin-left: var(--spacing-xs);
26
+ }
27
+ label.disabled {
28
+ opacity: 0.5;
29
+ cursor: not-allowed;
30
+ }</style>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ id?: string | undefined;
3
+ required?: boolean;
4
+ disabled?: boolean;
5
+ label?: string;
6
+ };
7
+ declare const FormLabel: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type FormLabel = ReturnType<typeof FormLabel>;
9
+ export default FormLabel;
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ children: Snippet;
6
+ }
7
+
8
+ let { children }: Props = $props();
9
+ </script>
10
+
11
+ <div class="form-row">
12
+ {@render children()}
13
+ </div>
14
+
15
+ <style>/* ============================================
16
+ BREAKPOINTS - Responsive Design
17
+ ============================================ */
18
+ .form-row {
19
+ display: flex;
20
+ flex-direction: row;
21
+ gap: var(--spacing-base);
22
+ align-items: flex-start;
23
+ }
24
+ @media (max-width: 479.98px) {
25
+ .form-row {
26
+ flex-direction: column;
27
+ align-items: stretch;
28
+ }
29
+ }</style>
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ children: Snippet;
4
+ }
5
+ declare const FormRow: import("svelte").Component<Props, {}, "">;
6
+ type FormRow = ReturnType<typeof FormRow>;
7
+ export default FormRow;
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { SectionLevel } from '../../types/generic.js';
4
+
5
+ let {
6
+ title = undefined,
7
+ level = 4,
8
+ children
9
+ }: {
10
+ title?: string | undefined;
11
+ level?: SectionLevel;
12
+ children: Snippet;
13
+ } = $props();
14
+ </script>
15
+
16
+ <fieldset>
17
+ {#if title}
18
+ <legend aria-level={level}>{title}</legend>
19
+ {/if}
20
+ {@render children?.()}
21
+ </fieldset>
22
+
23
+ <style>fieldset {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--spacing-base);
27
+ border: var(--border-thin) solid var(--form-input-border);
28
+ border-radius: var(--radius-md);
29
+ padding: var(--spacing-base);
30
+ }
31
+ fieldset legend {
32
+ font-size: var(--font-lg);
33
+ font-weight: 500;
34
+ line-height: var(--line-height-base);
35
+ color: var(--form-input-fg);
36
+ }</style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { SectionLevel } from '../../types/generic.js';
3
+ type $$ComponentProps = {
4
+ title?: string | undefined;
5
+ level?: SectionLevel;
6
+ children: Snippet;
7
+ };
8
+ declare const FormSection: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type FormSection = ReturnType<typeof FormSection>;
10
+ export default FormSection;
@@ -7,20 +7,24 @@ export { default as InfoBox } from './info-box/info-box.svelte';
7
7
  export { default as MoneyBox } from './money-box/money-box.svelte';
8
8
  export { default as NewOrExistingCombo } from './combo/new-or-existing-combo.svelte';
9
9
  export { default as NumberBox } from './number-box/number-box.svelte';
10
+ export { default as NumberRangeBox } from './number-range-box/number-range-box.svelte';
11
+ export { default as PhoneBox } from './phone-box/phone-box.svelte';
12
+ export { default as Slider } from './slider/slider.svelte';
13
+ export { default as SwitchBox } from './switch-box/switch-box.svelte';
14
+ export { default as TagInputBox } from './tag-input-box/tag-input-box.svelte';
10
15
  export { default as TextArea } from './text-area/text-area.svelte';
11
16
  export { default as TextBox } from './text-box/text-box.svelte';
17
+ export { default as TimeBox } from './time-box/time-box.svelte';
12
18
  export { default as UrlBox } from './url-box/url-box.svelte';
13
19
  export * from './check-box/index.js';
14
- export * from './combo-box/index.js';
15
20
  export * from './list-box/index.js';
16
21
  export * from './phone-box/index.js';
17
22
  export * from './radio-group/index.js';
18
23
  export { default as Form } from './form.svelte';
19
- export { default as FormField } from './form-field.svelte';
24
+ export { default as FormField } from './form-field/form-field.svelte';
20
25
  export { default as FormFooter } from './form-footer.svelte';
21
26
  export { default as FormHeader } from './form-header.svelte';
22
- export { default as FormLabel } from './form-label.svelte';
23
- export { default as FormSection } from './form-section.svelte';
24
- export { default as Slider } from './slider/slider.svelte';
25
- export { default as TimeBox } from './time-box/time-box.svelte';
27
+ export { default as FormLabel } from './form-label/form-label.svelte';
28
+ export { default as FormSection } from './form-section/form-section.svelte';
29
+ export { default as FormRow } from './form-row/form-row.svelte';
26
30
  export * from './validation.js';
@@ -8,24 +8,27 @@ export { default as InfoBox } from './info-box/info-box.svelte';
8
8
  export { default as MoneyBox } from './money-box/money-box.svelte';
9
9
  export { default as NewOrExistingCombo } from './combo/new-or-existing-combo.svelte';
10
10
  export { default as NumberBox } from './number-box/number-box.svelte';
11
+ export { default as NumberRangeBox } from './number-range-box/number-range-box.svelte';
12
+ export { default as PhoneBox } from './phone-box/phone-box.svelte';
13
+ export { default as Slider } from './slider/slider.svelte';
14
+ export { default as SwitchBox } from './switch-box/switch-box.svelte';
15
+ export { default as TagInputBox } from './tag-input-box/tag-input-box.svelte';
11
16
  export { default as TextArea } from './text-area/text-area.svelte';
12
17
  export { default as TextBox } from './text-box/text-box.svelte';
18
+ export { default as TimeBox } from './time-box/time-box.svelte';
13
19
  export { default as UrlBox } from './url-box/url-box.svelte';
14
20
  // Form components with barrel files
15
21
  export * from './check-box/index.js';
16
- export * from './combo-box/index.js';
17
22
  export * from './list-box/index.js';
18
23
  export * from './phone-box/index.js';
19
24
  export * from './radio-group/index.js';
20
25
  // Form structure components
21
26
  export { default as Form } from './form.svelte';
22
- export { default as FormField } from './form-field.svelte';
27
+ export { default as FormField } from './form-field/form-field.svelte';
23
28
  export { default as FormFooter } from './form-footer.svelte';
24
29
  export { default as FormHeader } from './form-header.svelte';
25
- export { default as FormLabel } from './form-label.svelte';
26
- export { default as FormSection } from './form-section.svelte';
27
- // New form components
28
- export { default as Slider } from './slider/slider.svelte';
29
- export { default as TimeBox } from './time-box/time-box.svelte';
30
+ export { default as FormLabel } from './form-label/form-label.svelte';
31
+ export { default as FormSection } from './form-section/form-section.svelte';
32
+ export { default as FormRow } from './form-row/form-row.svelte';
30
33
  // Validation utilities
31
34
  export * from './validation.js';
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import LinkIcon from '../../icons/link-icon.svelte';
3
3
  import type { FormFieldSizeOptions } from '../../index.js';
4
- import FormField from '../form-field.svelte';
4
+ import FormField from '../form-field/form-field.svelte';
5
5
 
6
6
  let {
7
7
  size = 'md' as FormFieldSizeOptions,
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { DropdownOption, FormFieldSizeOptions, MenuOption } from '../../types/form.js';
3
- import FormField from '../form-field.svelte';
3
+ import FormField from '../form-field/form-field.svelte';
4
4
  import { uniqueId } from '../../helpers/unique-id.js';
5
5
  import Menu from '../../generic/menu/menu.svelte';
6
6
  import AngleUpIcon from '../../icons/angle-up-icon.svelte';
@@ -45,7 +45,7 @@
45
45
  let highlightIndex = $state(-1);
46
46
  let filteredItems = $state<MenuOption[]>([]);
47
47
  let isSeachable = $derived(searchable || !!search);
48
-
48
+
49
49
  // Get the ID of the highlighted option for ARIA
50
50
  let activeDescendant = $derived(
51
51
  highlightIndex >= 0 && filteredItems[highlightIndex]
@@ -201,7 +201,7 @@
201
201
  {open}
202
202
  closeAfterSelect={false}
203
203
  searchText={text}
204
- onSelect={onSelect}
204
+ {onSelect}
205
205
  size="full"
206
206
  bind:highlightIndex
207
207
  bind:value
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { uniqueId, type FormFieldSizeOptions } from '../../index.js';
3
- import FormField from '../form-field.svelte';
3
+ import FormField from '../form-field/form-field.svelte';
4
4
  import { untrack } from 'svelte';
5
-
5
+
6
6
  const id = uniqueId();
7
7
 
8
8
  let {
@@ -35,18 +35,18 @@
35
35
 
36
36
  let isValueInCents = $derived(units === 'cents');
37
37
  const fieldOrder = ['dollars', 'cents'];
38
-
38
+
39
39
  const getDollarsFromValue = () => {
40
40
  if (!value) return '0';
41
41
  if (isValueInCents) return String(Math.abs(Math.floor(value / 100)));
42
42
  return String(Math.abs(Math.floor(value)));
43
- }
43
+ };
44
44
 
45
45
  const getCentsFromValue = () => {
46
46
  if (!value) return '00';
47
47
  if (isValueInCents) return String(Math.abs(Math.round(value % 100))).padStart(2, '0');
48
48
  return String(Math.abs(Math.round((value % 1) * 100))).padStart(2, '0');
49
- }
49
+ };
50
50
 
51
51
  let dollars = $state(getDollarsFromValue());
52
52
  let cents = $state(getCentsFromValue());
@@ -74,9 +74,10 @@
74
74
  const selection = [target.selectionStart ?? 0, target.selectionEnd ?? 0];
75
75
  const key = e instanceof KeyboardEvent ? e.key : '';
76
76
  const isNumber = !isNaN(Number(key));
77
- const isDecimal =key === '.';
77
+ const isDecimal = key === '.';
78
78
  const isBackspace = key === 'Backspace';
79
- const isAllowed = isNumber || isDecimal || ['Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key);
79
+ const isAllowed =
80
+ isNumber || isDecimal || ['Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key);
80
81
  return {
81
82
  element: target,
82
83
  name,
@@ -91,8 +92,8 @@
91
92
  isAllowed,
92
93
  lastState: lastState[index],
93
94
  value: target.value,
94
- next: nextName ? document.getElementById(`${id}-${nextName}`) as HTMLInputElement : null,
95
- previous: prevName ? document.getElementById(`${id}-${prevName}`) as HTMLInputElement : null
95
+ next: nextName ? (document.getElementById(`${id}-${nextName}`) as HTMLInputElement) : null,
96
+ previous: prevName ? (document.getElementById(`${id}-${prevName}`) as HTMLInputElement) : null
96
97
  };
97
98
  };
98
99
 
@@ -116,8 +117,8 @@
116
117
 
117
118
  const moveExtraCentsToDollars = (centsValue: string, append = true) => {
118
119
  if (centsValue.length > 2 && isNumericString(centsValue) && Number(centsValue) > 0) {
119
- const whole = centsValue.substring(0, centsValue.length -2);
120
- const decimal = centsValue.substring(centsValue.length -2);
120
+ const whole = centsValue.substring(0, centsValue.length - 2);
121
+ const decimal = centsValue.substring(centsValue.length - 2);
121
122
  dollars = append ? `${dollars}${whole}` : whole;
122
123
  cents = decimal;
123
124
  }
@@ -130,7 +131,7 @@
130
131
  e.preventDefault();
131
132
  if (target.next && allowCents) focusAndHighlightText(target.next);
132
133
  return;
133
- };
134
+ }
134
135
  if (target.name === 'cents' && target.value.length >= 2 && !target.isHighligted) {
135
136
  if (target.isNumber) moveExtraCentsToDollars(`${target.value}${e.key}`);
136
137
  return e.preventDefault();
@@ -141,14 +142,24 @@
141
142
  const onKeyUp = (e: KeyboardEvent) => {
142
143
  const target = getTargetProperties(e);
143
144
  // Back arrow
144
- if (target.key === 'ArrowLeft' && !target.isHighligted && target.previous && target.lastState.selectionStart === 0) {
145
+ if (
146
+ target.key === 'ArrowLeft' &&
147
+ !target.isHighligted &&
148
+ target.previous &&
149
+ target.lastState.selectionStart === 0
150
+ ) {
145
151
  const preservedValue = String(target.previous.value);
146
152
  focusAndHighlightText(target.previous);
147
153
  target.previous.value = preservedValue;
148
154
  return e.preventDefault();
149
155
  }
150
156
  // Right arrow
151
- if (target.key === 'ArrowRight' && !target.isHighligted && target.next && target.lastState.selectionStart === target.value.length) {
157
+ if (
158
+ target.key === 'ArrowRight' &&
159
+ !target.isHighligted &&
160
+ target.next &&
161
+ target.lastState.selectionStart === target.value.length
162
+ ) {
152
163
  focusAndHighlightText(target.next);
153
164
  return e.preventDefault();
154
165
  }
@@ -197,8 +208,8 @@
197
208
  let centValue = Math.abs(isNumericString(cents) ? Number(cents) : 0);
198
209
  let dollarValue = Math.abs(isNumericString(dollars) ? Number(dollars) : 0);
199
210
  // Update value
200
- if (isValueInCents) value = (dollarValue * 100) + centValue;
201
- else value = dollarValue + (centValue / 100);
211
+ if (isValueInCents) value = dollarValue * 100 + centValue;
212
+ else value = dollarValue + centValue / 100;
202
213
  // Enforce min and max
203
214
  if (min && value < min) value = min;
204
215
  if (max && value > max) value = max;
@@ -206,7 +217,6 @@
206
217
  cents = String(centValue).padStart(2, '0');
207
218
  onChange?.(value);
208
219
  };
209
-
210
220
  </script>
211
221
 
212
222
  <FormField {size} {label} {id}>
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { roundToDecimals } from '../../helpers/round-to-decimals.js';
3
3
  import { uniqueId } from '../../helpers/unique-id.js';
4
- import FormField from '../form-field.svelte';
4
+ import FormField from '../form-field/form-field.svelte';
5
5
  import type { FormFieldSizeOptions } from '../../types/form.js';
6
6
  const id = uniqueId();
7
7
 
@@ -54,7 +54,10 @@
54
54
  const onKeyPress = (e: KeyboardEvent) => {
55
55
  const isNumber = !isNaN(Number(e.key));
56
56
  const isDecimal = e.key === '.';
57
- const isAllowed = isNumber || isDecimal || ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(e.key);
57
+ const isAllowed =
58
+ isNumber ||
59
+ isDecimal ||
60
+ ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(e.key);
58
61
  if (!isAllowed) return e.preventDefault();
59
62
  if (isDecimal && decimals === 0) return e.preventDefault();
60
63
  const newValue = `${value}${e.key}`;