sveltacular 1.0.18 → 1.0.21

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 (42) hide show
  1. package/README.md +1 -2
  2. package/dist/forms/bool-box/bool-box.svelte +392 -13
  3. package/dist/forms/bool-box/bool-box.svelte.d.ts +2 -0
  4. package/dist/forms/bool-box/index.d.ts +2 -0
  5. package/dist/forms/bool-box/index.js +2 -0
  6. package/dist/forms/date-box/date-box.svelte +104 -53
  7. package/dist/forms/date-box/date-box.svelte.d.ts +8 -3
  8. package/dist/forms/dimension-box/dimension-box.svelte +15 -50
  9. package/dist/forms/file-box/file-box.svelte +7 -25
  10. package/dist/forms/form-input-wrapper/form-input-wrapper.svelte +203 -0
  11. package/dist/forms/form-input-wrapper/form-input-wrapper.svelte.d.ts +16 -0
  12. package/dist/forms/form-input-wrapper/index.d.ts +2 -0
  13. package/dist/forms/form-input-wrapper/index.js +2 -0
  14. package/dist/forms/index.d.ts +0 -1
  15. package/dist/forms/index.js +0 -1
  16. package/dist/forms/list-box/list-box.svelte +26 -9
  17. package/dist/forms/list-box/list-box.svelte.d.ts +3 -0
  18. package/dist/forms/money-box/money-box.svelte +104 -65
  19. package/dist/forms/money-box/money-box.svelte.d.ts +6 -0
  20. package/dist/forms/number-box/number-box.svelte +93 -49
  21. package/dist/forms/number-box/number-box.svelte.d.ts +6 -0
  22. package/dist/forms/number-range-box/number-range-box.svelte +22 -58
  23. package/dist/forms/phone-box/phone-box.svelte +101 -38
  24. package/dist/forms/phone-box/phone-box.svelte.d.ts +6 -1
  25. package/dist/forms/slider/slider.svelte +13 -6
  26. package/dist/forms/slider/slider.svelte.d.ts +6 -2
  27. package/dist/forms/tag-box/tag-box.svelte +5 -3
  28. package/dist/forms/text-area/text-area.svelte +22 -2
  29. package/dist/forms/text-area/text-area.svelte.d.ts +4 -0
  30. package/dist/forms/text-box/text-box.svelte +97 -131
  31. package/dist/forms/text-box/text-box.svelte.d.ts +7 -2
  32. package/dist/forms/time-box/time-box.svelte +106 -37
  33. package/dist/forms/time-box/time-box.svelte.d.ts +10 -3
  34. package/dist/forms/url-box/url-box.svelte +26 -5
  35. package/dist/forms/url-box/url-box.svelte.d.ts +7 -1
  36. package/dist/generic/theme-provider/theme-provider-demo.svelte +7 -2
  37. package/dist/navigation/dropdown-button/dropdown-button.svelte +102 -3
  38. package/dist/navigation/dropdown-button/dropdown-manager.svelte.d.ts +47 -0
  39. package/dist/navigation/dropdown-button/dropdown-manager.svelte.js +47 -0
  40. package/dist/tables/table-cell.svelte +2 -0
  41. package/dist/tables/table-row.svelte +1 -0
  42. package/package.json +1 -1
@@ -11,16 +11,21 @@ type $$ComponentProps = {
11
11
  size?: FormFieldSizeOptions;
12
12
  placeholder?: string;
13
13
  nullable?: boolean;
14
- enabled?: boolean;
14
+ disabled?: boolean;
15
+ readonly?: boolean;
15
16
  type?: 'date' | 'datetime-local';
16
17
  required?: boolean;
17
18
  steps?: DateIncrementStep[];
18
19
  onChange?: ((value: string | null) => void) | undefined;
19
- onCheckChanged?: ((enabled: boolean) => void) | undefined;
20
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
21
+ onInput?: ((value: string | null) => void) | undefined;
22
+ onFocus?: ((e: FocusEvent) => void) | undefined;
23
+ onBlur?: ((e: FocusEvent) => void) | undefined;
20
24
  label?: string;
21
25
  helperText?: string;
26
+ nullText?: string;
22
27
  feedback?: FormFieldFeedback;
23
28
  };
