@getmicdrop/svelte-components 5.5.5 → 5.6.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 (266) hide show
  1. package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
  3. package/dist/calendar/FAQs/FAQs.svelte +75 -75
  4. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
  5. package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
  6. package/dist/calendar/PublicCard/PublicCard.svelte +134 -134
  7. package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
  8. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
  9. package/dist/components/Layout/AppShell.svelte +104 -0
  10. package/dist/components/Layout/AppShell.svelte.d.ts +26 -0
  11. package/dist/components/Layout/AppShell.svelte.d.ts.map +1 -0
  12. package/dist/components/Layout/ContentSection.svelte +80 -0
  13. package/dist/components/Layout/ContentSection.svelte.d.ts +23 -0
  14. package/dist/components/Layout/ContentSection.svelte.d.ts.map +1 -0
  15. package/dist/components/Layout/Grid.svelte +4 -4
  16. package/dist/components/Layout/Heading.svelte +81 -0
  17. package/dist/components/Layout/Heading.svelte.d.ts +24 -0
  18. package/dist/components/Layout/Heading.svelte.d.ts.map +1 -0
  19. package/dist/components/Layout/PageContainer.svelte +69 -0
  20. package/dist/components/Layout/PageContainer.svelte.d.ts +23 -0
  21. package/dist/components/Layout/PageContainer.svelte.d.ts.map +1 -0
  22. package/dist/components/Layout/Responsive.svelte +75 -0
  23. package/dist/components/Layout/Responsive.svelte.d.ts +19 -0
  24. package/dist/components/Layout/Responsive.svelte.d.ts.map +1 -0
  25. package/dist/components/Layout/Section.svelte +80 -80
  26. package/dist/components/Layout/ShowOnDesktop.svelte +37 -0
  27. package/dist/components/Layout/ShowOnDesktop.svelte.d.ts +16 -0
  28. package/dist/components/Layout/ShowOnDesktop.svelte.d.ts.map +1 -0
  29. package/dist/components/Layout/ShowOnMobile.svelte +37 -0
  30. package/dist/components/Layout/ShowOnMobile.svelte.d.ts +16 -0
  31. package/dist/components/Layout/ShowOnMobile.svelte.d.ts.map +1 -0
  32. package/dist/components/Layout/Sidebar.svelte +108 -108
  33. package/dist/components/Layout/Stack.spec.js +1 -1
  34. package/dist/components/Layout/Stack.svelte +6 -6
  35. package/dist/components/Layout/Text.svelte +87 -0
  36. package/dist/components/Layout/Text.svelte.d.ts +28 -0
  37. package/dist/components/Layout/Text.svelte.d.ts.map +1 -0
  38. package/dist/components/Layout/TwoColumn.svelte +108 -0
  39. package/dist/components/Layout/TwoColumn.svelte.d.ts +28 -0
  40. package/dist/components/Layout/TwoColumn.svelte.d.ts.map +1 -0
  41. package/dist/components/Layout/__tests__/Heading.test.d.ts +2 -0
  42. package/dist/components/Layout/__tests__/Heading.test.d.ts.map +1 -0
  43. package/dist/components/Layout/__tests__/Heading.test.js +123 -0
  44. package/dist/components/Layout/__tests__/ShowOnDesktop.test.d.ts +2 -0
  45. package/dist/components/Layout/__tests__/ShowOnDesktop.test.d.ts.map +1 -0
  46. package/dist/components/Layout/__tests__/ShowOnDesktop.test.js +84 -0
  47. package/dist/components/Layout/__tests__/ShowOnMobile.test.d.ts +2 -0
  48. package/dist/components/Layout/__tests__/ShowOnMobile.test.d.ts.map +1 -0
  49. package/dist/components/Layout/__tests__/ShowOnMobile.test.js +80 -0
  50. package/dist/components/Layout/__tests__/Text.test.d.ts +2 -0
  51. package/dist/components/Layout/__tests__/Text.test.d.ts.map +1 -0
  52. package/dist/components/Layout/__tests__/Text.test.js +146 -0
  53. package/dist/components/Layout/__tests__/TwoColumn.test.d.ts +2 -0
  54. package/dist/components/Layout/__tests__/TwoColumn.test.d.ts.map +1 -0
  55. package/dist/components/Layout/__tests__/TwoColumn.test.js +129 -0
  56. package/dist/constants/validation.js +91 -91
  57. package/dist/constants/validation.spec.js +64 -64
  58. package/dist/datetime/__tests__/format.test.js +1 -1
  59. package/dist/datetime/__tests__/parse.test.js +1 -1
  60. package/dist/datetime/__tests__/timezone.test.js +1 -1
  61. package/dist/datetime/parse.js +1 -1
  62. package/dist/forms/createFormStore.svelte.js +1 -0
  63. package/dist/index.js +40 -40
  64. package/dist/patterns/data/DataGrid.svelte +45 -45
  65. package/dist/patterns/data/DataList.svelte +24 -24
  66. package/dist/patterns/data/DataTable.svelte +36 -36
  67. package/dist/patterns/forms/FormActions.spec.js +95 -95
  68. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  69. package/dist/patterns/forms/FormActions.svelte +46 -46
  70. package/dist/patterns/forms/FormGrid.svelte +33 -33
  71. package/dist/patterns/forms/FormSection.svelte +32 -32
  72. package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
  73. package/dist/patterns/forms/FormValidationSummary.svelte +74 -74
  74. package/dist/patterns/layout/Sidebar.svelte +39 -39
  75. package/dist/patterns/layout/index.d.ts +9 -0
  76. package/dist/patterns/layout/index.js +22 -0
  77. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  78. package/dist/patterns/navigation/BottomNav.svelte +64 -64
  79. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  80. package/dist/patterns/navigation/Header.svelte +193 -193
  81. package/dist/patterns/page/PageHeader.svelte +18 -18
  82. package/dist/patterns/page/PageLayout.svelte +40 -40
  83. package/dist/patterns/page/PageLoader.spec.js +57 -57
  84. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  85. package/dist/patterns/page/PageLoader.svelte +24 -24
  86. package/dist/patterns/page/SectionHeader.svelte +29 -29
  87. package/dist/presets/badges.js +112 -112
  88. package/dist/presets/buttons.js +76 -76
  89. package/dist/presets/index.js +9 -9
  90. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  91. package/dist/primitives/Accordion/Accordion.svelte +42 -42
  92. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  93. package/dist/primitives/Alert/Alert.spec.js +173 -173
  94. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  95. package/dist/primitives/Alert/Alert.svelte +27 -27
  96. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  97. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  98. package/dist/primitives/Badges/Badge.spec.js +144 -144
  99. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  100. package/dist/primitives/Badges/Badge.svelte +79 -79
  101. package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -136
  102. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  103. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  104. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -122
  105. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  106. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
  107. package/dist/primitives/Button/Button.spec.js +223 -223
  108. package/dist/primitives/Button/Button.stories.svelte +76 -76
  109. package/dist/primitives/Button/Button.svelte +270 -270
  110. package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -146
  111. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  112. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  113. package/dist/primitives/Card.spec.js +49 -49
  114. package/dist/primitives/Card.stories.svelte +22 -22
  115. package/dist/primitives/Card.svelte +28 -28
  116. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  117. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  118. package/dist/primitives/DarkModeToggle.spec.js +390 -390
  119. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  120. package/dist/primitives/DarkModeToggle.svelte +136 -136
  121. package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
  122. package/dist/primitives/Drawer/Drawer.svelte +120 -120
  123. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  124. package/dist/primitives/Dropdown/Dropdown.svelte +14 -14
  125. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  126. package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
  127. package/dist/primitives/Icons/ArrowRight.svelte +8 -8
  128. package/dist/primitives/Icons/Availability.svelte +14 -14
  129. package/dist/primitives/Icons/Back.svelte +14 -14
  130. package/dist/primitives/Icons/CheckCircle.svelte +6 -6
  131. package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
  132. package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
  133. package/dist/primitives/Icons/ChevronRight.svelte +4 -4
  134. package/dist/primitives/Icons/Copy.svelte +15 -15
  135. package/dist/primitives/Icons/Cross.svelte +5 -5
  136. package/dist/primitives/Icons/DownArrow.svelte +8 -8
  137. package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
  138. package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
  139. package/dist/primitives/Icons/Home.svelte +15 -15
  140. package/dist/primitives/Icons/Icon.spec.js +169 -169
  141. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  142. package/dist/primitives/Icons/Icon.svelte +52 -52
  143. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  144. package/dist/primitives/Icons/Info.svelte +7 -7
  145. package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
  146. package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
  147. package/dist/primitives/Icons/Message.svelte +15 -15
  148. package/dist/primitives/Icons/MoonIcon.svelte +5 -5
  149. package/dist/primitives/Icons/More.svelte +21 -21
  150. package/dist/primitives/Icons/MoreHori.spec.js +61 -61
  151. package/dist/primitives/Icons/MoreHori.svelte +22 -22
  152. package/dist/primitives/Icons/Notification.svelte +14 -14
  153. package/dist/primitives/Icons/Payment.svelte +14 -14
  154. package/dist/primitives/Icons/Profile.svelte +21 -21
  155. package/dist/primitives/Icons/Reload.svelte +29 -29
  156. package/dist/primitives/Icons/Shows.svelte +21 -21
  157. package/dist/primitives/Icons/Signout.svelte +21 -21
  158. package/dist/primitives/Icons/SunIcon.svelte +8 -8
  159. package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
  160. package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
  161. package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
  162. package/dist/primitives/Icons/WarningIcon.svelte +5 -5
  163. package/dist/primitives/Input/Input.spec.js +573 -573
  164. package/dist/primitives/Input/Input.stories.svelte +139 -139
  165. package/dist/primitives/Input/Input.svelte +417 -417
  166. package/dist/primitives/Input/Select.spec.js +212 -212
  167. package/dist/primitives/Input/Select.stories.svelte +112 -112
  168. package/dist/primitives/Input/Select.svelte +128 -128
  169. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  170. package/dist/primitives/Input/Textarea.svelte +35 -35
  171. package/dist/primitives/Label/Label.svelte +37 -37
  172. package/dist/primitives/Modal/Modal.spec.js +99 -99
  173. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  174. package/dist/primitives/Modal/Modal.svelte +158 -158
  175. package/dist/primitives/NumberInput/NumberInput.svelte +106 -106
  176. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  177. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  178. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  179. package/dist/primitives/Radio/Radio.svelte +67 -67
  180. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  181. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  182. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  183. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  184. package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
  185. package/dist/primitives/Spinner/Spinner.spec.js +71 -71
  186. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  187. package/dist/primitives/Spinner/Spinner.svelte +20 -20
  188. package/dist/primitives/Tabs/TabItem.svelte +49 -49
  189. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  190. package/dist/primitives/Tabs/Tabs.svelte +123 -123
  191. package/dist/primitives/Toggle.spec.js +143 -143
  192. package/dist/primitives/Toggle.stories.svelte +92 -92
  193. package/dist/primitives/Toggle.svelte +71 -71
  194. package/dist/primitives/Typography/Typography.svelte +53 -53
  195. package/dist/primitives/ValidationError.spec.js +103 -103
  196. package/dist/primitives/ValidationError.stories.svelte +69 -69
  197. package/dist/primitives/ValidationError.svelte +29 -29
  198. package/dist/primitives/index.js +84 -84
  199. package/dist/recipes/CropImage/CropImage.spec.js +208 -208
  200. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  201. package/dist/recipes/CropImage/CropImage.svelte +238 -238
  202. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  203. package/dist/recipes/ImageUploader/ImageUploader.svelte +804 -804
  204. package/dist/recipes/SuperLogin/SuperLogin.spec.js +17 -17
  205. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  206. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
  207. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  208. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
  209. package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
  210. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -133
  211. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
  212. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  213. package/dist/recipes/fields/FormField.svelte +58 -58
  214. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  215. package/dist/recipes/fields/SelectField.svelte +80 -80
  216. package/dist/recipes/fields/TextareaField.svelte +97 -97
  217. package/dist/recipes/fields/ToggleField.svelte +60 -60
  218. package/dist/recipes/fields/index.js +7 -7
  219. package/dist/recipes/inputs/MultiSelect.spec.js +258 -258
  220. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  221. package/dist/recipes/inputs/MultiSelect.svelte +256 -256
  222. package/dist/recipes/inputs/OTPInput.spec.js +251 -251
  223. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  224. package/dist/recipes/inputs/OTPInput.svelte +29 -29
  225. package/dist/recipes/inputs/PasswordInput.svelte +22 -22
  226. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +117 -117
  227. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
  228. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +326 -326
  229. package/dist/recipes/inputs/Search.svelte +37 -37
  230. package/dist/recipes/inputs/SelectDropdown.svelte +57 -57
  231. package/dist/recipes/modals/AlertModal.svelte +130 -130
  232. package/dist/recipes/modals/ConfirmationModal.spec.js +206 -206
  233. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  234. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  235. package/dist/recipes/modals/InputModal.svelte +182 -182
  236. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  237. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  238. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  239. package/dist/recipes/modals/StatusModal.svelte +206 -206
  240. package/dist/services/EventService.js +75 -75
  241. package/dist/services/EventService.spec.js +217 -217
  242. package/dist/services/ShowService.spec.js +345 -345
  243. package/dist/stores/auth.js +36 -36
  244. package/dist/stores/auth.spec.js +139 -139
  245. package/dist/stores/toaster.js +13 -13
  246. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  247. package/dist/stories/ButtonAuditReview.svelte +427 -427
  248. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  249. package/dist/stories/PatternsGallery.svelte +206 -206
  250. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  251. package/dist/stories/PrimitivesGallery.svelte +725 -725
  252. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  253. package/dist/stories/RecipesGallery.svelte +271 -271
  254. package/dist/stories/button-audit-manifest.json +11186 -11186
  255. package/dist/tailwind/preset.cjs +82 -82
  256. package/dist/telemetry.js +405 -405
  257. package/dist/telemetry.spec.js +1144 -1144
  258. package/dist/tokens/__tests__/typography-base.test.d.ts +2 -0
  259. package/dist/tokens/__tests__/typography-base.test.d.ts.map +1 -0
  260. package/dist/tokens/__tests__/typography-base.test.js +138 -0
  261. package/dist/tokens/tokens.css +87 -87
  262. package/dist/tokens/typography-base.css +163 -0
  263. package/dist/utils/apiConfig.spec.js +219 -219
  264. package/dist/utils/transitions.js +62 -62
  265. package/dist/utils/utils.js +354 -354
  266. package/package.json +292 -291
