@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.
- package/README.md +262 -0
- package/dist/app.css +2 -0
- package/dist/app.html +12 -0
- package/dist/data-display/DataTable/DataTable.svelte +773 -0
- package/dist/data-display/DataTable/DataTable.svelte.d.ts +120 -0
- package/dist/data-display/DataTable/DataTable.svelte.d.ts.map +1 -0
- package/dist/data-display/DataTable/index.d.ts +2 -0
- package/dist/data-display/DataTable/index.d.ts.map +1 -0
- package/dist/data-display/DataTable/index.js +1 -0
- package/dist/data-display/StatCard/StatCard.svelte +409 -0
- package/dist/data-display/StatCard/StatCard.svelte.d.ts +63 -0
- package/dist/data-display/StatCard/StatCard.svelte.d.ts.map +1 -0
- package/dist/data-display/StatCard/index.d.ts +2 -0
- package/dist/data-display/StatCard/index.d.ts.map +1 -0
- package/dist/data-display/StatCard/index.js +1 -0
- package/dist/data-display/index.d.ts +8 -0
- package/dist/data-display/index.d.ts.map +1 -0
- package/dist/data-display/index.js +7 -0
- package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte +693 -0
- package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte.d.ts +66 -0
- package/dist/dialogs/ConfirmDialog/ConfirmDialog.svelte.d.ts.map +1 -0
- package/dist/dialogs/ConfirmDialog/index.d.ts +2 -0
- package/dist/dialogs/ConfirmDialog/index.d.ts.map +1 -0
- package/dist/dialogs/ConfirmDialog/index.js +1 -0
- package/dist/dialogs/Modal/Modal.svelte +441 -0
- package/dist/dialogs/Modal/Modal.svelte.d.ts +69 -0
- package/dist/dialogs/Modal/Modal.svelte.d.ts.map +1 -0
- package/dist/dialogs/Modal/index.d.ts +2 -0
- package/dist/dialogs/Modal/index.d.ts.map +1 -0
- package/dist/dialogs/Modal/index.js +1 -0
- package/dist/dialogs/index.d.ts +8 -0
- package/dist/dialogs/index.d.ts.map +1 -0
- package/dist/dialogs/index.js +7 -0
- package/dist/feedback/Alert/Alert.svelte +565 -0
- package/dist/feedback/Alert/Alert.svelte.d.ts +60 -0
- package/dist/feedback/Alert/Alert.svelte.d.ts.map +1 -0
- package/dist/feedback/Alert/index.d.ts +2 -0
- package/dist/feedback/Alert/index.d.ts.map +1 -0
- package/dist/feedback/Alert/index.js +1 -0
- package/dist/feedback/EmptyState/EmptyState.svelte +377 -0
- package/dist/feedback/EmptyState/EmptyState.svelte.d.ts +63 -0
- package/dist/feedback/EmptyState/EmptyState.svelte.d.ts.map +1 -0
- package/dist/feedback/EmptyState/index.d.ts +2 -0
- package/dist/feedback/EmptyState/index.d.ts.map +1 -0
- package/dist/feedback/EmptyState/index.js +1 -0
- package/dist/feedback/ProgressBar/ProgressBar.svelte +585 -0
- package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts +68 -0
- package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts.map +1 -0
- package/dist/feedback/ProgressBar/index.d.ts +2 -0
- package/dist/feedback/ProgressBar/index.d.ts.map +1 -0
- package/dist/feedback/ProgressBar/index.js +1 -0
- package/dist/feedback/Skeleton/Skeleton.svelte +568 -0
- package/dist/feedback/Skeleton/Skeleton.svelte.d.ts +54 -0
- package/dist/feedback/Skeleton/Skeleton.svelte.d.ts.map +1 -0
- package/dist/feedback/Skeleton/index.d.ts +2 -0
- package/dist/feedback/Skeleton/index.d.ts.map +1 -0
- package/dist/feedback/Skeleton/index.js +1 -0
- package/dist/feedback/Spinner/Spinner.svelte +434 -0
- package/dist/feedback/Spinner/Spinner.svelte.d.ts +49 -0
- package/dist/feedback/Spinner/Spinner.svelte.d.ts.map +1 -0
- package/dist/feedback/Spinner/index.d.ts +2 -0
- package/dist/feedback/Spinner/index.d.ts.map +1 -0
- package/dist/feedback/Spinner/index.js +1 -0
- package/dist/feedback/Toast/Toast.svelte +587 -0
- package/dist/feedback/Toast/Toast.svelte.d.ts +55 -0
- package/dist/feedback/Toast/Toast.svelte.d.ts.map +1 -0
- package/dist/feedback/Toast/ToastContainer.svelte +168 -0
- package/dist/feedback/Toast/ToastContainer.svelte.d.ts +28 -0
- package/dist/feedback/Toast/ToastContainer.svelte.d.ts.map +1 -0
- package/dist/feedback/Toast/index.d.ts +4 -0
- package/dist/feedback/Toast/index.d.ts.map +1 -0
- package/dist/feedback/Toast/index.js +3 -0
- package/dist/feedback/Toast/toast-store.d.ts +72 -0
- package/dist/feedback/Toast/toast-store.d.ts.map +1 -0
- package/dist/feedback/Toast/toast-store.js +157 -0
- package/dist/feedback/index.d.ts +13 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/index.js +12 -0
- package/dist/forms/Checkbox/Checkbox.svelte +404 -0
- package/dist/forms/Checkbox/Checkbox.svelte.d.ts +62 -0
- package/dist/forms/Checkbox/Checkbox.svelte.d.ts.map +1 -0
- package/dist/forms/Checkbox/index.d.ts +2 -0
- package/dist/forms/Checkbox/index.d.ts.map +1 -0
- package/dist/forms/Checkbox/index.js +1 -0
- package/dist/forms/FormField/FormField.svelte +299 -0
- package/dist/forms/FormField/FormField.svelte.d.ts +43 -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/RadioGroup/RadioGroup.svelte +418 -0
- package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts +70 -0
- package/dist/forms/RadioGroup/RadioGroup.svelte.d.ts.map +1 -0
- package/dist/forms/RadioGroup/index.d.ts +2 -0
- package/dist/forms/RadioGroup/index.d.ts.map +1 -0
- package/dist/forms/RadioGroup/index.js +1 -0
- package/dist/forms/Select/Select.svelte +548 -0
- package/dist/forms/Select/Select.svelte.d.ts +74 -0
- package/dist/forms/Select/Select.svelte.d.ts.map +1 -0
- package/dist/forms/Select/index.d.ts +2 -0
- package/dist/forms/Select/index.d.ts.map +1 -0
- package/dist/forms/Select/index.js +1 -0
- package/dist/forms/TextInput/TextInput.svelte +628 -0
- package/dist/forms/TextInput/TextInput.svelte.d.ts +97 -0
- package/dist/forms/TextInput/TextInput.svelte.d.ts.map +1 -0
- package/dist/forms/TextInput/index.d.ts +2 -0
- package/dist/forms/TextInput/index.d.ts.map +1 -0
- package/dist/forms/TextInput/index.js +1 -0
- package/dist/forms/Textarea/Textarea.svelte +587 -0
- package/dist/forms/Textarea/Textarea.svelte.d.ts +71 -0
- package/dist/forms/Textarea/Textarea.svelte.d.ts.map +1 -0
- package/dist/forms/Textarea/index.d.ts +2 -0
- package/dist/forms/Textarea/index.d.ts.map +1 -0
- package/dist/forms/Textarea/index.js +1 -0
- package/dist/forms/index.d.ts +13 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +12 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/layout/Card/Card.svelte +316 -0
- package/dist/layout/Card/Card.svelte.d.ts +63 -0
- package/dist/layout/Card/Card.svelte.d.ts.map +1 -0
- package/dist/layout/Card/index.d.ts +2 -0
- package/dist/layout/Card/index.d.ts.map +1 -0
- package/dist/layout/Card/index.js +1 -0
- package/dist/layout/Container/Container.svelte +252 -0
- package/dist/layout/Container/Container.svelte.d.ts +50 -0
- package/dist/layout/Container/Container.svelte.d.ts.map +1 -0
- package/dist/layout/Container/index.d.ts +2 -0
- package/dist/layout/Container/index.d.ts.map +1 -0
- package/dist/layout/Container/index.js +1 -0
- package/dist/layout/index.d.ts +8 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +7 -0
- package/dist/navigation/StepIndicator/StepIndicator.svelte +601 -0
- package/dist/navigation/StepIndicator/StepIndicator.svelte.d.ts +70 -0
- package/dist/navigation/StepIndicator/StepIndicator.svelte.d.ts.map +1 -0
- package/dist/navigation/StepIndicator/index.d.ts +2 -0
- package/dist/navigation/StepIndicator/index.d.ts.map +1 -0
- package/dist/navigation/StepIndicator/index.js +1 -0
- package/dist/navigation/index.d.ts +7 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +6 -0
- package/dist/primitives/Badge/Badge.svelte +365 -0
- package/dist/primitives/Badge/Badge.svelte.d.ts +39 -0
- package/dist/primitives/Badge/Badge.svelte.d.ts.map +1 -0
- package/dist/primitives/Badge/index.d.ts +2 -0
- package/dist/primitives/Badge/index.d.ts.map +1 -0
- package/dist/primitives/Badge/index.js +1 -0
- package/dist/primitives/Button/Button.svelte +430 -0
- package/dist/primitives/Button/Button.svelte.d.ts +50 -0
- package/dist/primitives/Button/Button.svelte.d.ts.map +1 -0
- package/dist/primitives/Button/index.d.ts +2 -0
- package/dist/primitives/Button/index.d.ts.map +1 -0
- package/dist/primitives/Button/index.js +1 -0
- package/dist/primitives/index.d.ts +9 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +8 -0
- package/dist/routes/+layout.svelte +12 -0
- package/dist/routes/+layout.svelte.d.ts +12 -0
- package/dist/routes/+layout.svelte.d.ts.map +1 -0
- package/dist/routes/+page.svelte +53 -0
- package/dist/routes/+page.svelte.d.ts +27 -0
- package/dist/routes/+page.svelte.d.ts.map +1 -0
- package/dist/styles/tokens.css +399 -0
- package/dist/types/index.d.ts +175 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/utils/accessibility.d.ts +103 -0
- package/dist/utils/accessibility.d.ts.map +1 -0
- package/dist/utils/accessibility.js +202 -0
- package/dist/utils/cn.d.ts +71 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +61 -0
- package/dist/utils/form-styles.d.ts +76 -0
- package/dist/utils/form-styles.d.ts.map +1 -0
- package/dist/utils/form-styles.js +95 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +13 -0
- package/dist/utils/keyboard.d.ts +94 -0
- package/dist/utils/keyboard.d.ts.map +1 -0
- package/dist/utils/keyboard.js +179 -0
- 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 @@
|
|
|
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';
|