@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 @@
1
+ {"version":3,"file":"TextInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/forms/TextInput/TextInput.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGnE,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErF,UAAU,KAAM,SAAQ,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,MAAM,CAAC;IAClE,sCAAsC;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,yBAAyB;IACzB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,wBAAwB;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,mBAAmB;IACnB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAoKF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as TextInput } from './TextInput.svelte';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/TextInput/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as TextInput } from './TextInput.svelte';
@@ -0,0 +1,587 @@
1
+ <!--
2
+ @component Textarea
3
+
4
+ A multi-line text input component for longer content like clinical notes,
5
+ patient histories, and detailed descriptions. Supports auto-resize,
6
+ character counting, and comprehensive accessibility.
7
+
8
+ @example
9
+ <Textarea
10
+ id="clinical-notes"
11
+ label="Clinical Notes"
12
+ value={notes}
13
+ placeholder="Enter clinical observations..."
14
+ maxLength={2000}
15
+ showCharCount
16
+ />
17
+ -->
18
+ <script lang="ts">
19
+ import { Check, AlertCircle, Info } from 'lucide-svelte';
20
+ import { cn } from '../../utils/cn.js';
21
+ import { generateId } from '../../utils/keyboard.js';
22
+
23
+ type TextareaSize = 'sm' | 'md' | 'lg';
24
+ type TextareaResize = 'none' | 'vertical' | 'horizontal' | 'both' | 'auto';
25
+
26
+ interface Props {
27
+ /** Unique identifier for the textarea */
28
+ id?: string;
29
+ /** Form field name */
30
+ name?: string;
31
+ /** Label text */
32
+ label: string;
33
+ /** Current value */
34
+ value?: string;
35
+ /** Placeholder text */
36
+ placeholder?: string;
37
+ /** Number of visible rows */
38
+ rows?: number;
39
+ /** Whether the field is required */
40
+ required?: boolean;
41
+ /** Whether the field is disabled */
42
+ disabled?: boolean;
43
+ /** Whether the field is read-only */
44
+ readonly?: boolean;
45
+ /** Error message to display */
46
+ error?: string;
47
+ /** Hint text */
48
+ hint?: string;
49
+ /** Success message when valid */
50
+ successMessage?: string;
51
+ /** Maximum character length */
52
+ maxLength?: number;
53
+ /** Minimum character length */
54
+ minLength?: number;
55
+ /** Whether to show character count */
56
+ showCharCount?: boolean;
57
+ /** Resize behavior */
58
+ resize?: TextareaResize;
59
+ /** Size of the textarea */
60
+ size?: TextareaSize;
61
+ /** Whether to hide the label visually */
62
+ hideLabel?: boolean;
63
+ /** Additional CSS classes */
64
+ class?: string;
65
+ /** Input handler */
66
+ oninput?: (event: Event) => void;
67
+ /** Blur handler */
68
+ onblur?: (event: FocusEvent) => void;
69
+ /** Focus handler */
70
+ onfocus?: (event: FocusEvent) => void;
71
+ /** Show validation state immediately without waiting for interaction */
72
+ validateOnMount?: boolean;
73
+ }
74
+
75
+ let {
76
+ id = generateId('textarea'),
77
+ name,
78
+ label,
79
+ value = '',
80
+ placeholder = '',
81
+ rows = 4,
82
+ required = false,
83
+ disabled = false,
84
+ readonly = false,
85
+ error = '',
86
+ hint = '',
87
+ successMessage = '',
88
+ maxLength,
89
+ minLength,
90
+ showCharCount = false,
91
+ resize = 'vertical',
92
+ size = 'md',
93
+ hideLabel = false,
94
+ class: className = '',
95
+ oninput,
96
+ onblur,
97
+ onfocus,
98
+ validateOnMount = false
99
+ }: Props = $props();
100
+
101
+ // Internal state
102
+ let isFocused = $state(false);
103
+ let hasInteracted = $state(false);
104
+ let textareaElement = $state<HTMLTextAreaElement | null>(null);
105
+
106
+ // Derived states
107
+ const shouldValidate = $derived(hasInteracted || validateOnMount);
108
+ const showError = $derived(shouldValidate && !!error);
109
+ const showSuccess = $derived(shouldValidate && !error && !!value && !!successMessage);
110
+ const isValid = $derived(shouldValidate && !error && !!value && !!successMessage);
111
+ const errorId = $derived(`${id}-error`);
112
+ const hintId = $derived(`${id}-hint`);
113
+ const charCount = $derived(value?.length || 0);
114
+ const isNearLimit = $derived(maxLength ? charCount > maxLength * 0.9 : false);
115
+ const isOverLimit = $derived(maxLength ? charCount > maxLength : false);
116
+
117
+ // Auto-resize effect
118
+ $effect(() => {
119
+ if (resize === 'auto' && textareaElement && value !== undefined) {
120
+ textareaElement.style.height = 'auto';
121
+ textareaElement.style.height = `${textareaElement.scrollHeight}px`;
122
+ }
123
+ });
124
+
125
+ function handleInput(e: Event) {
126
+ oninput?.(e);
127
+ }
128
+
129
+ function handleBlur(e: FocusEvent) {
130
+ isFocused = false;
131
+ hasInteracted = true;
132
+ onblur?.(e);
133
+ }
134
+
135
+ function handleFocus(e: FocusEvent) {
136
+ isFocused = true;
137
+ onfocus?.(e);
138
+ }
139
+
140
+ // Build aria-describedby
141
+ const ariaDescribedBy = $derived(
142
+ [error && errorId, hint && hintId].filter(Boolean).join(' ') || undefined
143
+ );
144
+ </script>
145
+
146
+ <div class={cn('textarea-wrapper', `textarea-${size}`, disabled && 'textarea-disabled', className)}>
147
+ <!-- Label -->
148
+ <label for={id} class={cn('textarea-label', hideLabel && 'sr-only')}>
149
+ {label}
150
+ {#if required}
151
+ <span class="textarea-required" aria-hidden="true">*</span>
152
+ <span class="sr-only">(required)</span>
153
+ {/if}
154
+ {#if hint && !hideLabel}
155
+ <span class="textarea-hint-icon" title={hint}>
156
+ <Info size={14} />
157
+ </span>
158
+ {/if}
159
+ </label>
160
+
161
+ <!-- Textarea -->
162
+ <div class="textarea-field-wrapper">
163
+ <textarea
164
+ bind:this={textareaElement}
165
+ {id}
166
+ {name}
167
+ {value}
168
+ {placeholder}
169
+ {rows}
170
+ {disabled}
171
+ {readonly}
172
+ {required}
173
+ maxlength={maxLength}
174
+ minlength={minLength}
175
+ aria-invalid={showError}
176
+ aria-describedby={ariaDescribedBy}
177
+ aria-required={required}
178
+ class={cn(
179
+ 'textarea-field',
180
+ `textarea-resize-${resize}`,
181
+ showError && 'textarea-error',
182
+ isValid && 'textarea-valid'
183
+ )}
184
+ oninput={handleInput}
185
+ onblur={handleBlur}
186
+ onfocus={handleFocus}
187
+ ></textarea>
188
+
189
+ <!-- Status icon -->
190
+ {#if showError}
191
+ <span class="textarea-icon textarea-icon-error" aria-hidden="true">
192
+ <AlertCircle size={18} />
193
+ </span>
194
+ {:else if isValid}
195
+ <span class="textarea-icon textarea-icon-success" aria-hidden="true">
196
+ <Check size={18} />
197
+ </span>
198
+ {/if}
199
+ </div>
200
+
201
+ <!-- Footer messages -->
202
+ <div class="textarea-footer">
203
+ <div class="textarea-messages">
204
+ {#if showError}
205
+ <p id={errorId} class="textarea-message textarea-message-error" role="alert" aria-live="assertive">
206
+ {error}
207
+ </p>
208
+ {:else if showSuccess}
209
+ <p class="textarea-message textarea-message-success">
210
+ {successMessage}
211
+ </p>
212
+ {:else if hint && isFocused}
213
+ <p id={hintId} class="textarea-message textarea-message-hint">
214
+ {hint}
215
+ </p>
216
+ {/if}
217
+ </div>
218
+
219
+ {#if showCharCount && maxLength}
220
+ <span
221
+ class={cn(
222
+ 'textarea-char-count',
223
+ isOverLimit && 'textarea-char-count-error',
224
+ isNearLimit && !isOverLimit && 'textarea-char-count-warning'
225
+ )}
226
+ aria-live="polite"
227
+ aria-atomic="true"
228
+ >
229
+ {charCount}/{maxLength}
230
+ </span>
231
+ {/if}
232
+ </div>
233
+ </div>
234
+
235
+ <style>
236
+ /* ========================================
237
+ BASE WRAPPER
238
+ ======================================== */
239
+ .textarea-wrapper {
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: 0.375rem;
243
+ }
244
+
245
+ .textarea-disabled {
246
+ opacity: 0.6;
247
+ }
248
+
249
+ /* ========================================
250
+ LABEL
251
+ ======================================== */
252
+ .textarea-label {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 0.375rem;
256
+ font-weight: 500;
257
+ color: var(--ui-text-primary);
258
+ }
259
+
260
+ .textarea-sm .textarea-label {
261
+ font-size: 0.75rem;
262
+ }
263
+
264
+ .textarea-md .textarea-label,
265
+ .textarea-lg .textarea-label {
266
+ font-size: 0.875rem;
267
+ }
268
+
269
+ .textarea-required {
270
+ color: rgb(var(--ui-color-error));
271
+ }
272
+
273
+ :global([data-theme='dark']) .textarea-required {
274
+ color: rgb(var(--ui-color-error-light));
275
+ }
276
+
277
+ @media (prefers-color-scheme: dark) {
278
+ :global(:root:not([data-theme='light'])) .textarea-required {
279
+ color: rgb(var(--ui-color-error-light));
280
+ }
281
+ }
282
+
283
+ .textarea-hint-icon {
284
+ color: var(--ui-text-tertiary);
285
+ cursor: help;
286
+ }
287
+
288
+ /* ========================================
289
+ FIELD WRAPPER
290
+ ======================================== */
291
+ .textarea-field-wrapper {
292
+ position: relative;
293
+ }
294
+
295
+ /* ========================================
296
+ TEXTAREA FIELD
297
+ ======================================== */
298
+ .textarea-field {
299
+ width: 100%;
300
+ border: 2px solid var(--ui-border-default);
301
+ border-radius: 0.5rem;
302
+ background: var(--ui-bg-secondary);
303
+ color: var(--ui-text-primary);
304
+ transition: all 0.2s ease;
305
+ }
306
+
307
+ .textarea-field::placeholder {
308
+ color: var(--ui-text-tertiary);
309
+ }
310
+
311
+ .textarea-field:focus-visible {
312
+ outline: none;
313
+ background: var(--ui-bg-primary);
314
+ border-color: rgb(var(--ui-color-primary));
315
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary) / 0.4);
316
+ }
317
+
318
+ :global([data-theme='dark']) .textarea-field:focus-visible {
319
+ border-color: rgb(var(--ui-color-primary-light));
320
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary-light) / 0.5);
321
+ }
322
+
323
+ @media (prefers-color-scheme: dark) {
324
+ :global(:root:not([data-theme='light'])) .textarea-field:focus-visible {
325
+ border-color: rgb(var(--ui-color-primary-light));
326
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-primary-light) / 0.5);
327
+ }
328
+ }
329
+
330
+ .textarea-field:disabled {
331
+ cursor: not-allowed;
332
+ background: var(--ui-bg-tertiary);
333
+ }
334
+
335
+ .textarea-field:read-only {
336
+ background: var(--ui-bg-tertiary);
337
+ cursor: default;
338
+ }
339
+
340
+ /* Size variants */
341
+ .textarea-sm .textarea-field {
342
+ padding: 0.5rem 0.75rem;
343
+ font-size: 0.875rem;
344
+ }
345
+
346
+ .textarea-md .textarea-field {
347
+ padding: 0.625rem 0.75rem;
348
+ font-size: 0.875rem;
349
+ }
350
+
351
+ .textarea-lg .textarea-field {
352
+ padding: 0.75rem 1rem;
353
+ font-size: 1rem;
354
+ }
355
+
356
+ /* Resize variants */
357
+ .textarea-resize-none {
358
+ resize: none;
359
+ }
360
+
361
+ .textarea-resize-vertical {
362
+ resize: vertical;
363
+ }
364
+
365
+ .textarea-resize-horizontal {
366
+ resize: horizontal;
367
+ }
368
+
369
+ .textarea-resize-both {
370
+ resize: both;
371
+ }
372
+
373
+ .textarea-resize-auto {
374
+ resize: none;
375
+ overflow: hidden;
376
+ }
377
+
378
+ /* Error state */
379
+ .textarea-error {
380
+ border-color: rgb(var(--ui-color-error));
381
+ }
382
+
383
+ .textarea-error:focus-visible {
384
+ border-color: rgb(var(--ui-color-error));
385
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-error) / 0.4);
386
+ }
387
+
388
+ :global([data-theme='dark']) .textarea-error {
389
+ border-color: rgb(var(--ui-color-error-light));
390
+ }
391
+
392
+ :global([data-theme='dark']) .textarea-error:focus-visible {
393
+ border-color: rgb(var(--ui-color-error-light));
394
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-error-light) / 0.5);
395
+ }
396
+
397
+ @media (prefers-color-scheme: dark) {
398
+ :global(:root:not([data-theme='light'])) .textarea-error {
399
+ border-color: rgb(var(--ui-color-error-light));
400
+ }
401
+
402
+ :global(:root:not([data-theme='light'])) .textarea-error:focus-visible {
403
+ border-color: rgb(var(--ui-color-error-light));
404
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-error-light) / 0.5);
405
+ }
406
+ }
407
+
408
+ /* Valid state */
409
+ .textarea-valid {
410
+ border-color: rgb(var(--ui-color-success));
411
+ }
412
+
413
+ .textarea-valid:focus-visible {
414
+ border-color: rgb(var(--ui-color-success));
415
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-success) / 0.4);
416
+ }
417
+
418
+ :global([data-theme='dark']) .textarea-valid {
419
+ border-color: rgb(var(--ui-color-success-light));
420
+ }
421
+
422
+ :global([data-theme='dark']) .textarea-valid:focus-visible {
423
+ border-color: rgb(var(--ui-color-success-light));
424
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-success-light) / 0.5);
425
+ }
426
+
427
+ @media (prefers-color-scheme: dark) {
428
+ :global(:root:not([data-theme='light'])) .textarea-valid {
429
+ border-color: rgb(var(--ui-color-success-light));
430
+ }
431
+
432
+ :global(:root:not([data-theme='light'])) .textarea-valid:focus-visible {
433
+ border-color: rgb(var(--ui-color-success-light));
434
+ box-shadow: 0 0 0 3px rgb(var(--ui-color-success-light) / 0.5);
435
+ }
436
+ }
437
+
438
+ /* ========================================
439
+ ICONS
440
+ ======================================== */
441
+ .textarea-icon {
442
+ position: absolute;
443
+ top: 0.75rem;
444
+ right: 0.75rem;
445
+ }
446
+
447
+ .textarea-icon-error {
448
+ color: rgb(var(--ui-color-error));
449
+ }
450
+
451
+ :global([data-theme='dark']) .textarea-icon-error {
452
+ color: rgb(var(--ui-color-error-light));
453
+ }
454
+
455
+ @media (prefers-color-scheme: dark) {
456
+ :global(:root:not([data-theme='light'])) .textarea-icon-error {
457
+ color: rgb(var(--ui-color-error-light));
458
+ }
459
+ }
460
+
461
+ .textarea-icon-success {
462
+ color: rgb(var(--ui-color-success));
463
+ }
464
+
465
+ :global([data-theme='dark']) .textarea-icon-success {
466
+ color: rgb(var(--ui-color-success-light));
467
+ }
468
+
469
+ @media (prefers-color-scheme: dark) {
470
+ :global(:root:not([data-theme='light'])) .textarea-icon-success {
471
+ color: rgb(var(--ui-color-success-light));
472
+ }
473
+ }
474
+
475
+ /* ========================================
476
+ FOOTER & MESSAGES
477
+ ======================================== */
478
+ .textarea-footer {
479
+ display: flex;
480
+ justify-content: space-between;
481
+ align-items: center;
482
+ min-height: 1.25rem;
483
+ }
484
+
485
+ .textarea-messages {
486
+ flex: 1;
487
+ }
488
+
489
+ .textarea-message {
490
+ font-size: 0.75rem;
491
+ animation: slide-in 0.2s ease-out;
492
+ }
493
+
494
+ .textarea-message-error {
495
+ color: rgb(var(--ui-color-error));
496
+ }
497
+
498
+ :global([data-theme='dark']) .textarea-message-error {
499
+ color: rgb(var(--ui-color-error-light));
500
+ }
501
+
502
+ @media (prefers-color-scheme: dark) {
503
+ :global(:root:not([data-theme='light'])) .textarea-message-error {
504
+ color: rgb(var(--ui-color-error-light));
505
+ }
506
+ }
507
+
508
+ .textarea-message-success {
509
+ color: rgb(var(--ui-color-success));
510
+ }
511
+
512
+ :global([data-theme='dark']) .textarea-message-success {
513
+ color: rgb(var(--ui-color-success-light));
514
+ }
515
+
516
+ @media (prefers-color-scheme: dark) {
517
+ :global(:root:not([data-theme='light'])) .textarea-message-success {
518
+ color: rgb(var(--ui-color-success-light));
519
+ }
520
+ }
521
+
522
+ .textarea-message-hint {
523
+ color: var(--ui-text-tertiary);
524
+ }
525
+
526
+ .textarea-char-count {
527
+ font-size: 0.75rem;
528
+ margin-left: 0.5rem;
529
+ color: var(--ui-text-tertiary);
530
+ font-variant-numeric: tabular-nums;
531
+ }
532
+
533
+ .textarea-char-count-warning {
534
+ color: rgb(var(--ui-color-warning));
535
+ }
536
+
537
+ :global([data-theme='dark']) .textarea-char-count-warning {
538
+ color: rgb(var(--ui-color-warning-light));
539
+ }
540
+
541
+ @media (prefers-color-scheme: dark) {
542
+ :global(:root:not([data-theme='light'])) .textarea-char-count-warning {
543
+ color: rgb(var(--ui-color-warning-light));
544
+ }
545
+ }
546
+
547
+ .textarea-char-count-error {
548
+ color: rgb(var(--ui-color-error));
549
+ font-weight: 500;
550
+ }
551
+
552
+ :global([data-theme='dark']) .textarea-char-count-error {
553
+ color: rgb(var(--ui-color-error-light));
554
+ }
555
+
556
+ @media (prefers-color-scheme: dark) {
557
+ :global(:root:not([data-theme='light'])) .textarea-char-count-error {
558
+ color: rgb(var(--ui-color-error-light));
559
+ }
560
+ }
561
+
562
+ /* ========================================
563
+ UTILITIES
564
+ ======================================== */
565
+ .sr-only {
566
+ position: absolute;
567
+ width: 1px;
568
+ height: 1px;
569
+ padding: 0;
570
+ margin: -1px;
571
+ overflow: hidden;
572
+ clip: rect(0, 0, 0, 0);
573
+ white-space: nowrap;
574
+ border: 0;
575
+ }
576
+
577
+ @keyframes slide-in {
578
+ from {
579
+ opacity: 0;
580
+ transform: translateY(-4px);
581
+ }
582
+ to {
583
+ opacity: 1;
584
+ transform: translateY(0);
585
+ }
586
+ }
587
+ </style>
@@ -0,0 +1,71 @@
1
+ type TextareaSize = 'sm' | 'md' | 'lg';
2
+ type TextareaResize = 'none' | 'vertical' | 'horizontal' | 'both' | 'auto';
3
+ interface Props {
4
+ /** Unique identifier for the textarea */
5
+ id?: string;
6
+ /** Form field name */
7
+ name?: string;
8
+ /** Label text */
9
+ label: string;
10
+ /** Current value */
11
+ value?: string;
12
+ /** Placeholder text */
13
+ placeholder?: string;
14
+ /** Number of visible rows */
15
+ rows?: number;
16
+ /** Whether the field is required */
17
+ required?: boolean;
18
+ /** Whether the field is disabled */
19
+ disabled?: boolean;
20
+ /** Whether the field is read-only */
21
+ readonly?: boolean;
22
+ /** Error message to display */
23
+ error?: string;
24
+ /** Hint text */
25
+ hint?: string;
26
+ /** Success message when valid */
27
+ successMessage?: string;
28
+ /** Maximum character length */
29
+ maxLength?: number;
30
+ /** Minimum character length */
31
+ minLength?: number;
32
+ /** Whether to show character count */
33
+ showCharCount?: boolean;
34
+ /** Resize behavior */
35
+ resize?: TextareaResize;
36
+ /** Size of the textarea */
37
+ size?: TextareaSize;
38
+ /** Whether to hide the label visually */
39
+ hideLabel?: boolean;
40
+ /** Additional CSS classes */
41
+ class?: string;
42
+ /** Input handler */
43
+ oninput?: (event: Event) => void;
44
+ /** Blur handler */
45
+ onblur?: (event: FocusEvent) => void;
46
+ /** Focus handler */
47
+ onfocus?: (event: FocusEvent) => void;
48
+ /** Show validation state immediately without waiting for interaction */
49
+ validateOnMount?: boolean;
50
+ }
51
+ /**
52
+ * Textarea
53
+ *
54
+ * A multi-line text input component for longer content like clinical notes,
55
+ * patient histories, and detailed descriptions. Supports auto-resize,
56
+ * character counting, and comprehensive accessibility.
57
+ *
58
+ * @example
59
+ * <Textarea
60
+ * id="clinical-notes"
61
+ * label="Clinical Notes"
62
+ * value={notes}
63
+ * placeholder="Enter clinical observations..."
64
+ * maxLength={2000}
65
+ * showCharCount
66
+ * />
67
+ */
68
+ declare const Textarea: import("svelte").Component<Props, {}, "">;
69
+ type Textarea = ReturnType<typeof Textarea>;
70
+ export default Textarea;
71
+ //# sourceMappingURL=Textarea.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Textarea.svelte.d.ts","sourceRoot":"","sources":["../../../src/forms/Textarea/Textarea.svelte.ts"],"names":[],"mappings":"AAQC,KAAK,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACvC,KAAK,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3E,UAAU,KAAK;IACd,yCAAyC;IACzC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sBAAsB;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,yCAAyC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,mBAAmB;IACnB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAuJF;;;;;;;;;;;;;;;;GAgBG;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 Textarea } from './Textarea.svelte';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/Textarea/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as Textarea } from './Textarea.svelte';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @synthaxai/ui - Form Components
3
+ *
4
+ * Accessible form components with validation support,
5
+ * designed for healthcare data entry workflows.
6
+ */
7
+ export { TextInput } from './TextInput/index.js';
8
+ export { Textarea } from './Textarea/index.js';
9
+ export { Select } from './Select/index.js';
10
+ export { Checkbox } from './Checkbox/index.js';
11
+ export { RadioGroup } from './RadioGroup/index.js';
12
+ export { FormField } from './FormField/index.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}