24
- declare const DateBox: import("svelte").Component<$$ComponentProps, {}, "value" | "enabled">;
29
+ declare const DateBox: import("svelte").Component<$$ComponentProps, {}, "value">;
25
30
  type DateBox = ReturnType<typeof DateBox>;
26
31
  export default DateBox;
@@ -2,6 +2,7 @@
2
2
  import { roundToDecimals } from '../../helpers/round-to-decimals.js';
3
3
  import { uniqueId } from '../../helpers/unique-id.js';
4
4
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
5
+ import FormInputWrapper from '../form-input-wrapper';
5
6
  import type { FormFieldSizeOptions } from '../../types/form.js';
6
7
 
7
8
  const baseId = uniqueId();
@@ -111,10 +112,12 @@
111
112
  <div class="dimension-inputs">
112
113
  {#each dimensions as dimension, index}
113
114
  <div class="input-group">
114
- <div class="input {disabled ? 'disabled' : ''}" class:error={hasError}>
115
- {#if prefix}
116
- <span class="prefix">{prefix}</span>
117
- {/if}
115
+ <FormInputWrapper
116
+ {disabled}
117
+ error={hasError}
118
+ prefix={prefix || undefined}
119
+ suffix={suffix || undefined}
120
+ >
118
121
  <input
119
122
  id={getDimensionId(index)}
120
123
  type="number"
@@ -129,10 +132,7 @@
129
132
  {required}
130
133
  {disabled}
131
134
  />
132
- {#if suffix}
133
- <span class="suffix">{suffix}</span>
134
- {/if}
135
- </div>
135
+ </FormInputWrapper>
136
136
  </div>
137
137
  {#if index < dimensions.length - 1}
138
138
  <span class="separator">×</span>
@@ -160,31 +160,7 @@
160
160
  flex-shrink: 0;
161
161
  }
162
162
 
163
- .input {
164
- display: flex;
165
- align-items: center;
166
- justify-content: flex-start;
167
- position: relative;
168
- width: 100%;
169
- height: 100%;
170
- border-radius: var(--radius-md);
171
- border: var(--border-thin) solid var(--form-input-border);
172
- background-color: var(--form-input-bg);
173
- color: var(--form-input-fg);
174
- font-size: var(--font-md);
175
- font-weight: 500;
176
- line-height: 2rem;
177
- transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out), fill var(--transition-base) var(--ease-in-out), stroke var(--transition-base) var(--ease-in-out);
178
- user-select: none;
179
- white-space: nowrap;
180
- }
181
- .input.disabled {
182
- opacity: 0.5;
183
- }
184
- .input.error {
185
- border-color: var(--color-error, #dc3545);
186
- }
187
- .input input {
163
+ input {
188
164
  background-color: transparent;
189
165
  border: none;
190
166
  line-height: 2rem;
@@ -193,24 +169,13 @@
193
169
  flex-grow: 1;
194
170
  padding-left: var(--spacing-base);
195
171
  }
196
- .input input:focus {
172
+ input:focus {
197
173
  outline: none;
198
174
  }
199
- .input input::placeholder {
200
- color: var(--form-input-placeholder);
201
- }
202
- .input .prefix,
203
- .input .suffix {
204
- font-size: var(--font-md);
205
- line-height: 2rem;
206
- padding-left: var(--spacing-base);
207
- padding-right: var(--spacing-base);
208
- background-color: var(--form-input-accent-bg);
209
- color: var(--form-input-accent-fg);
210
- }
211
- .input .prefix {
212
- border-right: var(--border-thin) solid var(--form-input-border);
175
+ input:focus-visible {
176
+ outline: 2px solid var(--focus-ring, #007bff);
177
+ outline-offset: 2px;
213
178
  }
214
- .input .suffix {
215
- border-left: var(--border-thin) solid var(--form-input-border);
179
+ input:disabled {
180
+ cursor: not-allowed;
216
181
  }</style>
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import { uniqueId } from '../../helpers/unique-id.js';
4
4
  import FormField from '../form-field/form-field.svelte';
5
+ import FormInputWrapper from '../form-input-wrapper';
5
6
  import type { FormFieldSizeOptions } from '../../types/form.js';
6
7
 
7
8
  const id = uniqueId();
@@ -32,7 +33,7 @@
32
33
  </script>
33
34
 
34
35
  <FormField {size} {label} {id} {required} {disabled}>
35
- <div class="input">
36
+ <FormInputWrapper {disabled}>
36
37
  <input
37
38
  {id}
38
39
  {placeholder}
@@ -45,29 +46,10 @@
45
46
  accept={mimeTypes.join(',')}
46
47
  {capture}
47
48
  />
48
- </div>
49
+ </FormInputWrapper>
49
50
  </FormField>
50
51
 
51
- <style>.input {
52
- display: flex;
53
- align-items: center;
54
- justify-content: flex-start;
55
- position: relative;
56
- width: 100%;
57
- height: 100%;
58
- box-sizing: border-box;
59
- border-radius: var(--radius-md);
60
- border: var(--border-thin) solid var(--form-input-border);
61
- background-color: var(--form-input-bg);
62
- color: var(--form-input-fg);
63
- font-size: var(--font-md);
64
- font-weight: 500;
65
- line-height: 2rem;
66
- transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out), fill var(--transition-base) var(--ease-in-out), stroke var(--transition-base) var(--ease-in-out);
67
- user-select: none;
68
- white-space: nowrap;
69
- }
70
- .input input {
52
+ <style>input {
71
53
  width: 100%;
72
54
  height: 100%;
73
55
  box-sizing: border-box;
@@ -81,14 +63,14 @@
81
63
  font-size: var(--font-md);
82
64
  color: inherit;
83
65
  }
84
- .input input:focus {
66
+ input:focus {
85
67
  outline: none;
86
68
  }
87
- .input input:focus-visible {
69
+ input:focus-visible {
88
70
  outline: 2px solid var(--focus-ring, #007bff);
89
71
  outline-offset: 2px;
90
72
  }
91
- .input input::placeholder {
73
+ input::placeholder {
92
74
  color: var(--form-input-placeholder, #888);
93
75
  font-style: italic;
94
76
  }</style>
@@ -0,0 +1,203 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { untrack } from 'svelte';
4
+ import { animateShake, animateScaleIn } from '../../helpers/animations.js';
5
+ import Icon from '../../icons/icon.svelte';
6
+
7
+ let {
8
+ disabled = false,
9
+ error = false,
10
+ success = false,
11
+ prefix = undefined,
12
+ suffix = undefined,
13
+ nullable = false,
14
+ nullText = '--',
15
+ isLoading = false,
16
+ onCheckChanged = undefined,
17
+ children
18
+ }: {
19
+ disabled?: boolean;
20
+ error?: boolean;
21
+ success?: boolean;
22
+ prefix?: string | undefined;
23
+ suffix?: string | undefined;
24
+ nullable?: boolean;
25
+ nullText?: string;
26
+ isLoading?: boolean;
27
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
28
+ children: Snippet;
29
+ } = $props();
30
+
31
+ // Track whether the nullable checkbox is checked (i.e., whether field has a value)
32
+ let isChecked = $state(true);
33
+
34
+ // Show input when not nullable, or when nullable and checked
35
+ let showInput = $derived(!nullable || isChecked);
36
+
37
+ let inputElement_ref: HTMLDivElement | null = $state(null);
38
+ let successIconElement: HTMLDivElement | null = $state(null);
39
+
40
+ // Trigger shake animation when error appears (track previous error state)
41
+ let prevHasError = $state(false);
42
+ $effect(() => {
43
+ if (error && !prevHasError && inputElement_ref) {
44
+ // Use untrack to prevent animation from triggering effect again
45
+ untrack(() => {
46
+ animateShake(inputElement_ref!);
47
+ });
48
+ }
49
+ prevHasError = error;
50
+ });
51
+
52
+ // Trigger scale-in animation when success appears (track previous success state)
53
+ let prevHasSuccess = $state(false);
54
+ $effect(() => {
55
+ if (success && !prevHasSuccess && successIconElement) {
56
+ // Use untrack to prevent animation from triggering effect again
57
+ untrack(() => {
58
+ animateScaleIn(successIconElement!);
59
+ });
60
+ }
61
+ prevHasSuccess = success;
62
+ });
63
+
64
+ const checkChanged = () => {
65
+ onCheckChanged?.(isChecked);
66
+ };
67
+ </script>
68
+
69
+ <div
70
+ class="input"
71
+ class:disabled
72
+ class:error
73
+ class:success
74
+ class:nullable
75
+ bind:this={inputElement_ref}
76
+ >
77
+ {#if prefix}
78
+ <div class="prefix">{prefix}</div>
79
+ {/if}
80
+ {#if showInput}
81
+ {@render children()}
82
+ {:else}
83
+ <div class="input-null-text">
84
+ {nullText}
85
+ </div>
86
+ {/if}
87
+ {#if isLoading}
88
+ <div class="loading-indicator" aria-label="Loading">
89
+ <div class="spinner"></div>
90
+ </div>
91
+ {:else if success}
92
+ <div class="success-indicator" bind:this={successIconElement}>
93
+ <Icon type="check" size="sm" />
94
+ </div>
95
+ {/if}
96
+ {#if suffix}
97
+ <div class="suffix">{suffix}</div>
98
+ {/if}
99
+ {#if nullable}
100
+ <span class="toggle">
101
+ <input type="checkbox" bind:checked={isChecked} onchange={checkChanged} />
102
+ </span>
103
+ {/if}
104
+ </div>
105
+
106
+ <style>.input {
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: flex-start;
110
+ position: relative;
111
+ width: 100%;
112
+ min-height: 2.125rem;
113
+ border-radius: var(--radius-md);
114
+ border: var(--border-thin) solid var(--form-input-border);
115
+ background-color: var(--form-input-bg);
116
+ color: var(--form-input-fg);
117
+ font-size: var(--font-md);
118
+ font-weight: 500;
119
+ line-height: 2rem;
120
+ padding: 0;
121
+ gap: var(--spacing-sm);
122
+ transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out), fill var(--transition-base) var(--ease-in-out), stroke var(--transition-base) var(--ease-in-out);
123
+ user-select: none;
124
+ white-space: nowrap;
125
+ }
126
+ .input.disabled {
127
+ opacity: 0.5;
128
+ }
129
+ .input.error {
130
+ border-color: var(--danger, #dc3545);
131
+ }
132
+ .input.success {
133
+ border-color: var(--success, #28a745);
134
+ }
135
+ .input.nullable .toggle {
136
+ position: absolute;
137
+ top: 50%;
138
+ transform: translateY(-50%);
139
+ left: 0.4rem;
140
+ z-index: 1;
141
+ }
142
+ .input.nullable .prefix {
143
+ padding-left: 2.5rem;
144
+ }
145
+ .input.nullable:not(:has(.prefix)) :global(input),
146
+ .input.nullable:not(:has(.prefix)) .input-null-text {
147
+ padding-left: 2.5rem;
148
+ }
149
+ .input .loading-indicator,
150
+ .input .success-indicator {
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ padding: 0 var(--spacing-base);
155
+ }
156
+ .input .loading-indicator .spinner {
157
+ width: 1rem;
158
+ height: 1rem;
159
+ border: 2px solid var(--form-input-border);
160
+ border-top-color: var(--primary-500, #3b82f6);
161
+ border-radius: 50%;
162
+ animation: spin 0.6s linear infinite;
163
+ }
164
+ @keyframes spin {
165
+ to {
166
+ transform: rotate(360deg);
167
+ }
168
+ }
169
+ .input .success-indicator {
170
+ color: var(--success, #28a745);
171
+ width: 1.5rem;
172
+ height: 1.5rem;
173
+ }
174
+ .input .success-indicator :global(svg) {
175
+ width: 100%;
176
+ height: 100%;
177
+ }
178
+ .input .input-null-text {
179
+ font-size: var(--font-md);
180
+ line-height: 2rem;
181
+ text-align: left;
182
+ padding-left: var(--spacing-base);
183
+ margin: 0;
184
+ flex-grow: 1;
185
+ display: flex;
186
+ align-items: center;
187
+ box-sizing: border-box;
188
+ }
189
+ .input .prefix,
190
+ .input .suffix {
191
+ font-size: var(--font-md);
192
+ line-height: 2rem;
193
+ padding-left: var(--spacing-base);
194
+ padding-right: var(--spacing-base);
195
+ background-color: var(--form-input-accent-bg);
196
+ color: var(--form-input-accent-fg);
197
+ }
198
+ .input .prefix {
199
+ border-right: var(--border-thin) solid var(--form-input-border);
200
+ }
201
+ .input .suffix {
202
+ border-left: var(--border-thin) solid var(--form-input-border);
203
+ }</style>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ disabled?: boolean;
4
+ error?: boolean;
5
+ success?: boolean;
6
+ prefix?: string | undefined;
7
+ suffix?: string | undefined;
8
+ nullable?: boolean;
9
+ nullText?: string;
10
+ isLoading?: boolean;
11
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
12
+ children: Snippet;
13
+ };
14
+ declare const FormInputWrapper: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type FormInputWrapper = ReturnType<typeof FormInputWrapper>;
16
+ export default FormInputWrapper;
@@ -0,0 +1,2 @@
1
+ export { default as FormInputWrapper } from './form-input-wrapper.svelte';
2
+ export { default } from './form-input-wrapper.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as FormInputWrapper } from './form-input-wrapper.svelte';
2
+ export { default } from './form-input-wrapper.svelte';
@@ -11,7 +11,6 @@ export { default as NumberBox } from './number-box/number-box.svelte';
11
11
  export { default as NumberRangeBox } from './number-range-box/number-range-box.svelte';
12
12
  export { default as PhoneBox } from './phone-box/phone-box.svelte';
13
13
  export { default as Slider } from './slider/slider.svelte';
14
- export { default as SwitchBox } from './switch-box/switch-box.svelte';
15
14
  export { default as TagBox } from './tag-box/tag-box.svelte';
16
15
  export { default as TextArea } from './text-area/text-area.svelte';
17
16
  export { default as TextBox } from './text-box/text-box.svelte';
@@ -12,7 +12,6 @@ export { default as NumberBox } from './number-box/number-box.svelte';
12
12
  export { default as NumberRangeBox } from './number-range-box/number-range-box.svelte';
13
13
  export { default as PhoneBox } from './phone-box/phone-box.svelte';
14
14
  export { default as Slider } from './slider/slider.svelte';
15
- export { default as SwitchBox } from './switch-box/switch-box.svelte';
16
15
  export { default as TagBox } from './tag-box/tag-box.svelte';
17
16
  export { default as TextArea } from './text-area/text-area.svelte';
18
17
  export { default as TextBox } from './text-box/text-box.svelte';
@@ -15,10 +15,13 @@
15
15
  size = 'full' as FormFieldSizeOptions,
16
16
  disabled = false,
17
17
  required = false,
18
+ readonly = false,
18
19
  searchable = false,
19
20
  search = undefined as SearchFunction | undefined,
20
21
  placeholder = '',
21
22
  onChange = undefined,
23
+ onFocus = undefined,
24
+ onBlur = undefined,
22
25
  label = undefined,
23
26
  helperText = undefined,
24
27
  feedback = undefined,
@@ -30,10 +33,13 @@
30
33
  size?: FormFieldSizeOptions;
31
34
  disabled?: boolean;
32
35
  required?: boolean;
36
+ readonly?: boolean;
33
37
  searchable?: boolean;
34
38
  search?: SearchFunction | undefined;
35
39
  placeholder?: string;
36
40
  onChange?: ((value: string | null) => void) | undefined;
41
+ onFocus?: ((e: FocusEvent) => void) | undefined;
42
+ onBlur?: ((e: FocusEvent) => void) | undefined;
37
43
  label?: string;
38
44
  helperText?: string;
39
45
  feedback?: FormFieldFeedback;
@@ -128,7 +134,7 @@
128
134
 
129
135
  // Open/close dropdown
130
136
  const openDropdown = () => {
131
- if (!disabled) {
137
+ if (!disabled && !readonly) {
132
138
  isMenuOpen = true;
133
139
  if (browser && inputElement) {
134
140
  inputElement.focus();
@@ -144,6 +150,14 @@
144
150
  text = getText();
145
151
  };
146
152
 
153
+ const handleFocus = (e: FocusEvent) => {
154
+ onFocus?.(e);
155
+ };
156
+
157
+ const handleBlur = (e: FocusEvent) => {
158
+ onBlur?.(e);
159
+ };
160
+
147
161
  const toggleDropdown = () => {
148
162
  if (isMenuOpen) {
149
163
  closeDropdown();
@@ -162,7 +176,7 @@
162
176
 
163
177
  // Handle clicks on the input (for non-searchable mode)
164
178
  const handleInputClick = (e: MouseEvent) => {
165
- if (disabled) return;
179
+ if (disabled || readonly) return;
166
180
  // For non-searchable mode, clicking the input should open the dropdown
167
181
  if (!isSearchable && !isMenuOpen) {
168
182
  e.preventDefault();
@@ -172,7 +186,7 @@
172
186
 
173
187
  // Handle key presses in the input
174
188
  const onInputKeyDown = (e: KeyboardEvent) => {
175
- if (disabled) return;
189
+ if (disabled || readonly) return;
176
190
 
177
191
  if (e.key === 'Escape') {
178
192
  e.preventDefault();
@@ -257,7 +271,7 @@
257
271
  const clear = (e: MouseEvent | KeyboardEvent) => {
258
272
  e.preventDefault();
259
273
  e.stopPropagation();
260
- if (disabled) return;
274
+ if (disabled || readonly) return;
261
275
  isUserTyping = false;
262
276
  text = '';
263
277
  value = null;
@@ -283,7 +297,7 @@
283
297
  }
284
298
  });
285
299
 
286
- let open = $derived(isMenuOpen && !disabled);
300
+ let open = $derived(isMenuOpen && !disabled && !readonly);
287
301
  </script>
288
302
 
289
303
  <FormField {size} {label} {id} {required} {disabled} {helperText} {feedback}>
@@ -307,7 +321,7 @@
307
321
  {required}
308
322
  {disabled}
309
323
  {placeholder}
310
- readonly={!isSearchable}
324
+ readonly={readonly || !isSearchable}
311
325
  role="combobox"
312
326
  aria-expanded={open}
313
327
  aria-controls={listboxId}
@@ -323,6 +337,8 @@
323
337
  triggerSearch();
324
338
  }
325
339
  }}
340
+ onfocus={handleFocus}
341
+ onblur={handleBlur}
326
342
  data-value={value}
327
343
  data-text={text}
328
344
  />
@@ -331,7 +347,7 @@
331
347
  class="icon"
332
348
  onclick={clickArrow}
333
349
  onkeydown={clickArrow}
334
- {disabled}
350
+ disabled={disabled || readonly}
335
351
  aria-label={open ? 'Close options' : 'Open options'}
336
352
  tabindex="-1"
337
353
  >
@@ -348,7 +364,7 @@
348
364
  class="clear"
349
365
  onclick={clear}
350
366
  onkeydown={clear}
351
- {disabled}
367
+ disabled={disabled || readonly}
352
368
  aria-label="Clear selection"
353
369
  tabindex="-1"
354
370
  >
@@ -383,7 +399,7 @@
383
399
  justify-content: flex-start;
384
400
  position: relative;
385
401
  width: 100%;
386
- height: 100%;
402
+ min-height: 2.125rem;
387
403
  border-radius: var(--radius-md);
388
404
  border: var(--border-thin) solid var(--form-input-border);
389
405
  background-color: var(--form-input-bg);
@@ -391,6 +407,7 @@
391
407
  font-size: var(--font-md);
392
408
  font-weight: 500;
393
409
  line-height: 2rem;
410
+ padding: 0;
394
411
  transition: background-color var(--transition-base) var(--ease-in-out), border-color var(--transition-base) var(--ease-in-out), color var(--transition-base) var(--ease-in-out), fill var(--transition-base) var(--ease-in-out), stroke var(--transition-base) var(--ease-in-out);
395
412
  }
396
413
  .listbox-container.disabled {
@@ -7,10 +7,13 @@ type $$ComponentProps = {
7
7
  size?: FormFieldSizeOptions;
8
8
  disabled?: boolean;
9
9
  required?: boolean;
10
+ readonly?: boolean;
10
11
  searchable?: boolean;
11
12
  search?: SearchFunction | undefined;
12
13
  placeholder?: string;
13
14
  onChange?: ((value: string | null) => void) | undefined;
15
+ onFocus?: ((e: FocusEvent) => void) | undefined;
16
+ onBlur?: ((e: FocusEvent) => void) | undefined;
14
17
  label?: string;
15
18
  helperText?: string;
16
19
  feedback?: FormFieldFeedback;