@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,418 @@
1
+ <!--
2
+ @component RadioGroup
3
+
4
+ An accessible radio button group component for selecting one option from a set.
5
+ Critical for healthcare forms where mutually exclusive options are common
6
+ (e.g., gender, status, priority level).
7
+
8
+ @example
9
+ <RadioGroup
10
+ id="priority"
11
+ label="Priority Level"
12
+ options={[
13
+ { value: 'low', label: 'Low' },
14
+ { value: 'medium', label: 'Medium' },
15
+ { value: 'high', label: 'High', description: 'Immediate attention required' }
16
+ ]}
17
+ bind:value={selectedPriority}
18
+ required
19
+ />
20
+ -->
21
+ <script lang="ts">
22
+ import { cn } from '../../utils/cn.js';
23
+ import { generateId } from '../../utils/keyboard.js';
24
+ import { announce } from '../../utils/accessibility.js';
25
+
26
+ type RadioSize = 'sm' | 'md' | 'lg';
27
+ type RadioOrientation = 'vertical' | 'horizontal';
28
+
29
+ interface RadioOption {
30
+ /** Unique value for the option */
31
+ value: string;
32
+ /** Display label */
33
+ label: string;
34
+ /** Optional description text */
35
+ description?: string;
36
+ /** Whether this option is disabled */
37
+ disabled?: boolean;
38
+ }
39
+
40
+ interface Props {
41
+ /** Unique identifier for the radio group */
42
+ id?: string;
43
+ /** Form field name */
44
+ name?: string;
45
+ /** Group label */
46
+ label: string;
47
+ /** Radio options */
48
+ options: RadioOption[];
49
+ /** Currently selected value */
50
+ value?: string;
51
+ /** Whether a selection is required */
52
+ required?: boolean;
53
+ /** Whether the entire group is disabled */
54
+ disabled?: boolean;
55
+ /** Error message to display */
56
+ error?: string;
57
+ /** Hint text */
58
+ hint?: string;
59
+ /** Size of the radio buttons */
60
+ size?: RadioSize;
61
+ /** Layout orientation */
62
+ orientation?: RadioOrientation;
63
+ /** Whether to hide the group label visually */
64
+ hideLabel?: boolean;
65
+ /** Additional CSS classes */
66
+ class?: string;
67
+ /** Change handler */
68
+ onchange?: (value: string) => void;
69
+ /** Test ID for e2e testing (Playwright, Cypress) */
70
+ testId?: string;
71
+ /** Show validation state immediately without waiting for interaction */
72
+ validateOnMount?: boolean;
73
+ }
74
+
75
+ let {
76
+ id = generateId('radio'),
77
+ name,
78
+ label,
79
+ options,
80
+ value = '',
81
+ required = false,
82
+ disabled = false,
83
+ error = '',
84
+ hint = '',
85
+ size = 'md',
86
+ orientation = 'vertical',
87
+ hideLabel = false,
88
+ class: className = '',
89
+ onchange,
90
+ testId,
91
+ validateOnMount = false
92
+ }: Props = $props();
93
+
94
+ // Internal state
95
+ let hasInteracted = $state(false);
96
+
97
+ // Derived states
98
+ const shouldValidate = $derived(hasInteracted || validateOnMount);
99
+ const showError = $derived(shouldValidate && !!error);
100
+ const errorId = $derived(`${id}-error`);
101
+ const hintId = $derived(`${id}-hint`);
102
+ const groupName = $derived(name || id);
103
+
104
+ // Size styles - label only, radio sizes handled in CSS
105
+ const sizeStyles: Record<RadioSize, { label: string }> = {
106
+ sm: { label: 'text-sm' },
107
+ md: { label: 'text-sm' },
108
+ lg: { label: 'text-base' }
109
+ };
110
+
111
+ function handleChange(optionValue: string) {
112
+ hasInteracted = true;
113
+ if (onchange) {
114
+ onchange(optionValue);
115
+ // Announce selection to screen readers
116
+ const selectedOption = options.find((opt) => opt.value === optionValue);
117
+ if (selectedOption) {
118
+ announce(`${selectedOption.label} selected`);
119
+ }
120
+ }
121
+ }
122
+
123
+ // Build aria-describedby
124
+ const ariaDescribedBy = $derived(
125
+ [error && errorId, hint && hintId].filter(Boolean).join(' ') || undefined
126
+ );
127
+ </script>
128
+
129
+ <fieldset
130
+ class={cn(
131
+ 'radio-group flex flex-col gap-2',
132
+ disabled && 'opacity-60',
133
+ className
134
+ )}
135
+ aria-describedby={ariaDescribedBy}
136
+ data-testid={testId}
137
+ {disabled}
138
+ >
139
+ <!-- Legend/Label -->
140
+ <legend
141
+ class={cn(
142
+ 'flex items-center gap-1.5 font-medium text-[var(--ui-text-primary)] mb-1',
143
+ sizeStyles[size].label,
144
+ hideLabel && 'sr-only'
145
+ )}
146
+ >
147
+ {label}
148
+ {#if required}
149
+ <span class="text-[rgb(var(--ui-color-error))]" aria-hidden="true">*</span>
150
+ <span class="sr-only">(required)</span>
151
+ {/if}
152
+ </legend>
153
+
154
+ <!-- Hint text -->
155
+ {#if hint && !hideLabel}
156
+ <p id={hintId} class="text-xs text-[var(--ui-text-tertiary)] -mt-1 mb-1">
157
+ {hint}
158
+ </p>
159
+ {/if}
160
+
161
+ <!-- Radio options -->
162
+ <div
163
+ class={cn(
164
+ 'flex gap-3',
165
+ orientation === 'vertical' ? 'flex-col' : 'flex-row flex-wrap'
166
+ )}
167
+ role="radiogroup"
168
+ aria-required={required}
169
+ >
170
+ {#each options as option (option.value)}
171
+ {@const optionId = `${id}-${option.value}`}
172
+ {@const isSelected = value === option.value}
173
+ {@const isDisabled = disabled || option.disabled}
174
+
175
+ <label
176
+ for={optionId}
177
+ class={cn(
178
+ 'radio-option',
179
+ `radio-${size}`,
180
+ isDisabled && 'radio-option-disabled'
181
+ )}
182
+ >
183
+ <!-- Styled radio input using appearance: none -->
184
+ <input
185
+ id={optionId}
186
+ type="radio"
187
+ name={groupName}
188
+ value={option.value}
189
+ checked={isSelected}
190
+ disabled={isDisabled}
191
+ {required}
192
+ aria-describedby={option.description ? `${optionId}-desc` : undefined}
193
+ class={cn(
194
+ 'radio-input',
195
+ showError && !isSelected && 'radio-error'
196
+ )}
197
+ onchange={() => handleChange(option.value)}
198
+ />
199
+
200
+ <!-- Label and description -->
201
+ <span class="radio-label-wrapper">
202
+ <span
203
+ class={cn(
204
+ 'radio-label-text',
205
+ sizeStyles[size].label
206
+ )}
207
+ >
208
+ {option.label}
209
+ </span>
210
+ {#if option.description}
211
+ <span
212
+ id={`${optionId}-desc`}
213
+ class="radio-description"
214
+ >
215
+ {option.description}
216
+ </span>
217
+ {/if}
218
+ </span>
219
+ </label>
220
+ {/each}
221
+ </div>
222
+
223
+ <!-- Error message -->
224
+ {#if showError}
225
+ <p
226
+ id={errorId}
227
+ class="text-xs text-[rgb(var(--ui-color-error))] flex items-center gap-1 mt-1"
228
+ role="alert"
229
+ >
230
+ {error}
231
+ </p>
232
+ {/if}
233
+ </fieldset>
234
+
235
+ <style>
236
+ /* ========================================
237
+ RADIO OPTION (label wrapper)
238
+ ======================================== */
239
+ .radio-option {
240
+ display: flex;
241
+ align-items: center;
242
+ gap: 0.5rem;
243
+ cursor: pointer;
244
+ user-select: none;
245
+ }
246
+
247
+ /* When there's a description, align to top */
248
+ .radio-option:has(.radio-description) {
249
+ align-items: flex-start;
250
+ }
251
+
252
+ .radio-option:has(.radio-description) .radio-input {
253
+ margin-top: 0.125rem;
254
+ }
255
+
256
+ .radio-option-disabled {
257
+ cursor: not-allowed;
258
+ opacity: 0.6;
259
+ }
260
+
261
+ /* ========================================
262
+ RADIO INPUT - Modern CSS approach
263
+ Uses appearance: none for full styling control
264
+ ======================================== */
265
+ .radio-input {
266
+ /* Reset native appearance */
267
+ -webkit-appearance: none;
268
+ appearance: none;
269
+ margin: 0;
270
+
271
+ /* Sizing - use grid for centering ::before */
272
+ display: grid;
273
+ place-content: center;
274
+ flex-shrink: 0;
275
+
276
+ /* Visual styling */
277
+ border: 2px solid var(--ui-border-default);
278
+ border-radius: 50%;
279
+ background: var(--ui-bg-secondary);
280
+ cursor: pointer;
281
+ transition: all 0.2s ease;
282
+ }
283
+
284
+ /* Size variants */
285
+ .radio-sm .radio-input {
286
+ width: 1rem;
287
+ height: 1rem;
288
+ }
289
+
290
+ .radio-md .radio-input {
291
+ width: 1.25rem;
292
+ height: 1.25rem;
293
+ }
294
+
295
+ .radio-lg .radio-input {
296
+ width: 1.5rem;
297
+ height: 1.5rem;
298
+ }
299
+
300
+ /* Checked indicator using ::before */
301
+ .radio-input::before {
302
+ content: "";
303
+ border-radius: 50%;
304
+ transform: scale(0);
305
+ transition: transform 0.15s ease-in-out;
306
+ background-color: white;
307
+ }
308
+
309
+ .radio-sm .radio-input::before {
310
+ width: 0.5rem;
311
+ height: 0.5rem;
312
+ }
313
+
314
+ .radio-md .radio-input::before {
315
+ width: 0.625rem;
316
+ height: 0.625rem;
317
+ }
318
+
319
+ .radio-lg .radio-input::before {
320
+ width: 0.75rem;
321
+ height: 0.75rem;
322
+ }
323
+
324
+ .radio-input:checked::before {
325
+ transform: scale(1);
326
+ }
327
+
328
+ /* Checked state */
329
+ .radio-input:checked {
330
+ border-color: rgb(var(--ui-color-primary));
331
+ background-color: rgb(var(--ui-color-primary));
332
+ }
333
+
334
+ :global([data-theme='dark']) .radio-input:checked {
335
+ border-color: rgb(var(--ui-color-primary-light));
336
+ background-color: rgb(var(--ui-color-primary-light));
337
+ }
338
+
339
+ @media (prefers-color-scheme: dark) {
340
+ :global(:root:not([data-theme='light'])) .radio-input:checked {
341
+ border-color: rgb(var(--ui-color-primary-light));
342
+ background-color: rgb(var(--ui-color-primary-light));
343
+ }
344
+ }
345
+
346
+ /* Hover state */
347
+ .radio-input:not(:disabled):not(:checked):hover {
348
+ border-color: rgb(var(--ui-color-primary));
349
+ }
350
+
351
+ :global([data-theme='dark']) .radio-input:not(:disabled):not(:checked):hover {
352
+ border-color: rgb(var(--ui-color-primary-light));
353
+ }
354
+
355
+ @media (prefers-color-scheme: dark) {
356
+ :global(:root:not([data-theme='light'])) .radio-input:not(:disabled):not(:checked):hover {
357
+ border-color: rgb(var(--ui-color-primary-light));
358
+ }
359
+ }
360
+
361
+ /* Focus state - matches Button's 40%/50% opacity */
362
+ .radio-input:focus-visible {
363
+ outline: none;
364
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary) / 0.4);
365
+ }
366
+
367
+ :global([data-theme='dark']) .radio-input:focus-visible {
368
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary-light) / 0.5);
369
+ }
370
+
371
+ @media (prefers-color-scheme: dark) {
372
+ :global(:root:not([data-theme='light'])) .radio-input:focus-visible {
373
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary-light) / 0.5);
374
+ }
375
+ }
376
+
377
+ /* Disabled state */
378
+ .radio-input:disabled {
379
+ cursor: not-allowed;
380
+ background: var(--ui-bg-tertiary);
381
+ }
382
+
383
+ /* Error state */
384
+ .radio-error {
385
+ border-color: rgb(var(--ui-color-error));
386
+ }
387
+
388
+ :global([data-theme='dark']) .radio-error {
389
+ border-color: rgb(var(--ui-color-error-light));
390
+ }
391
+
392
+ @media (prefers-color-scheme: dark) {
393
+ :global(:root:not([data-theme='light'])) .radio-error {
394
+ border-color: rgb(var(--ui-color-error-light));
395
+ }
396
+ }
397
+
398
+ /* ========================================
399
+ LABEL TEXT
400
+ ======================================== */
401
+ .radio-label-wrapper {
402
+ display: flex;
403
+ flex-direction: column;
404
+ gap: 0.125rem;
405
+ }
406
+
407
+ .radio-label-text {
408
+ font-weight: 500;
409
+ color: var(--ui-text-primary);
410
+ line-height: 1.25;
411
+ }
412
+
413
+ .radio-description {
414
+ font-size: 0.75rem;
415
+ color: var(--ui-text-tertiary);
416
+ }
417
+ </style>
418
+
@@ -0,0 +1,70 @@
1
+ type RadioSize = 'sm' | 'md' | 'lg';
2
+ type RadioOrientation = 'vertical' | 'horizontal';
3
+ interface RadioOption {
4
+ /** Unique value for the option */
5
+ value: string;
6
+ /** Display label */
7
+ label: string;
8
+ /** Optional description text */
9
+ description?: string;
10
+ /** Whether this option is disabled */
11
+ disabled?: boolean;
12
+ }
13
+ interface Props {
14
+ /** Unique identifier for the radio group */
15
+ id?: string;
16
+ /** Form field name */
17
+ name?: string;
18
+ /** Group label */
19
+ label: string;
20
+ /** Radio options */
21
+ options: RadioOption[];
22
+ /** Currently selected value */
23
+ value?: string;
24
+ /** Whether a selection is required */
25
+ required?: boolean;
26
+ /** Whether the entire group is disabled */
27
+ disabled?: boolean;
28
+ /** Error message to display */
29
+ error?: string;
30
+ /** Hint text */
31
+ hint?: string;
32
+ /** Size of the radio buttons */
33
+ size?: RadioSize;
34
+ /** Layout orientation */
35
+ orientation?: RadioOrientation;
36
+ /** Whether to hide the group label visually */
37
+ hideLabel?: boolean;
38
+ /** Additional CSS classes */
39
+ class?: string;
40
+ /** Change handler */
41
+ onchange?: (value: string) => void;
42
+ /** Test ID for e2e testing (Playwright, Cypress) */
43
+ testId?: string;
44
+ /** Show validation state immediately without waiting for interaction */
45
+ validateOnMount?: boolean;
46
+ }
47
+ /**
48
+ * RadioGroup
49
+ *
50
+ * An accessible radio button group component for selecting one option from a set.
51
+ * Critical for healthcare forms where mutually exclusive options are common
52
+ * (e.g., gender, status, priority level).
53
+ *
54
+ * @example
55
+ * <RadioGroup
56
+ * id="priority"
57
+ * label="Priority Level"
58
+ * options={[
59
+ * { value: 'low', label: 'Low' },
60
+ * { value: 'medium', label: 'Medium' },
61
+ * { value: 'high', label: 'High', description: 'Immediate attention required' }
62
+ * ]}
63
+ * bind:value={selectedPriority}
64
+ * required
65
+ * />
66
+ */
67
+ declare const RadioGroup: import("svelte").Component<Props, {}, "">;
68
+ type RadioGroup = ReturnType<typeof RadioGroup>;
69
+ export default RadioGroup;
70
+ //# sourceMappingURL=RadioGroup.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RadioGroup.svelte.d.ts","sourceRoot":"","sources":["../../../src/forms/RadioGroup/RadioGroup.svelte.ts"],"names":[],"mappings":"AAQC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACrC,KAAK,gBAAgB,GAAG,UAAU,GAAG,YAAY,CAAC;AAElD,UAAU,WAAW;IACpB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,KAAK;IACd,4CAA4C;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,yBAAyB;IACzB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AA8ID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as RadioGroup } from './RadioGroup.svelte';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/RadioGroup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as RadioGroup } from './RadioGroup.svelte';