@aspect-ops/exon-ui 0.0.2 → 0.1.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 (237) hide show
  1. package/README.md +793 -43
  2. package/dist/components/Accordion/Accordion.svelte +79 -0
  3. package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
  4. package/dist/components/Accordion/AccordionItem.svelte +198 -0
  5. package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/Accordion/index.js +2 -0
  8. package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
  9. package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
  10. package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
  11. package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
  12. package/dist/components/ActionSheet/index.d.ts +3 -0
  13. package/dist/components/ActionSheet/index.js +2 -0
  14. package/dist/components/Alert/Alert.svelte +165 -0
  15. package/dist/components/Alert/Alert.svelte.d.ts +11 -0
  16. package/dist/components/Alert/index.d.ts +1 -0
  17. package/dist/components/Alert/index.js +1 -0
  18. package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
  19. package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
  20. package/dist/components/AspectRatio/index.d.ts +1 -0
  21. package/dist/components/AspectRatio/index.js +1 -0
  22. package/dist/components/Avatar/Avatar.svelte +147 -0
  23. package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
  24. package/dist/components/Avatar/AvatarGroup.svelte +153 -0
  25. package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
  26. package/dist/components/Avatar/index.d.ts +2 -0
  27. package/dist/components/Avatar/index.js +2 -0
  28. package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
  29. package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
  30. package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
  31. package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
  32. package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
  33. package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
  34. package/dist/components/BottomSheet/index.d.ts +3 -0
  35. package/dist/components/BottomSheet/index.js +3 -0
  36. package/dist/components/Box/Box.svelte +41 -0
  37. package/dist/components/Box/Box.svelte.d.ts +7 -0
  38. package/dist/components/Box/index.d.ts +1 -0
  39. package/dist/components/Box/index.js +1 -0
  40. package/dist/components/Card/Card.svelte +95 -0
  41. package/dist/components/Card/Card.svelte.d.ts +10 -0
  42. package/dist/components/Card/CardBody.svelte +32 -0
  43. package/dist/components/Card/CardBody.svelte.d.ts +7 -0
  44. package/dist/components/Card/CardFooter.svelte +34 -0
  45. package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
  46. package/dist/components/Card/CardHeader.svelte +67 -0
  47. package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
  48. package/dist/components/Card/index.d.ts +4 -0
  49. package/dist/components/Card/index.js +4 -0
  50. package/dist/components/Carousel/Carousel.svelte +454 -0
  51. package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
  52. package/dist/components/Carousel/CarouselSlide.svelte +22 -0
  53. package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
  54. package/dist/components/Carousel/index.d.ts +2 -0
  55. package/dist/components/Carousel/index.js +2 -0
  56. package/dist/components/Center/Center.svelte +28 -0
  57. package/dist/components/Center/Center.svelte.d.ts +8 -0
  58. package/dist/components/Center/index.d.ts +1 -0
  59. package/dist/components/Center/index.js +1 -0
  60. package/dist/components/Chip/Chip.svelte +461 -0
  61. package/dist/components/Chip/Chip.svelte.d.ts +17 -0
  62. package/dist/components/Chip/ChipGroup.svelte +76 -0
  63. package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
  64. package/dist/components/Chip/index.d.ts +2 -0
  65. package/dist/components/Chip/index.js +2 -0
  66. package/dist/components/Container/Container.svelte +58 -0
  67. package/dist/components/Container/Container.svelte.d.ts +10 -0
  68. package/dist/components/Container/index.d.ts +1 -0
  69. package/dist/components/Container/index.js +1 -0
  70. package/dist/components/DatePicker/DatePicker.svelte +746 -0
  71. package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
  72. package/dist/components/DatePicker/index.d.ts +1 -0
  73. package/dist/components/DatePicker/index.js +1 -0
  74. package/dist/components/Divider/Divider.svelte +38 -0
  75. package/dist/components/Divider/Divider.svelte.d.ts +9 -0
  76. package/dist/components/Divider/index.d.ts +1 -0
  77. package/dist/components/Divider/index.js +1 -0
  78. package/dist/components/EmptyState/EmptyState.svelte +164 -0
  79. package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
  80. package/dist/components/EmptyState/index.d.ts +1 -0
  81. package/dist/components/EmptyState/index.js +1 -0
  82. package/dist/components/FAB/FAB.svelte +242 -0
  83. package/dist/components/FAB/FAB.svelte.d.ts +9 -0
  84. package/dist/components/FAB/FABGroup.svelte +449 -0
  85. package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
  86. package/dist/components/FAB/index.d.ts +3 -0
  87. package/dist/components/FAB/index.js +2 -0
  88. package/dist/components/FileUpload/FileUpload.svelte +484 -0
  89. package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
  90. package/dist/components/FileUpload/index.d.ts +1 -0
  91. package/dist/components/FileUpload/index.js +1 -0
  92. package/dist/components/Grid/Grid.svelte +136 -0
  93. package/dist/components/Grid/Grid.svelte.d.ts +12 -0
  94. package/dist/components/Grid/GridItem.svelte +21 -0
  95. package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
  96. package/dist/components/Grid/index.d.ts +2 -0
  97. package/dist/components/Grid/index.js +2 -0
  98. package/dist/components/Image/Image.svelte +223 -0
  99. package/dist/components/Image/Image.svelte.d.ts +19 -0
  100. package/dist/components/Image/index.d.ts +1 -0
  101. package/dist/components/Image/index.js +1 -0
  102. package/dist/components/List/List.svelte +42 -0
  103. package/dist/components/List/List.svelte.d.ts +18 -0
  104. package/dist/components/List/ListItem.svelte +139 -0
  105. package/dist/components/List/ListItem.svelte.d.ts +36 -0
  106. package/dist/components/List/index.d.ts +2 -0
  107. package/dist/components/List/index.js +2 -0
  108. package/dist/components/Modal/Modal.svelte +204 -0
  109. package/dist/components/Modal/Modal.svelte.d.ts +7 -0
  110. package/dist/components/Modal/ModalBody.svelte +50 -0
  111. package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
  112. package/dist/components/Modal/ModalFooter.svelte +37 -0
  113. package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
  114. package/dist/components/Modal/ModalHeader.svelte +73 -0
  115. package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
  116. package/dist/components/Modal/index.d.ts +4 -0
  117. package/dist/components/Modal/index.js +4 -0
  118. package/dist/components/OTPInput/OTPInput.svelte +312 -0
  119. package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
  120. package/dist/components/OTPInput/index.d.ts +1 -0
  121. package/dist/components/OTPInput/index.js +1 -0
  122. package/dist/components/Popover/Popover.svelte +14 -0
  123. package/dist/components/Popover/Popover.svelte.d.ts +7 -0
  124. package/dist/components/Popover/PopoverContent.svelte +63 -0
  125. package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
  126. package/dist/components/Popover/PopoverTrigger.svelte +14 -0
  127. package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
  128. package/dist/components/Popover/index.d.ts +3 -0
  129. package/dist/components/Popover/index.js +3 -0
  130. package/dist/components/Progress/ProgressBar.svelte +86 -0
  131. package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
  132. package/dist/components/Progress/ProgressCircle.svelte +134 -0
  133. package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
  134. package/dist/components/Progress/Spinner.svelte +68 -0
  135. package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
  136. package/dist/components/Progress/index.d.ts +3 -0
  137. package/dist/components/Progress/index.js +3 -0
  138. package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
  139. package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
  140. package/dist/components/PullToRefresh/index.d.ts +1 -0
  141. package/dist/components/PullToRefresh/index.js +1 -0
  142. package/dist/components/Rating/Rating.svelte +316 -0
  143. package/dist/components/Rating/Rating.svelte.d.ts +16 -0
  144. package/dist/components/Rating/index.d.ts +1 -0
  145. package/dist/components/Rating/index.js +1 -0
  146. package/dist/components/SafeArea/SafeArea.svelte +33 -0
  147. package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
  148. package/dist/components/SearchInput/SearchInput.svelte +480 -0
  149. package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
  150. package/dist/components/SearchInput/index.d.ts +1 -0
  151. package/dist/components/SearchInput/index.js +1 -0
  152. package/dist/components/Select/Select.svelte +55 -12
  153. package/dist/components/Skeleton/Skeleton.svelte +59 -0
  154. package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
  155. package/dist/components/Skeleton/index.d.ts +1 -0
  156. package/dist/components/Skeleton/index.js +1 -0
  157. package/dist/components/Slider/Slider.svelte +324 -0
  158. package/dist/components/Slider/Slider.svelte.d.ts +14 -0
  159. package/dist/components/Slider/index.d.ts +1 -0
  160. package/dist/components/Slider/index.js +1 -0
  161. package/dist/components/Spacer/Spacer.svelte +56 -0
  162. package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
  163. package/dist/components/Spacer/index.d.ts +1 -0
  164. package/dist/components/Spacer/index.js +1 -0
  165. package/dist/components/Stack/Stack.svelte +117 -0
  166. package/dist/components/Stack/Stack.svelte.d.ts +13 -0
  167. package/dist/components/Stack/index.d.ts +1 -0
  168. package/dist/components/Stack/index.js +1 -0
  169. package/dist/components/Stepper/Stepper.svelte +100 -0
  170. package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
  171. package/dist/components/Stepper/StepperStep.svelte +391 -0
  172. package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
  173. package/dist/components/Stepper/index.d.ts +2 -0
  174. package/dist/components/Stepper/index.js +2 -0
  175. package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
  176. package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
  177. package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
  178. package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
  179. package/dist/components/SwipeActions/index.d.ts +2 -0
  180. package/dist/components/SwipeActions/index.js +2 -0
  181. package/dist/components/Switch/Switch.svelte +29 -9
  182. package/dist/components/Table/Table.svelte +175 -0
  183. package/dist/components/Table/Table.svelte.d.ts +38 -0
  184. package/dist/components/Table/TableBody.svelte +26 -0
  185. package/dist/components/Table/TableBody.svelte.d.ts +13 -0
  186. package/dist/components/Table/TableCell.svelte +85 -0
  187. package/dist/components/Table/TableCell.svelte.d.ts +28 -0
  188. package/dist/components/Table/TableHead.svelte +36 -0
  189. package/dist/components/Table/TableHead.svelte.d.ts +13 -0
  190. package/dist/components/Table/TableHeader.svelte +217 -0
  191. package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
  192. package/dist/components/Table/TableRow.svelte +92 -0
  193. package/dist/components/Table/TableRow.svelte.d.ts +28 -0
  194. package/dist/components/Table/index.d.ts +6 -0
  195. package/dist/components/Table/index.js +6 -0
  196. package/dist/components/Tag/Tag.svelte +189 -0
  197. package/dist/components/Tag/Tag.svelte.d.ts +13 -0
  198. package/dist/components/Tag/index.d.ts +1 -0
  199. package/dist/components/Tag/index.js +1 -0
  200. package/dist/components/TimePicker/TimePicker.svelte +803 -0
  201. package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
  202. package/dist/components/TimePicker/index.d.ts +1 -0
  203. package/dist/components/TimePicker/index.js +1 -0
  204. package/dist/components/Toast/Toast.svelte +241 -0
  205. package/dist/components/Toast/Toast.svelte.d.ts +18 -0
  206. package/dist/components/Toast/ToastContainer.svelte +110 -0
  207. package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
  208. package/dist/components/Toast/index.d.ts +3 -0
  209. package/dist/components/Toast/index.js +3 -0
  210. package/dist/components/Toast/toast.d.ts +13 -0
  211. package/dist/components/Toast/toast.js +55 -0
  212. package/dist/components/Tooltip/Tooltip.svelte +71 -0
  213. package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
  214. package/dist/components/Tooltip/index.d.ts +2 -0
  215. package/dist/components/Tooltip/index.js +1 -0
  216. package/dist/index.d.ts +38 -1
  217. package/dist/index.js +41 -0
  218. package/dist/styles/tokens.css +5 -0
  219. package/dist/types/data-display.d.ts +161 -0
  220. package/dist/types/data-display.js +1 -0
  221. package/dist/types/feedback.d.ts +92 -0
  222. package/dist/types/feedback.js +1 -0
  223. package/dist/types/index.d.ts +6 -1
  224. package/dist/types/input.d.ts +67 -0
  225. package/dist/types/input.js +2 -0
  226. package/dist/types/layout.d.ts +57 -0
  227. package/dist/types/layout.js +1 -0
  228. package/dist/types/mobile.d.ts +91 -0
  229. package/dist/types/mobile.js +1 -0
  230. package/dist/types/navigation.d.ts +15 -0
  231. package/dist/utils/gestures.d.ts +219 -0
  232. package/dist/utils/gestures.js +492 -0
  233. package/dist/utils/haptics.d.ts +89 -0
  234. package/dist/utils/haptics.js +198 -0
  235. package/dist/utils/platform.d.ts +47 -0
  236. package/dist/utils/platform.js +156 -0
  237. package/package.json +1 -1
