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
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { uniqueId, type FormFieldSizeOptions } from '../../index.js';
3
3
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
4
+ import FormInputWrapper from '../form-input-wrapper';
4
5
  import { untrack } from 'svelte';
5
6
 
6
7
  const id = uniqueId();
@@ -17,11 +18,17 @@
17
18
  min = 0,
18
19
  max = null as number | null,
19
20
  required = false,
21
+ nullable = false,
20
22
  helperText = undefined as string | undefined,
21
23
  feedback = undefined as FormFieldFeedback | undefined,
22
24
  disabled = false,
23
25
  onChange = undefined,
24
- label = undefined
26
+ onCheckChanged = undefined,
27
+ onInput = undefined,
28
+ onFocus = undefined,
29
+ onBlur = undefined,
30
+ label = undefined,
31
+ nullText = ''
25
32
  }: {
26
33
  value?: number | null;
27
34
  prefix?: string;
@@ -34,11 +41,17 @@
34
41
  min?: number;
35
42
  max?: number | null;
36
43
  required?: boolean;
44
+ nullable?: boolean;
37
45
  helperText?: string;
38
46
  feedback?: FormFieldFeedback;
39
47
  disabled?: boolean;
40
48
  onChange?: ((value: number | null) => void) | undefined;
49
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
50
+ onInput?: ((value: number | null) => void) | undefined;
51
+ onFocus?: ((e: FocusEvent) => void) | undefined;
52
+ onBlur?: ((e: FocusEvent) => void) | undefined;
41
53
  label?: string;
54
+ nullText?: string;
42
55
  } = $props();
43
56
 
44
57
  let isValueInCents = $derived(units === 'cents');
@@ -181,7 +194,7 @@
181
194
  updateLastState(e);
182
195
  };
183
196
 
