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
|
@@ -11,16 +11,21 @@ type $$ComponentProps = {
|
|
|
11
11
|
size?: FormFieldSizeOptions;
|
|
12
12
|
placeholder?: string;
|
|
13
13
|
nullable?: boolean;
|
|
14
|
-
|
|
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?: ((
|
|
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"
|
|
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
|
-
<
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
172
|
+
input:focus {
|
|
197
173
|
outline: none;
|
|
198
174
|
}
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
-
<
|
|
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
|
-
</
|
|
49
|
+
</FormInputWrapper>
|
|
49
50
|
</FormField>
|
|
50
51
|
|
|
51
|
-
<style
|
|
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
|
-
|
|
66
|
+
input:focus {
|
|
85
67
|
outline: none;
|
|
86
68
|
}
|
|
87
|
-
|
|
69
|
+
input:focus-visible {
|
|
88
70
|
outline: 2px solid var(--focus-ring, #007bff);
|
|
89
71
|
outline-offset: 2px;
|
|
90
72
|
}
|
|
91
|
-
|
|
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;
|
package/dist/forms/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/forms/index.js
CHANGED
|
@@ -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:
|
|
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;
|