@synthaxai/ui 1.0.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.
Files changed (185) hide show
  1. package/README.md +262 -0
  2. package/dist/app.css +2 -0
  3. package/dist/app.html +12 -0
  4. package/dist/data-display/DataTable/DataTable.svelte +773 -0
  5. package/dist/data-display/DataTable/DataTable.svelte.d.ts +120 -0
  6. package/dist/data-display/DataTable/DataTable.svelte.d.ts.map +1 -0
  7. package/dist/data-display/DataTable/index.d.ts +2 -0
  8. package/dist/data-display/DataTable/index.d.ts.map +1 -0
  9. package/dist/data-display/DataTable/index.js +1 -0
  10. package/dist/data-display/StatCard/StatCard.svelte +409 -0
  11. package/dist/data-display/StatCard/StatCard.svelte.d.ts +63 -0
  12. package/dist/data-display/StatCard/StatCard.svelte.d.ts.map +1 -0
  13. package/dist/data-display/StatCard/index.d.ts +2 -0
  14. package/dist/data-display/StatCard/index.d.ts.map +1 -0
  15. package/dist/data-display/StatCard/index.js +1 -0
  16. package/dist/data-display/index.d.ts +8 -0
  17. package/dist/data-display/index.d.ts.map +1 -0
  18. package/dist/data-display/index.js +7 -0
  19. package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte +693 -0
  20. package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte.d.ts +66 -0
  21. package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte.d.ts.map +1 -0
  22. package/dist/dialogs/ConfirmDialog/index.d.ts +2 -0
  23. package/dist/dialogs/ConfirmDialog/index.d.ts.map +1 -0
  24. package/dist/dialogs/ConfirmDialog/index.js +1 -0
  25. package/dist/dialogs/Modal/Modal.svelte +441 -0
  26. package/dist/dialogs/Modal/Modal.svelte.d.ts +69 -0
  27. package/dist/dialogs/Modal/Modal.svelte.d.ts.map +1 -0
  28. package/dist/dialogs/Modal/index.d.ts +2 -0
  29. package/dist/dialogs/Modal/index.d.ts.map +1 -0
  30. package/dist/dialogs/Modal/index.js +1 -0
  31. package/dist/dialogs/index.d.ts +8 -0
  32. package/dist/dialogs/index.d.ts.map +1 -0
  33. package/dist/dialogs/index.js +7 -0
  34. package/dist/feedback/Alert/Alert.svelte +565 -0
  35. package/dist/feedback/Alert/Alert.svelte.d.ts +60 -0
  36. package/dist/feedback/Alert/Alert.svelte.d.ts.map +1 -0
  37. package/dist/feedback/Alert/index.d.ts +2 -0
  38. package/dist/feedback/Alert/index.d.ts.map +1 -0
  39. package/dist/feedback/Alert/index.js +1 -0
  40. package/dist/feedback/EmptyState/EmptyState.svelte +377 -0
  41. package/dist/feedback/EmptyState/EmptyState.svelte.d.ts +63 -0
  42. package/dist/feedback/EmptyState/EmptyState.svelte.d.ts.map +1 -0
  43. package/dist/feedback/EmptyState/index.d.ts +2 -0
  44. package/dist/feedback/EmptyState/index.d.ts.map +1 -0
  45. package/dist/feedback/EmptyState/index.js +1 -0
  46. package/dist/feedback/ProgressBar/ProgressBar.svelte +585 -0
  47. package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts +68 -0
  48. package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts.map +1 -0
  49. package/dist/feedback/ProgressBar/index.d.ts +2 -0
  50. package/dist/feedback/ProgressBar/index.d.ts.map +1 -0
  51. package/dist/feedback/ProgressBar/index.js +1 -0
  52. package/dist/feedback/Skeleton/Skeleton.svelte +568 -0
  53. package/dist/feedback/Skeleton/Skeleton.svelte.d.ts +54 -0
  54. package/dist/feedback/Skeleton/Skeleton.svelte.d.ts.map +1 -0
  55. package/dist/feedback/Skeleton/index.d.ts +2 -0
  56. package/dist/feedback/Skeleton/index.d.ts.map +1 -0
  57. package/dist/feedback/Skeleton/index.js +1 -0
  58. package/dist/feedback/Spinner/Spinner.svelte +434 -0
  59. package/dist/feedback/Spinner/Spinner.svelte.d.ts +49 -0
  60. package/dist/feedback/Spinner/Spinner.svelte.d.ts.map +1 -0
  61. package/dist/feedback/Spinner/index.d.ts +2 -0
  62. package/dist/feedback/Spinner/index.d.ts.map +1 -0
  63. package/dist/feedback/Spinner/index.js +1 -0
  64. package/dist/feedback/Toast/Toast.svelte +587 -0
  65. package/dist/feedback/Toast/Toast.svelte.d.ts +55 -0
  66. package/dist/feedback/Toast/Toast.svelte.d.ts.map +1 -0
  67. package/dist/feedback/Toast/ToastContainer.svelte +168 -0
  68. package/dist/feedback/Toast/ToastContainer.svelte.d.ts +28 -0
  69. package/dist/feedback/Toast/ToastContainer.svelte.d.ts.map +1 -0
  70. package/dist/feedback/Toast/index.d.ts +4 -0
  71. package/dist/feedback/Toast/index.d.ts.map +1 -0
  72. package/dist/feedback/Toast/index.js +3 -0
  73. package/dist/feedback/Toast/toast-store.d.ts +72 -0
  74. package/dist/feedback/Toast/toast-store.d.ts.map +1 -0
  75. package/dist/feedback/Toast/toast-store.js +157 -0
  76. package/dist/feedback/index.d.ts +13 -0
  77. package/dist/feedback/index.d.ts.map +1 -0
  78. package/dist/feedback/index.js +12 -0
  79. package/dist/forms/Checkbox/Checkbox.svelte +404 -0
  80. package/dist/forms/Checkbox/Checkbox.svelte.d.ts +62 -0
  81. package/dist/forms/Checkbox/Checkbox.svelte.d.ts.map +1 -0
  82. package/dist/forms/Checkbox/index.d.ts +2 -0
  83. package/dist/forms/Checkbox/index.d.ts.map +1 -0
  84. package/dist/forms/Checkbox/index.js +1 -0
  85. package/dist/forms/FormField/FormField.svelte +299 -0
  86. package/dist/forms/FormField/FormField.svelte.d.ts +43 -0
  87. package/dist/forms/FormField/FormField.svelte.d.ts.map +1 -0
  88. package/dist/forms/FormField/index.d.ts +2 -0
  89. package/dist/forms/FormField/index.d.ts.map +1 -0
  90. package/dist/forms/FormField/index.js +1 -0
  91. package/dist/forms/RadioGroup/RadioGroup.svelte +418 -0
  92. package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts +70 -0
  93. package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts.map +1 -0
  94. package/dist/forms/RadioGroup/index.d.ts +2 -0
  95. package/dist/forms/RadioGroup/index.d.ts.map +1 -0
  96. package/dist/forms/RadioGroup/index.js +1 -0
  97. package/dist/forms/Select/Select.svelte +548 -0
  98. package/dist/forms/Select/Select.svelte.d.ts +74 -0
  99. package/dist/forms/Select/Select.svelte.d.ts.map +1 -0
  100. package/dist/forms/Select/index.d.ts +2 -0
  101. package/dist/forms/Select/index.d.ts.map +1 -0
  102. package/dist/forms/Select/index.js +1 -0
  103. package/dist/forms/TextInput/TextInput.svelte +628 -0
  104. package/dist/forms/TextInput/TextInput.svelte.d.ts +97 -0
  105. package/dist/forms/TextInput/TextInput.svelte.d.ts.map +1 -0
  106. package/dist/forms/TextInput/index.d.ts +2 -0
  107. package/dist/forms/TextInput/index.d.ts.map +1 -0
  108. package/dist/forms/TextInput/index.js +1 -0
  109. package/dist/forms/Textarea/Textarea.svelte +587 -0
  110. package/dist/forms/Textarea/Textarea.svelte.d.ts +71 -0
  111. package/dist/forms/Textarea/Textarea.svelte.d.ts.map +1 -0
  112. package/dist/forms/Textarea/index.d.ts +2 -0
  113. package/dist/forms/Textarea/index.d.ts.map +1 -0
  114. package/dist/forms/Textarea/index.js +1 -0
  115. package/dist/forms/index.d.ts +13 -0
  116. package/dist/forms/index.d.ts.map +1 -0
  117. package/dist/forms/index.js +12 -0
  118. package/dist/index.d.ts +37 -0
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +65 -0
  121. package/dist/layout/Card/Card.svelte +316 -0
  122. package/dist/layout/Card/Card.svelte.d.ts +63 -0
  123. package/dist/layout/Card/Card.svelte.d.ts.map +1 -0
  124. package/dist/layout/Card/index.d.ts +2 -0
  125. package/dist/layout/Card/index.d.ts.map +1 -0
  126. package/dist/layout/Card/index.js +1 -0
  127. package/dist/layout/Container/Container.svelte +252 -0
  128. package/dist/layout/Container/Container.svelte.d.ts +50 -0
  129. package/dist/layout/Container/Container.svelte.d.ts.map +1 -0
  130. package/dist/layout/Container/index.d.ts +2 -0
  131. package/dist/layout/Container/index.d.ts.map +1 -0
  132. package/dist/layout/Container/index.js +1 -0
  133. package/dist/layout/index.d.ts +8 -0
  134. package/dist/layout/index.d.ts.map +1 -0
  135. package/dist/layout/index.js +7 -0
  136. package/dist/navigation/StepIndicator/StepIndicator.svelte +601 -0
  137. package/dist/navigation/StepIndicator/StepIndicator.svelte.d.ts +70 -0
  138. package/dist/navigation/StepIndicator/StepIndicator.svelte.d.ts.map +1 -0
  139. package/dist/navigation/StepIndicator/index.d.ts +2 -0
  140. package/dist/navigation/StepIndicator/index.d.ts.map +1 -0
  141. package/dist/navigation/StepIndicator/index.js +1 -0
  142. package/dist/navigation/index.d.ts +7 -0
  143. package/dist/navigation/index.d.ts.map +1 -0
  144. package/dist/navigation/index.js +6 -0
  145. package/dist/primitives/Badge/Badge.svelte +365 -0
  146. package/dist/primitives/Badge/Badge.svelte.d.ts +39 -0
  147. package/dist/primitives/Badge/Badge.svelte.d.ts.map +1 -0
  148. package/dist/primitives/Badge/index.d.ts +2 -0
  149. package/dist/primitives/Badge/index.d.ts.map +1 -0
  150. package/dist/primitives/Badge/index.js +1 -0
  151. package/dist/primitives/Button/Button.svelte +430 -0
  152. package/dist/primitives/Button/Button.svelte.d.ts +50 -0
  153. package/dist/primitives/Button/Button.svelte.d.ts.map +1 -0
  154. package/dist/primitives/Button/index.d.ts +2 -0
  155. package/dist/primitives/Button/index.d.ts.map +1 -0
  156. package/dist/primitives/Button/index.js +1 -0
  157. package/dist/primitives/index.d.ts +9 -0
  158. package/dist/primitives/index.d.ts.map +1 -0
  159. package/dist/primitives/index.js +8 -0
  160. package/dist/routes/+layout.svelte +12 -0
  161. package/dist/routes/+layout.svelte.d.ts +12 -0
  162. package/dist/routes/+layout.svelte.d.ts.map +1 -0
  163. package/dist/routes/+page.svelte +53 -0
  164. package/dist/routes/+page.svelte.d.ts +27 -0
  165. package/dist/routes/+page.svelte.d.ts.map +1 -0
  166. package/dist/styles/tokens.css +399 -0
  167. package/dist/types/index.d.ts +175 -0
  168. package/dist/types/index.d.ts.map +1 -0
  169. package/dist/types/index.js +7 -0
  170. package/dist/utils/accessibility.d.ts +103 -0
  171. package/dist/utils/accessibility.d.ts.map +1 -0
  172. package/dist/utils/accessibility.js +202 -0
  173. package/dist/utils/cn.d.ts +71 -0
  174. package/dist/utils/cn.d.ts.map +1 -0
  175. package/dist/utils/cn.js +61 -0
  176. package/dist/utils/form-styles.d.ts +76 -0
  177. package/dist/utils/form-styles.d.ts.map +1 -0
  178. package/dist/utils/form-styles.js +95 -0
  179. package/dist/utils/index.d.ts +10 -0
  180. package/dist/utils/index.d.ts.map +1 -0
  181. package/dist/utils/index.js +13 -0
  182. package/dist/utils/keyboard.d.ts +94 -0
  183. package/dist/utils/keyboard.d.ts.map +1 -0
  184. package/dist/utils/keyboard.js +179 -0
  185. package/package.json +119 -0
