@salmexio/ui 1.2.0 → 1.3.0
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/dist/feedback/Alert/Alert.svelte +4 -1
- package/dist/feedback/Alert/Alert.svelte.d.ts +1 -0
- package/dist/feedback/Alert/Alert.svelte.d.ts.map +1 -1
- package/dist/feedback/Spinner/Spinner.svelte +4 -1
- package/dist/feedback/Spinner/Spinner.svelte.d.ts +1 -0
- package/dist/feedback/Spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/forms/DatePicker/DatePicker.svelte +725 -0
- package/dist/forms/DatePicker/DatePicker.svelte.d.ts +48 -0
- package/dist/forms/DatePicker/DatePicker.svelte.d.ts.map +1 -0
- package/dist/forms/DatePicker/index.d.ts +2 -0
- package/dist/forms/DatePicker/index.d.ts.map +1 -0
- package/dist/forms/DatePicker/index.js +1 -0
- package/dist/forms/FormField/FormField.svelte +173 -0
- package/dist/forms/FormField/FormField.svelte.d.ts +46 -0
- package/dist/forms/FormField/FormField.svelte.d.ts.map +1 -0
- package/dist/forms/FormField/index.d.ts +2 -0
- package/dist/forms/FormField/index.d.ts.map +1 -0
- package/dist/forms/FormField/index.js +1 -0
- package/dist/forms/MultiSelect/MultiSelect.svelte +820 -0
- package/dist/forms/MultiSelect/MultiSelect.svelte.d.ts +69 -0
- package/dist/forms/MultiSelect/MultiSelect.svelte.d.ts.map +1 -0
- package/dist/forms/MultiSelect/index.d.ts +3 -0
- package/dist/forms/MultiSelect/index.d.ts.map +1 -0
- package/dist/forms/MultiSelect/index.js +1 -0
- package/dist/forms/PhoneInput/PhoneInput.svelte +591 -0
- package/dist/forms/PhoneInput/PhoneInput.svelte.d.ts +57 -0
- package/dist/forms/PhoneInput/PhoneInput.svelte.d.ts.map +1 -0
- package/dist/forms/PhoneInput/index.d.ts +4 -0
- package/dist/forms/PhoneInput/index.d.ts.map +1 -0
- package/dist/forms/PhoneInput/index.js +2 -0
- package/dist/forms/RadioGroup/RadioGroup.svelte +417 -0
- package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts +62 -0
- package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts.map +1 -0
- package/dist/forms/RadioGroup/index.d.ts +3 -0
- package/dist/forms/RadioGroup/index.d.ts.map +1 -0
- package/dist/forms/RadioGroup/index.js +1 -0
- package/dist/forms/SearchInput/SearchInput.svelte +788 -0
- package/dist/forms/SearchInput/SearchInput.svelte.d.ts +79 -0
- package/dist/forms/SearchInput/SearchInput.svelte.d.ts.map +1 -0
- package/dist/forms/SearchInput/index.d.ts +3 -0
- package/dist/forms/SearchInput/index.d.ts.map +1 -0
- package/dist/forms/SearchInput/index.js +1 -0
- package/dist/forms/Select/Select.svelte +14 -8
- package/dist/forms/Select/Select.svelte.d.ts +2 -0
- package/dist/forms/Select/Select.svelte.d.ts.map +1 -1
- package/dist/forms/TextInput/TextInput.svelte +38 -16
- package/dist/forms/TextInput/TextInput.svelte.d.ts +6 -0
- package/dist/forms/TextInput/TextInput.svelte.d.ts.map +1 -1
- package/dist/forms/Textarea/Textarea.svelte +7 -1
- package/dist/forms/Textarea/Textarea.svelte.d.ts +2 -0
- package/dist/forms/Textarea/Textarea.svelte.d.ts.map +1 -1
- package/dist/forms/TimePicker/TimePicker.svelte +417 -0
- package/dist/forms/TimePicker/TimePicker.svelte.d.ts +53 -0
- package/dist/forms/TimePicker/TimePicker.svelte.d.ts.map +1 -0
- package/dist/forms/TimePicker/index.d.ts +2 -0
- package/dist/forms/TimePicker/index.d.ts.map +1 -0
- package/dist/forms/TimePicker/index.js +1 -0
- package/dist/forms/index.d.ts +12 -0
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +8 -0
- package/dist/layout/Container/Container.svelte +3 -0
- package/dist/layout/Container/Container.svelte.d.ts +1 -0
- package/dist/layout/Container/Container.svelte.d.ts.map +1 -1
- package/dist/primitives/Badge/Badge.svelte +5 -1
- package/dist/primitives/Badge/Badge.svelte.d.ts +1 -0
- package/dist/primitives/Badge/Badge.svelte.d.ts.map +1 -1
- package/dist/primitives/Tooltip/Tooltip.svelte +30 -0
- package/dist/primitives/Tooltip/Tooltip.svelte.d.ts.map +1 -1
- package/dist/utils/accessibility.d.ts +16 -0
- package/dist/utils/accessibility.d.ts.map +1 -0
- package/dist/utils/accessibility.js +80 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/keyboard.d.ts +6 -0
- package/dist/utils/keyboard.d.ts.map +1 -1
- package/dist/utils/keyboard.js +15 -9
- package/package.json +21 -1
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component TimePicker
|
|
3
|
+
|
|
4
|
+
INFRARED — Time input with hour/minute fields, optional seconds,
|
|
5
|
+
12/24-hour format, and keyboard increment support.
|
|
6
|
+
Output in HH:MM or HH:MM:SS format.
|
|
7
|
+
|
|
8
|
+
@example
|
|
9
|
+
<TimePicker label="Start time" bind:value={time} />
|
|
10
|
+
<TimePicker label="Alarm" format="12h" step={15} />
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { cn } from '../../utils/cn.js';
|
|
14
|
+
import { generateId } from '../../utils/keyboard.js';
|
|
15
|
+
|
|
16
|
+
type TimeSize = 'sm' | 'md' | 'lg';
|
|
17
|
+
type TimeFormat = '12h' | '24h';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
/** Visible label. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** Time value in HH:MM or HH:MM:SS format (24h). */
|
|
23
|
+
value?: string;
|
|
24
|
+
/** Time format display. */
|
|
25
|
+
format?: TimeFormat;
|
|
26
|
+
/** Minute step increment. */
|
|
27
|
+
step?: number;
|
|
28
|
+
/** Show seconds field. */
|
|
29
|
+
showSeconds?: boolean;
|
|
30
|
+
/** Minimum time (HH:MM). */
|
|
31
|
+
min?: string;
|
|
32
|
+
/** Maximum time (HH:MM). */
|
|
33
|
+
max?: string;
|
|
34
|
+
/** Size variant. */
|
|
35
|
+
size?: TimeSize;
|
|
36
|
+
/** Required field. */
|
|
37
|
+
required?: boolean;
|
|
38
|
+
/** Disabled state. */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Error message. */
|
|
41
|
+
error?: string;
|
|
42
|
+
/** Hint text. */
|
|
43
|
+
hint?: string;
|
|
44
|
+
/** Hide the visible label. */
|
|
45
|
+
hideLabel?: boolean;
|
|
46
|
+
/** Reserve footer space even when empty. */
|
|
47
|
+
alwaysShowFooter?: boolean;
|
|
48
|
+
/** Additional CSS class. */
|
|
49
|
+
class?: string;
|
|
50
|
+
/** Called when time changes. */
|
|
51
|
+
onchange?: (value: string) => void;
|
|
52
|
+
/** Test ID. */
|
|
53
|
+
testId?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let {
|
|
57
|
+
label,
|
|
58
|
+
value = $bindable(''),
|
|
59
|
+
format = '24h',
|
|
60
|
+
step = 1,
|
|
61
|
+
showSeconds = false,
|
|
62
|
+
min,
|
|
63
|
+
max,
|
|
64
|
+
size = 'md',
|
|
65
|
+
required = false,
|
|
66
|
+
disabled = false,
|
|
67
|
+
error = '',
|
|
68
|
+
hint = '',
|
|
69
|
+
hideLabel = false,
|
|
70
|
+
alwaysShowFooter = true,
|
|
71
|
+
class: className = '',
|
|
72
|
+
onchange,
|
|
73
|
+
testId
|
|
74
|
+
}: Props = $props();
|
|
75
|
+
|
|
76
|
+
const id = generateId('timepicker');
|
|
77
|
+
const errorId = `${id}-error`;
|
|
78
|
+
const hintId = `${id}-hint`;
|
|
79
|
+
|
|
80
|
+
// Guard: step must be a positive integer
|
|
81
|
+
const safeStep = $derived(Math.max(1, Math.round(step)));
|
|
82
|
+
|
|
83
|
+
let hasInteracted = $state(false);
|
|
84
|
+
|
|
85
|
+
const showError = $derived(hasInteracted && !!error);
|
|
86
|
+
const ariaDescribedBy = $derived(
|
|
87
|
+
[showError && errorId, hint && hintId].filter(Boolean).join(' ') || undefined
|
|
88
|
+
);
|
|
89
|
+
const hasFooterContent = $derived(showError || !!hint);
|
|
90
|
+
|
|
91
|
+
// Parse value into parts
|
|
92
|
+
const parsed = $derived.by(() => {
|
|
93
|
+
const parts = (value || '00:00:00').split(':');
|
|
94
|
+
return {
|
|
95
|
+
hours: parseInt(parts[0] || '0', 10),
|
|
96
|
+
minutes: parseInt(parts[1] || '0', 10),
|
|
97
|
+
seconds: parseInt(parts[2] || '0', 10)
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const displayHours = $derived.by(() => {
|
|
102
|
+
if (format === '12h') {
|
|
103
|
+
const h = parsed.hours % 12;
|
|
104
|
+
return h === 0 ? 12 : h;
|
|
105
|
+
}
|
|
106
|
+
return parsed.hours;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const period = $derived<'AM' | 'PM'>(parsed.hours >= 12 ? 'PM' : 'AM');
|
|
110
|
+
|
|
111
|
+
function pad(n: number): string {
|
|
112
|
+
return String(n).padStart(2, '0');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildValue(h: number, m: number, s: number): string {
|
|
116
|
+
h = Math.max(0, Math.min(23, h));
|
|
117
|
+
m = Math.max(0, Math.min(59, m));
|
|
118
|
+
s = Math.max(0, Math.min(59, s));
|
|
119
|
+
return showSeconds ? `${pad(h)}:${pad(m)}:${pad(s)}` : `${pad(h)}:${pad(m)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeTimeStr(t: string): string {
|
|
123
|
+
const parts = t.split(':');
|
|
124
|
+
const h = (parts[0] || '00').padStart(2, '0');
|
|
125
|
+
const m = (parts[1] || '00').padStart(2, '0');
|
|
126
|
+
const s = (parts[2] || '00').padStart(2, '0');
|
|
127
|
+
return showSeconds ? `${h}:${m}:${s}` : `${h}:${m}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function clampToRange(val: string): string {
|
|
131
|
+
if (min) {
|
|
132
|
+
const normMin = normalizeTimeStr(min);
|
|
133
|
+
if (val < normMin) return normMin;
|
|
134
|
+
}
|
|
135
|
+
if (max) {
|
|
136
|
+
const normMax = normalizeTimeStr(max);
|
|
137
|
+
if (val > normMax) return normMax;
|
|
138
|
+
}
|
|
139
|
+
return val;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function updateValue(h: number, m: number, s: number) {
|
|
143
|
+
const newVal = clampToRange(buildValue(h, m, s));
|
|
144
|
+
value = newVal;
|
|
145
|
+
hasInteracted = true;
|
|
146
|
+
onchange?.(newVal);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function handleHourInput(e: Event) {
|
|
150
|
+
const target = e.target as HTMLInputElement;
|
|
151
|
+
let h = parseInt(target.value, 10);
|
|
152
|
+
if (isNaN(h)) return;
|
|
153
|
+
if (format === '12h') {
|
|
154
|
+
if (period === 'PM' && h < 12) h += 12;
|
|
155
|
+
else if (period === 'AM' && h === 12) h = 0;
|
|
156
|
+
}
|
|
157
|
+
updateValue(h, parsed.minutes, parsed.seconds);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function handleMinuteInput(e: Event) {
|
|
161
|
+
const target = e.target as HTMLInputElement;
|
|
162
|
+
let m = parseInt(target.value, 10);
|
|
163
|
+
if (isNaN(m)) return;
|
|
164
|
+
m = Math.round(m / safeStep) * safeStep;
|
|
165
|
+
updateValue(parsed.hours, m, parsed.seconds);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function handleSecondInput(e: Event) {
|
|
169
|
+
const target = e.target as HTMLInputElement;
|
|
170
|
+
const s = parseInt(target.value, 10);
|
|
171
|
+
if (isNaN(s)) return;
|
|
172
|
+
updateValue(parsed.hours, parsed.minutes, s);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function togglePeriod() {
|
|
176
|
+
const h = parsed.hours >= 12 ? parsed.hours - 12 : parsed.hours + 12;
|
|
177
|
+
updateValue(h, parsed.minutes, parsed.seconds);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function handleKeydown(e: KeyboardEvent, field: 'hour' | 'minute' | 'second') {
|
|
181
|
+
const delta = e.key === 'ArrowUp' ? 1 : e.key === 'ArrowDown' ? -1 : 0;
|
|
182
|
+
if (!delta) return;
|
|
183
|
+
e.preventDefault();
|
|
184
|
+
|
|
185
|
+
let { hours, minutes, seconds } = parsed;
|
|
186
|
+
if (field === 'hour') {
|
|
187
|
+
hours = (hours + delta + 24) % 24;
|
|
188
|
+
} else if (field === 'minute') {
|
|
189
|
+
minutes = (minutes + delta * safeStep + 60) % 60;
|
|
190
|
+
} else {
|
|
191
|
+
seconds = (seconds + delta + 60) % 60;
|
|
192
|
+
}
|
|
193
|
+
updateValue(hours, minutes, seconds);
|
|
194
|
+
}
|
|
195
|
+
</script>
|
|
196
|
+
|
|
197
|
+
<div
|
|
198
|
+
class={cn('sx-timepicker', `sx-timepicker-${size}`, disabled && 'sx-timepicker-disabled', className)}
|
|
199
|
+
data-testid={testId}
|
|
200
|
+
>
|
|
201
|
+
<label for={`${id}-hour`} class={cn('sx-timepicker-label', hideLabel && 'sx-sr-only')}>
|
|
202
|
+
{label}
|
|
203
|
+
{#if required}
|
|
204
|
+
<span class="sx-timepicker-required" aria-hidden="true">*</span>
|
|
205
|
+
{/if}
|
|
206
|
+
</label>
|
|
207
|
+
|
|
208
|
+
<div
|
|
209
|
+
class={cn(
|
|
210
|
+
'sx-timepicker-field',
|
|
211
|
+
showError && 'sx-timepicker-field-error'
|
|
212
|
+
)}
|
|
213
|
+
role="group"
|
|
214
|
+
aria-label={label}
|
|
215
|
+
aria-describedby={ariaDescribedBy}
|
|
216
|
+
>
|
|
217
|
+
<span class="sx-timepicker-icon" aria-hidden="true">
|
|
218
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
219
|
+
<circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
|
|
220
|
+
</svg>
|
|
221
|
+
</span>
|
|
222
|
+
|
|
223
|
+
<input
|
|
224
|
+
id={`${id}-hour`}
|
|
225
|
+
type="number"
|
|
226
|
+
class="sx-timepicker-input"
|
|
227
|
+
value={pad(displayHours)}
|
|
228
|
+
min={format === '12h' ? 1 : 0}
|
|
229
|
+
max={format === '12h' ? 12 : 23}
|
|
230
|
+
{disabled}
|
|
231
|
+
aria-label="Hours"
|
|
232
|
+
oninput={handleHourInput}
|
|
233
|
+
onkeydown={(e) => handleKeydown(e, 'hour')}
|
|
234
|
+
/>
|
|
235
|
+
|
|
236
|
+
<span class="sx-timepicker-sep" aria-hidden="true">:</span>
|
|
237
|
+
|
|
238
|
+
<input
|
|
239
|
+
id={`${id}-minute`}
|
|
240
|
+
type="number"
|
|
241
|
+
class="sx-timepicker-input"
|
|
242
|
+
value={pad(parsed.minutes)}
|
|
243
|
+
min={0}
|
|
244
|
+
max={59}
|
|
245
|
+
step={safeStep}
|
|
246
|
+
{disabled}
|
|
247
|
+
aria-label="Minutes"
|
|
248
|
+
oninput={handleMinuteInput}
|
|
249
|
+
onkeydown={(e) => handleKeydown(e, 'minute')}
|
|
250
|
+
/>
|
|
251
|
+
|
|
252
|
+
{#if showSeconds}
|
|
253
|
+
<span class="sx-timepicker-sep" aria-hidden="true">:</span>
|
|
254
|
+
<input
|
|
255
|
+
id={`${id}-second`}
|
|
256
|
+
type="number"
|
|
257
|
+
class="sx-timepicker-input"
|
|
258
|
+
value={pad(parsed.seconds)}
|
|
259
|
+
min={0}
|
|
260
|
+
max={59}
|
|
261
|
+
{disabled}
|
|
262
|
+
aria-label="Seconds"
|
|
263
|
+
oninput={handleSecondInput}
|
|
264
|
+
onkeydown={(e) => handleKeydown(e, 'second')}
|
|
265
|
+
/>
|
|
266
|
+
{/if}
|
|
267
|
+
|
|
268
|
+
{#if format === '12h'}
|
|
269
|
+
<button
|
|
270
|
+
type="button"
|
|
271
|
+
class="sx-timepicker-period"
|
|
272
|
+
{disabled}
|
|
273
|
+
aria-label={`Switch to ${period === 'AM' ? 'PM' : 'AM'}`}
|
|
274
|
+
onclick={togglePeriod}
|
|
275
|
+
>
|
|
276
|
+
{period}
|
|
277
|
+
</button>
|
|
278
|
+
{/if}
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
{#if alwaysShowFooter || hasFooterContent}
|
|
282
|
+
<div class="sx-timepicker-footer">
|
|
283
|
+
{#if showError}
|
|
284
|
+
<p id={errorId} class="sx-timepicker-error" role="alert" aria-live="assertive">{error}</p>
|
|
285
|
+
{:else if hint}
|
|
286
|
+
<p id={hintId} class="sx-timepicker-hint">{hint}</p>
|
|
287
|
+
{/if}
|
|
288
|
+
</div>
|
|
289
|
+
{/if}
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<style>
|
|
293
|
+
.sx-timepicker {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: column;
|
|
296
|
+
gap: var(--sx-space-1);
|
|
297
|
+
font-family: var(--sx-font-body);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.sx-timepicker-disabled { opacity: 0.5; }
|
|
301
|
+
|
|
302
|
+
.sx-timepicker-label {
|
|
303
|
+
font-size: var(--sx-text-sm);
|
|
304
|
+
font-weight: 500;
|
|
305
|
+
color: var(--sx-color-text-secondary);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.sx-timepicker-required { color: var(--sx-color-red); margin-left: 2px; }
|
|
309
|
+
|
|
310
|
+
.sx-sr-only {
|
|
311
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
312
|
+
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.sx-timepicker-field {
|
|
316
|
+
display: inline-flex;
|
|
317
|
+
align-items: center;
|
|
318
|
+
gap: var(--sx-space-1);
|
|
319
|
+
border: 1px solid var(--sx-color-border-strong);
|
|
320
|
+
border-radius: var(--sx-radius-md);
|
|
321
|
+
background: var(--sx-color-surface);
|
|
322
|
+
transition: border-color var(--sx-transition-fast), box-shadow var(--sx-transition-fast);
|
|
323
|
+
box-shadow:
|
|
324
|
+
inset 0 1px 3px rgba(0, 0, 0, 0.3),
|
|
325
|
+
inset 0 0 0 1px rgba(0, 0, 0, 0.06);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.sx-timepicker-field:focus-within {
|
|
329
|
+
border-color: var(--sx-color-primary);
|
|
330
|
+
box-shadow:
|
|
331
|
+
inset 0 1px 2px rgba(0, 0, 0, 0.2),
|
|
332
|
+
0 0 0 3px var(--sx-color-primary-ring);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.sx-timepicker-field-error {
|
|
336
|
+
border-color: var(--sx-color-red);
|
|
337
|
+
box-shadow:
|
|
338
|
+
inset 0 1px 2px rgba(0, 0, 0, 0.2),
|
|
339
|
+
0 0 0 3px var(--sx-color-red-ring);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.sx-timepicker-sm .sx-timepicker-field { min-height: 32px; padding: 0 var(--sx-space-2); }
|
|
343
|
+
.sx-timepicker-md .sx-timepicker-field { min-height: 40px; padding: 0 var(--sx-space-3); }
|
|
344
|
+
.sx-timepicker-lg .sx-timepicker-field { min-height: 48px; padding: 0 var(--sx-space-4); }
|
|
345
|
+
|
|
346
|
+
.sx-timepicker-icon {
|
|
347
|
+
flex-shrink: 0;
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
color: var(--sx-color-text-secondary);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.sx-timepicker-input {
|
|
354
|
+
width: 32px;
|
|
355
|
+
border: none;
|
|
356
|
+
background: transparent;
|
|
357
|
+
color: var(--sx-color-text);
|
|
358
|
+
font-family: var(--sx-font-mono);
|
|
359
|
+
font-size: var(--sx-text-sm);
|
|
360
|
+
font-weight: 600;
|
|
361
|
+
text-align: center;
|
|
362
|
+
outline: none;
|
|
363
|
+
appearance: textfield;
|
|
364
|
+
-moz-appearance: textfield;
|
|
365
|
+
padding: var(--sx-space-1) 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.sx-timepicker-input::-webkit-inner-spin-button,
|
|
369
|
+
.sx-timepicker-input::-webkit-outer-spin-button {
|
|
370
|
+
-webkit-appearance: none;
|
|
371
|
+
margin: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.sx-timepicker-input:focus {
|
|
375
|
+
background: var(--sx-color-surface-2);
|
|
376
|
+
border-radius: var(--sx-radius-sm);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.sx-timepicker-sep {
|
|
380
|
+
color: var(--sx-color-text-disabled);
|
|
381
|
+
font-family: var(--sx-font-mono);
|
|
382
|
+
font-size: var(--sx-text-sm);
|
|
383
|
+
font-weight: 600;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.sx-timepicker-period {
|
|
387
|
+
border: none;
|
|
388
|
+
background: var(--sx-color-surface-2);
|
|
389
|
+
border-radius: var(--sx-radius-sm);
|
|
390
|
+
color: var(--sx-color-primary);
|
|
391
|
+
font-family: var(--sx-font-mono);
|
|
392
|
+
font-size: var(--sx-text-xs);
|
|
393
|
+
font-weight: 700;
|
|
394
|
+
padding: var(--sx-space-1) var(--sx-space-2);
|
|
395
|
+
cursor: pointer;
|
|
396
|
+
transition: background var(--sx-transition-fast);
|
|
397
|
+
margin-left: var(--sx-space-1);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.sx-timepicker-period:hover {
|
|
401
|
+
background: var(--sx-color-primary-subtle);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.sx-timepicker-period:focus-visible {
|
|
405
|
+
outline: 2px solid var(--sx-color-primary);
|
|
406
|
+
outline-offset: 1px;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Footer */
|
|
410
|
+
.sx-timepicker-footer { min-height: 1.25rem; }
|
|
411
|
+
.sx-timepicker-error { font-size: var(--sx-text-xs); font-weight: 500; color: var(--sx-color-red); margin: 0; }
|
|
412
|
+
.sx-timepicker-hint { font-size: var(--sx-text-xs); color: var(--sx-color-text-secondary); margin: 0; }
|
|
413
|
+
|
|
414
|
+
@media (prefers-reduced-motion: reduce) {
|
|
415
|
+
.sx-timepicker-field, .sx-timepicker-period { transition: none; }
|
|
416
|
+
}
|
|
417
|
+
</style>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type TimeSize = 'sm' | 'md' | 'lg';
|
|
2
|
+
type TimeFormat = '12h' | '24h';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Visible label. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Time value in HH:MM or HH:MM:SS format (24h). */
|
|
7
|
+
value?: string;
|
|
8
|
+
/** Time format display. */
|
|
9
|
+
format?: TimeFormat;
|
|
10
|
+
/** Minute step increment. */
|
|
11
|
+
step?: number;
|
|
12
|
+
/** Show seconds field. */
|
|
13
|
+
showSeconds?: boolean;
|
|
14
|
+
/** Minimum time (HH:MM). */
|
|
15
|
+
min?: string;
|
|
16
|
+
/** Maximum time (HH:MM). */
|
|
17
|
+
max?: string;
|
|
18
|
+
/** Size variant. */
|
|
19
|
+
size?: TimeSize;
|
|
20
|
+
/** Required field. */
|
|
21
|
+
required?: boolean;
|
|
22
|
+
/** Disabled state. */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** Error message. */
|
|
25
|
+
error?: string;
|
|
26
|
+
/** Hint text. */
|
|
27
|
+
hint?: string;
|
|
28
|
+
/** Hide the visible label. */
|
|
29
|
+
hideLabel?: boolean;
|
|
30
|
+
/** Reserve footer space even when empty. */
|
|
31
|
+
alwaysShowFooter?: boolean;
|
|
32
|
+
/** Additional CSS class. */
|
|
33
|
+
class?: string;
|
|
34
|
+
/** Called when time changes. */
|
|
35
|
+
onchange?: (value: string) => void;
|
|
36
|
+
/** Test ID. */
|
|
37
|
+
testId?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* TimePicker
|
|
41
|
+
*
|
|
42
|
+
* INFRARED — Time input with hour/minute fields, optional seconds,
|
|
43
|
+
* 12/24-hour format, and keyboard increment support.
|
|
44
|
+
* Output in HH:MM or HH:MM:SS format.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* <TimePicker label="Start time" bind:value={time} />
|
|
48
|
+
* <TimePicker label="Alarm" format="12h" step={15} />
|
|
49
|
+
*/
|
|
50
|
+
declare const TimePicker: import("svelte").Component<Props, {}, "value">;
|
|
51
|
+
type TimePicker = ReturnType<typeof TimePicker>;
|
|
52
|
+
export default TimePicker;
|
|
53
|
+
//# sourceMappingURL=TimePicker.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePicker.svelte.d.ts","sourceRoot":"","sources":["../../../src/forms/TimePicker/TimePicker.svelte.ts"],"names":[],"mappings":"AAOA,KAAK,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,KAAK,UAAU,GAAG,KAAK,GAAG,KAAK,CAAC;AAEhC,UAAU,KAAK;IACd,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAyMD;;;;;;;;;;GAUG;AACH,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/TimePicker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as TimePicker } from './TimePicker.svelte';
|
package/dist/forms/index.d.ts
CHANGED
|
@@ -5,4 +5,16 @@ export type { SelectOption, SelectGroup } from './Select/index.js';
|
|
|
5
5
|
export { Textarea } from './Textarea/index.js';
|
|
6
6
|
export { Slider } from './Slider/index.js';
|
|
7
7
|
export { Toggle } from './Toggle/index.js';
|
|
8
|
+
export { FormField } from './FormField/index.js';
|
|
9
|
+
export { RadioGroup } from './RadioGroup/index.js';
|
|
10
|
+
export type { RadioOption } from './RadioGroup/index.js';
|
|
11
|
+
export { SearchInput } from './SearchInput/index.js';
|
|
12
|
+
export type { SearchOption, SearchGroup } from './SearchInput/index.js';
|
|
13
|
+
export { MultiSelect } from './MultiSelect/index.js';
|
|
14
|
+
export type { MultiSelectOption, MultiSelectGroup } from './MultiSelect/index.js';
|
|
15
|
+
export { DatePicker } from './DatePicker/index.js';
|
|
16
|
+
export { TimePicker } from './TimePicker/index.js';
|
|
17
|
+
export { PhoneInput } from './PhoneInput/index.js';
|
|
18
|
+
export type { Country } from './PhoneInput/index.js';
|
|
19
|
+
export { DEFAULT_COUNTRIES } from './PhoneInput/index.js';
|
|
8
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/forms/index.js
CHANGED
|
@@ -4,3 +4,11 @@ export { Select } from './Select/index.js';
|
|
|
4
4
|
export { Textarea } from './Textarea/index.js';
|
|
5
5
|
export { Slider } from './Slider/index.js';
|
|
6
6
|
export { Toggle } from './Toggle/index.js';
|
|
7
|
+
export { FormField } from './FormField/index.js';
|
|
8
|
+
export { RadioGroup } from './RadioGroup/index.js';
|
|
9
|
+
export { SearchInput } from './SearchInput/index.js';
|
|
10
|
+
export { MultiSelect } from './MultiSelect/index.js';
|
|
11
|
+
export { DatePicker } from './DatePicker/index.js';
|
|
12
|
+
export { TimePicker } from './TimePicker/index.js';
|
|
13
|
+
export { PhoneInput } from './PhoneInput/index.js';
|
|
14
|
+
export { DEFAULT_COUNTRIES } from './PhoneInput/index.js';
|
|
@@ -22,6 +22,7 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'class'> {
|
|
|
22
22
|
id?: string;
|
|
23
23
|
class?: string;
|
|
24
24
|
children?: Snippet;
|
|
25
|
+
testId?: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
let {
|
|
@@ -34,6 +35,7 @@ let {
|
|
|
34
35
|
id,
|
|
35
36
|
class: className = '',
|
|
36
37
|
children,
|
|
38
|
+
testId,
|
|
37
39
|
...restProps
|
|
38
40
|
}: Props = $props();
|
|
39
41
|
|
|
@@ -52,6 +54,7 @@ const resolvedPaddingY = $derived(paddingY ?? 'none');
|
|
|
52
54
|
centered && 'sx-container-centered',
|
|
53
55
|
className
|
|
54
56
|
)}
|
|
57
|
+
data-testid={testId}
|
|
55
58
|
{...restProps}
|
|
56
59
|
>
|
|
57
60
|
{#if children}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Container.svelte.d.ts","sourceRoot":"","sources":["../../../src/layout/Container/Container.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,KAAK,aAAa,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AACjF,KAAK,gBAAgB,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3D,UAAU,KAAM,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACpE,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Container.svelte.d.ts","sourceRoot":"","sources":["../../../src/layout/Container/Container.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,KAAK,aAAa,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AACjF,KAAK,gBAAgB,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3D,UAAU,KAAM,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACpE,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AA6CD;;;;;GAKG;AACH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -30,6 +30,7 @@ interface Props {
|
|
|
30
30
|
children?: Snippet;
|
|
31
31
|
/** Accessible label when dot (required when dot for a11y) */
|
|
32
32
|
'aria-label'?: string;
|
|
33
|
+
testId?: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
let {
|
|
@@ -39,7 +40,8 @@ let {
|
|
|
39
40
|
icon,
|
|
40
41
|
class: className = '',
|
|
41
42
|
children,
|
|
42
|
-
'aria-label': ariaLabel
|
|
43
|
+
'aria-label': ariaLabel,
|
|
44
|
+
testId
|
|
43
45
|
}: Props = $props();
|
|
44
46
|
</script>
|
|
45
47
|
|
|
@@ -54,6 +56,7 @@ let {
|
|
|
54
56
|
)}
|
|
55
57
|
role="status"
|
|
56
58
|
aria-label={ariaLabel ?? status}
|
|
59
|
+
data-testid={testId}
|
|
57
60
|
>
|
|
58
61
|
<span class="sx-sr-only">{ariaLabel ?? status}</span>
|
|
59
62
|
</span>
|
|
@@ -66,6 +69,7 @@ let {
|
|
|
66
69
|
className
|
|
67
70
|
)}
|
|
68
71
|
role="status"
|
|
72
|
+
data-testid={testId}
|
|
69
73
|
>
|
|
70
74
|
{#if icon}
|
|
71
75
|
<span class="sx-badge-icon" aria-hidden="true">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Badge/Badge.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAItC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhG,UAAU,KAAK;IACd,gCAAgC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW;IACX,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iEAAiE;IACjE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Badge.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Badge/Badge.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAItC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhG,UAAU,KAAK;IACd,gCAAgC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW;IACX,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iEAAiE;IACjE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAqDD;;;;;;;;;GASG;AACH,QAAA,MAAM,KAAK,2CAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import type { Snippet } from 'svelte';
|
|
18
18
|
import { cn } from '../../utils/cn.js';
|
|
19
19
|
import { tick } from 'svelte';
|
|
20
|
+
import { onMount } from 'svelte';
|
|
20
21
|
|
|
21
22
|
type Placement = 'top' | 'bottom' | 'left' | 'right';
|
|
22
23
|
|
|
@@ -140,6 +141,34 @@ function positionTooltip() {
|
|
|
140
141
|
computedLeft = `${left}px`;
|
|
141
142
|
placementOverride = finalPlacement;
|
|
142
143
|
}
|
|
144
|
+
|
|
145
|
+
// Portal: move tooltip to document.body so position:fixed is viewport-relative
|
|
146
|
+
// (ancestors with transform/backdrop-filter would otherwise create a containing block)
|
|
147
|
+
$effect(() => {
|
|
148
|
+
if (tooltipEl && visible) {
|
|
149
|
+
document.body.appendChild(tooltipEl);
|
|
150
|
+
// Position only after the node is in body and laid out (showcase-style behavior)
|
|
151
|
+
const run = () => {
|
|
152
|
+
tick().then(() => {
|
|
153
|
+
positionTooltip();
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
requestAnimationFrame(run);
|
|
157
|
+
return () => {
|
|
158
|
+
if (tooltipEl?.parentNode === document.body) {
|
|
159
|
+
document.body.removeChild(tooltipEl);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
onMount(() => {
|
|
166
|
+
return () => {
|
|
167
|
+
if (tooltipEl?.parentNode === document.body) {
|
|
168
|
+
document.body.removeChild(tooltipEl);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
});
|
|
143
172
|
</script>
|
|
144
173
|
|
|
145
174
|
<svelte:window onkeydown={handleKeydown} />
|
|
@@ -204,6 +233,7 @@ function positionTooltip() {
|
|
|
204
233
|
0 0 8px -4px rgba(255, 107, 53, 0.1);
|
|
205
234
|
pointer-events: none;
|
|
206
235
|
animation: sx-tooltip-in 150ms var(--sx-ease-out) both;
|
|
236
|
+
white-space: normal;
|
|
207
237
|
word-wrap: break-word;
|
|
208
238
|
}
|
|
209
239
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Tooltip/Tooltip.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"Tooltip.svelte.d.ts","sourceRoot":"","sources":["../../../src/primitives/Tooltip/Tooltip.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,KAAK,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,UAAU,KAAK;IACd,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAsKD;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,OAAO,2CAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility utilities for WCAG 2.1 AA compliance.
|
|
3
|
+
* Focus management, screen reader announcements, media query helpers.
|
|
4
|
+
*/
|
|
5
|
+
/** Check whether an element can receive focus. */
|
|
6
|
+
export declare function isElementFocusable(el: HTMLElement): boolean;
|
|
7
|
+
/** Get all focusable elements within a container, in DOM order. */
|
|
8
|
+
export declare function getFocusableElements(container: HTMLElement): HTMLElement[];
|
|
9
|
+
/**
|
|
10
|
+
* Announce a message to screen readers via an ARIA live region.
|
|
11
|
+
* Creates the region lazily and reuses it across calls.
|
|
12
|
+
*/
|
|
13
|
+
export declare function announce(message: string, priority?: 'polite' | 'assertive'): void;
|
|
14
|
+
/** Check if the user prefers reduced motion. */
|
|
15
|
+
export declare function prefersReducedMotion(): boolean;
|
|
16
|
+
//# sourceMappingURL=accessibility.d.ts.map
|