@sentropic/design-system-svelte 0.4.1 → 0.5.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatePicker.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DatePicker.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;CAClB,CAAC;AAGJ,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GAAG;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,eAAe,GAAG,IAAI,CAAC;IACtC,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA4UJ,QAAA,MAAM,UAAU,0DAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,431 @@
1
+ <script lang="ts" module>
2
+ export type FileUploadStatus = "idle" | "uploading" | "complete" | "error";
3
+
4
+ export type FileUploadItem = {
5
+ file: File;
6
+ status: FileUploadStatus;
7
+ progress?: number;
8
+ error?: string;
9
+ };
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import type { HTMLAttributes } from "svelte/elements";
14
+
15
+ type FileUploaderProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
16
+ label?: string;
17
+ helperText?: string;
18
+ errorText?: string;
19
+ invalid?: boolean;
20
+ accept?: string;
21
+ multiple?: boolean;
22
+ maxSizeBytes?: number;
23
+ disabled?: boolean;
24
+ files?: File[];
25
+ onfiles?: (files: File[]) => void;
26
+ triggerLabel?: string;
27
+ dropzoneLabel?: string;
28
+ removeLabel?: (filename: string) => string;
29
+ maxSizeErrorLabel?: (filename: string, maxSizeBytes: number) => string;
30
+ id?: string;
31
+ class?: string;
32
+ };
33
+
34
+ let {
35
+ label,
36
+ helperText,
37
+ errorText,
38
+ invalid = false,
39
+ accept,
40
+ multiple = false,
41
+ maxSizeBytes,
42
+ disabled = false,
43
+ files = $bindable([]),
44
+ onfiles,
45
+ triggerLabel,
46
+ dropzoneLabel = "Drag and drop files here",
47
+ removeLabel = (filename) => `Remove ${filename}`,
48
+ maxSizeErrorLabel = (filename, max) =>
49
+ `${filename} exceeds the ${formatSize(max)} size limit`,
50
+ id,
51
+ class: className,
52
+ ...rest
53
+ }: FileUploaderProps = $props();
54
+
55
+ const generatedId = `st-file-uploader-${Math.random().toString(36).slice(2, 9)}`;
56
+ const inputId = $derived(id ?? generatedId);
57
+ const helperId = $derived(`${inputId}-help`);
58
+ const errorId = $derived(`${inputId}-error`);
59
+
60
+ let inputRef = $state<HTMLInputElement | null>(null);
61
+ let isDragOver = $state(false);
62
+
63
+ const isInvalid = $derived(invalid || Boolean(errorText));
64
+ const effectiveTriggerLabel = $derived(
65
+ triggerLabel ?? (multiple ? "Choose files" : "Choose file")
66
+ );
67
+
68
+ function fieldClasses() {
69
+ return ["st-field", "st-fileUploader-field", className].filter(Boolean).join(" ");
70
+ }
71
+
72
+ function dropzoneClasses() {
73
+ return [
74
+ "st-fileUploader__dropzone",
75
+ isDragOver ? "st-fileUploader__dropzone--dragover" : null,
76
+ isInvalid ? "st-fileUploader__dropzone--invalid" : null,
77
+ disabled ? "st-fileUploader__dropzone--disabled" : null
78
+ ]
79
+ .filter(Boolean)
80
+ .join(" ");
81
+ }
82
+
83
+ function formatSize(bytes: number): string {
84
+ if (!Number.isFinite(bytes) || bytes < 0) return "";
85
+ if (bytes === 0) return "0 B";
86
+ const units = ["B", "KB", "MB", "GB", "TB"];
87
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
88
+ const value = bytes / Math.pow(1024, i);
89
+ const formatted = value >= 10 || i === 0 ? value.toFixed(0) : value.toFixed(1);
90
+ return `${formatted} ${units[i]}`;
91
+ }
92
+
93
+ function appendFiles(incoming: File[]) {
94
+ if (disabled || incoming.length === 0) return;
95
+ const next = multiple ? [...files, ...incoming] : incoming.slice(0, 1);
96
+ files = next;
97
+ onfiles?.(next);
98
+ }
99
+
100
+ function onChange(event: Event) {
101
+ const input = event.currentTarget as HTMLInputElement;
102
+ const list = input.files ? Array.from(input.files) : [];
103
+ appendFiles(list);
104
+ // Allow re-selecting the same file later.
105
+ input.value = "";
106
+ }
107
+
108
+ function openPicker() {
109
+ if (disabled) return;
110
+ inputRef?.click();
111
+ }
112
+
113
+ function onTriggerKeydown(event: KeyboardEvent) {
114
+ if (disabled) return;
115
+ if (event.key === "Enter" || event.key === " ") {
116
+ event.preventDefault();
117
+ openPicker();
118
+ }
119
+ }
120
+
121
+ function onDragOver(event: DragEvent) {
122
+ if (disabled) return;
123
+ event.preventDefault();
124
+ isDragOver = true;
125
+ }
126
+
127
+ function onDragLeave(event: DragEvent) {
128
+ event.preventDefault();
129
+ isDragOver = false;
130
+ }
131
+
132
+ function onDrop(event: DragEvent) {
133
+ if (disabled) return;
134
+ event.preventDefault();
135
+ isDragOver = false;
136
+ const dropped = event.dataTransfer?.files ? Array.from(event.dataTransfer.files) : [];
137
+ if (dropped.length === 0) return;
138
+ appendFiles(dropped);
139
+ }
140
+
141
+ function removeAt(index: number) {
142
+ if (disabled) return;
143
+ const next = files.filter((_, i) => i !== index);
144
+ files = next;
145
+ onfiles?.(next);
146
+ }
147
+
148
+ function fileError(file: File): string | undefined {
149
+ if (typeof maxSizeBytes === "number" && file.size > maxSizeBytes) {
150
+ return maxSizeErrorLabel(file.name, maxSizeBytes);
151
+ }
152
+ return undefined;
153
+ }
154
+ </script>
155
+
156
+ <div {...rest} class={fieldClasses()}>
157
+ {#if label}
158
+ <label class="st-field__label" for={inputId}>{label}</label>
159
+ {/if}
160
+
161
+ <div
162
+ class={dropzoneClasses()}
163
+ role="presentation"
164
+ ondragover={onDragOver}
165
+ ondragenter={onDragOver}
166
+ ondragleave={onDragLeave}
167
+ ondrop={onDrop}
168
+ >
169
+ <input
170
+ bind:this={inputRef}
171
+ id={inputId}
172
+ type="file"
173
+ class="st-fileUploader__input"
174
+ {accept}
175
+ {multiple}
176
+ {disabled}
177
+ aria-invalid={isInvalid ? "true" : undefined}
178
+ aria-describedby={errorText ? errorId : helperText ? helperId : undefined}
179
+ onchange={onChange}
180
+ />
181
+ <div class="st-fileUploader__content">
182
+ <button
183
+ type="button"
184
+ class="st-fileUploader__trigger"
185
+ {disabled}
186
+ onclick={openPicker}
187
+ onkeydown={onTriggerKeydown}
188
+ >
189
+ {effectiveTriggerLabel}
190
+ </button>
191
+ <span class="st-fileUploader__hint">{dropzoneLabel}</span>
192
+ </div>
193
+ </div>
194
+
195
+ {#if errorText}
196
+ <span class="st-field__error" id={errorId}>{errorText}</span>
197
+ {:else if helperText}
198
+ <span class="st-field__help" id={helperId}>{helperText}</span>
199
+ {/if}
200
+
201
+ {#if files.length > 0}
202
+ <ul class="st-fileUploader__list">
203
+ {#each files as file, index (file.name + index)}
204
+ {@const itemError = fileError(file)}
205
+ <li
206
+ class={[
207
+ "st-fileUploader__item",
208
+ itemError ? "st-fileUploader__item--error" : null
209
+ ]
210
+ .filter(Boolean)
211
+ .join(" ")}
212
+ >
213
+ <span class="st-fileUploader__itemMeta">
214
+ <span class="st-fileUploader__itemName">{file.name}</span>
215
+ <span class="st-fileUploader__itemSize">{formatSize(file.size)}</span>
216
+ {#if itemError}
217
+ <span class="st-fileUploader__itemError">{itemError}</span>
218
+ {/if}
219
+ </span>
220
+ <button
221
+ type="button"
222
+ class="st-fileUploader__remove"
223
+ aria-label={removeLabel(file.name)}
224
+ {disabled}
225
+ onclick={() => removeAt(index)}
226
+ >
227
+ <span aria-hidden="true">×</span>
228
+ </button>
229
+ </li>
230
+ {/each}
231
+ </ul>
232
+ {/if}
233
+ </div>
234
+
235
+ <style>
236
+ .st-fileUploader-field {
237
+ display: grid;
238
+ gap: var(--st-component-field-gap, 0.5rem);
239
+ color: var(--st-component-field-labelText, var(--st-semantic-text-primary));
240
+ max-width: var(--st-component-field-maxWidth, 28rem);
241
+ }
242
+
243
+ .st-field__label {
244
+ font-size: 0.875rem;
245
+ font-weight: 600;
246
+ }
247
+
248
+ .st-field__help,
249
+ .st-field__error {
250
+ font-size: 0.8125rem;
251
+ line-height: 1.4;
252
+ }
253
+
254
+ .st-field__help {
255
+ color: var(--st-component-field-helpText, var(--st-semantic-text-secondary));
256
+ }
257
+
258
+ .st-field__error {
259
+ color: var(--st-component-field-errorText, var(--st-semantic-feedback-error));
260
+ }
261
+
262
+ .st-fileUploader__dropzone {
263
+ align-items: center;
264
+ background: var(--st-component-control-background, var(--st-semantic-surface-default));
265
+ border: 1px dashed var(--st-component-control-border, var(--st-semantic-border-subtle));
266
+ border-radius: var(--st-component-control-radius, 0.375rem);
267
+ color: var(--st-component-control-text, var(--st-semantic-text-primary));
268
+ display: flex;
269
+ flex-wrap: wrap;
270
+ gap: var(--st-spacing-3, 0.75rem);
271
+ justify-content: flex-start;
272
+ padding: var(--st-spacing-4, 1rem);
273
+ transition:
274
+ background var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
275
+ border-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
276
+ }
277
+
278
+ .st-fileUploader__dropzone:hover:not(.st-fileUploader__dropzone--disabled) {
279
+ border-color: var(--st-component-control-hoverBorder, var(--st-semantic-border-strong));
280
+ }
281
+
282
+ .st-fileUploader__dropzone--dragover {
283
+ background: var(--st-component-control-hoverBackground, var(--st-semantic-surface-subtle));
284
+ border-color: var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
285
+ }
286
+
287
+ .st-fileUploader__dropzone--invalid {
288
+ border-color: var(--st-component-control-invalidBorder, var(--st-semantic-feedback-error));
289
+ }
290
+
291
+ .st-fileUploader__dropzone--disabled {
292
+ background: var(--st-component-control-disabledBackground, var(--st-semantic-surface-subtle));
293
+ color: var(--st-component-control-disabledText, var(--st-semantic-text-muted));
294
+ cursor: not-allowed;
295
+ opacity: 0.7;
296
+ }
297
+
298
+ .st-fileUploader__input {
299
+ border: 0;
300
+ clip: rect(0 0 0 0);
301
+ height: 1px;
302
+ margin: -1px;
303
+ overflow: hidden;
304
+ padding: 0;
305
+ position: absolute;
306
+ width: 1px;
307
+ }
308
+
309
+ .st-fileUploader__content {
310
+ align-items: center;
311
+ display: flex;
312
+ flex-wrap: wrap;
313
+ gap: var(--st-spacing-3, 0.75rem);
314
+ }
315
+
316
+ .st-fileUploader__trigger {
317
+ align-items: center;
318
+ background: var(--st-semantic-action-primary);
319
+ border: 1px solid transparent;
320
+ border-radius: var(--st-component-button-radius, 0.375rem);
321
+ color: var(--st-semantic-action-primaryText, #ffffff);
322
+ cursor: pointer;
323
+ display: inline-flex;
324
+ font: inherit;
325
+ font-weight: 600;
326
+ justify-content: center;
327
+ min-height: 2.25rem;
328
+ padding: 0 var(--st-spacing-3, 0.75rem);
329
+ transition:
330
+ background var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
331
+ box-shadow var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
332
+ }
333
+
334
+ .st-fileUploader__trigger:hover:not(:disabled) {
335
+ background: color-mix(in srgb, var(--st-semantic-action-primary) 88%, black);
336
+ }
337
+
338
+ .st-fileUploader__trigger:focus-visible {
339
+ box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
340
+ outline: none;
341
+ }
342
+
343
+ .st-fileUploader__trigger:disabled {
344
+ cursor: not-allowed;
345
+ opacity: 0.6;
346
+ }
347
+
348
+ .st-fileUploader__hint {
349
+ color: var(--st-semantic-text-secondary);
350
+ font-size: 0.875rem;
351
+ }
352
+
353
+ .st-fileUploader__list {
354
+ display: grid;
355
+ gap: var(--st-spacing-2, 0.5rem);
356
+ list-style: none;
357
+ margin: 0;
358
+ padding: 0;
359
+ }
360
+
361
+ .st-fileUploader__item {
362
+ align-items: center;
363
+ background: var(--st-semantic-surface-subtle);
364
+ border: 1px solid var(--st-semantic-border-subtle);
365
+ border-radius: var(--st-component-control-radius, 0.375rem);
366
+ display: flex;
367
+ gap: var(--st-spacing-3, 0.75rem);
368
+ justify-content: space-between;
369
+ padding: var(--st-spacing-2, 0.5rem) var(--st-spacing-3, 0.75rem);
370
+ }
371
+
372
+ .st-fileUploader__item--error {
373
+ border-color: var(--st-semantic-feedback-error);
374
+ }
375
+
376
+ .st-fileUploader__itemMeta {
377
+ display: grid;
378
+ gap: 0.125rem;
379
+ min-width: 0;
380
+ }
381
+
382
+ .st-fileUploader__itemName {
383
+ font-weight: 600;
384
+ overflow: hidden;
385
+ text-overflow: ellipsis;
386
+ white-space: nowrap;
387
+ }
388
+
389
+ .st-fileUploader__itemSize {
390
+ color: var(--st-semantic-text-muted);
391
+ font-size: 0.8125rem;
392
+ }
393
+
394
+ .st-fileUploader__itemError {
395
+ color: var(--st-semantic-feedback-error);
396
+ font-size: 0.8125rem;
397
+ }
398
+
399
+ .st-fileUploader__remove {
400
+ align-items: center;
401
+ background: transparent;
402
+ border: 0;
403
+ border-radius: 50%;
404
+ color: var(--st-semantic-text-secondary);
405
+ cursor: pointer;
406
+ display: inline-flex;
407
+ flex: 0 0 auto;
408
+ font: inherit;
409
+ font-size: 1.125rem;
410
+ height: 1.75rem;
411
+ justify-content: center;
412
+ line-height: 1;
413
+ padding: 0;
414
+ transition: background-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
415
+ width: 1.75rem;
416
+ }
417
+
418
+ .st-fileUploader__remove:hover:not(:disabled) {
419
+ background: color-mix(in srgb, currentColor 14%, transparent);
420
+ }
421
+
422
+ .st-fileUploader__remove:focus-visible {
423
+ outline: 2px solid var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
424
+ outline-offset: 1px;
425
+ }
426
+
427
+ .st-fileUploader__remove:disabled {
428
+ cursor: not-allowed;
429
+ opacity: 0.6;
430
+ }
431
+ </style>
@@ -0,0 +1,30 @@
1
+ export type FileUploadStatus = "idle" | "uploading" | "complete" | "error";
2
+ export type FileUploadItem = {
3
+ file: File;
4
+ status: FileUploadStatus;
5
+ progress?: number;
6
+ error?: string;
7
+ };
8
+ import type { HTMLAttributes } from "svelte/elements";
9
+ type FileUploaderProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
10
+ label?: string;
11
+ helperText?: string;
12
+ errorText?: string;
13
+ invalid?: boolean;
14
+ accept?: string;
15
+ multiple?: boolean;
16
+ maxSizeBytes?: number;
17
+ disabled?: boolean;
18
+ files?: File[];
19
+ onfiles?: (files: File[]) => void;
20
+ triggerLabel?: string;
21
+ dropzoneLabel?: string;
22
+ removeLabel?: (filename: string) => string;
23
+ maxSizeErrorLabel?: (filename: string, maxSizeBytes: number) => string;
24
+ id?: string;
25
+ class?: string;
26
+ };
27
+ declare const FileUploader: import("svelte").Component<FileUploaderProps, {}, "files">;
28
+ type FileUploader = ReturnType<typeof FileUploader>;
29
+ export default FileUploader;
30
+ //# sourceMappingURL=FileUploader.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUploader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/FileUploader.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGJ,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,iBAAiB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GAAG;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAoLJ,QAAA,MAAM,YAAY,4DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+
4
+ type InlineLoadingProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
5
+ label?: string;
6
+ status?: "active" | "success" | "error" | "inactive";
7
+ class?: string;
8
+ };
9
+
10
+ let {
11
+ label,
12
+ status = "active",
13
+ class: className,
14
+ ...rest
15
+ }: InlineLoadingProps = $props();
16
+
17
+ const classes = () =>
18
+ ["st-inlineLoading", `st-inlineLoading--${status}`, className].filter(Boolean).join(" ");
19
+
20
+ const role = () => (status === "error" ? "alert" : "status");
21
+ </script>
22
+
23
+ <div {...rest} class={classes()} role={role()} aria-live="polite">
24
+ <span class="st-inlineLoading__icon" aria-hidden="true">
25
+ {#if status === "active"}
26
+ <svg viewBox="0 0 16 16" width="16" height="16">
27
+ <circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-opacity="0.2" />
28
+ <path d="M14 8a6 6 0 0 0-6-6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
29
+ <animateTransform attributeName="transform" type="rotate" from="0 8 8" to="360 8 8" dur="0.9s" repeatCount="indefinite" />
30
+ </path>
31
+ </svg>
32
+ {:else if status === "success"}
33
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" width="14" height="14">
34
+ <path d="m3 8 3.5 3.5L13 5" stroke-linecap="round" stroke-linejoin="round" />
35
+ </svg>
36
+ {:else if status === "error"}
37
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" width="14" height="14">
38
+ <path d="M4 4l8 8M12 4l-8 8" stroke-linecap="round" />
39
+ </svg>
40
+ {/if}
41
+ </span>
42
+ {#if label}<span class="st-inlineLoading__label">{label}</span>{/if}
43
+ </div>
44
+
45
+ <style>
46
+ .st-inlineLoading {
47
+ align-items: center;
48
+ color: var(--st-semantic-text-secondary);
49
+ display: inline-flex;
50
+ font-size: 0.875rem;
51
+ gap: 0.5rem;
52
+ }
53
+
54
+ .st-inlineLoading__icon {
55
+ align-items: center;
56
+ display: inline-flex;
57
+ justify-content: center;
58
+ }
59
+
60
+ .st-inlineLoading--active .st-inlineLoading__icon {
61
+ color: var(--st-semantic-action-primary);
62
+ }
63
+
64
+ .st-inlineLoading--success {
65
+ color: var(--st-semantic-feedback-success);
66
+ }
67
+
68
+ .st-inlineLoading--error {
69
+ color: var(--st-semantic-feedback-error);
70
+ }
71
+
72
+ .st-inlineLoading--inactive {
73
+ color: var(--st-semantic-text-muted);
74
+ }
75
+
76
+ @media (prefers-reduced-motion: reduce) {
77
+ .st-inlineLoading__icon svg path animateTransform {
78
+ display: none;
79
+ }
80
+ }
81
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ type InlineLoadingProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
3
+ label?: string;
4
+ status?: "active" | "success" | "error" | "inactive";
5
+ class?: string;
6
+ };
7
+ declare const InlineLoading: import("svelte").Component<InlineLoadingProps, {}, "">;
8
+ type InlineLoading = ReturnType<typeof InlineLoading>;
9
+ export default InlineLoading;
10
+ //# sourceMappingURL=InlineLoading.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InlineLoading.svelte.d.ts","sourceRoot":"","sources":["../src/lib/InlineLoading.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,kBAAkB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GAAG;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAC;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4CJ,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+
4
+ type SkeletonTextProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
5
+ lines?: number;
6
+ width?: string;
7
+ heading?: boolean;
8
+ paragraph?: boolean;
9
+ class?: string;
10
+ };
11
+
12
+ let {
13
+ lines = 1,
14
+ width,
15
+ heading = false,
16
+ paragraph = false,
17
+ class: className,
18
+ ...rest
19
+ }: SkeletonTextProps = $props();
20
+
21
+ const wrapperClasses = () => ["st-skeleton", className].filter(Boolean).join(" ");
22
+ const lineClasses = () =>
23
+ ["st-skeleton__line", heading ? "st-skeleton__line--heading" : null].filter(Boolean).join(" ");
24
+ const lineCount = () => (paragraph ? Math.max(lines, 3) : lines);
25
+
26
+ function lineWidth(index: number, total: number): string | undefined {
27
+ if (width && index === 0) return width;
28
+ if (paragraph && index === total - 1) return "60%";
29
+ return undefined;
30
+ }
31
+ </script>
32
+
33
+ <div {...rest} class={wrapperClasses()} role="status" aria-label="Loading…" aria-busy="true">
34
+ {#each Array.from({ length: lineCount() }) as _, i (i)}
35
+ <span class={lineClasses()} style={lineWidth(i, lineCount()) ? `width:${lineWidth(i, lineCount())}` : undefined}></span>
36
+ {/each}
37
+ </div>
38
+
39
+ <style>
40
+ .st-skeleton {
41
+ display: grid;
42
+ gap: 0.5rem;
43
+ width: 100%;
44
+ }
45
+
46
+ .st-skeleton__line {
47
+ background: linear-gradient(
48
+ 90deg,
49
+ var(--st-semantic-surface-subtle, #eaeef2),
50
+ var(--st-semantic-border-subtle, #d8dee4),
51
+ var(--st-semantic-surface-subtle, #eaeef2)
52
+ );
53
+ background-size: 200% 100%;
54
+ border-radius: 0.25rem;
55
+ display: block;
56
+ height: 0.875rem;
57
+ width: 100%;
58
+ animation: st-skeleton-shimmer 1.4s ease-in-out infinite;
59
+ }
60
+
61
+ .st-skeleton__line--heading {
62
+ height: 1.25rem;
63
+ }
64
+
65
+ @keyframes st-skeleton-shimmer {
66
+ 0% {
67
+ background-position: 200% 0;
68
+ }
69
+ 100% {
70
+ background-position: -200% 0;
71
+ }
72
+ }
73
+
74
+ @media (prefers-reduced-motion: reduce) {
75
+ .st-skeleton__line {
76
+ animation: none;
77
+ }
78
+ }
79
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ type SkeletonTextProps = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
3
+ lines?: number;
4
+ width?: string;
5
+ heading?: boolean;
6
+ paragraph?: boolean;
7
+ class?: string;
8
+ };
9
+ declare const SkeletonText: import("svelte").Component<SkeletonTextProps, {}, "">;
10
+ type SkeletonText = ReturnType<typeof SkeletonText>;
11
+ export default SkeletonText;
12
+ //# sourceMappingURL=SkeletonText.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkeletonText.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SkeletonText.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,iBAAiB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GAAG;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAoCJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}