@@ -54,30 +54,30 @@
54
54
  let displayText = $derived(selectedItem ? selectedItem.name : placeholder);
55
55
 
56
56
  let sizeClass = $derived(formControlSizes[size] || formControlSizes.md);
57
-
58
- function updateDropdownPosition() {
59
- if (!triggerElement || !portal) return;
60
- const rect = triggerElement.getBoundingClientRect();
61
- dropdownPosition = {
62
- top: rect.bottom + 4,
63
- left: rect.left,
64
- width: rect.width
65
- };
66
- }
67
-
68
- async function toggle() {
69
- if (disabled) return;
70
- isOpen = !isOpen;
71
- if (isOpen) {
72
- focusedIndex = items.findIndex((item) => item.value === value);
73
- window.dispatchEvent(new CustomEvent('select-opened', { detail: { instanceId } }));
74
- if (portal) {
75
- await tick();
76
- updateDropdownPosition();
77
- }
78
- }
79
- }
80
-
57
+
58
+ function updateDropdownPosition() {
59
+ if (!triggerElement || !portal) return;
60
+ const rect = triggerElement.getBoundingClientRect();
61
+ dropdownPosition = {
62
+ top: rect.bottom + 4,
63
+ left: rect.left,
64
+ width: rect.width
65
+ };
66
+ }
67
+
68
+ async function toggle() {
69
+ if (disabled) return;
70
+ isOpen = !isOpen;
71
+ if (isOpen) {
72
+ focusedIndex = items.findIndex((item) => item.value === value);
73
+ window.dispatchEvent(new CustomEvent('select-opened', { detail: { instanceId } }));
74
+ if (portal) {
75
+ await tick();
76
+ updateDropdownPosition();
77
+ }
78
+ }
79
+ }
80
+
81
81
  function handleOtherSelectOpened(event: Event) {
82
82
  const customEvent = event as CustomEvent<{ instanceId: string }>;
83
83
  if (customEvent.detail.instanceId !== instanceId && isOpen) {
@@ -143,107 +143,107 @@
143
143
  close();
144
144
  }
145
145
  }
