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.
- package/README.md +1 -2
- package/dist/forms/bool-box/bool-box.svelte +392 -13
- package/dist/forms/bool-box/bool-box.svelte.d.ts +2 -0
- package/dist/forms/bool-box/index.d.ts +2 -0
- package/dist/forms/bool-box/index.js +2 -0
- package/dist/forms/date-box/date-box.svelte +104 -53
- package/dist/forms/date-box/date-box.svelte.d.ts +8 -3
- package/dist/forms/dimension-box/dimension-box.svelte +15 -50
- package/dist/forms/file-box/file-box.svelte +7 -25
- package/dist/forms/form-input-wrapper/form-input-wrapper.svelte +203 -0
- package/dist/forms/form-input-wrapper/form-input-wrapper.svelte.d.ts +16 -0
- package/dist/forms/form-input-wrapper/index.d.ts +2 -0
- package/dist/forms/form-input-wrapper/index.js +2 -0
- package/dist/forms/index.d.ts +0 -1
- package/dist/forms/index.js +0 -1
- package/dist/forms/list-box/list-box.svelte +26 -9
- package/dist/forms/list-box/list-box.svelte.d.ts +3 -0
- package/dist/forms/money-box/money-box.svelte +104 -65
- package/dist/forms/money-box/money-box.svelte.d.ts +6 -0
- package/dist/forms/number-box/number-box.svelte +93 -49
- package/dist/forms/number-box/number-box.svelte.d.ts +6 -0
- package/dist/forms/number-range-box/number-range-box.svelte +22 -58
- package/dist/forms/phone-box/phone-box.svelte +101 -38
- package/dist/forms/phone-box/phone-box.svelte.d.ts +6 -1
- package/dist/forms/slider/slider.svelte +13 -6
- package/dist/forms/slider/slider.svelte.d.ts +6 -2
- package/dist/forms/tag-box/tag-box.svelte +5 -3
- package/dist/forms/text-area/text-area.svelte +22 -2
- package/dist/forms/text-area/text-area.svelte.d.ts +4 -0
- package/dist/forms/text-box/text-box.svelte +97 -131
- package/dist/forms/text-box/text-box.svelte.d.ts +7 -2
- package/dist/forms/time-box/time-box.svelte +106 -37
- package/dist/forms/time-box/time-box.svelte.d.ts +10 -3
- package/dist/forms/url-box/url-box.svelte +26 -5
- package/dist/forms/url-box/url-box.svelte.d.ts +7 -1
- package/dist/generic/theme-provider/theme-provider-demo.svelte +7 -2
- package/dist/navigation/dropdown-button/dropdown-button.svelte +102 -3
- package/dist/navigation/dropdown-button/dropdown-manager.svelte.d.ts +47 -0
- package/dist/navigation/dropdown-button/dropdown-manager.svelte.js +47 -0
- package/dist/tables/table-cell.svelte +2 -0
- package/dist/tables/table-row.svelte +1 -0
- 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
|
-
|
|
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
|
|
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
|
-
<
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
-
{
|
|
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={
|
|
306
|
+
oninput={handleInputEvent}
|
|
246
307
|
onchange={handleChange}
|
|
247
308
|
onmouseup={onSaveStateEvent}
|
|
248
|
-
onfocus={
|
|
249
|
-
|
|
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
|
-
{
|
|
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={
|
|
332
|
+
oninput={handleInputEvent}
|
|
266
333
|
onchange={handleChange}
|
|
267
334
|
onmouseup={onSaveStateEvent}
|
|
268
|
-
onfocus={
|
|
269
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
-
|
|
363
|
+
input:focus {
|
|
320
364
|
outline: none;
|
|
321
365
|
}
|
|
322
|
-
|
|
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
|
-
|
|
377
|
+
|
|
378
|
+
.separator {
|
|
326
379
|
width: var(--spacing-base);
|
|
327
380
|
text-align: center;
|
|
328
381
|
}
|
|
329
|
-
|
|
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
|
-
|
|
105
|
+
handleInputChange();
|
|
54
106
|
};
|
|
55
107
|
|
|
56
|
-
const
|
|
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
|
-
<
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
{
|
|
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
|
-
{
|
|
165
|
+
disabled={inputDisabled}
|
|
94
166
|
{readonly}
|
|
95
167
|
{required}
|
|
96
168
|
onchange={valueChanged}
|
|
97
|
-
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
|
|
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
|
|
184
|
+
padding: 0 0 0 var(--spacing-base);
|
|
133
185
|
}
|
|
134
|
-
|
|
186
|
+
input:focus {
|
|
135
187
|
outline: none;
|
|
136
188
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
150
|
-
|
|
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
|
-
<
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
{
|
|
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
|
-
|
|
139
|
-
<span class="suffix">{suffix}</span>
|
|
140
|
-
{/if}
|
|
141
|
-
</div>
|
|
141
|
+
</FormInputWrapper>
|
|
142
142
|
</div>
|
|
143
143
|
<div class="input-group">
|
|
144
|
-
<
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
+
input:focus {
|
|
214
189
|
outline: none;
|
|
215
190
|
}
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
232
|
-
|
|
195
|
+
input:disabled {
|
|
196
|
+
cursor: not-allowed;
|
|
233
197
|
}</style>
|