sveltacular 1.0.1 → 1.0.5

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 (32) hide show
  1. package/README.md +63 -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 +4 -4
  16. package/dist/forms/index.js +4 -4
  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/phone-box/phone-box.svelte +25 -13
  22. package/dist/forms/radio-group/radio-group.svelte +1 -1
  23. package/dist/forms/slider/slider.svelte +15 -15
  24. package/dist/forms/text-area/text-area.svelte +1 -1
  25. package/dist/forms/text-box/text-box.svelte +1 -1
  26. package/dist/forms/time-box/time-box.svelte +42 -17
  27. package/dist/generic/avatar/avatar.svelte +3 -0
  28. package/dist/generic/chip/chip.svelte +3 -0
  29. package/dist/navigation/context-menu/README.md +3 -0
  30. package/dist/navigation/context-menu/context-menu-divider.svelte +3 -0
  31. package/dist/sveltacular.css +522 -0
  32. package/package.json +11 -5
package/README.md CHANGED
@@ -12,6 +12,28 @@ npm i sveltacular
12
12
 
13
13
  ## Quick Start
14
14
 
15
+ ### 1. Import the default stylesheet (once, in your app root)
16
+
17
+ **For SvelteKit**, add this to `src/routes/+layout.svelte`:
18
+
19
+ ```svelte
20
+ <script lang="ts">
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
+
15
37
  ```svelte
16
38
  <script lang="ts">
17
39
  import { Button } from 'sveltacular';
@@ -20,9 +42,12 @@ npm i sveltacular
20
42
  <Button variant="primary" label="Hello World" />
21
43
  ```
22
44
 
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).
46
+
23
47
  ## Component Catalog
24
48
 
25
49
  ### Forms
50
+
26
51
  - **Button** - Multiple variants (primary, secondary, positive, danger, outline)
27
52
  - **TextBox** - Text input with validation and formatting options
28
53
  - **NumberBox** - Number input with min/max/decimals
@@ -38,6 +63,7 @@ npm i sveltacular
38
63
  - **Form** - Form container with validation
39
64
 
40
65
  ### Generic Components
66
+
41
67
  - **Card** - Card container
42
68
  - **Pill** - Badge/pill component
43
69
  - **Badge** - Notification badge
@@ -52,6 +78,7 @@ npm i sveltacular
52
78
  - **List** - Styled list component
53
79
 
54
80
  ### Navigation
81
+
55
82
  - **AppBar** - Application bar
56
83
  - **SideBar** - Side navigation
57
84
  - **Breadcrumbs** - Breadcrumb navigation
@@ -62,16 +89,19 @@ npm i sveltacular
62
89
  - **Drawer** - Slide-out drawer
63
90
 
64
91
  ### Modals
92
+
65
93
  - **Modal** - Generic modal dialog
66
94
  - **Alert** - Alert dialog
67
95
  - **Confirm** - Confirmation dialog
68
96
  - **Prompt** - Input prompt dialog
69
97
 
70
98
  ### Tables
99
+
71
100
  - **Table** - Table component with header/body/footer
72
101
  - **DataGrid** - Advanced data grid
73
102
 
74
103
  ### Typography
104
+
75
105
  - **Headline** - Heading component
76
106
  - **Subtitle** - Subtitle component
77
107
  - **Text** - Text component
@@ -79,6 +109,7 @@ npm i sveltacular
79
109
  - **CodeBlock** - Code block
80
110
 
81
111
  ### Layout
112
+
82
113
  - **FlexRow** / **FlexCol** - Flexbox layout
83
114
  - **Grid** - Grid layout
84
115
 
@@ -102,11 +133,19 @@ import { CheckBox, CheckBoxGroup } from 'sveltacular/forms/check-box';
102
133
 
103
134
  ## Theming
104
135
 
105
- Sveltacular uses CSS variables for theming. See [THEMING.md](./THEMING.md) for a complete list of available CSS variables.
136
+ Sveltacular uses CSS variables for theming. When you import `sveltacular/styles.css`, all default CSS variables are included. You can override any of these variables to customize the appearance of components.
106
137
 
107
- Example:
138
+ See [THEMING.md](./THEMING.md) for a complete list of available CSS variables.
139
+
140
+ ### Customizing the Theme
141
+
142
+ Override CSS variables in your own stylesheet (after importing the default styles):
108
143
 
109
144
  ```css