146
-
147
- onMount(() => {
148
- document.addEventListener("click", handleClickOutside, true);
149
- window.addEventListener("select-opened", handleOtherSelectOpened);
150
-
151
- // Update position on scroll/resize when portal is enabled
152
- if (portal) {
153
- window.addEventListener("scroll", updateDropdownPosition, true);
154
- window.addEventListener("resize", updateDropdownPosition);
155
- }
156
-
157
- return () => {
158
- document.removeEventListener("click", handleClickOutside, true);
159
- window.removeEventListener("select-opened", handleOtherSelectOpened);
160
- if (portal) {
161
- window.removeEventListener("scroll", updateDropdownPosition, true);
162
- window.removeEventListener("resize", updateDropdownPosition);
163
- }
164
- };
165
- });
166
- </script>
167
-
168
- <div class="w-full flex flex-col gap-2" {...restProps}>
169
- {#if label}
170
- <div class="flex justify-start items-center gap-1">
171
- <label for={id || name} class={typography.label}>
172
- {label}{#if required}<span class="text-red-500 font-medium text-sm ml-0.5">*</span>{/if}
173
- </label>
174
- </div>
175
- {/if}
176
-
177
- <div class="relative w-full">
178
- <button
179
- type="button"
180
- bind:this={triggerElement}
181
- {id}
182
- {name}
183
- class="flex items-center justify-between w-full {sizeClass} bg-gray-50 dark:bg-gray-800 border rounded-lg cursor-pointer transition-colors text-left focus:outline-none focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 {error ? 'border-red-500 dark:border-red-500' : 'border-gray-300 dark:border-gray-600 hover:border-blue-500 dark:hover:border-blue-500'} {disabled ? 'opacity-50 cursor-not-allowed' : ''} {!selectedItem ? `${typography.textMuted}` : `${typography.body}`}"
184
- {disabled}
185
- aria-haspopup="listbox"
186
- aria-expanded={isOpen}
187
- onclick={toggle}
188
- onkeydown={handleKeydown}
189
- >
190
- <span class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap">{displayText}</span>
191
- <ChevronDownOutline class="w-4 h-4 shrink-0 {typography.iconMuted} transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
192
- </button>
193
-
194
- {#if isOpen && !portal}
195
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
196
- <ul
197
- bind:this={dropdownElement}
198
- class="absolute top-full left-0 right-0 z-50 mt-1 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-60 overflow-y-auto py-1"
199
- role="listbox"
200
- tabindex="-1"
201
- >
202
- {#each items as item, index}
203
- <!-- svelte-ignore a11y_click_events_have_key_events -->
204
- <li
205
- id="{id || name}-option-{index}"
206
- class={`px-4 py-2 cursor-pointer ${typography.sm} transition-colors ${item.value === value ? '' : `${typography.textMuted}`} ${index === focusedIndex ? 'bg-gray-100 dark:bg-gray-600' : 'hover:bg-gray-100 dark:hover:bg-gray-600'}`}
207
- role="option"
208
- aria-selected={item.value === value}
209
- onclick={() => selectItem(item)}
210
- onmouseenter={() => (focusedIndex = index)}
211
- >
212
- {item.name}
213
- </li>
214
- {/each}
215
- </ul>
216
- {/if}
217
- </div>
218
-
219
- {#if error}
220
- <p class={typography.error}>{error}</p>
221
- {/if}
222
- </div>
223
-
224
- {#if isOpen && portal}
225
- <!-- Portal dropdown - truly moved to document.body to escape overflow containers and transforms -->
226
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
227
- <ul
228
- bind:this={dropdownElement}
229
- use:portalAction
230
- class="fixed z-[9999] bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-60 overflow-y-auto py-1"
231
- style="top: {dropdownPosition.top}px; left: {dropdownPosition.left}px; width: {dropdownPosition.width}px;"
232
- role="listbox"
233
- tabindex="-1"
234
- >
235
- {#each items as item, index}
236
- <!-- svelte-ignore a11y_click_events_have_key_events -->
237
- <li
238
- id="{id || name}-option-{index}"
239
- class={`px-4 py-2 cursor-pointer ${typography.sm} transition-colors ${item.value === value ? '' : `${typography.textMuted}`} ${index === focusedIndex ? 'bg-gray-100 dark:bg-gray-600' : 'hover:bg-gray-100 dark:hover:bg-gray-600'}`}
240
- role="option"
241
- aria-selected={item.value === value}
242
- onclick={() => selectItem(item)}
243
- onmouseenter={() => (focusedIndex = index)}
244
- >
245
- {item.name}
246
- </li>
247
- {/each}
248
- </ul>
249
- {/if}
146
+
147
+ onMount(() => {
148
+ document.addEventListener("click", handleClickOutside, true);
149
+ window.addEventListener("select-opened", handleOtherSelectOpened);
150
+
151
+ // Update position on scroll/resize when portal is enabled
152
+ if (portal) {
153
+ window.addEventListener("scroll", updateDropdownPosition, true);
154
+ window.addEventListener("resize", updateDropdownPosition);
155
+ }
156
+
157
+ return () => {
158
+ document.removeEventListener("click", handleClickOutside, true);
159
+ window.removeEventListener("select-opened", handleOtherSelectOpened);
160
+ if (portal) {
161
+ window.removeEventListener("scroll", updateDropdownPosition, true);
162
+ window.removeEventListener("resize", updateDropdownPosition);
163
+ }
164
+ };
165
+ });
166
+ </script>
167
+
168
+ <div class="w-full flex flex-col gap-2" {...restProps}>
169
+ {#if label}
170
+ <div class="flex justify-start items-center gap-1">
171
+ <label for={id || name} class={typography.label}>
172
+ {label}{#if required}<span class="text-red-500 font-medium text-sm ml-0.5">*</span>{/if}
173
+ </label>
174
+ </div>
175
+ {/if}
176
+
177
+ <div class="relative w-full">
178
+ <button
179
+ type="button"
180
+ bind:this={triggerElement}
181
+ {id}
182
+ {name}
183
+ class="flex items-center justify-between w-full {sizeClass} bg-gray-50 dark:bg-gray-800 border rounded-lg cursor-pointer transition-colors text-left focus:outline-none focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 {error ? 'border-red-500 dark:border-red-500' : 'border-gray-300 dark:border-gray-600 hover:border-blue-500 dark:hover:border-blue-500'} {disabled ? 'opacity-50 cursor-not-allowed' : ''} {!selectedItem ? `${typography.textMuted}` : `${typography.body}`}"
184
+ {disabled}
185
+ aria-haspopup="listbox"
186
+ aria-expanded={isOpen}
187
+ onclick={toggle}
188
+ onkeydown={handleKeydown}
189
+ >
190
+ <span class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap">{displayText}</span>
191
+ <ChevronDownOutline class="w-4 h-4 shrink-0 {typography.iconMuted} transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
192
+ </button>
193
+
194
+ {#if isOpen && !portal}
195
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
196
+ <ul
197
+ bind:this={dropdownElement}
198
+ class="absolute top-full left-0 right-0 z-50 mt-1 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-60 overflow-y-auto py-1"
199
+ role="listbox"
200
+ tabindex="-1"
201
+ >
202
+ {#each items as item, index}
203
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
204
+ <li
205
+ id="{id || name}-option-{index}"
206
+ class={`px-4 py-2 cursor-pointer ${typography.sm} transition-colors ${item.value === value ? '' : `${typography.textMuted}`} ${index === focusedIndex ? 'bg-gray-100 dark:bg-gray-600' : 'hover:bg-gray-100 dark:hover:bg-gray-600'}`}
207
+ role="option"
208
+ aria-selected={item.value === value}
209
+ onclick={() => selectItem(item)}
210
+ onmouseenter={() => (focusedIndex = index)}
211
+ >
212
+ {item.name}
213
+ </li>
214
+ {/each}
215
+ </ul>
216
+ {/if}
217
+ </div>
218
+
219
+ {#if error}
220
+ <p class={typography.error}>{error}</p>
221
+ {/if}
222
+ </div>
223
+
224
+ {#if isOpen && portal}
225
+ <!-- Portal dropdown - truly moved to document.body to escape overflow containers and transforms -->
226
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
227
+ <ul
228
+ bind:this={dropdownElement}
229
+ use:portalAction
230
+ class="fixed z-[9999] bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-60 overflow-y-auto py-1"
231
+ style="top: {dropdownPosition.top}px; left: {dropdownPosition.left}px; width: {dropdownPosition.width}px;"
232
+ role="listbox"
233
+ tabindex="-1"
234
+ >
235
+ {#each items as item, index}
236
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
237
+ <li
238
+ id="{id || name}-option-{index}"
239
+ class={`px-4 py-2 cursor-pointer ${typography.sm} transition-colors ${item.value === value ? '' : `${typography.textMuted}`} ${index === focusedIndex ? 'bg-gray-100 dark:bg-gray-600' : 'hover:bg-gray-100 dark:hover:bg-gray-600'}`}
240
+ role="option"
241
+ aria-selected={item.value === value}
242
+ onclick={() => selectItem(item)}
243
+ onmouseenter={() => (focusedIndex = index)}
244
+ >
245
+ {item.name}
246
+ </li>
247
+ {/each}
248
+ </ul>
249
+ {/if}
@@ -1,137 +1,137 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import Textarea from './Textarea.svelte';
4
-
5
- const { Story } = defineMeta({
6
- title: 'Primitives/Textarea',
7
- component: Textarea,
8
- tags: ['autodocs'],
9
- argTypes: {
10
- label: { control: 'text' },
11
- placeholder: { control: 'text' },
12
- rows: { control: 'number' },
13
- disabled: { control: 'boolean' },
14
- required: { control: 'boolean' },
15
- maxlength: { control: 'number' },
16
- errorText: { control: 'text' },
17
- },
18
- parameters: {
19
- docs: {
20
- description: {
21
- component: 'Multi-line text input component.',
22
- },
23
- },
24
- },
25
- });
26
-
27
- let value = $state('');
28
- </script>
29
-
30
- <Story name="Default">
31
- {#snippet template()}
32
- <div class="max-w-md">
33
- <Textarea
34
- label="Description"
35
- placeholder="Enter your description..."
36
- />
37
- </div>
38
- {/snippet}
39
- </Story>
40
-
41
- <Story name="With Value">
42
- {#snippet template()}
43
- <div class="max-w-md">
44
- <Textarea
45
- label="Bio"
46
- placeholder="Tell us about yourself..."
47
- bind:value={value}
48
- />
49
- <p class="text-sm text-gray-500 mt-2">{value.length} characters</p>
50
- </div>
51
- {/snippet}
52
- </Story>
53
-
54
- <Story name="With Max Length">
55
- {#snippet template()}
56
- <div class="max-w-md">
57
- <Textarea
58
- label="Short Bio"
59
- placeholder="Keep it brief..."
60
- maxlength={200}
61
- helperText="Maximum 200 characters"
62
- />
63
- </div>
64
- {/snippet}
65
- </Story>
66
-
67
- <Story name="With Error">
68
- {#snippet template()}
69
- <div class="max-w-md">
70
- <Textarea
71
- label="Comments"
72
- placeholder="Enter your comments..."
73
- errorText="Please provide at least 10 characters"
74
- />
75
- </div>
76
- {/snippet}
77
- </Story>
78
-
79
- <Story name="Custom Rows">
80
- {#snippet template()}
81
- <div class="space-y-6 max-w-md">
82
- <Textarea
83
- label="Small (2 rows)"
84
- placeholder="Two rows..."
85
- rows={2}
86
- />
87
- <Textarea
88
- label="Medium (4 rows)"
89
- placeholder="Four rows..."
90
- rows={4}
91
- />
92
- <Textarea
93
- label="Large (8 rows)"
94
- placeholder="Eight rows..."
95
- rows={8}
96
- />
97
- </div>
98
- {/snippet}
99
- </Story>
100
-
101
- <Story name="Disabled">
102
- {#snippet template()}
103
- <div class="max-w-md">
104
- <Textarea
105
- label="Disabled Textarea"
106
- placeholder="Cannot edit this..."
107
- disabled
108
- />
109
- </div>
110
- {/snippet}
111
- </Story>
112
-
113
- <Story name="All States">
114
- {#snippet template()}
115
- <div class="grid grid-cols-2 gap-6 max-w-2xl">
116
- <Textarea
117
- label="Default"
118
- placeholder="Enter text..."
119
- />
120
- <Textarea
121
- label="With Error"
122
- placeholder="Enter text..."
123
- errorText="This field is required"
124
- />
125
- <Textarea
126
- label="Disabled"
127
- placeholder="Cannot edit..."
128
- disabled
129
- />
130
- <Textarea
131
- label="With Helper"
132
- placeholder="Enter text..."
133
- helperText="Optional additional information"
134
- />
135
- </div>
136
- {/snippet}
137
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import Textarea from './Textarea.svelte';
4
+
5
+ const { Story } = defineMeta({
6
+ title: 'Primitives/Textarea',
7
+ component: Textarea,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ label: { control: 'text' },
11
+ placeholder: { control: 'text' },
12
+ rows: { control: 'number' },
13
+ disabled: { control: 'boolean' },
14
+ required: { control: 'boolean' },
15
+ maxlength: { control: 'number' },
16
+ errorText: { control: 'text' },
17
+ },
18
+ parameters: {
19
+ docs: {
20
+ description: {
21
+ component: 'Multi-line text input component.',
22
+ },
23
+ },
24
+ },
25
+ });
26
+
27
+ let value = $state('');
28
+ </script>
29
+
30
+ <Story name="Default">
31
+ {#snippet template()}
32
+ <div class="max-w-md">
33
+ <Textarea
34
+ label="Description"
35
+ placeholder="Enter your description..."
36
+ />
37
+ </div>
38
+ {/snippet}
39
+ </Story>
40
+
41
+ <Story name="With Value">
42
+ {#snippet template()}
43
+ <div class="max-w-md">
44
+ <Textarea
45
+ label="Bio"
46
+ placeholder="Tell us about yourself..."
47
+ bind:value={value}
48
+ />
49
+ <p class="text-sm text-gray-500 mt-2">{value.length} characters</p>
50
+ </div>
51
+ {/snippet}
52
+ </Story>
53
+
54
+ <Story name="With Max Length">
55
+ {#snippet template()}
56
+ <div class="max-w-md">
57
+ <Textarea
58
+ label="Short Bio"
59
+ placeholder="Keep it brief..."
60
+ maxlength={200}
61
+ helperText="Maximum 200 characters"
62
+ />
63
+ </div>
64
+ {/snippet}
65
+ </Story>
66
+
67
+ <Story name="With Error">
68
+ {#snippet template()}
69
+ <div class="max-w-md">
70
+ <Textarea
71
+ label="Comments"
72
+ placeholder="Enter your comments..."
73
+ errorText="Please provide at least 10 characters"
74
+ />
75
+ </div>
76
+ {/snippet}
77
+ </Story>
78
+
79
+ <Story name="Custom Rows">
80
+ {#snippet template()}
81
+ <div class="space-y-6 max-w-md">
82
+ <Textarea
83
+ label="Small (2 rows)"
84
+ placeholder="Two rows..."
85
+ rows={2}
86
+ />
87
+ <Textarea
88
+ label="Medium (4 rows)"
89
+ placeholder="Four rows..."
90
+ rows={4}
91
+ />
92
+ <Textarea
93
+ label="Large (8 rows)"
94
+ placeholder="Eight rows..."
95
+ rows={8}
96
+ />
97
+ </div>
98
+ {/snippet}
99
+ </Story>
100
+
101
+ <Story name="Disabled">
102
+ {#snippet template()}
103
+ <div class="max-w-md">
104
+ <Textarea
105
+ label="Disabled Textarea"
106
+ placeholder="Cannot edit this..."
107
+ disabled
108
+ />
109
+ </div>
110
+ {/snippet}
111
+ </Story>
112
+
113
+ <Story name="All States">
114
+ {#snippet template()}
115
+ <div class="grid grid-cols-2 gap-6 max-w-2xl">
116
+ <Textarea
117
+ label="Default"
118
+ placeholder="Enter text..."
119
+ />
120
+ <Textarea
121
+ label="With Error"
122
+ placeholder="Enter text..."
123
+ errorText="This field is required"
124
+ />
125
+ <Textarea
126
+ label="Disabled"
127
+ placeholder="Cannot edit..."
128
+ disabled
129
+ />
130
+ <Textarea
131
+ label="With Helper"
132
+ placeholder="Enter text..."
133
+ helperText="Optional additional information"
134
+ />
135
+ </div>
136
+ {/snippet}
137
+ </Story>
@@ -68,38 +68,38 @@
68
68
  onfocus?.(event);
69
69
  }
70
70
  </script>
71
-
72
- <div class="flex flex-col gap-2 w-full">
73
- {#if label}
74
- <label for={id || name} class={`${typography.label} leading-tight`}>
75
- {label}{#if required}<span class="text-red-500 font-medium text-sm ml-0.5">*</span>{/if}
76
- </label>
77
- {/if}
78
-
79
- <textarea
80
- {id}
81
- {name}
82
- {placeholder}
83
- {rows}
84
- {disabled}
85
- {readonly}
86
- {maxlength}
87
- {minlength}
88
- class="{typography.sm} w-full p-2.5 bg-gray-50 dark:bg-gray-800 leading-normal border rounded-lg resize-y transition-colors focus:outline-none focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 placeholder-gray-500 dark:placeholder-gray-400 {error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600 hover:border-blue-500 focus:border-blue-500'} {disabled ? 'opacity-50 cursor-not-allowed' : ''} {className}"
89
- bind:value
90
- oninput={handleInput}
91
- onchange={handleChange}
92
- onblur={handleBlur}
93
- onfocus={handleFocus}
94
- {onkeydown}
95
- {onkeyup}
96
- {onkeypress}
97
- aria-required={required}
98
- aria-invalid={!!error}
99
- {...restProps}
100
- ></textarea>
101
-
102
- {#if error}
103
- <p class={typography.error}>{error}</p>
104
- {/if}
105
- </div>
71
+
72
+ <div class="flex flex-col gap-2 w-full">
73
+ {#if label}
74
+ <label for={id || name} class={`${typography.label} leading-tight`}>
75
+ {label}{#if required}<span class="text-red-500 font-medium text-sm ml-0.5">*</span>{/if}
76
+ </label>
77
+ {/if}
78
+
79
+ <textarea
80
+ {id}
81
+ {name}
82
+ {placeholder}
83
+ {rows}
84
+ {disabled}
85
+ {readonly}
86
+ {maxlength}
87
+ {minlength}
88
+ class="{typography.sm} w-full p-2.5 bg-gray-50 dark:bg-gray-800 leading-normal border rounded-lg resize-y transition-colors focus:outline-none focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 placeholder-gray-500 dark:placeholder-gray-400 {error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600 hover:border-blue-500 focus:border-blue-500'} {disabled ? 'opacity-50 cursor-not-allowed' : ''} {className}"
89
+ bind:value
90
+ oninput={handleInput}
91
+ onchange={handleChange}
92
+ onblur={handleBlur}
93
+ onfocus={handleFocus}
94
+ {onkeydown}
95
+ {onkeyup}
96
+ {onkeypress}
97
+ aria-required={required}
98
+ aria-invalid={!!error}
99
+ {...restProps}
100
+ ></textarea>
101
+
102
+ {#if error}
103
+ <p class={typography.error}>{error}</p>
104
+ {/if}
105
+ </div>