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
|
@@ -2,25 +2,36 @@
|
|
|
2
2
|
import { untrack } from 'svelte';
|
|
3
3
|
import { uniqueId, type FormFieldSizeOptions } from '../../index.js';
|
|
4
4
|
import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
|
|
5
|
+
import FormInputWrapper from '../form-input-wrapper';
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
value = $bindable('' as string | null),
|
|
8
9
|
size = 'md' as FormFieldSizeOptions,
|
|
10
|
+
placeholder = '',
|
|
9
11
|
onChange = undefined,
|
|
12
|
+
onCheckChanged = undefined,
|
|
10
13
|
label = undefined,
|
|
11
14
|
helperText = undefined,
|
|
12
15
|
feedback = undefined,
|
|
13
16
|
disabled = false,
|
|
14
|
-
required = false
|
|
17
|
+
required = false,
|
|
18
|
+
readonly = false,
|
|
19
|
+
nullable = false,
|
|
20
|
+
nullText = ''
|
|
15
21
|
}: {
|
|
16
22
|
value?: string | null;
|
|
17
23
|
size?: FormFieldSizeOptions;
|
|
18
|
-
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
onChange?: ((value: string | null) => void) | undefined;
|
|
26
|
+
onCheckChanged?: ((isChecked: boolean) => void) | undefined;
|
|
19
27
|
label?: string;
|
|
20
28
|
helperText?: string;
|
|
21
29
|
feedback?: FormFieldFeedback;
|
|
22
30
|
disabled?: boolean;
|
|
23
31
|
required?: boolean;
|
|
32
|
+
readonly?: boolean;
|
|
33
|
+
nullable?: boolean;
|
|
34
|
+
nullText?: string;
|
|
24
35
|
} = $props();
|
|
25
36
|
|
|
26
37
|
const id = uniqueId();
|
|
@@ -65,8 +76,9 @@
|
|
|
65
76
|
};
|
|
66
77
|
|
|
67
78
|
const publishChange = () => {
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
const currentValue = !nullable || isChecked ? getCombinedValue() : null;
|
|
80
|
+
value = currentValue;
|
|
81
|
+
onChange?.(currentValue);
|
|
70
82
|
return value;
|
|
71
83
|
};
|
|
72
84
|
|
|
@@ -139,9 +151,57 @@
|
|
|
139
151
|
}
|
|
140
152
|
};
|
|
141
153
|
|
|
154
|
+
// Track whether the nullable checkbox is checked (i.e., whether field has a value)
|
|
155
|
+
let isChecked = $state(untrack(() => !!value));
|
|
156
|
+
|
|
157
|
+
// Remember the last non-null value so we can restore it when re-checking
|
|
158
|
+
let lastValue = $state<string | undefined>(undefined);
|
|
159
|
+
|
|
160
|
+
// Derive the actual disabled state: disabled prop OR (nullable and unchecked)
|
|
161
|
+
let inputDisabled = $derived(disabled || (nullable && !isChecked));
|
|
162
|
+
|
|
163
|
+
let showInput = $derived(!nullable || isChecked);
|
|
164
|
+
|
|
165
|
+
let effectiveNullText = $derived(nullText || '(___) ___-____');
|
|
166
|
+
|
|
167
|
+
const checkChanged = () => {
|
|
168
|
+
if (nullable) {
|
|
169
|
+
if (isChecked) {
|
|
170
|
+
// Restore last value if available, otherwise use empty string
|
|
171
|
+
const restoreValue = lastValue || '';
|
|
172
|
+
setValue(restoreValue);
|
|
173
|
+
} else {
|
|
174
|
+
// Store current value before clearing
|
|
175
|
+
const currentValue = getCombinedValue();
|
|
176
|
+
if (currentValue) {
|
|
177
|
+
lastValue = currentValue;
|
|
178
|
+
}
|
|
179
|
+
areaCode = '';
|
|
180
|
+
localExt = '';
|
|
181
|
+
lastFour = '';
|
|
182
|
+
value = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
onCheckChanged?.(isChecked);
|
|
186
|
+
};
|
|
187
|
+
|
|
142
188
|
// Set the initial value
|
|
143
189
|
setValue(value ?? '');
|
|
144
190
|
|
|
191
|
+
$effect(() => {
|
|
192
|
+
if (!value) {
|
|
193
|
+
// Use untrack to prevent writes to isChecked/value from triggering this effect again
|
|
194
|
+
untrack(() => {
|
|
195
|
+
if (nullable) isChecked = false;
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
// Initialize lastValue if we have an initial value
|
|
199
|
+
if (!lastValue) {
|
|
200
|
+
lastValue = value;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
145
205
|
$effect(() => {
|
|
146
206
|
// Only trigger when the phone number parts change
|
|
147
207
|
const hasValue = areaCode || localExt || lastFour;
|
|
@@ -155,19 +215,26 @@
|
|
|
155
215
|
</script>
|
|
156
216
|
|
|
157
217
|
<FormField {size} {label} id="{id}-areaCode" {required} {disabled} {helperText} {feedback}>
|
|
158
|
-
<
|
|
218
|
+
<FormInputWrapper
|
|
219
|
+
{disabled}
|
|
220
|
+
{nullable}
|
|
221
|
+
nullText={effectiveNullText}
|
|
222
|
+
onCheckChanged={checkChanged}
|
|
223
|
+
>
|
|
159
224
|
<span class="areaCode segment">
|
|
160
225
|
<span>(</span>
|
|
161
226
|
<input
|
|
162
227
|
id="{id}-areaCode"
|
|
163
228
|
type="text"
|
|
229
|
+
{placeholder}
|
|
164
230
|
oninput={valueChanged}
|
|
165
231
|
onkeyup={keyUp}
|
|
166
232
|
onchange={valueChanged}
|
|
167
233
|
bind:value={areaCode}
|
|
168
234
|
name="areaCode"
|
|
169
235
|
data-maxlength="3"
|
|
170
|
-
{
|
|
236
|
+
disabled={inputDisabled}
|
|
237
|
+
{readonly}
|
|
171
238
|
{required}
|
|
172
239
|
/>
|
|
173
240
|
<span>)</span>
|
|
@@ -176,13 +243,15 @@
|
|
|
176
243
|
<input
|
|
177
244
|
id="{id}-localExt"
|
|
178
245
|
type="text"
|
|
246
|
+
{placeholder}
|
|
179
247
|
oninput={valueChanged}
|
|
180
248
|
onchange={valueChanged}
|
|
181
249
|
onkeyup={keyUp}
|
|
182
250
|
bind:value={localExt}
|
|
183
251
|
name="localExt"
|
|
184
252
|
data-maxlength="3"
|
|
185
|
-
{
|
|
253
|
+
disabled={inputDisabled}
|
|
254
|
+
{readonly}
|
|
186
255
|
{required}
|
|
187
256
|
/>
|
|
188
257
|
</span>
|
|
@@ -191,55 +260,43 @@
|
|
|
191
260
|
<input
|
|
192
261
|
id="{id}-lastFour"
|
|
193
262
|
type="text"
|
|
263
|
+
{placeholder}
|
|
194
264
|
oninput={valueChanged}
|
|
195
265
|
onchange={valueChanged}
|
|
196
266
|
onkeyup={keyUp}
|
|
197
267
|
bind:value={lastFour}
|
|
198
268
|
name="lastFour"
|
|
199
269
|
data-maxlength="4"
|
|
200
|
-
{
|
|
270
|
+
disabled={inputDisabled}
|
|
271
|
+
{readonly}
|
|
201
272
|
{required}
|
|
202
|
-
|
|
203
|
-
>
|
|
204
|
-
</
|
|
273
|
+
/>
|
|
274
|
+
</span>
|
|
275
|
+
</FormInputWrapper>
|
|
205
276
|
</FormField>
|
|
206
277
|
|
|
207
|
-
<style>.
|
|
208
|
-
display: flex;
|
|
209
|
-
align-items: center;
|
|
210
|
-
justify-content: flex-start;
|
|
211
|
-
position: relative;
|
|
212
|
-
width: 100%;
|
|
213
|
-
height: 100%;
|
|
214
|
-
border-radius: var(--radius-md);
|
|
215
|
-
border: var(--border-thin) solid var(--form-input-border);
|
|
216
|
-
background-color: var(--form-input-bg);
|
|
217
|
-
color: var(--form-input-fg);
|
|
218
|
-
font-size: var(--font-md);
|
|
219
|
-
font-weight: 500;
|
|
220
|
-
line-height: 2rem;
|
|
221
|
-
padding-left: var(--spacing-base);
|
|
222
|
-
gap: var(--spacing-sm);
|
|
223
|
-
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);
|
|
224
|
-
user-select: none;
|
|
225
|
-
white-space: nowrap;
|
|
226
|
-
}
|
|
227
|
-
.input .segment {
|
|
278
|
+
<style>.segment {
|
|
228
279
|
position: relative;
|
|
229
280
|
display: flex;
|
|
230
281
|
align-items: center;
|
|
231
282
|
justify-content: center;
|
|
283
|
+
gap: var(--spacing-sm);
|
|
232
284
|
}
|
|
233
|
-
|
|
285
|
+
|
|
286
|
+
.areaCode {
|
|
234
287
|
flex-basis: 100px;
|
|
288
|
+
padding-left: var(--spacing-base);
|
|
235
289
|
}
|
|
236
|
-
|
|
290
|
+
|
|
291
|
+
.localExt {
|
|
237
292
|
flex-basis: 80px;
|
|
238
293
|
}
|
|
239
|
-
|
|
294
|
+
|
|
295
|
+
.lastFour {
|
|
240
296
|
flex-basis: 140px;
|
|
241
297
|
}
|
|
242
|
-
|
|
298
|
+
|
|
299
|
+
input {
|
|
243
300
|
background-color: transparent;
|
|
244
301
|
border: none;
|
|
245
302
|
line-height: 2rem;
|
|
@@ -251,10 +308,16 @@
|
|
|
251
308
|
text-align: center;
|
|
252
309
|
color: inherit;
|
|
253
310
|
}
|
|
254
|
-
|
|
311
|
+
input:focus {
|
|
255
312
|
outline: none;
|
|
256
313
|
}
|
|
257
|
-
|
|
314
|
+
input:focus-visible {
|
|
258
315
|
outline: 2px solid var(--focus-ring, #007bff);
|
|
259
316
|
outline-offset: 2px;
|
|
317
|
+
}
|
|
318
|
+
input:disabled {
|
|
319
|
+
cursor: not-allowed;
|
|
320
|
+
}
|
|
321
|
+
input:read-only {
|
|
322
|
+
cursor: default;
|
|
260
323
|
}</style>
|
|
@@ -3,12 +3,17 @@ import { type FormFieldFeedback } from '../form-field/form-field.svelte';
|
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
value?: string | null;
|
|
5
5
|
size?: FormFieldSizeOptions;
|
|
6
|
-
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
onChange?: ((value: string | null) => void) | undefined;
|
|
8
|
+
onCheckChanged?: ((isChecked: boolean) => void) | undefined;
|
|
7
9
|
label?: string;
|
|
8
10
|
helperText?: string;
|
|
9
11
|
feedback?: FormFieldFeedback;
|
|
10
12
|
disabled?: boolean;
|
|
11
13
|
required?: boolean;
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
nullable?: boolean;
|
|
16
|
+
nullText?: string;
|
|
12
17
|
};
|
|
13
18
|
declare const PhoneBox: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
14
19
|
type PhoneBox = ReturnType<typeof PhoneBox>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { uniqueId } from '../../helpers/unique-id.js';
|
|
3
|
-
import FormField from '../form-field/form-field.svelte';
|
|
4
|
-
import type {
|
|
3
|
+
import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
|
|
4
|
+
import type { FormFieldSizeOptions } from '../../types/form.js';
|
|
5
5
|
|
|
6
6
|
const id = uniqueId();
|
|
7
7
|
|
|
@@ -11,24 +11,30 @@
|
|
|
11
11
|
max = 100,
|
|
12
12
|
step = 1,
|
|
13
13
|
disabled = false,
|
|
14
|
-
|
|
14
|
+
required = false,
|
|
15
|
+
size = 'full' as FormFieldSizeOptions,
|
|
15
16
|
showTooltip = true,
|
|
16
17
|
showValue = true,
|
|
17
18
|
formatValue = undefined,
|
|
18
19
|
onChange = undefined,
|
|
19
|
-
label = undefined
|
|
20
|
+
label = undefined,
|
|
21
|
+
helperText = undefined,
|
|
22
|
+
feedback = undefined
|
|
20
23
|
}: {
|
|
21
24
|
value?: number;
|
|
22
25
|
min?: number;
|
|
23
26
|
max?: number;
|
|
24
27
|
step?: number;
|
|
25
28
|
disabled?: boolean;
|
|
26
|
-
|
|
29
|
+
required?: boolean;
|
|
30
|
+
size?: FormFieldSizeOptions;
|
|
27
31
|
showTooltip?: boolean;
|
|
28
32
|
showValue?: boolean;
|
|
29
33
|
formatValue?: ((value: number) => string) | undefined;
|
|
30
34
|
onChange?: ((value: number) => void) | undefined;
|
|
31
35
|
label?: string;
|
|
36
|
+
helperText?: string;
|
|
37
|
+
feedback?: FormFieldFeedback;
|
|
32
38
|
} = $props();
|
|
33
39
|
|
|
34
40
|
let isDragging = $state(false);
|
|
@@ -52,7 +58,7 @@
|
|
|
52
58
|
let displayValue = $derived(formatValue ? formatValue(value) : String(value));
|
|
53
59
|
</script>
|
|
54
60
|
|
|
55
|
-
<FormField {size} {label} {id} {disabled}>
|
|
61
|
+
<FormField {size} {label} {id} {disabled} {required} {helperText} {feedback}>
|
|
56
62
|
<div class="slider-wrapper">
|
|
57
63
|
<div class="slider-track-container">
|
|
58
64
|
<input
|
|
@@ -63,6 +69,7 @@
|
|
|
63
69
|
{max}
|
|
64
70
|
{step}
|
|
65
71
|
{disabled}
|
|
72
|
+
{required}
|
|
66
73
|
bind:value
|
|
67
74
|
oninput={handleInput}
|
|
68
75
|
onmousedown={handleMouseDown}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FormFieldFeedback } from '../form-field/form-field.svelte';
|
|
2
|
+
import type { FormFieldSizeOptions } from '../../types/form.js';
|
|
2
3
|
type $$ComponentProps = {
|
|
3
4
|
value?: number;
|
|
4
5
|
min?: number;
|
|
5
6
|
max?: number;
|
|
6
7
|
step?: number;
|
|
7
8
|
disabled?: boolean;
|
|
8
|
-
|
|
9
|
+
required?: boolean;
|
|
10
|
+
size?: FormFieldSizeOptions;
|
|
9
11
|
showTooltip?: boolean;
|
|
10
12
|
showValue?: boolean;
|
|
11
13
|
formatValue?: ((value: number) => string) | undefined;
|
|
12
14
|
onChange?: ((value: number) => void) | undefined;
|
|
13
15
|
label?: string;
|
|
16
|
+
helperText?: string;
|
|
17
|
+
feedback?: FormFieldFeedback;
|
|
14
18
|
};
|
|
15
19
|
declare const Slider: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
16
20
|
type Slider = ReturnType<typeof Slider>;
|
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
justify-content: flex-start;
|
|
404
404
|
position: relative;
|
|
405
405
|
width: 100%;
|
|
406
|
-
height:
|
|
406
|
+
min-height: 2.125rem;
|
|
407
407
|
border-radius: var(--radius-md);
|
|
408
408
|
border: var(--border-thin) solid var(--form-input-border);
|
|
409
409
|
background-color: var(--form-input-bg);
|
|
@@ -411,6 +411,7 @@
|
|
|
411
411
|
font-size: var(--font-md);
|
|
412
412
|
font-weight: 500;
|
|
413
413
|
line-height: 2rem;
|
|
414
|
+
padding: 0;
|
|
414
415
|
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), box-shadow var(--transition-base) var(--ease-in-out);
|
|
415
416
|
user-select: none;
|
|
416
417
|
white-space: nowrap;
|
|
@@ -433,11 +434,12 @@
|
|
|
433
434
|
background-color: transparent;
|
|
434
435
|
border: none;
|
|
435
436
|
line-height: 2rem;
|
|
437
|
+
height: 2rem;
|
|
436
438
|
font-size: var(--font-md);
|
|
437
439
|
width: 100%;
|
|
438
440
|
flex-grow: 1;
|
|
439
|
-
padding
|
|
440
|
-
|
|
441
|
+
padding: 0 var(--spacing-base);
|
|
442
|
+
margin: 0;
|
|
441
443
|
}
|
|
442
444
|
.tag-box .input-container .input input:focus {
|
|
443
445
|
outline: none;
|
|
@@ -24,7 +24,11 @@
|
|
|
24
24
|
showCharacterCount = false,
|
|
25
25
|
maxlength = undefined,
|
|
26
26
|
minlength = undefined,
|
|
27
|
-
pattern = undefined
|
|
27
|
+
pattern = undefined,
|
|
28
|
+
onChange = undefined,
|
|
29
|
+
onInput = undefined,
|
|
30
|
+
onFocus = undefined,
|
|
31
|
+
onBlur = undefined
|
|
28
32
|
}: {
|
|
29
33
|
size?: FormFieldSizeOptions;
|
|
30
34
|
value?: string | null;
|
|
@@ -44,6 +48,10 @@
|
|
|
44
48
|
maxlength?: number | undefined;
|
|
45
49
|
minlength?: number | undefined;
|
|
46
50
|
pattern?: string | undefined;
|
|
51
|
+
onChange?: ((value: string) => void) | undefined;
|
|
52
|
+
onInput?: ((value: string) => void) | undefined;
|
|
53
|
+
onFocus?: ((e: FocusEvent) => void) | undefined;
|
|
54
|
+
onBlur?: ((e: FocusEvent) => void) | undefined;
|
|
47
55
|
} = $props();
|
|
48
56
|
|
|
49
57
|
let textareaElement: HTMLTextAreaElement | null = $state(null);
|
|
@@ -76,6 +84,15 @@
|
|
|
76
84
|
textareaElement.style.height = `${newHeight}px`;
|
|
77
85
|
};
|
|
78
86
|
|
|
87
|
+
const handleInput = (e: Event) => {
|
|
88
|
+
handleAutoResize();
|
|
89
|
+
onInput?.(value || '');
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleChange = (e: Event) => {
|
|
93
|
+
onChange?.(value || '');
|
|
94
|
+
};
|
|
95
|
+
|
|
79
96
|
// Run auto-resize when value changes
|
|
80
97
|
$effect(() => {
|
|
81
98
|
if (value !== null && value !== undefined) {
|
|
@@ -113,7 +130,10 @@
|
|
|
113
130
|
{minlength}
|
|
114
131
|
aria-busy={isLoading}
|
|
115
132
|
data-auto-resize={autoResize}
|
|
116
|
-
oninput={
|
|
133
|
+
oninput={handleInput}
|
|
134
|
+
onchange={handleChange}
|
|
135
|
+
onfocus={onFocus}
|
|
136
|
+
onblur={onBlur}
|
|
117
137
|
></textarea>
|
|
118
138
|
{#if showCharacterCount && maxlength}
|
|
119
139
|
<div class="character-count {characterLimitClass}">
|
|
@@ -19,6 +19,10 @@ type $$ComponentProps = {
|
|
|
19
19
|
maxlength?: number | undefined;
|
|
20
20
|
minlength?: number | undefined;
|
|
21
21
|
pattern?: string | undefined;
|
|
22
|
+
onChange?: ((value: string) => void) | undefined;
|
|
23
|
+
onInput?: ((value: string) => void) | undefined;
|
|
24
|
+
onFocus?: ((e: FocusEvent) => void) | undefined;
|
|
25
|
+
onBlur?: ((e: FocusEvent) => void) | undefined;
|
|
22
26
|
};
|
|
23
27
|
declare const TextArea: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
24
28
|
type TextArea = ReturnType<typeof TextArea>;
|