184
- const onInput = (e: Event) => {
197
+ const handleInputEvent = (e: Event) => {
185
198
  const target = getTargetProperties(e);
186
199
  // If they pasted in something non-numeric, revert to last state
187
200
  if (!isNumericString(target.value)) {
@@ -224,17 +237,65 @@
224
237
  // Cents should be padded to 2 digits, so that "5" becomes "05"
225
238
  cents = String(centValue).padStart(2, '0');
226
239
  onChange?.(value);
240
+ onInput?.(value);
227
241
  };
228
242
 
229
243
  let hasError = $derived(!!feedback?.isError);
244
+
245
+ // Track whether the nullable checkbox is checked (i.e., whether field has a value)
246
+ let isChecked = $state(untrack(() => value !== null && value !== undefined));
247
+
248
+ // Remember the last non-null value so we can restore it when re-checking
249
+ let lastValue = $state<number | undefined>(undefined);
250
+
251
+ // Derive the actual disabled state: disabled prop OR (nullable and unchecked)
252
+ let inputDisabled = $derived(disabled || (nullable && !isChecked));
253
+
254
+ let showInput = $derived(!nullable || isChecked);
255
+
256
+ let effectiveNullText = $derived(nullText || placeholder || '--');
257
+
258
+ const checkChanged = () => {
259
+ if (nullable) {
260
+ if (isChecked) {
261
+ // Restore last value if available, otherwise use 0
262
+ value = lastValue !== undefined ? lastValue : 0;
263
+ } else {
264
+ // Store current value before clearing
265
+ if (value !== null && value !== undefined) {
266
+ lastValue = value;
267
+ }
268
+ value = null;
269
+ }
270
+ }
271
+ onCheckChanged?.(isChecked);
272
+ };
273
+
274
+ $effect(() => {
275
+ if (value === null || value === undefined) {
276
+ // Use untrack to prevent writes to isChecked/value from triggering this effect again
277
+ untrack(() => {
278
+ if (nullable) isChecked = false;
279
+ });
280
+ } else {
281
+ // Initialize lastValue if we have an initial value
282
+ if (lastValue === undefined) {
283
+ lastValue = value;
284
+ }
285
+ }
286
+ });
230
287
  </script>
231
288
 
232
289
  <FormField {size} {label} {id} {required} {disabled} {helperText} {feedback}>
233
- <div class="input {currency}" class:allowCents class:disabled class:error={hasError} {id}>
234
- {#if prefix}
235
- <span class="prefix">{prefix}</span>
236
- {/if}
237
-
290
+ <FormInputWrapper
291
+ {disabled}
292
+ error={hasError}
293
+ {prefix}
294
+ {suffix}
295
+ {nullable}
296
+ nullText={effectiveNullText}
297
+ onCheckChanged={checkChanged}
298
+ >
238
299
  <input
239
300
  class="dollars"
240
301
  {placeholder}
@@ -242,16 +303,22 @@
242
303
  type="text"
243
304
  onkeypress={onKeyPress}
244
305
  onkeyup={onKeyUp}
245
- oninput={onInput}
306
+ oninput={handleInputEvent}
246
307
  onchange={handleChange}
247
308
  onmouseup={onSaveStateEvent}
248
- onfocus={onSaveStateEvent}
249
- onblur={onSaveStateEvent}
309
+ onfocus={(e) => {
310
+ onSaveStateEvent(e);
311
+ onFocus?.(e);
312
+ }}
313
+ onblur={(e) => {
314
+ onSaveStateEvent(e);
315
+ onBlur?.(e);
316
+ }}
250
317
  name="dollars"
251
318
  id="{id}-dollars"
252
319
  inputmode="numeric"
253
320
  {required}
254
- {disabled}
321
+ disabled={inputDisabled}
255
322
  />
256
323
  {#if allowCents}
257
324
  <span class="separator">.</span>
@@ -262,51 +329,28 @@
262
329
  class="cents"
263
330
  onkeypress={onKeyPress}
264
331
  onkeyup={onKeyUp}
265
- oninput={onInput}
332
+ oninput={handleInputEvent}
266
333
  onchange={handleChange}
267
334
  onmouseup={onSaveStateEvent}
268
- onfocus={onSaveStateEvent}
269
- onblur={onSaveStateEvent}
335
+ onfocus={(e) => {
336
+ onSaveStateEvent(e);
337
+ onFocus?.(e);
338
+ }}
339
+ onblur={(e) => {
340
+ onSaveStateEvent(e);
341
+ onBlur?.(e);
342
+ }}
270
343
  name="cents"
271
344
  id="{id}-cents"
272
345
  inputmode="numeric"
273
346
  {required}
274
- {disabled}
347
+ disabled={inputDisabled}
275
348
  />
276
349
  {/if}
277
-
278
- {#if suffix}
279
- <span class="suffix">{suffix}</span>
280
- {/if}
281
- </div>
350
+ </FormInputWrapper>
282
351
  </FormField>
283
352
 
284
- <style>.input {
285
- display: flex;
286
- align-items: center;
287
- justify-content: flex-start;
288
- position: relative;
289
- width: 100%;
290
- height: 100%;
291
- box-sizing: border-box;
292
- border-radius: var(--radius-md);
293
- border: var(--border-thin) solid var(--form-input-border);
294
- background-color: var(--form-input-bg);
295
- color: var(--form-input-fg);
296
- font-size: var(--font-md);
297
- font-weight: 500;
298
- line-height: 2rem;
299
- 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);
300
- user-select: none;
301
- white-space: nowrap;
302
- }
303
- .input.disabled {
304
- opacity: 0.5;
305
- }
306
- .input.error {
307
- border-color: var(--color-error, #dc3545);
308
- }
309
- .input input {
353
+ <style>input {
310
354
  background-color: transparent;
311
355
  border: none;
312
356
  box-sizing: border-box;
@@ -316,31 +360,26 @@
316
360
  padding-left: var(--spacing-base);
317
361
  margin: 0;
318
362
  }
319
- .input input:focus {
363
+ input:focus {
320
364
  outline: none;
321
365
  }
322
- .input .dollars {
366
+ input:focus-visible {
367
+ outline: 2px solid var(--focus-ring, #007bff);
368
+ outline-offset: 2px;
369
+ }
370
+ input:disabled {
371
+ cursor: not-allowed;
372
+ }
373
+
374
+ .dollars {
323
375
  flex-grow: 1;
324
376
  }
325
- .input .separator {
377
+
378
+ .separator {
326
379
  width: var(--spacing-base);
327
380
  text-align: center;
328
381
  }
329
- .input .cents {
382
+
383
+ .cents {
330
384
  width: 4rem;
331
- }
332
- .input .prefix,
333
- .input .suffix {
334
- font-size: var(--font-md);
335
- line-height: 2rem;
336
- padding-left: var(--spacing-base);
337
- padding-right: var(--spacing-base);
338
- background-color: var(--form-input-accent-bg);
339
- color: var(--form-input-accent-fg);
340
- }
341
- .input .prefix {
342
- border-right: var(--border-thin) solid var(--form-input-border);
343
- }
344
- .input .suffix {
345
- border-left: var(--border-thin) solid var(--form-input-border);
346
385
  }</style>
@@ -12,11 +12,17 @@ type $$ComponentProps = {
12
12
  min?: number;
13
13
  max?: number | null;
14
14
  required?: boolean;
15
+ nullable?: boolean;
15
16
  helperText?: string;
16
17
  feedback?: FormFieldFeedback;
17
18
  disabled?: boolean;
18
19
  onChange?: ((value: number | null) => void) | undefined;
20
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
21
+ onInput?: ((value: number | null) => void) | undefined;
22
+ onFocus?: ((e: FocusEvent) => void) | undefined;
23
+ onBlur?: ((e: FocusEvent) => void) | undefined;
19
24
  label?: string;
25
+ nullText?: string;
20
26
  };
21
27
  declare const MoneyBox: import("svelte").Component<$$ComponentProps, {}, "value">;
22
28
  type MoneyBox = ReturnType<typeof MoneyBox>;
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
+ import { untrack } from 'svelte';
2
3
  import { roundToDecimals } from '../../helpers/round-to-decimals.js';
3
4
  import { uniqueId } from '../../helpers/unique-id.js';
4
5
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
6
+ import FormInputWrapper from '../form-input-wrapper';
5
7
  import type { FormFieldSizeOptions } from '../../types/form.js';
6
8
  const id = uniqueId();
7
9
 
@@ -18,13 +20,19 @@
18
20
  prefix = null as string | null,
19
21
  suffix = null as string | null,
20
22
  step = 1,
23
+ nullable = false,
21
24
  onChange = undefined,
25
+ onCheckChanged = undefined,
26
+ onInput = undefined,
27
+ onFocus = undefined,
28
+ onBlur = undefined,
22
29
  label = undefined,
23
30
  helperText = undefined,
24
31
  feedback = undefined,
25
32
  disabled = false,
26
33
  required = false,
27
- readonly = false
34
+ readonly = false,
35
+ nullText = ''
28
36
  }: {
29
37
  value?: number | null;
30
38
  placeholder?: string;
@@ -36,30 +44,91 @@
36
44
  prefix?: string | null;
37
45
  suffix?: string | null;
38
46
  step?: number;
47
+ nullable?: boolean;
39
48
  onChange?: ((value: number | null) => void) | undefined;
49
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
50
+ onInput?: ((value: number | null) => void) | undefined;
51
+ onFocus?: ((e: FocusEvent) => void) | undefined;
52
+ onBlur?: ((e: FocusEvent) => void) | undefined;
40
53
  label?: string;
41
54
  helperText?: string;
42
55
  feedback?: FormFieldFeedback;
43
56
  disabled?: boolean;
44
57
  required?: boolean;
45
58
  readonly?: boolean;
59
+ nullText?: string;
46
60
  } = $props();
47
61
 
62
+ // Track whether the nullable checkbox is checked (i.e., whether field has a value)
63
+ let isChecked = $state(untrack(() => value !== null && value !== undefined));
64
+
65
+ // Remember the last non-null value so we can restore it when re-checking
66
+ let lastValue = $state<number | undefined>(undefined);
67
+
68
+ // Derive the actual disabled state: disabled prop OR (nullable and unchecked)
69
+ let inputDisabled = $derived(disabled || (nullable && !isChecked));
70
+
71
+ let showInput = $derived(!nullable || isChecked);
72
+
73
+ const checkChanged = () => {
74
+ if (nullable) {
75
+ if (isChecked) {
76
+ // Restore last value if available, otherwise use 0
77
+ value = lastValue !== undefined ? lastValue : 0;
78
+ } else {
79
+ // Store current value before clearing
80
+ if (value !== null && value !== undefined) {
81
+ lastValue = value;
82
+ }
83
+ value = null;
84
+ }
85
+ }
86
+ onCheckChanged?.(isChecked);
87
+ handleInputChange();
88
+ };
89
+
90
+ const handleInputChange = () => {
91
+ const currentValue = !nullable || isChecked ? value : null;
92
+ // Remember the value if it's not null
93
+ if (isChecked && value !== null && value !== undefined) {
94
+ lastValue = value;
95
+ }
96
+ onChange?.(currentValue);
97
+ onInput?.(currentValue);
98
+ };
99
+
48
100
  const valueChanged = () => {
49
101
  if (value === null || value === undefined) return;
50
102
  if (value < min) value = min;
51
103
  if (value > max) value = max;
52
104
  value = roundToDecimals(value, decimals);
53
- onChange?.(value);
105
+ handleInputChange();
54
106
  };
55
107
 
56
- const onInput = (e: Event) => {
108
+ const handleInput = (e: Event) => {
57
109
  const input = e.target as HTMLInputElement;
58
110
  const newValue = parseFloat(input.value);
59
111
  if (isNaN(newValue)) return;
60
112
  value = newValue;
113
+ handleInputChange();
61
114
  };
62
115
 
116
+ $effect(() => {
117
+ if (value === null || value === undefined) {
118
+ // Use untrack to prevent writes to isChecked/value from triggering this effect again
119
+ untrack(() => {
120
+ if (nullable) isChecked = false;
121
+ });
122
+ } else {
123
+ // Initialize lastValue if we have an initial value
124
+ if (lastValue === undefined) {
125
+ lastValue = value;
126
+ }
127
+ }
128
+ });
129
+
130
+ let effectiveNullText = $derived(nullText || placeholder || '--');
131
+
63
132
  // Don't allow certain characters to be typed into the input
64
133
  const onKeyPress = (e: KeyboardEvent) => {
65
134
  const isNumber = !isNaN(Number(e.key));
@@ -77,11 +146,14 @@
77
146
  </script>
78
147
 
79
148
  <FormField {size} {label} {id} {required} {disabled} {helperText} {feedback}>
80
- <div class="input {type}">
81
- {#if prefix}
82
- <span class="prefix">{prefix}</span>
83
- {/if}
84
-
149
+ <FormInputWrapper
150
+ {disabled}
151
+ prefix={prefix || undefined}
152
+ suffix={suffix || undefined}
153
+ {nullable}
154
+ nullText={effectiveNullText}
155
+ onCheckChanged={checkChanged}
156
+ >
85
157
  <input
86
158
  {id}
87
159
  {placeholder}
@@ -90,62 +162,34 @@
90
162
  {step}
91
163
  {min}
92
164
  {max}
93
- {disabled}
165
+ disabled={inputDisabled}
94
166
  {readonly}
95
167
  {required}
96
168
  onchange={valueChanged}
97
- oninput={onInput}
169
+ oninput={handleInput}
98
170
  onkeypress={onKeyPress}
171
+ onfocus={onFocus}
172
+ onblur={onBlur}
99
173
  />
100
-
101
- {#if suffix}
102
- <span class="suffix">{suffix}</span>
103
- {/if}
104
- </div>
174
+ </FormInputWrapper>
105
175
  </FormField>
106
176
 
107
- <style>.input {
108
- display: flex;
109
- align-items: center;
110
- justify-content: flex-start;
111
- position: relative;
112
- width: 100%;
113
- height: 100%;
114
- border-radius: var(--radius-md);
115
- border: var(--border-thin) solid var(--form-input-border);
116
- background-color: var(--form-input-bg);
117
- color: var(--form-input-fg);
118
- font-size: var(--font-md);
119
- font-weight: 500;
120
- line-height: 2rem;
121
- 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);
122
- user-select: none;
123
- white-space: nowrap;
124
- }
125
- .input input {
177
+ <style>input {
126
178
  background-color: transparent;
127
179
  border: none;
128
180
  line-height: 2rem;
129
181
  font-size: var(--font-md);
130
182
  width: 100%;
131
183
  flex-grow: 1;
132
- padding-left: var(--spacing-base);
184
+ padding: 0 0 0 var(--spacing-base);
133
185
  }
134
- .input input:focus {
186
+ input:focus {
135
187
  outline: none;
136
188
  }
137
- .input .prefix,
138
- .input .suffix {
139
- font-size: var(--font-md);
140
- line-height: 2rem;
141
- padding-left: var(--spacing-base);
142
- padding-right: var(--spacing-base);
143
- background-color: var(--form-input-accent-bg);
144
- color: var(--form-input-accent-fg);
145
- }
146
- .input .prefix {
147
- border-right: var(--border-thin) solid var(--form-input-border);
189
+ input:focus-visible {
190
+ outline: 2px solid var(--focus-ring, #007bff);
191
+ outline-offset: 2px;
148
192
  }
149
- .input .suffix {
150
- border-left: var(--border-thin) solid var(--form-input-border);
193
+ input:disabled {
194
+ cursor: not-allowed;
151
195
  }</style>
@@ -12,13 +12,19 @@ type $$ComponentProps = {
12
12
  prefix?: string | null;
13
13
  suffix?: string | null;
14
14
  step?: number;
15
+ nullable?: boolean;
15
16
  onChange?: ((value: number | null) => void) | undefined;
17
+ onCheckChanged?: ((isChecked: boolean) => void) | undefined;
18
+ onInput?: ((value: number | null) => void) | undefined;
19
+ onFocus?: ((e: FocusEvent) => void) | undefined;
20
+ onBlur?: ((e: FocusEvent) => void) | undefined;
16
21
  label?: string;
17
22
  helperText?: string;
18
23
  feedback?: FormFieldFeedback;
19
24
  disabled?: boolean;
20
25
  required?: boolean;
21
26
  readonly?: boolean;
27
+ nullText?: string;
22
28
  };
23
29
  declare const NumberBox: import("svelte").Component<$$ComponentProps, {}, "value">;
24
30
  type NumberBox = ReturnType<typeof NumberBox>;
@@ -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 minId = uniqueId();
@@ -117,10 +118,12 @@
117
118
  <FormField {size} {label} id={minId} {required} {disabled} {helperText} {feedback}>
118
119
  <div class="number-range-inputs">
119
120
  <div class="input-group">
120
- <div class="input {disabled ? 'disabled' : ''}" class:error={hasError}>
121
- {#if prefix}
122
- <span class="prefix">{prefix}</span>
123
- {/if}
121
+ <FormInputWrapper
122
+ {disabled}
123
+ error={hasError}
124
+ prefix={prefix || undefined}
125
+ suffix={suffix || undefined}
126
+ >
124
127
  <input
125
128
  id={minId}
126
129
  type="number"
@@ -135,16 +138,15 @@
135
138
  {required}
136
139
  {disabled}
137
140
  />
138
- {#if suffix}
139
- <span class="suffix">{suffix}</span>
140
- {/if}
141
- </div>
141
+ </FormInputWrapper>
142
142
  </div>
143
143
  <div class="input-group">
144
- <div class="input {disabled ? 'disabled' : ''}" class:error={hasError}>
145
- {#if prefix}
146
- <span class="prefix">{prefix}</span>
147
- {/if}
144
+ <FormInputWrapper
145
+ {disabled}
146
+ error={hasError}
147
+ prefix={prefix || undefined}
148
+ suffix={suffix || undefined}
149
+ >
148
150
  <input
149
151
  id={maxId}
150
152
  type="number"
@@ -159,10 +161,7 @@
159
161
  {required}
160
162
  {disabled}
161
163
  />
162
- {#if suffix}
163
- <span class="suffix">{suffix}</span>
164
- {/if}
165
- </div>
164
+ </FormInputWrapper>
166
165
  </div>
167
166
  </div>
168
167
  </FormField>
@@ -177,31 +176,7 @@
177
176
  flex: 1;
178
177
  }
179
178
 
180
- .input {
181
- display: flex;
182
- align-items: center;
183
- justify-content: flex-start;
184
- position: relative;
185
- width: 100%;
186
- height: 100%;
187
- border-radius: var(--radius-md);
188
- border: var(--border-thin) solid var(--form-input-border);
189
- background-color: var(--form-input-bg);
190
- color: var(--form-input-fg);
191
- font-size: var(--font-md);
192
- font-weight: 500;
193
- line-height: 2rem;
194
- 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);
195
- user-select: none;
196
- white-space: nowrap;
197
- }
198
- .input.disabled {
199
- opacity: 0.5;
200
- }
201
- .input.error {
202
- border-color: var(--color-error, #dc3545);
203
- }
204
- .input input {
179
+ input {
205
180
  background-color: transparent;
206
181
  border: none;
207
182
  line-height: 2rem;
@@ -210,24 +185,13 @@
210
185
  flex-grow: 1;
211
186
  padding-left: var(--spacing-base);
212
187
  }
213
- .input input:focus {
188
+ input:focus {
214
189
  outline: none;
215
190
  }
216
- .input input::placeholder {
217
- color: var(--form-input-placeholder);
218
- }
219
- .input .prefix,
220
- .input .suffix {
221
- font-size: var(--font-md);
222
- line-height: 2rem;
223
- padding-left: var(--spacing-base);
224
- padding-right: var(--spacing-base);
225
- background-color: var(--form-input-accent-bg);
226
- color: var(--form-input-accent-fg);
227
- }
228
- .input .prefix {
229
- border-right: var(--border-thin) solid var(--form-input-border);
191
+ input:focus-visible {
192
+ outline: 2px solid var(--focus-ring, #007bff);
193
+ outline-offset: 2px;
230
194
  }
231
- .input .suffix {
232
- border-left: var(--border-thin) solid var(--form-input-border);
195
+ input:disabled {
196
+ cursor: not-allowed;
233
197
  }</style>