@@ -0,0 +1,480 @@
1
+ <script lang="ts">
2
+ import type { InputSize } from '../../types/index.js';
3
+
4
+ interface Props {
5
+ value?: string;
6
+ placeholder?: string;
7
+ suggestions?: string[];
8
+ size?: InputSize;
9
+ disabled?: boolean;
10
+ loading?: boolean;
11
+ maxSuggestions?: number;
12
+ class?: string;
13
+ oninput?: (e: Event & { currentTarget: HTMLInputElement }) => void;
14
+ onchange?: (e: Event & { currentTarget: HTMLInputElement }) => void;
15
+ onselect?: (value: string) => void;
16
+ onclear?: () => void;
17
+ }
18
+
19
+ let {
20
+ value = $bindable(''),
21
+ placeholder = 'Search...',
22
+ suggestions = [],
23
+ size = 'md',
24
+ disabled = false,
25
+ loading = false,
26
+ maxSuggestions = 5,
27
+ class: className = '',
28
+ oninput,
29
+ onchange,
30
+ onselect,
31
+ onclear
32
+ }: Props = $props();
33
+
34
+ let isOpen = $state(false);
35
+ let highlightedIndex = $state(-1);
36
+ let inputRef: HTMLInputElement;
37
+ let dropdownRef = $state<HTMLDivElement>();
38
+
39
+ // Filter and limit suggestions
40
+ const filteredSuggestions = $derived(() => {
41
+ if (!value.trim() || !suggestions.length) return [];
42
+ const filtered = suggestions.filter((suggestion) =>
43
+ suggestion.toLowerCase().includes(value.toLowerCase())
44
+ );
45
+ return filtered.slice(0, maxSuggestions);
46
+ });
47
+
48
+ const hasSuggestions = $derived(filteredSuggestions().length > 0);
49
+ const showClearButton = $derived(value.length > 0 && !disabled);
50
+ const showDropdown = $derived(isOpen && hasSuggestions && !disabled);
51
+
52
+ // Handle input changes
53
+ function handleInput(e: Event & { currentTarget: HTMLInputElement }) {
54
+ value = e.currentTarget.value;
55
+ isOpen = true;
56
+ highlightedIndex = -1;
57
+ oninput?.(e);
58
+ }
59
+
60
+ // Handle clear button click
61
+ function handleClear() {
62
+ value = '';
63
+ isOpen = false;
64
+ highlightedIndex = -1;
65
+ inputRef?.focus();
66
+ onclear?.();
67
+ }
68
+
69
+ // Handle suggestion selection
70
+ function selectSuggestion(suggestion: string) {
71
+ value = suggestion;
72
+ isOpen = false;
73
+ highlightedIndex = -1;
74
+ onselect?.(suggestion);
75
+ }
76
+
77
+ // Handle keyboard navigation
78
+ function handleKeyDown(e: KeyboardEvent) {
79
+ const suggestions = filteredSuggestions();
80
+
81
+ if (!suggestions.length) return;
82
+
83
+ switch (e.key) {
84
+ case 'ArrowDown':
85
+ e.preventDefault();
86
+ highlightedIndex = Math.min(highlightedIndex + 1, suggestions.length - 1);
87
+ isOpen = true;
88
+ break;
89
+ case 'ArrowUp':
90
+ e.preventDefault();
91
+ highlightedIndex = Math.max(highlightedIndex - 1, -1);
92
+ break;
93
+ case 'Enter':
94
+ e.preventDefault();
95
+ if (highlightedIndex >= 0 && highlightedIndex < suggestions.length) {
96
+ selectSuggestion(suggestions[highlightedIndex]);
97
+ }
98
+ break;
99
+ case 'Escape':
100
+ e.preventDefault();
101
+ isOpen = false;
102
+ highlightedIndex = -1;
103
+ inputRef?.blur();
104
+ break;
105
+ }
106
+ }
107
+
108
+ // Handle click outside to close dropdown
109
+ function handleClickOutside(e: MouseEvent) {
110
+ const target = e.target as Node;
111
+ if (inputRef && !inputRef.contains(target) && dropdownRef && !dropdownRef.contains(target)) {
112
+ isOpen = false;
113
+ highlightedIndex = -1;
114
+ }
115
+ }
116
+
117
+ // Highlight matching text in suggestions
118
+ function highlightMatch(text: string, query: string): string {
119
+ if (!query) return text;
120
+ const regex = new RegExp(`(${query})`, 'gi');
121
+ return text.replace(regex, '<mark>$1</mark>');
122
+ }
123
+
124
+ $effect(() => {
125
+ if (showDropdown) {
126
+ document.addEventListener('click', handleClickOutside);
127
+ return () => {
128
+ document.removeEventListener('click', handleClickOutside);
129
+ };
130
+ }
131
+ });
132
+ </script>
133
+
134
+ <div
135
+ class="search-input search-input--{size} {disabled ? 'search-input--disabled' : ''} {className}"
136
+ >
137
+ <div class="search-input__wrapper">
138
+ <!-- Search Icon -->
139
+ <span class="search-input__icon search-input__icon--search" aria-hidden="true">
140
+ <svg
141
+ width="20"
142
+ height="20"
143
+ viewBox="0 0 20 20"
144
+ fill="none"
145
+ xmlns="http://www.w3.org/2000/svg"
146
+ >
147
+ <path
148
+ d="M9 17A8 8 0 1 0 9 1a8 8 0 0 0 0 16zM18 18l-4.35-4.35"
149
+ stroke="currentColor"
150
+ stroke-width="2"
151
+ stroke-linecap="round"
152
+ stroke-linejoin="round"
153
+ />
154
+ </svg>
155
+ </span>
156
+
157
+ <!-- Input Field -->
158
+ <input
159
+ bind:this={inputRef}
160
+ type="search"
161
+ class="search-input__field"
162
+ bind:value
163
+ {placeholder}
164
+ {disabled}
165
+ role="combobox"
166
+ aria-autocomplete="list"
167
+ aria-expanded={showDropdown}
168
+ aria-controls="search-suggestions"
169
+ aria-activedescendant={highlightedIndex >= 0 ? `suggestion-${highlightedIndex}` : undefined}
170
+ oninput={handleInput}
171
+ {onchange}
172
+ onkeydown={handleKeyDown}
173
+ onfocus={() => {
174
+ if (value && filteredSuggestions().length > 0) {
175
+ isOpen = true;
176
+ }
177
+ }}
178
+ />
179
+
180
+ <!-- Loading Spinner -->
181
+ {#if loading}
182
+ <span class="search-input__icon search-input__icon--trailing" aria-hidden="true">
183
+ <svg
184
+ class="search-input__spinner"
185
+ width="20"
186
+ height="20"
187
+ viewBox="0 0 20 20"
188
+ fill="none"
189
+ xmlns="http://www.w3.org/2000/svg"
190
+ >
191
+ <circle
192
+ cx="10"
193
+ cy="10"
194
+ r="8"
195
+ stroke="currentColor"
196
+ stroke-width="2"
197
+ stroke-linecap="round"
198
+ stroke-dasharray="40 40"
199
+ stroke-dashoffset="0"
200
+ />
201
+ </svg>
202
+ </span>
203
+ {/if}
204
+
205
+ <!-- Clear Button -->
206
+ {#if showClearButton && !loading}
207
+ <button
208
+ type="button"
209
+ class="search-input__clear"
210
+ aria-label="Clear search"
211
+ onclick={handleClear}
212
+ >
213
+ <svg
214
+ width="20"
215
+ height="20"
216
+ viewBox="0 0 20 20"
217
+ fill="none"
218
+ xmlns="http://www.w3.org/2000/svg"
219
+ >
220
+ <path
221
+ d="M15 5L5 15M5 5l10 10"
222
+ stroke="currentColor"
223
+ stroke-width="2"
224
+ stroke-linecap="round"
225
+ stroke-linejoin="round"
226
+ />
227
+ </svg>
228
+ </button>
229
+ {/if}
230
+ </div>
231
+
232
+ <!-- Suggestions Dropdown -->
233
+ {#if showDropdown}
234
+ <div
235
+ bind:this={dropdownRef}
236
+ class="search-input__suggestions"
237
+ id="search-suggestions"
238
+ role="listbox"
239
+ >
240
+ {#each filteredSuggestions() as suggestion, index}
241
+ <button
242
+ type="button"
243
+ class="search-input__suggestion"
244
+ class:search-input__suggestion--highlighted={index === highlightedIndex}
245
+ id="suggestion-{index}"
246
+ role="option"
247
+ aria-selected={index === highlightedIndex}
248
+ onclick={() => selectSuggestion(suggestion)}
249
+ onmouseenter={() => {
250
+ highlightedIndex = index;
251
+ }}
252
+ >
253
+ {@html highlightMatch(suggestion, value)}
254
+ </button>
255
+ {/each}
256
+ </div>
257
+ {/if}
258
+ </div>
259
+
260
+ <style>
261
+ .search-input {
262
+ position: relative;
263
+ display: flex;
264
+ flex-direction: column;
265
+ width: 100%;
266
+ }
267
+
268
+ .search-input--disabled {
269
+ opacity: 0.5;
270
+ }
271
+
272
+ .search-input__wrapper {
273
+ position: relative;
274
+ display: flex;
275
+ align-items: center;
276
+ width: 100%;
277
+ }
278
+
279
+ .search-input__field {
280
+ width: 100%;
281
+ min-height: var(--touch-target-min, 44px);
282
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
283
+ padding-left: calc(var(--space-md, 1rem) * 2 + 1.25rem);
284
+ padding-right: calc(var(--space-md, 1rem) * 2 + 1.25rem);
285
+ border: 1px solid var(--color-border, #e5e7eb);
286
+ border-radius: var(--radius-md, 0.375rem);
287
+ background: var(--color-bg, #ffffff);
288
+ color: var(--color-text, #1f2937);
289
+ font-family: inherit;
290
+ font-size: var(--text-sm, 0.875rem);
291
+ line-height: 1.5;
292
+ transition:
293
+ border-color var(--transition-fast, 150ms ease),
294
+ box-shadow var(--transition-fast, 150ms ease);
295
+ -webkit-tap-highlight-color: transparent;
296
+ }
297
+
298
+ .search-input__field::placeholder {
299
+ color: var(--color-text-muted, #9ca3af);
300
+ }
301
+
302
+ .search-input__field:focus {
303
+ outline: none;
304
+ border-color: var(--color-primary, #3b82f6);
305
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
306
+ }
307
+
308
+ .search-input__field:disabled {
309
+ background: var(--color-bg-muted, #f3f4f6);
310
+ cursor: not-allowed;
311
+ }
312
+
313
+ /* Remove default search input clear button */
314
+ .search-input__field::-webkit-search-cancel-button {
315
+ display: none;
316
+ }
317
+
318
+ /* Sizes */
319
+ .search-input--sm .search-input__field {
320
+ min-height: var(--touch-target-min, 44px);
321
+ padding: var(--space-xs, 0.25rem) var(--space-sm, 0.5rem);
322
+ padding-left: calc(var(--space-sm, 0.5rem) * 2 + 1.25rem);
323
+ padding-right: calc(var(--space-sm, 0.5rem) * 2 + 1.25rem);
324
+ font-size: var(--text-xs, 0.75rem);
325
+ }
326
+
327
+ .search-input--md .search-input__field {
328
+ min-height: var(--touch-target-min, 44px);
329
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
330
+ padding-left: calc(var(--space-md, 1rem) * 2 + 1.25rem);
331
+ padding-right: calc(var(--space-md, 1rem) * 2 + 1.25rem);
332
+ font-size: var(--text-sm, 0.875rem);
333
+ }
334
+
335
+ .search-input--lg .search-input__field {
336
+ min-height: 3.25rem;
337
+ padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
338
+ padding-left: calc(var(--space-lg, 1.5rem) * 2 + 1.5rem);
339
+ padding-right: calc(var(--space-lg, 1.5rem) * 2 + 1.5rem);
340
+ font-size: var(--text-base, 1rem);
341
+ }
342
+
343
+ /* Icons */
344
+ .search-input__icon {
345
+ position: absolute;
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ color: var(--color-text-muted, #6b7280);
350
+ pointer-events: none;
351
+ }
352
+
353
+ .search-input__icon--search {
354
+ left: var(--space-md, 1rem);
355
+ }
356
+
357
+ .search-input__icon--trailing {
358
+ right: var(--space-md, 1rem);
359
+ }
360
+
361
+ .search-input--sm .search-input__icon--search {
362
+ left: var(--space-sm, 0.5rem);
363
+ }
364
+
365
+ .search-input--sm .search-input__icon--trailing {
366
+ right: var(--space-sm, 0.5rem);
367
+ }
368
+
369
+ .search-input--lg .search-input__icon--search {
370
+ left: var(--space-lg, 1.5rem);
371
+ }
372
+
373
+ .search-input--lg .search-input__icon--trailing {
374
+ right: var(--space-lg, 1.5rem);
375
+ }
376
+
377
+ /* Loading Spinner */
378
+ .search-input__spinner {
379
+ animation: spin 1s linear infinite;
380
+ }
381
+
382
+ @keyframes spin {
383
+ from {
384
+ transform: rotate(0deg);
385
+ }
386
+ to {
387
+ transform: rotate(360deg);
388
+ }
389
+ }
390
+
391
+ /* Clear Button */
392
+ .search-input__clear {
393
+ position: absolute;
394
+ right: var(--space-md, 1rem);
395
+ display: flex;
396
+ align-items: center;
397
+ justify-content: center;
398
+ min-width: var(--touch-target-min, 44px);
399
+ min-height: var(--touch-target-min, 44px);
400
+ padding: 0;
401
+ border: none;
402
+ background: transparent;
403
+ color: var(--color-text-muted, #6b7280);
404
+ cursor: pointer;
405
+ transition: color var(--transition-fast, 150ms ease);
406
+ -webkit-tap-highlight-color: transparent;
407
+ }
408
+
409
+ .search-input__clear:hover {
410
+ color: var(--color-text, #1f2937);
411
+ }
412
+
413
+ .search-input__clear:focus {
414
+ outline: none;
415
+ color: var(--color-primary, #3b82f6);
416
+ }
417
+
418
+ .search-input--sm .search-input__clear {
419
+ right: var(--space-sm, 0.5rem);
420
+ }
421
+
422
+ .search-input--lg .search-input__clear {
423
+ right: var(--space-lg, 1.5rem);
424
+ }
425
+
426
+ /* Suggestions Dropdown */
427
+ .search-input__suggestions {
428
+ position: absolute;
429
+ top: calc(100% + var(--space-xs, 0.25rem));
430
+ left: 0;
431
+ right: 0;
432
+ z-index: var(--z-dropdown, 100);
433
+ max-height: 300px;
434
+ overflow-y: auto;
435
+ border: 1px solid var(--color-border, #e5e7eb);
436
+ border-radius: var(--radius-md, 0.375rem);
437
+ background: var(--color-bg-elevated, #ffffff);
438
+ box-shadow: var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
439
+ }
440
+
441
+ .search-input__suggestion {
442
+ display: block;
443
+ width: 100%;
444
+ min-height: var(--touch-target-min, 44px);
445
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
446
+ border: none;
447
+ background: transparent;
448
+ color: var(--color-text, #1f2937);
449
+ font-family: inherit;
450
+ font-size: var(--text-sm, 0.875rem);
451
+ line-height: 1.5;
452
+ text-align: left;
453
+ cursor: pointer;
454
+ transition: background-color var(--transition-fast, 150ms ease);
455
+ -webkit-tap-highlight-color: transparent;
456
+ }
457
+
458
+ .search-input__suggestion:hover,
459
+ .search-input__suggestion--highlighted {
460
+ background: var(--color-bg-hover, #f3f4f6);
461
+ }
462
+
463
+ .search-input__suggestion:active {
464
+ background: var(--color-bg-active, #e5e7eb);
465
+ }
466
+
467
+ /* Highlight matched text */
468
+ .search-input__suggestion :global(mark) {
469
+ background: var(--color-primary-bg, #e6f4fe);
470
+ color: var(--color-primary, #3b82f6);
471
+ font-weight: var(--font-medium, 500);
472
+ }
473
+
474
+ /* Mobile optimizations */
475
+ @media (max-width: 640px) {
476
+ .search-input__suggestions {
477
+ max-height: 200px;
478
+ }
479
+ }
480
+ </style>
@@ -0,0 +1,22 @@
1
+ import type { InputSize } from '../../types/index.js';
2
+ interface Props {
3
+ value?: string;
4
+ placeholder?: string;
5
+ suggestions?: string[];
6
+ size?: InputSize;
7
+ disabled?: boolean;
8
+ loading?: boolean;
9
+ maxSuggestions?: number;
10
+ class?: string;
11
+ oninput?: (e: Event & {
12
+ currentTarget: HTMLInputElement;
13
+ }) => void;
14
+ onchange?: (e: Event & {
15
+ currentTarget: HTMLInputElement;
16
+ }) => void;
17
+ onselect?: (value: string) => void;
18
+ onclear?: () => void;
19
+ }
20
+ declare const SearchInput: import("svelte").Component<Props, {}, "value">;
21
+ type SearchInput = ReturnType<typeof SearchInput>;
22
+ export default SearchInput;
@@ -0,0 +1 @@
1
+ export { default as SearchInput } from './SearchInput.svelte';
@@ -0,0 +1 @@
1
+ export { default as SearchInput } from './SearchInput.svelte';
@@ -79,7 +79,7 @@
79
79
  </SelectPrimitive.Trigger>
80
80
 
81
81
  <SelectPrimitive.Portal>
82
- <SelectPrimitive.Content class="select__content" sideOffset={4}>
82
+ <SelectPrimitive.Content class="select__content" sideOffset={4} align="start">
83
83
  <SelectPrimitive.Viewport class="select__viewport">
84
84
  {#each options as option (option.value)}
85
85
  <SelectPrimitive.Item
@@ -195,46 +195,89 @@
195
195
 
196
196
  :global(.select__content) {
197
197
  z-index: 50;
198
- min-width: 8rem;
199
198
  overflow: hidden;
200
199
  border: 1px solid var(--color-border, #e5e7eb);
201
200
  border-radius: var(--radius-md, 0.375rem);
202
201
  background: var(--color-bg, #ffffff);
203
- box-shadow: var(--shadow-md, 0 4px 6px rgba(0, 0, 0, 0.1));
202
+ box-shadow: var(--shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));
204
203
  font-family: var(--font-family, system-ui, -apple-system, sans-serif);
204
+ /* Match trigger width - bits-ui provides this CSS variable */
205
+ min-width: var(--bits-floating-anchor-width, 200px);
206
+ width: var(--bits-floating-anchor-width, auto);
207
+ }
208
+
209
+ :global(.select__content[data-state='open']) {
210
+ animation: select-in 150ms ease-out;
211
+ }
212
+
213
+ :global(.select__content[data-state='closed']) {
214
+ animation: select-out 100ms ease-in;
215
+ }
216
+
217
+ @keyframes -global-select-in {
218
+ from {
219
+ opacity: 0;
220
+ transform: translateY(-4px);
221
+ }
222
+ to {
223
+ opacity: 1;
224
+ transform: translateY(0);
225
+ }
226
+ }
227
+
228
+ @keyframes -global-select-out {
229
+ from {
230
+ opacity: 1;
231
+ transform: translateY(0);
232
+ }
233
+ to {
234
+ opacity: 0;
235
+ transform: translateY(-4px);
236
+ }
205
237
  }
206
238
 
207
239
  :global(.select__viewport) {
208
- padding: var(--space-xs, 0.25rem);
240
+ padding: var(--space-xs, 0.25rem) 0;
241
+ max-height: 20rem;
242
+ overflow-y: auto;
209
243
  }
210
244
 
211
245
  :global(.select__item) {
212
246
  display: flex;
213
247
  align-items: center;
214
248
  justify-content: space-between;
215
- padding: 0.375rem 0.5rem;
216
- border-radius: var(--radius-sm, 0.25rem);
249
+ padding: 0.625rem 1rem;
217
250
  font-size: var(--text-sm, 0.875rem);
218
- line-height: 1.25;
251
+ line-height: 1.5;
219
252
  color: var(--color-text, #1f2937);
220
253
  cursor: pointer;
221
254
  outline: none;
222
- transition: background var(--transition-fast, 150ms ease);
255
+ transition:
256
+ background var(--transition-fast, 150ms ease),
257
+ color var(--transition-fast, 150ms ease);
223
258
  }
224
259
 
225
260
  :global(.select__item:hover),
226
261
  :global(.select__item[data-highlighted]) {
227
- background: var(--color-bg-muted, #f3f4f6);
262
+ background: var(--color-primary, #3b82f6);
263
+ color: var(--color-text-inverse, #ffffff);
228
264
  }
229
265
 
230
266
  :global(.select__item[data-selected]) {
231
- background: var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
232
- color: var(--color-primary, #3b82f6);
267
+ background: var(--color-primary, #3b82f6);
268
+ color: var(--color-text-inverse, #ffffff);
233
269
  }
234
270
 
235
271
  :global(.select__item[data-disabled]) {
236
272
  opacity: 0.5;
237
273
  cursor: not-allowed;
274
+ background: transparent;
275
+ color: var(--color-text-muted, #9ca3af);
276
+ }
277
+
278
+ :global(.select__item[data-disabled]:hover) {
279
+ background: transparent;
280
+ color: var(--color-text-muted, #9ca3af);
238
281
  }
239
282
 
240
283
  .select__item-text {
@@ -245,7 +288,7 @@
245
288
  display: flex;
246
289
  align-items: center;
247
290
  justify-content: center;
248
- color: var(--color-primary, #3b82f6);
291
+ color: currentColor;
249
292
  margin-left: var(--space-sm, 0.5rem);
250
293
  }
251
294
  </style>
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ import type { SkeletonProps } from '../../types/index.js';
3
+
4
+ interface Props extends SkeletonProps {
5
+ variant?: 'text' | 'circle' | 'rectangle';
6
+ width?: string;
7
+ height?: string;
8
+ class?: string;
9
+ }
10
+
11
+ let { variant = 'text', width, height, class: className = '' }: Props = $props();
12
+
13
+ // Determine default dimensions based on variant
14
+ const defaultWidth = $derived(
15
+ variant === 'text' ? '100%' : variant === 'circle' ? '2.5rem' : '100%'
16
+ );
17
+ const defaultHeight = $derived(
18
+ variant === 'text' ? '1rem' : variant === 'circle' ? '2.5rem' : '8rem'
19
+ );
20
+
21
+ // Use provided dimensions or defaults
22
+ const finalWidth = $derived(width ?? defaultWidth);
23
+ const finalHeight = $derived(height ?? defaultHeight);
24
+
25
+ // Border radius based on variant
26
+ const borderRadius = $derived(
27
+ variant === 'circle'
28
+ ? '50%'
29
+ : variant === 'text'
30
+ ? 'var(--exon-radius-sm, 0.25rem)'
31
+ : 'var(--exon-radius-md, 0.375rem)'
32
+ );
33
+ </script>
34
+
35
+ <div
36
+ class="skeleton {className}"
37
+ aria-hidden="true"
38
+ role="presentation"
39
+ style="width: {finalWidth}; height: {finalHeight}; border-radius: {borderRadius};"
40
+ ></div>
41
+
42
+ <style>
43
+ .skeleton {
44
+ display: block;
45
+ background: var(--exon-skeleton-bg, var(--color-neutral-200, #e5e7eb));
46
+ font-family: inherit;
47
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
48
+ }
49
+
50
+ @keyframes skeleton-pulse {
51
+ 0%,
52
+ 100% {
53
+ opacity: 1;
54
+ }
55
+ 50% {
56
+ opacity: 0.5;
57
+ }
58
+ }
59
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { SkeletonProps } from '../../types/index.js';
2
+ interface Props extends SkeletonProps {
3
+ variant?: 'text' | 'circle' | 'rectangle';
4
+ width?: string;
5
+ height?: string;
6
+ class?: string;
7
+ }
8
+ declare const Skeleton: import("svelte").Component<Props, {}, "">;
9
+ type Skeleton = ReturnType<typeof Skeleton>;
10
+ export default Skeleton;
@@ -0,0 +1 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';
@@ -0,0 +1 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';