145
+ /* Import default styles first */
146
+ @import 'sveltacular/styles.css';
147
+
148
+ /* Then override variables as needed */
110
149
  :root {
111
150
  --button-primary-bg: #1e88e5;
112
151
  --form-input-border: #e0e0e0;
@@ -114,6 +153,27 @@ Example:
114
153
  }
115
154
  ```
116
155
 
156
+ Or in a Svelte component:
157
+
158
+ ```svelte
159
+ <script>
160
+ import 'sveltacular/styles.css';
161
+ import { Button } from 'sveltacular';
162
+ </script>
163
+
164
+ <style>
165
+ :global(:root) {
166
+ --button-primary-bg: #1e88e5;
167
+ --form-input-border: #e0e0e0;
168
+ --base-color-bg: #ffffff;
169
+ }
170
+ </style>
171
+ ```
172
+
173
+ ### Providing Your Own Theme
174
+
175
+ If you prefer not to use the default stylesheet, you can define all CSS variables yourself. See [THEMING.md](./THEMING.md) for the complete list of required variables.
176
+
117
177
  ## Form Validation
118
178
 
119
179
  Sveltacular includes a validation system:
@@ -136,6 +196,7 @@ if (!result.isValid) {
136
196
  ## Accessibility
137
197
 
138
198
  Sveltacular components include:
199
+
139
200
  - ARIA attributes for screen readers
140
201
  - Keyboard navigation support
141
202
  - Focus management utilities
@@ -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;
@@ -11,16 +11,16 @@ export { default as TextArea } from './text-area/text-area.svelte';
11
11
  export { default as TextBox } from './text-box/text-box.svelte';
12
12
  export { default as UrlBox } from './url-box/url-box.svelte';
13
13
  export * from './check-box/index.js';
14
- export * from './combo-box/index.js';
15
14
  export * from './list-box/index.js';
16
15
  export * from './phone-box/index.js';
17
16
  export * from './radio-group/index.js';
18
17
  export { default as Form } from './form.svelte';
19
- export { default as FormField } from './form-field.svelte';
18
+ export { default as FormField } from './form-field/form-field.svelte';
20
19
  export { default as FormFooter } from './form-footer.svelte';
21
20
  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';
21
+ export { default as FormLabel } from './form-label/form-label.svelte';
22
+ export { default as FormSection } from './form-section/form-section.svelte';
23
+ export { default as FormRow } from './form-row/form-row.svelte';
24
24
  export { default as Slider } from './slider/slider.svelte';
25
25
  export { default as TimeBox } from './time-box/time-box.svelte';
26
26
  export * from './validation.js';
@@ -13,17 +13,17 @@ export { default as TextBox } from './text-box/text-box.svelte';
13
13
  export { default as UrlBox } from './url-box/url-box.svelte';
14
14
  // Form components with barrel files
15
15
  export * from './check-box/index.js';
16
- export * from './combo-box/index.js';
17
16
  export * from './list-box/index.js';
18
17
  export * from './phone-box/index.js';
19
18
  export * from './radio-group/index.js';
20
19
  // Form structure components
21
20
  export { default as Form } from './form.svelte';
22
- export { default as FormField } from './form-field.svelte';
21
+ export { default as FormField } from './form-field/form-field.svelte';
23
22
  export { default as FormFooter } from './form-footer.svelte';
24
23
  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';
24
+ export { default as FormLabel } from './form-label/form-label.svelte';
25
+ export { default as FormSection } from './form-section/form-section.svelte';
26
+ export { default as FormRow } from './form-row/form-row.svelte';
27
27
  // New form components
28
28
  export { default as Slider } from './slider/slider.svelte';
29
29
  export { default as TimeBox } from './time-box/time-box.svelte';
@@ -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}>