@@ -0,0 +1,568 @@
1
+ <!--
2
+ @component Skeleton
3
+
4
+ A world-class loading placeholder component with multiple variants and animations.
5
+ Designed for healthcare applications where data loading states must be clearly
6
+ communicated without causing anxiety.
7
+
8
+ Features:
9
+ - Multiple preset variants (text, avatar, card, table-row, patient-card, etc.)
10
+ - Wave animation (perceived as faster than pulse)
11
+ - Composite skeletons for common patterns
12
+ - Proper accessibility without over-announcing
13
+ - Reduced motion support
14
+
15
+ @example
16
+ <Skeleton width="200px" height="20px" />
17
+
18
+ @example
19
+ <Skeleton variant="text" lines={3} />
20
+
21
+ @example
22
+ <Skeleton variant="patient-card" />
23
+ -->
24
+ <script lang="ts">
25
+ type SkeletonVariant =
26
+ | 'rectangle'
27
+ | 'circle'
28
+ | 'text'
29
+ | 'avatar'
30
+ | 'button'
31
+ | 'card'
32
+ | 'image'
33
+ | 'table-row'
34
+ | 'patient-card'
35
+ | 'stat-card'
36
+ | 'list-item';
37
+
38
+ type SkeletonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
39
+ type SkeletonAnimation = 'wave' | 'pulse' | 'none';
40
+
41
+ interface Props {
42
+ /** Skeleton variant */
43
+ variant?: SkeletonVariant;
44
+ /** Size preset (for avatar, button, text variants) */
45
+ size?: SkeletonSize;
46
+ /** Custom width (CSS value) */
47
+ width?: string;
48
+ /** Custom height (CSS value) */
49
+ height?: string;
50
+ /** Number of lines (for text variant) */
51
+ lines?: number;
52
+ /** Animation style - wave is perceived as faster */
53
+ animation?: SkeletonAnimation;
54
+ /** Border radius preset or custom CSS value */
55
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | string;
56
+ /** Accessible label for screen readers */
57
+ label?: string;
58
+ /** Number of items for list-based variants (table-row, list-item) */
59
+ count?: number;
60
+ /** Additional CSS classes */
61
+ class?: string;
62
+ /** ID for the skeleton element */
63
+ id?: string;
64
+ }
65
+
66
+ let {
67
+ variant = 'rectangle',
68
+ size = 'md',
69
+ width,
70
+ height,
71
+ lines = 1,
72
+ animation = 'wave',
73
+ rounded = 'md',
74
+ label = 'Loading content',
75
+ count = 1,
76
+ class: className = '',
77
+ id
78
+ }: Props = $props();
79
+
80
+ // Size presets for different variants
81
+ const avatarSizes: Record<SkeletonSize, { width: string; height: string }> = {
82
+ xs: { width: '1.5rem', height: '1.5rem' },
83
+ sm: { width: '2rem', height: '2rem' },
84
+ md: { width: '2.5rem', height: '2.5rem' },
85
+ lg: { width: '3rem', height: '3rem' },
86
+ xl: { width: '4rem', height: '4rem' }
87
+ };
88
+
89
+ const buttonSizes: Record<SkeletonSize, { width: string; height: string }> = {
90
+ xs: { width: '4rem', height: '1.75rem' },
91
+ sm: { width: '5rem', height: '2rem' },
92
+ md: { width: '6rem', height: '2.5rem' },
93
+ lg: { width: '8rem', height: '3rem' },
94
+ xl: { width: '10rem', height: '3.5rem' }
95
+ };
96
+
97
+ const textHeights: Record<SkeletonSize, string> = {
98
+ xs: '0.625rem',
99
+ sm: '0.75rem',
100
+ md: '1rem',
101
+ lg: '1.25rem',
102
+ xl: '1.5rem'
103
+ };
104
+
105
+ // Border radius presets
106
+ const radiusMap: Record<string, string> = {
107
+ none: '0',
108
+ sm: '0.25rem',
109
+ md: '0.375rem',
110
+ lg: '0.5rem',
111
+ xl: '0.75rem',
112
+ full: '9999px'
113
+ };
114
+
115
+ const getRadius = (r: string) => radiusMap[r] || r;
116
+
117
+ // Generate array for multiple items
118
+ const itemArray = $derived(Array.from({ length: count }, (_, i) => i));
119
+ const lineArray = $derived(Array.from({ length: lines }, (_, i) => i));
120
+
121
+ // Last line width for text variant (shorter to look natural)
122
+ const getLineWidth = (index: number) => {
123
+ if (lines === 1) return '100%';
124
+ if (index === lines - 1) return `${55 + Math.random() * 25}%`;
125
+ return '100%';
126
+ };
127
+
128
+ // Check if variant is a composite type
129
+ const isComposite = $derived(
130
+ ['patient-card', 'stat-card', 'table-row', 'list-item'].includes(variant)
131
+ );
132
+ </script>
133
+
134
+ <!-- Composite: Patient Card Skeleton -->
135
+ {#if variant === 'patient-card'}
136
+ <div
137
+ {id}
138
+ class="skeleton-composite skeleton-patient-card {className}"
139
+ class:skeleton-wave={animation === 'wave'}
140
+ class:skeleton-pulse={animation === 'pulse'}
141
+ role="status"
142
+ aria-label={label}
143
+ aria-busy="true"
144
+ >
145
+ <span class="sr-only">{label}</span>
146
+ <div class="skeleton-patient-header">
147
+ <div class="skeleton-base skeleton-avatar" aria-hidden="true"></div>
148
+ <div class="skeleton-patient-info">
149
+ <div class="skeleton-base skeleton-text-lg" aria-hidden="true"></div>
150
+ <div class="skeleton-base skeleton-text-sm skeleton-short" aria-hidden="true"></div>
151
+ </div>
152
+ </div>
153
+ <div class="skeleton-patient-body">
154
+ <div class="skeleton-base skeleton-text-md" aria-hidden="true"></div>
155
+ <div class="skeleton-base skeleton-text-md skeleton-medium" aria-hidden="true"></div>
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Composite: Stat Card Skeleton -->
160
+ {:else if variant === 'stat-card'}
161
+ <div
162
+ {id}
163
+ class="skeleton-composite skeleton-stat-card {className}"
164
+ class:skeleton-wave={animation === 'wave'}
165
+ class:skeleton-pulse={animation === 'pulse'}
166
+ role="status"
167
+ aria-label={label}
168
+ aria-busy="true"
169
+ >
170
+ <span class="sr-only">{label}</span>
171
+ <div class="skeleton-base skeleton-text-sm skeleton-short" aria-hidden="true"></div>
172
+ <div class="skeleton-base skeleton-stat-value" aria-hidden="true"></div>
173
+ <div class="skeleton-base skeleton-text-xs skeleton-medium" aria-hidden="true"></div>
174
+ </div>
175
+
176
+ <!-- Composite: Table Row Skeleton -->
177
+ {:else if variant === 'table-row'}
178
+ <div
179
+ {id}
180
+ class="skeleton-composite skeleton-table-rows {className}"
181
+ role="status"
182
+ aria-label={label}
183
+ aria-busy="true"
184
+ >
185
+ <span class="sr-only">{label}</span>
186
+ {#each itemArray as index (index)}
187
+ <div
188
+ class="skeleton-table-row"
189
+ class:skeleton-wave={animation === 'wave'}
190
+ class:skeleton-pulse={animation === 'pulse'}
191
+ aria-hidden="true"
192
+ >
193
+ <div class="skeleton-base skeleton-cell skeleton-cell-sm"></div>
194
+ <div class="skeleton-base skeleton-cell skeleton-cell-lg"></div>
195
+ <div class="skeleton-base skeleton-cell skeleton-cell-md"></div>
196
+ <div class="skeleton-base skeleton-cell skeleton-cell-sm"></div>
197
+ </div>
198
+ {/each}
199
+ </div>
200
+
201
+ <!-- Composite: List Item Skeleton -->
202
+ {:else if variant === 'list-item'}
203
+ <div
204
+ {id}
205
+ class="skeleton-composite skeleton-list-items {className}"
206
+ role="status"
207
+ aria-label={label}
208
+ aria-busy="true"
209
+ >
210
+ <span class="sr-only">{label}</span>
211
+ {#each itemArray as index (index)}
212
+ <div
213
+ class="skeleton-list-item"
214
+ class:skeleton-wave={animation === 'wave'}
215
+ class:skeleton-pulse={animation === 'pulse'}
216
+ aria-hidden="true"
217
+ >
218
+ <div class="skeleton-base skeleton-avatar-sm"></div>
219
+ <div class="skeleton-list-content">
220
+ <div class="skeleton-base skeleton-text-md skeleton-medium"></div>
221
+ <div class="skeleton-base skeleton-text-sm skeleton-short"></div>
222
+ </div>
223
+ </div>
224
+ {/each}
225
+ </div>
226
+
227
+ <!-- Text with multiple lines -->
228
+ {:else if variant === 'text' && lines > 1}
229
+ <div
230
+ {id}
231
+ class="skeleton-text-group {className}"
232
+ role="status"
233
+ aria-label={label}
234
+ aria-busy="true"
235
+ >
236
+ <span class="sr-only">{label}</span>
237
+ {#each lineArray as index (index)}
238
+ <div
239
+ class="skeleton-base skeleton-text"
240
+ class:skeleton-wave={animation === 'wave'}
241
+ class:skeleton-pulse={animation === 'pulse'}
242
+ style="width: {getLineWidth(index)}; height: {textHeights[size]};"
243
+ aria-hidden="true"
244
+ ></div>
245
+ {/each}
246
+ </div>
247
+
248
+ <!-- Simple variants -->
249
+ {:else}
250
+ <div
251
+ {id}
252
+ class="skeleton-base {className}"
253
+ class:skeleton-wave={animation === 'wave'}
254
+ class:skeleton-pulse={animation === 'pulse'}
255
+ style="
256
+ width: {width || (variant === 'avatar' || variant === 'circle' ? avatarSizes[size].width : variant === 'button' ? buttonSizes[size].width : variant === 'text' ? '100%' : variant === 'card' ? '100%' : variant === 'image' ? '100%' : '100%')};
257
+ height: {height || (variant === 'avatar' || variant === 'circle' ? avatarSizes[size].height : variant === 'button' ? buttonSizes[size].height : variant === 'text' ? textHeights[size] : variant === 'card' ? '8rem' : variant === 'image' ? '12rem' : '1rem')};
258
+ border-radius: {variant === 'avatar' || variant === 'circle' ? '9999px' : variant === 'button' ? '0.5rem' : variant === 'card' || variant === 'image' ? '0.75rem' : getRadius(rounded)};
259
+ "
260
+ role="status"
261
+ aria-label={label}
262
+ aria-busy="true"
263
+ >
264
+ <span class="sr-only">{label}</span>
265
+ </div>
266
+ {/if}
267
+
268
+ <style>
269
+ /* ========================================
270
+ BASE SKELETON STYLES
271
+ ======================================== */
272
+ .skeleton-base {
273
+ background-color: var(--ui-bg-tertiary);
274
+ position: relative;
275
+ overflow: hidden;
276
+ }
277
+
278
+ /* Screen reader only */
279
+ .sr-only {
280
+ position: absolute;
281
+ width: 1px;
282
+ height: 1px;
283
+ padding: 0;
284
+ margin: -1px;
285
+ overflow: hidden;
286
+ clip: rect(0, 0, 0, 0);
287
+ white-space: nowrap;
288
+ border: 0;
289
+ }
290
+
291
+ /* ========================================
292
+ WAVE ANIMATION (Perceived as faster)
293
+ Uses clip-path to prevent overflow issues
294
+ ======================================== */
295
+ .skeleton-wave {
296
+ overflow: hidden;
297
+ }
298
+
299
+ .skeleton-wave::after {
300
+ content: '';
301
+ position: absolute;
302
+ top: 0;
303
+ left: 0;
304
+ right: 0;
305
+ bottom: 0;
306
+ background: linear-gradient(
307
+ 90deg,
308
+ transparent 0%,
309
+ rgba(255, 255, 255, 0.4) 50%,
310
+ transparent 100%
311
+ );
312
+ animation: skeleton-wave 1.6s ease-in-out infinite;
313
+ transform: translateX(-100%);
314
+ }
315
+
316
+ :global([data-theme='dark']) .skeleton-wave::after {
317
+ background: linear-gradient(
318
+ 90deg,
319
+ transparent 0%,
320
+ rgba(255, 255, 255, 0.08) 50%,
321
+ transparent 100%
322
+ );
323
+ }
324
+
325
+ @media (prefers-color-scheme: dark) {
326
+ :global(:root:not([data-theme='light'])) .skeleton-wave::after {
327
+ background: linear-gradient(
328
+ 90deg,
329
+ transparent 0%,
330
+ rgba(255, 255, 255, 0.08) 50%,
331
+ transparent 100%
332
+ );
333
+ }
334
+ }
335
+
336
+ @keyframes skeleton-wave {
337
+ 0% {
338
+ transform: translateX(-100%);
339
+ }
340
+ 60%, 100% {
341
+ transform: translateX(100%);
342
+ }
343
+ }
344
+
345
+ /* ========================================
346
+ PULSE ANIMATION
347
+ ======================================== */
348
+ .skeleton-pulse {
349
+ animation: skeleton-pulse 2s ease-in-out infinite;
350
+ }
351
+
352
+ @keyframes skeleton-pulse {
353
+ 0%, 100% {
354
+ opacity: 1;
355
+ }
356
+ 50% {
357
+ opacity: 0.5;
358
+ }
359
+ }
360
+
361
+ /* ========================================
362
+ TEXT SKELETON
363
+ ======================================== */
364
+ .skeleton-text-group {
365
+ display: flex;
366
+ flex-direction: column;
367
+ gap: 0.5rem;
368
+ overflow: hidden;
369
+ }
370
+
371
+ .skeleton-text {
372
+ border-radius: 0.25rem;
373
+ overflow: hidden;
374
+ }
375
+
376
+ /* ========================================
377
+ COMPOSITE SHARED STYLES
378
+ ======================================== */
379
+ .skeleton-composite {
380
+ overflow: hidden;
381
+ }
382
+
383
+ /* ========================================
384
+ COMPOSITE: PATIENT CARD
385
+ ======================================== */
386
+ .skeleton-patient-card {
387
+ padding: 1rem;
388
+ background: var(--ui-bg-primary);
389
+ border: 1px solid var(--ui-border-default);
390
+ border-radius: 0.75rem;
391
+ overflow: hidden;
392
+ }
393
+
394
+ .skeleton-patient-header {
395
+ display: flex;
396
+ align-items: center;
397
+ gap: 0.75rem;
398
+ margin-bottom: 1rem;
399
+ }
400
+
401
+ .skeleton-avatar {
402
+ width: 3rem;
403
+ height: 3rem;
404
+ border-radius: 9999px;
405
+ flex-shrink: 0;
406
+ }
407
+
408
+ .skeleton-avatar-sm {
409
+ width: 2.5rem;
410
+ height: 2.5rem;
411
+ border-radius: 9999px;
412
+ flex-shrink: 0;
413
+ }
414
+
415
+ .skeleton-patient-info {
416
+ flex: 1;
417
+ display: flex;
418
+ flex-direction: column;
419
+ gap: 0.375rem;
420
+ }
421
+
422
+ .skeleton-patient-body {
423
+ display: flex;
424
+ flex-direction: column;
425
+ gap: 0.375rem;
426
+ }
427
+
428
+ /* ========================================
429
+ COMPOSITE: STAT CARD
430
+ ======================================== */
431
+ .skeleton-stat-card {
432
+ padding: 1.25rem;
433
+ background: var(--ui-bg-primary);
434
+ border: 1px solid var(--ui-border-default);
435
+ border-radius: 0.75rem;
436
+ display: flex;
437
+ flex-direction: column;
438
+ gap: 0.5rem;
439
+ overflow: hidden;
440
+ }
441
+
442
+ .skeleton-stat-value {
443
+ height: 2rem;
444
+ width: 5rem;
445
+ border-radius: 0.375rem;
446
+ }
447
+
448
+ /* ========================================
449
+ COMPOSITE: TABLE ROW
450
+ ======================================== */
451
+ .skeleton-table-rows {
452
+ display: flex;
453
+ flex-direction: column;
454
+ overflow: hidden;
455
+ }
456
+
457
+ .skeleton-table-row {
458
+ display: flex;
459
+ align-items: center;
460
+ gap: 1rem;
461
+ padding: 0.875rem 1rem;
462
+ border-bottom: 1px solid var(--ui-border-default);
463
+ overflow: hidden;
464
+ }
465
+
466
+ .skeleton-table-row:last-child {
467
+ border-bottom: none;
468
+ }
469
+
470
+ .skeleton-cell {
471
+ height: 1rem;
472
+ border-radius: 0.25rem;
473
+ }
474
+
475
+ .skeleton-cell-sm {
476
+ width: 4rem;
477
+ }
478
+
479
+ .skeleton-cell-md {
480
+ width: 8rem;
481
+ }
482
+
483
+ .skeleton-cell-lg {
484
+ flex: 1;
485
+ }
486
+
487
+ /* ========================================
488
+ COMPOSITE: LIST ITEM
489
+ ======================================== */
490
+ .skeleton-list-items {
491
+ display: flex;
492
+ flex-direction: column;
493
+ overflow: hidden;
494
+ }
495
+
496
+ .skeleton-list-item {
497
+ display: flex;
498
+ align-items: center;
499
+ gap: 0.75rem;
500
+ padding: 0.75rem 0;
501
+ border-bottom: 1px solid var(--ui-border-default);
502
+ overflow: hidden;
503
+ }
504
+
505
+ .skeleton-list-item:last-child {
506
+ border-bottom: none;
507
+ }
508
+
509
+ .skeleton-list-content {
510
+ flex: 1;
511
+ display: flex;
512
+ flex-direction: column;
513
+ gap: 0.25rem;
514
+ }
515
+
516
+ /* ========================================
517
+ TEXT SIZE HELPERS
518
+ ======================================== */
519
+ .skeleton-text-xs {
520
+ height: 0.625rem;
521
+ border-radius: 0.125rem;
522
+ }
523
+
524
+ .skeleton-text-sm {
525
+ height: 0.75rem;
526
+ border-radius: 0.125rem;
527
+ }
528
+
529
+ .skeleton-text-md {
530
+ height: 1rem;
531
+ border-radius: 0.25rem;
532
+ }
533
+
534
+ .skeleton-text-lg {
535
+ height: 1.25rem;
536
+ border-radius: 0.25rem;
537
+ }
538
+
539
+ /* Width helpers */
540
+ .skeleton-short {
541
+ width: 40%;
542
+ }
543
+
544
+ .skeleton-medium {
545
+ width: 70%;
546
+ }
547
+
548
+ /* ========================================
549
+ REDUCED MOTION
550
+ ======================================== */
551
+ @media (prefers-reduced-motion: reduce) {
552
+ .skeleton-wave::after {
553
+ animation: none;
554
+ transform: none;
555
+ background: none;
556
+ }
557
+
558
+ .skeleton-pulse {
559
+ animation: none;
560
+ }
561
+
562
+ /* Show static slightly lighter state instead */
563
+ .skeleton-wave,
564
+ .skeleton-pulse {
565
+ opacity: 0.7;
566
+ }
567
+ }
568
+ </style>
@@ -0,0 +1,54 @@
1
+ type SkeletonVariant = 'rectangle' | 'circle' | 'text' | 'avatar' | 'button' | 'card' | 'image' | 'table-row' | 'patient-card' | 'stat-card' | 'list-item';
2
+ type SkeletonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
3
+ type SkeletonAnimation = 'wave' | 'pulse' | 'none';
4
+ interface Props {
5
+ /** Skeleton variant */
6
+ variant?: SkeletonVariant;
7
+ /** Size preset (for avatar, button, text variants) */
8
+ size?: SkeletonSize;
9
+ /** Custom width (CSS value) */
10
+ width?: string;
11
+ /** Custom height (CSS value) */
12
+ height?: string;
13
+ /** Number of lines (for text variant) */
14
+ lines?: number;
15
+ /** Animation style - wave is perceived as faster */
16
+ animation?: SkeletonAnimation;
17
+ /** Border radius preset or custom CSS value */
18
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | string;
19
+ /** Accessible label for screen readers */
20
+ label?: string;
21
+ /** Number of items for list-based variants (table-row, list-item) */
22
+ count?: number;
23
+ /** Additional CSS classes */
24
+ class?: string;
25
+ /** ID for the skeleton element */
26
+ id?: string;
27
+ }
28
+ /**
29
+ * Skeleton
30
+ *
31
+ * A world-class loading placeholder component with multiple variants and animations.
32
+ * Designed for healthcare applications where data loading states must be clearly
33
+ * communicated without causing anxiety.
34
+ *
35
+ * Features:
36
+ * - Multiple preset variants (text, avatar, card, table-row, patient-card, etc.)
37
+ * - Wave animation (perceived as faster than pulse)
38
+ * - Composite skeletons for common patterns
39
+ * - Proper accessibility without over-announcing
40
+ * - Reduced motion support
41
+ *
42
+ * @example
43
+ * <Skeleton width="200px" height="20px" />
44
+ *
45
+ * @example
46
+ * <Skeleton variant="text" lines={3} />
47
+ *
48
+ * @example
49
+ * <Skeleton variant="patient-card" />
50
+ */
51
+ declare const Skeleton: import("svelte").Component<Props, {}, "">;
52
+ type Skeleton = ReturnType<typeof Skeleton>;
53
+ export default Skeleton;
54
+ //# sourceMappingURL=Skeleton.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.svelte.d.ts","sourceRoot":"","sources":["../../../src/feedback/Skeleton/Skeleton.svelte.ts"],"names":[],"mappings":"AAGC,KAAK,eAAe,GACjB,WAAW,GACX,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,OAAO,GACP,WAAW,GACX,cAAc,GACd,WAAW,GACX,WAAW,CAAC;AAEf,KAAK,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,KAAK,iBAAiB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEnD,UAAU,KAAK;IACd,uBAAuB;IACvB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,sDAAsD;IACtD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/D,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ;AA2JF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/feedback/Skeleton/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';