@getmicdrop/svelte-components 5.3.14 → 5.4.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/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 +145 -145
  7. package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
  8. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
  9. package/dist/components/Layout/Grid.svelte +109 -109
  10. package/dist/components/Layout/Section.svelte +80 -80
  11. package/dist/components/Layout/Sidebar.svelte +108 -108
  12. package/dist/components/Layout/Stack.svelte +90 -90
  13. package/dist/constants/validation.js +91 -91
  14. package/dist/constants/validation.spec.js +64 -64
  15. package/dist/datetime/constants.d.ts +7 -0
  16. package/dist/datetime/constants.d.ts.map +1 -1
  17. package/dist/datetime/constants.js +26 -0
  18. package/dist/datetime/index.d.ts +3 -2
  19. package/dist/datetime/index.d.ts.map +1 -1
  20. package/dist/datetime/index.js +2 -2
  21. package/dist/datetime/timezone.d.ts +72 -0
  22. package/dist/datetime/timezone.d.ts.map +1 -1
  23. package/dist/datetime/timezone.js +138 -0
  24. package/dist/forms/createFieldTracker.svelte.d.ts +41 -0
  25. package/dist/forms/createFieldTracker.svelte.d.ts.map +1 -0
  26. package/dist/forms/createFieldTracker.svelte.js +91 -0
  27. package/dist/forms/createFormStore.svelte.d.ts +47 -0
  28. package/dist/forms/createFormStore.svelte.d.ts.map +1 -0
  29. package/dist/forms/createFormStore.svelte.js +223 -0
  30. package/dist/forms/index.d.ts +61 -0
  31. package/dist/forms/index.d.ts.map +1 -0
  32. package/dist/forms/index.js +60 -0
  33. package/dist/index.js +223 -223
  34. package/dist/patterns/data/DataGrid.svelte +45 -45
  35. package/dist/patterns/data/DataList.svelte +24 -24
  36. package/dist/patterns/data/DataTable.svelte +40 -40
  37. package/dist/patterns/forms/FormActions.spec.js +88 -88
  38. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  39. package/dist/patterns/forms/FormActions.svelte +46 -46
  40. package/dist/patterns/forms/FormGrid.svelte +33 -33
  41. package/dist/patterns/forms/FormSection.svelte +32 -32
  42. package/dist/patterns/forms/FormValidationSummary.spec.js +203 -203
  43. package/dist/patterns/forms/FormValidationSummary.stories.svelte +97 -97
  44. package/dist/patterns/forms/FormValidationSummary.svelte +67 -67
  45. package/dist/patterns/layout/Grid.svelte +35 -35
  46. package/dist/patterns/layout/Sidebar.svelte +39 -39
  47. package/dist/patterns/layout/Stack.svelte +45 -45
  48. package/dist/patterns/navigation/BottomNav.spec.js +130 -130
  49. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  50. package/dist/patterns/navigation/BottomNav.svelte +54 -54
  51. package/dist/patterns/navigation/Header.spec.js +203 -203
  52. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  53. package/dist/patterns/navigation/Header.svelte +240 -240
  54. package/dist/patterns/page/PageHeader.svelte +36 -36
  55. package/dist/patterns/page/PageLayout.svelte +40 -40
  56. package/dist/patterns/page/PageLoader.spec.js +54 -54
  57. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  58. package/dist/patterns/page/PageLoader.svelte +41 -41
  59. package/dist/patterns/page/SectionHeader.svelte +41 -41
  60. package/dist/presets/badges.js +112 -112
  61. package/dist/presets/buttons.js +76 -76
  62. package/dist/presets/index.js +9 -9
  63. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  64. package/dist/primitives/Accordion/Accordion.svelte +61 -61
  65. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  66. package/dist/primitives/Alert/Alert.spec.js +170 -170
  67. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  68. package/dist/primitives/Alert/Alert.svelte +65 -65
  69. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  70. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  71. package/dist/primitives/Badges/Badge.spec.js +103 -103
  72. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  73. package/dist/primitives/Badges/Badge.svelte +142 -142
  74. package/dist/primitives/BottomSheet/BottomSheet.spec.js +127 -127
  75. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  76. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  77. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +120 -120
  78. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  79. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
  80. package/dist/primitives/Button/Button.spec.js +211 -211
  81. package/dist/primitives/Button/Button.stories.svelte +76 -76
  82. package/dist/primitives/Button/Button.svelte +301 -301
  83. package/dist/primitives/Button/ButtonSaveDemo.spec.js +48 -48
  84. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  85. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  86. package/dist/primitives/Card.spec.js +49 -49
  87. package/dist/primitives/Card.stories.svelte +22 -22
  88. package/dist/primitives/Card.svelte +28 -28
  89. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  90. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  91. package/dist/primitives/DarkModeToggle.spec.js +357 -357
  92. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  93. package/dist/primitives/DarkModeToggle.svelte +136 -136
  94. package/dist/primitives/Drawer/Drawer.stories.svelte +100 -100
  95. package/dist/primitives/Drawer/Drawer.svelte +214 -214
  96. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  97. package/dist/primitives/Dropdown/Dropdown.svelte +148 -148
  98. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  99. package/dist/primitives/Icons/ArrowLeft.svelte +20 -20
  100. package/dist/primitives/Icons/ArrowRight.svelte +20 -20
  101. package/dist/primitives/Icons/Availability.svelte +26 -26
  102. package/dist/primitives/Icons/Back.svelte +26 -26
  103. package/dist/primitives/Icons/CheckCircle.svelte +18 -18
  104. package/dist/primitives/Icons/CheckCircleOutline.svelte +27 -27
  105. package/dist/primitives/Icons/ChevronLeft.svelte +16 -16
  106. package/dist/primitives/Icons/ChevronRight.svelte +16 -16
  107. package/dist/primitives/Icons/Copy.svelte +27 -27
  108. package/dist/primitives/Icons/Cross.svelte +17 -17
  109. package/dist/primitives/Icons/DownArrow.svelte +20 -20
  110. package/dist/primitives/Icons/ErrorCircle.svelte +18 -18
  111. package/dist/primitives/Icons/FacebookIcon.svelte +13 -13
  112. package/dist/primitives/Icons/Home.svelte +27 -27
  113. package/dist/primitives/Icons/Icon.spec.js +175 -175
  114. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  115. package/dist/primitives/Icons/Icon.svelte +63 -63
  116. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  117. package/dist/primitives/Icons/ImageOutline.svelte +19 -19
  118. package/dist/primitives/Icons/Info.svelte +19 -19
  119. package/dist/primitives/Icons/InstagramIcon.svelte +19 -19
  120. package/dist/primitives/Icons/LogoInstagram.svelte +15 -15
  121. package/dist/primitives/Icons/Message.svelte +27 -27
  122. package/dist/primitives/Icons/MoonIcon.svelte +16 -16
  123. package/dist/primitives/Icons/More.svelte +33 -33
  124. package/dist/primitives/Icons/MoreHori.spec.js +67 -67
  125. package/dist/primitives/Icons/MoreHori.svelte +34 -34
  126. package/dist/primitives/Icons/Notification.svelte +26 -26
  127. package/dist/primitives/Icons/Payment.svelte +26 -26
  128. package/dist/primitives/Icons/Profile.svelte +33 -33
  129. package/dist/primitives/Icons/Reload.svelte +41 -41
  130. package/dist/primitives/Icons/Shows.svelte +33 -33
  131. package/dist/primitives/Icons/Signout.svelte +33 -33
  132. package/dist/primitives/Icons/SunIcon.svelte +19 -19
  133. package/dist/primitives/Icons/TiktokIcon.svelte +13 -13
  134. package/dist/primitives/Icons/TrashBinOutline.svelte +19 -19
  135. package/dist/primitives/Icons/TwitterIcon.svelte +13 -13
  136. package/dist/primitives/Icons/WarningIcon.spec.js +30 -30
  137. package/dist/primitives/Icons/WarningIcon.svelte +24 -24
  138. package/dist/primitives/Input/Input.spec.js +573 -573
  139. package/dist/primitives/Input/Input.stories.svelte +139 -139
  140. package/dist/primitives/Input/Input.svelte +444 -444
  141. package/dist/primitives/Input/Select.spec.js +218 -218
  142. package/dist/primitives/Input/Select.stories.svelte +112 -112
  143. package/dist/primitives/Input/Select.svelte +232 -232
  144. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  145. package/dist/primitives/Input/Textarea.svelte +79 -79
  146. package/dist/primitives/Label/Label.svelte +37 -37
  147. package/dist/primitives/Modal/Modal.spec.js +95 -95
  148. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  149. package/dist/primitives/Modal/Modal.svelte +158 -158
  150. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  151. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  152. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  153. package/dist/primitives/Radio/Radio.svelte +67 -67
  154. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  155. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  156. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  157. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  158. package/dist/primitives/Skeleton/Skeleton.svelte +52 -52
  159. package/dist/primitives/Spinner/Spinner.spec.js +75 -75
  160. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  161. package/dist/primitives/Spinner/Spinner.svelte +57 -57
  162. package/dist/primitives/Tabs/TabItem.svelte +51 -51
  163. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  164. package/dist/primitives/Tabs/Tabs.svelte +128 -128
  165. package/dist/primitives/Toggle.spec.js +127 -127
  166. package/dist/primitives/Toggle.stories.svelte +92 -92
  167. package/dist/primitives/Toggle.svelte +71 -71
  168. package/dist/primitives/Typography/Typography.svelte +53 -53
  169. package/dist/primitives/ValidationError.spec.js +103 -103
  170. package/dist/primitives/ValidationError.stories.svelte +111 -111
  171. package/dist/primitives/ValidationError.svelte +29 -29
  172. package/dist/recipes/CropImage/CropImage.spec.js +216 -216
  173. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  174. package/dist/recipes/CropImage/CropImage.svelte +238 -238
  175. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  176. package/dist/recipes/ImageUploader/ImageUploader.svelte +980 -980
  177. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  178. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +47 -47
  179. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  180. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +112 -112
  181. package/dist/recipes/feedback/ErrorDisplay.svelte +38 -38
  182. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +129 -129
  183. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +167 -167
  184. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  185. package/dist/recipes/fields/FormField.svelte +58 -58
  186. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  187. package/dist/recipes/fields/SelectField.svelte +82 -82
  188. package/dist/recipes/fields/TextareaField.svelte +101 -101
  189. package/dist/recipes/fields/ToggleField.svelte +60 -60
  190. package/dist/recipes/fields/index.js +7 -7
  191. package/dist/recipes/inputs/MultiSelect.spec.js +257 -257
  192. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  193. package/dist/recipes/inputs/MultiSelect.svelte +244 -244
  194. package/dist/recipes/inputs/OTPInput.spec.js +238 -238
  195. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  196. package/dist/recipes/inputs/OTPInput.svelte +102 -102
  197. package/dist/recipes/inputs/PasswordInput.svelte +100 -100
  198. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +173 -173
  199. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +108 -108
  200. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +300 -300
  201. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +165 -165
  202. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +337 -337
  203. package/dist/recipes/inputs/Search.svelte +85 -85
  204. package/dist/recipes/inputs/SelectDropdown.svelte +161 -161
  205. package/dist/recipes/modals/AlertModal.svelte +130 -130
  206. package/dist/recipes/modals/ConfirmationModal.spec.js +191 -191
  207. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  208. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  209. package/dist/recipes/modals/InputModal.svelte +182 -182
  210. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  211. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  212. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  213. package/dist/recipes/modals/StatusModal.svelte +206 -206
  214. package/dist/services/EventService.js +75 -75
  215. package/dist/services/EventService.spec.js +217 -217
  216. package/dist/services/ShowService.spec.js +342 -342
  217. package/dist/stores/auth.js +36 -36
  218. package/dist/stores/auth.spec.js +139 -139
  219. package/dist/stores/createFormStore.d.ts +3 -0
  220. package/dist/stores/createFormStore.d.ts.map +1 -1
  221. package/dist/stores/createFormStore.js +24 -0
  222. package/dist/stores/index.d.ts +6 -0
  223. package/dist/stores/index.js +10 -0
  224. package/dist/stores/toaster.js +13 -13
  225. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  226. package/dist/stories/ButtonAuditReview.svelte +427 -427
  227. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  228. package/dist/stories/PatternsGallery.svelte +388 -388
  229. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  230. package/dist/stories/PrimitivesGallery.svelte +752 -752
  231. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  232. package/dist/stories/RecipesGallery.svelte +441 -441
  233. package/dist/stories/button-audit-manifest.json +11186 -11186
  234. package/dist/tailwind/preset.cjs +82 -82
  235. package/dist/tokens/tokens.css +87 -87
  236. package/dist/utils/utils.js +354 -354
  237. package/package.json +211 -199
@@ -1,301 +1,301 @@
1
- <script>
2
- /**
3
- * Button Component - Flowbite Native (Pure Tailwind)
4
- * Migrated to Svelte 5 runes
5
- *
6
- * Variants (Flowbite naming):
7
- * - default: Primary blue button
8
- * - alternative: Gray outline button
9
- * - outline: Blue outline button
10
- * - red: Destructive solid button
11
- * - red-outline: Destructive outline button
12
- * - ghost: Text-only button
13
- * - ghost-red: Destructive text button
14
- * - icon: Icon-only button
15
- * - toggle: Toggle/pill button
16
- * - avatar: Avatar/image trigger button (no padding, opacity hover)
17
- * - menu-item: Dropdown/sheet menu item (full width, left-aligned)
18
- * - menu-item-danger: Destructive menu item (red text)
19
- * - card: Selectable card button (bordered, for list selections)
20
- * - sidebar-toggle: Compact pill for sidebar expand/collapse
21
- *
22
- * Sizes (Flowbite native): xs, sm, md, lg, xl
23
- */
24
- import { CheckOutline } from '../Icons';
25
- import { twMerge } from 'tailwind-merge';
26
-
27
- /** @type {{
28
- variant?: string,
29
- size?: string,
30
- disabled?: boolean,
31
- loading?: boolean,
32
- success?: boolean,
33
- successText?: string,
34
- active?: boolean,
35
- href?: string | null,
36
- type?: 'button' | 'submit' | 'reset',
37
- children?: any,
38
- trailing?: any,
39
- class?: string,
40
- onclick?: (e: MouseEvent) => void,
41
- [key: string]: any
42
- }} */
43
- let {
44
- variant = "default",
45
- size = "md",
46
- disabled = false,
47
- loading = false,
48
- success = false,
49
- successText = "",
50
- active = false,
51
- href = null,
52
- type = "button",
53
- children,
54
- trailing,
55
- class: className = "",
56
- onclick,
57
- ...restProps
58
- } = $props();
59
-
60
- // Legacy variant name mapping
61
- const variantMap = {
62
- "blue-solid": "default",
63
- "gray-outline": "alternative",
64
- "blue-outline": "outline",
65
- "red-solid": "red",
66
- "red-text": "ghost-red",
67
- "gray-text": "ghost",
68
- };
69
-
70
- // Resolve legacy names
71
- let resolvedVariant = $derived(variantMap[variant] || variant);
72
- let effectiveDisabled = $derived(disabled || loading || success);
73
-
74
- // Size classes - Flowbite native sizing
75
- const sizeClasses = {
76
- xs: "px-3 py-1.5 text-xs",
77
- sm: "px-3 py-2 text-sm",
78
- md: "px-4 py-2.5 text-sm",
79
- lg: "px-5 py-3 text-sm",
80
- xl: "px-6 py-3.5 text-sm",
81
- "xl-medium": "w-full px-6 py-3.5 text-sm",
82
- full: "w-full px-5 py-3 text-sm",
83
- "full-md-auto": "w-full px-5 py-3 text-sm md:w-auto",
84
- half: "w-1/2 px-4 py-2.5 text-sm",
85
- };
86
-
87
- // Icon variant sizes
88
- const iconSizes = {
89
- xs: "p-1.5",
90
- sm: "p-2",
91
- md: "p-2.5",
92
- lg: "p-3",
93
- };
94
-
95
- // Avatar variant sizes (no padding, just min dimensions)
96
- const avatarSizes = {
97
- sm: "min-w-8 min-h-8",
98
- md: "min-w-10 min-h-10",
99
- lg: "min-w-12 min-h-12",
100
- };
101
-
102
- // Menu item sizes
103
- const menuItemSizes = {
104
- sm: "py-2 px-4 text-sm",
105
- md: "py-3 px-4 text-sm",
106
- lg: "py-4 px-6 text-base",
107
- nav: "py-2 px-3 text-base font-normal",
108
- };
109
-
110
- // Card sizes (larger padding for card-like buttons)
111
- const cardSizes = {
112
- sm: "p-3 text-sm",
113
- md: "p-4 text-sm",
114
- lg: "p-5 text-base",
115
- };
116
-
117
- // Variant classes with all states in Tailwind (no focus rings - design decision)
118
- const variantClasses = {
119
- default: "text-white bg-blue-700 border border-blue-700 hover:bg-blue-800 dark:bg-blue-600 dark:hover:bg-blue-700",
120
- alternative: "text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700",
121
- outline: "text-blue-700 bg-transparent border border-blue-700 hover:text-white hover:bg-blue-800 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500",
122
- red: "text-white bg-red-700 border border-red-700 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700",
123
- "red-outline": "text-red-700 bg-transparent border border-red-700 hover:text-white hover:bg-red-800 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600",
124
- ghost: "text-gray-500 bg-transparent border-transparent hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white",
125
- "ghost-red": "text-red-700 bg-transparent border-transparent hover:bg-red-100 hover:text-red-900 dark:text-red-500 dark:hover:bg-red-900/50 dark:hover:text-red-400",
126
- link: "text-blue-700 bg-transparent border-transparent hover:underline dark:text-blue-500",
127
- icon: "text-gray-500 bg-transparent border-transparent hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700",
128
- toggle: "text-gray-500 bg-gray-100 border border-gray-200 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-600",
129
- success: "text-white bg-green-600 border border-green-600",
130
- // Avatar/image trigger - no background, opacity hover
131
- avatar: "bg-transparent border-transparent hover:opacity-80",
132
- // Menu items - full width, left-aligned, for dropdowns/sheets and sidebar nav
133
- "menu-item": "w-full text-left whitespace-nowrap text-gray-900 bg-transparent border-transparent hover:bg-gray-100 dark:text-white dark:hover:bg-gray-600",
134
- // Danger menu item - red text version
135
- "menu-item-danger": "w-full text-left whitespace-nowrap text-red-600 bg-transparent border-transparent hover:bg-red-50 dark:text-red-500 dark:hover:bg-gray-600",
136
- // Selectable card - bordered card-like button for list selections
137
- card: "w-full text-left text-gray-900 bg-white border border-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700",
138
- // Search result item - blue hover for search dropdowns
139
- "search-result": "w-full text-left text-gray-900 bg-transparent border-transparent hover:bg-blue-50 focus:bg-blue-50 dark:text-white dark:hover:bg-blue-900/20 dark:focus:bg-blue-900/20",
140
- // Sidebar toggle - compact pill for sidebar expand/collapse
141
- "sidebar-toggle": "w-6 h-7 text-gray-900 bg-blue-100 border border-blue-200 hover:bg-blue-200 shadow-lg dark:text-white dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700",
142
- // Bottom navigation item - vertical layout, transparent, for mobile nav bars
143
- nav: "flex-col h-full py-2 text-gray-500 bg-transparent border-transparent hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-500",
144
- // Calendar day cell - base styling with hover, colors overridden via className
145
- "calendar-day": "border-transparent text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700",
146
- };
147
-
148
- // Active state classes for ghost, toggle, menu-item, and nav
149
- const activeClasses = {
150
- ghost: "text-blue-700 bg-blue-50 dark:text-white dark:bg-gray-900",
151
- toggle: "text-white bg-blue-600 border-blue-600 hover:bg-blue-700 dark:text-white dark:bg-blue-600 dark:border-blue-600 dark:hover:bg-blue-700",
152
- "menu-item": "bg-blue-50 dark:bg-gray-700",
153
- nav: "text-blue-600 dark:text-blue-500",
154
- };
155
-
156
- // Disabled classes
157
- const disabledClasses = "bg-gray-200 border-gray-200 text-gray-400 cursor-not-allowed dark:bg-gray-700 dark:border-gray-700 dark:text-gray-500";
158
- // Disabled classes for transparent variants (link, ghost) - no background
159
- const disabledTransparentClasses = "text-gray-400 cursor-not-allowed dark:text-gray-500";
160
-
161
- let sizeClass = $derived((() => {
162
- if (resolvedVariant === "icon") return iconSizes[size] || iconSizes.sm;
163
- if (resolvedVariant === "avatar") return avatarSizes[size] || avatarSizes.md;
164
- if (resolvedVariant === "menu-item" || resolvedVariant === "menu-item-danger" || resolvedVariant === "search-result") {
165
- return menuItemSizes[size] || menuItemSizes.md;
166
- }
167
- if (resolvedVariant === "card") return cardSizes[size] || cardSizes.md;
168
- if (resolvedVariant === "link") return "text-sm";
169
- if (resolvedVariant === "sidebar-toggle") return ""; // Fixed dimensions in variant
170
- if (resolvedVariant === "nav") return ""; // Nav has sizing in variant class
171
- return sizeClasses[size] || sizeClasses.md;
172
- })());
173
-
174
- // Variants that should stay transparent when disabled
175
- const transparentVariants = ["link", "ghost", "ghost-red", "icon"];
176
-
177
- let variantClass = $derived((() => {
178
- if (success) return variantClasses.success;
179
- if (disabled && !loading && !success) {
180
- return transparentVariants.includes(resolvedVariant)
181
- ? disabledTransparentClasses
182
- : disabledClasses;
183
- }
184
- if (active && activeClasses[resolvedVariant]) {
185
- // When active, use ONLY active classes (they include all necessary styling)
186
- return activeClasses[resolvedVariant];
187
- }
188
- return variantClasses[resolvedVariant] || variantClasses.default;
189
- })());
190
-
191
- // Menu items, cards, and search results need left alignment, others are centered
192
- let isLeftAligned = $derived(
193
- resolvedVariant === "menu-item" ||
194
- resolvedVariant === "menu-item-danger" ||
195
- resolvedVariant === "card" ||
196
- resolvedVariant === "search-result"
197
- );
198
-
199
- // Use justify-between when there's trailing content (e.g., chevrons in nav items)
200
- let hasTrailing = $derived(typeof trailing === 'function' || trailing);
201
-
202
- let buttonClasses = $derived(twMerge(
203
- "relative",
204
- isLeftAligned
205
- ? (hasTrailing ? "flex items-center justify-between" : "flex items-center justify-start")
206
- : "inline-flex items-center justify-center",
207
- "rounded-lg",
208
- "font-medium leading-none",
209
- "focus:outline-none",
210
- // Apple-style micro-interactions (only when not disabled)
211
- "transition-all duration-150 ease-out",
212
- effectiveDisabled ? "" : "active:scale-[0.97] active:opacity-90",
213
- "select-none",
214
- sizeClass,
215
- variantClass,
216
- effectiveDisabled ? "cursor-not-allowed" : "cursor-pointer",
217
- className
218
- ));
219
- </script>
220
-
221
- {#if href}
222
- <a
223
- {href}
224
- class="{buttonClasses} {loading ? 'loading-pulse' : ''}"
225
- {onclick}
226
- {...restProps}
227
- >
228
- {#if loading}
229
- <span class="shimmer-overlay"></span>
230
- {/if}
231
- <span class="inline-flex items-center gap-1.5" class:invisible={loading || success}>
232
- {#if typeof children === 'function'}{@render children()}{:else if children}{children}{/if}
233
- </span>
234
- {#if typeof trailing === 'function'}{@render trailing()}{:else if trailing}{trailing}{/if}
235
- </a>
236
- {:else}
237
- <button
238
- {type}
239
- class="{buttonClasses} {loading ? 'loading-pulse' : ''}"
240
- disabled={effectiveDisabled}
241
- {onclick}
242
- {...restProps}
243
- >
244
- {#if loading}
245
- <span class="shimmer-overlay"></span>
246
- {/if}
247
- <span class="inline-flex items-center gap-1.5" class:invisible={loading || success}>
248
- {#if typeof children === 'function'}{@render children()}{:else if children}{children}{/if}
249
- </span>
250
- {#if typeof trailing === 'function'}{@render trailing()}{:else if trailing}{trailing}{/if}
251
- {#if success}
252
- <span class="absolute inset-0 flex items-center justify-center gap-2">
253
- <CheckOutline class="w-5 h-5" />
254
- {#if successText}<span class="font-medium">{successText}</span>{/if}
255
- </span>
256
- {/if}
257
- </button>
258
- {/if}
259
-
260
- <style>
261
- .loading-pulse {
262
- animation: pulse-bg 2s ease-in-out infinite;
263
- }
264
-
265
- @keyframes pulse-bg {
266
- 0%, 100% { filter: brightness(1); }
267
- 50% { filter: brightness(1.15); }
268
- }
269
-
270
- .shimmer-overlay {
271
- position: absolute;
272
- inset: 0;
273
- overflow: hidden;
274
- border-radius: inherit;
275
- }
276
-
277
- .shimmer-overlay::after {
278
- content: '';
279
- position: absolute;
280
- top: 0;
281
- left: -100%;
282
- width: 100%;
283
- height: 100%;
284
- background: linear-gradient(
285
- 90deg,
286
- transparent 0%,
287
- rgba(255, 255, 255, 0.2) 50%,
288
- transparent 100%
289
- );
290
- animation: shimmer 1s ease-in-out infinite;
291
- }
292
-
293
- @keyframes shimmer {
294
- 0% {
295
- left: -100%;
296
- }
297
- 100% {
298
- left: 100%;
299
- }
300
- }
301
- </style>
1
+ <script>
2
+ /**
3
+ * Button Component - Flowbite Native (Pure Tailwind)
4
+ * Migrated to Svelte 5 runes
5
+ *
6
+ * Variants (Flowbite naming):
7
+ * - default: Primary blue button
8
+ * - alternative: Gray outline button
9
+ * - outline: Blue outline button
10
+ * - red: Destructive solid button
11
+ * - red-outline: Destructive outline button
12
+ * - ghost: Text-only button
13
+ * - ghost-red: Destructive text button
14
+ * - icon: Icon-only button
15
+ * - toggle: Toggle/pill button
16
+ * - avatar: Avatar/image trigger button (no padding, opacity hover)
17
+ * - menu-item: Dropdown/sheet menu item (full width, left-aligned)
18
+ * - menu-item-danger: Destructive menu item (red text)
19
+ * - card: Selectable card button (bordered, for list selections)
20
+ * - sidebar-toggle: Compact pill for sidebar expand/collapse
21
+ *
22
+ * Sizes (Flowbite native): xs, sm, md, lg, xl
23
+ */
24
+ import { CheckOutline } from '../Icons';
25
+ import { twMerge } from 'tailwind-merge';
26
+
27
+ /** @type {{
28
+ variant?: string,
29
+ size?: string,
30
+ disabled?: boolean,
31
+ loading?: boolean,
32
+ success?: boolean,
33
+ successText?: string,
34
+ active?: boolean,
35
+ href?: string | null,
36
+ type?: 'button' | 'submit' | 'reset',
37
+ children?: any,
38
+ trailing?: any,
39
+ class?: string,
40
+ onclick?: (e: MouseEvent) => void,
41
+ [key: string]: any
42
+ }} */
43
+ let {
44
+ variant = "default",
45
+ size = "md",
46
+ disabled = false,
47
+ loading = false,
48
+ success = false,
49
+ successText = "",
50
+ active = false,
51
+ href = null,
52
+ type = "button",
53
+ children,
54
+ trailing,
55
+ class: className = "",
56
+ onclick,
57
+ ...restProps
58
+ } = $props();
59
+
60
+ // Legacy variant name mapping
61
+ const variantMap = {
62
+ "blue-solid": "default",
63
+ "gray-outline": "alternative",
64
+ "blue-outline": "outline",
65
+ "red-solid": "red",
66
+ "red-text": "ghost-red",
67
+ "gray-text": "ghost",
68
+ };
69
+
70
+ // Resolve legacy names
71
+ let resolvedVariant = $derived(variantMap[variant] || variant);
72
+ let effectiveDisabled = $derived(disabled || loading || success);
73
+
74
+ // Size classes - Flowbite native sizing
75
+ const sizeClasses = {
76
+ xs: "px-3 py-1.5 text-xs",
77
+ sm: "px-3 py-2 text-sm",
78
+ md: "px-4 py-2.5 text-sm",
79
+ lg: "px-5 py-3 text-sm",
80
+ xl: "px-6 py-3.5 text-sm",
81
+ "xl-medium": "w-full px-6 py-3.5 text-sm",
82
+ full: "w-full px-5 py-3 text-sm",
83
+ "full-md-auto": "w-full px-5 py-3 text-sm md:w-auto",
84
+ half: "w-1/2 px-4 py-2.5 text-sm",
85
+ };
86
+
87
+ // Icon variant sizes
88
+ const iconSizes = {
89
+ xs: "p-1.5",
90
+ sm: "p-2",
91
+ md: "p-2.5",
92
+ lg: "p-3",
93
+ };
94
+
95
+ // Avatar variant sizes (no padding, just min dimensions)
96
+ const avatarSizes = {
97
+ sm: "min-w-8 min-h-8",
98
+ md: "min-w-10 min-h-10",
99
+ lg: "min-w-12 min-h-12",
100
+ };
101
+
102
+ // Menu item sizes
103
+ const menuItemSizes = {
104
+ sm: "py-2 px-4 text-sm",
105
+ md: "py-3 px-4 text-sm",
106
+ lg: "py-4 px-6 text-base",
107
+ nav: "py-2 px-3 text-base font-normal",
108
+ };
109
+
110
+ // Card sizes (larger padding for card-like buttons)
111
+ const cardSizes = {
112
+ sm: "p-3 text-sm",
113
+ md: "p-4 text-sm",
114
+ lg: "p-5 text-base",
115
+ };
116
+
117
+ // Variant classes with all states in Tailwind (no focus rings - design decision)
118
+ const variantClasses = {
119
+ default: "text-white bg-blue-700 border border-blue-700 hover:bg-blue-800 dark:bg-blue-600 dark:hover:bg-blue-700",
120
+ alternative: "text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700",
121
+ outline: "text-blue-700 bg-transparent border border-blue-700 hover:text-white hover:bg-blue-800 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500",
122
+ red: "text-white bg-red-700 border border-red-700 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700",
123
+ "red-outline": "text-red-700 bg-transparent border border-red-700 hover:text-white hover:bg-red-800 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600",
124
+ ghost: "text-gray-500 bg-transparent border-transparent hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white",
125
+ "ghost-red": "text-red-700 bg-transparent border-transparent hover:bg-red-100 hover:text-red-900 dark:text-red-500 dark:hover:bg-red-900/50 dark:hover:text-red-400",
126
+ link: "text-blue-700 bg-transparent border-transparent hover:underline dark:text-blue-500",
127
+ icon: "text-gray-500 bg-transparent border-transparent hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700",
128
+ toggle: "text-gray-500 bg-gray-100 border border-gray-200 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-600",
129
+ success: "text-white bg-green-600 border border-green-600",
130
+ // Avatar/image trigger - no background, opacity hover
131
+ avatar: "bg-transparent border-transparent hover:opacity-80",
132
+ // Menu items - full width, left-aligned, for dropdowns/sheets and sidebar nav
133
+ "menu-item": "w-full text-left whitespace-nowrap text-gray-900 bg-transparent border-transparent hover:bg-gray-100 dark:text-white dark:hover:bg-gray-600",
134
+ // Danger menu item - red text version
135
+ "menu-item-danger": "w-full text-left whitespace-nowrap text-red-600 bg-transparent border-transparent hover:bg-red-50 dark:text-red-500 dark:hover:bg-gray-600",
136
+ // Selectable card - bordered card-like button for list selections
137
+ card: "w-full text-left text-gray-900 bg-white border border-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700",
138
+ // Search result item - blue hover for search dropdowns
139
+ "search-result": "w-full text-left text-gray-900 bg-transparent border-transparent hover:bg-blue-50 focus:bg-blue-50 dark:text-white dark:hover:bg-blue-900/20 dark:focus:bg-blue-900/20",
140
+ // Sidebar toggle - compact pill for sidebar expand/collapse
141
+ "sidebar-toggle": "w-6 h-7 text-gray-900 bg-blue-100 border border-blue-200 hover:bg-blue-200 shadow-lg dark:text-white dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700",
142
+ // Bottom navigation item - vertical layout, transparent, for mobile nav bars
143
+ nav: "flex-col h-full py-2 text-gray-500 bg-transparent border-transparent hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-500",
144
+ // Calendar day cell - base styling with hover, colors overridden via className
145
+ "calendar-day": "border-transparent text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700",
146
+ };
147
+
148
+ // Active state classes for ghost, toggle, menu-item, and nav
149
+ const activeClasses = {
150
+ ghost: "text-blue-700 bg-blue-50 dark:text-white dark:bg-gray-900",
151
+ toggle: "text-white bg-blue-600 border-blue-600 hover:bg-blue-700 dark:text-white dark:bg-blue-600 dark:border-blue-600 dark:hover:bg-blue-700",
152
+ "menu-item": "bg-blue-50 dark:bg-gray-700",
153
+ nav: "text-blue-600 dark:text-blue-500",
154
+ };
155
+
156
+ // Disabled classes
157
+ const disabledClasses = "bg-gray-200 border-gray-200 text-gray-400 cursor-not-allowed dark:bg-gray-700 dark:border-gray-700 dark:text-gray-500";
158
+ // Disabled classes for transparent variants (link, ghost) - no background
159
+ const disabledTransparentClasses = "text-gray-400 cursor-not-allowed dark:text-gray-500";
160
+
161
+ let sizeClass = $derived((() => {
162
+ if (resolvedVariant === "icon") return iconSizes[size] || iconSizes.sm;
163
+ if (resolvedVariant === "avatar") return avatarSizes[size] || avatarSizes.md;
164
+ if (resolvedVariant === "menu-item" || resolvedVariant === "menu-item-danger" || resolvedVariant === "search-result") {
165
+ return menuItemSizes[size] || menuItemSizes.md;
166
+ }
167
+ if (resolvedVariant === "card") return cardSizes[size] || cardSizes.md;
168
+ if (resolvedVariant === "link") return "text-sm";
169
+ if (resolvedVariant === "sidebar-toggle") return ""; // Fixed dimensions in variant
170
+ if (resolvedVariant === "nav") return ""; // Nav has sizing in variant class
171
+ return sizeClasses[size] || sizeClasses.md;
172
+ })());
173
+
174
+ // Variants that should stay transparent when disabled
175
+ const transparentVariants = ["link", "ghost", "ghost-red", "icon"];
176
+
177
+ let variantClass = $derived((() => {
178
+ if (success) return variantClasses.success;
179
+ if (disabled && !loading && !success) {
180
+ return transparentVariants.includes(resolvedVariant)
181
+ ? disabledTransparentClasses
182
+ : disabledClasses;
183
+ }
184
+ if (active && activeClasses[resolvedVariant]) {
185
+ // When active, use ONLY active classes (they include all necessary styling)
186
+ return activeClasses[resolvedVariant];
187
+ }
188
+ return variantClasses[resolvedVariant] || variantClasses.default;
189
+ })());
190
+
191
+ // Menu items, cards, and search results need left alignment, others are centered
192
+ let isLeftAligned = $derived(
193
+ resolvedVariant === "menu-item" ||
194
+ resolvedVariant === "menu-item-danger" ||
195
+ resolvedVariant === "card" ||
196
+ resolvedVariant === "search-result"
197
+ );
198
+
199
+ // Use justify-between when there's trailing content (e.g., chevrons in nav items)
200
+ let hasTrailing = $derived(typeof trailing === 'function' || trailing);
201
+
202
+ let buttonClasses = $derived(twMerge(
203
+ "relative",
204
+ isLeftAligned
205
+ ? (hasTrailing ? "flex items-center justify-between" : "flex items-center justify-start")
206
+ : "inline-flex items-center justify-center",
207
+ "rounded-lg",
208
+ "font-medium leading-none",
209
+ "focus:outline-none",
210
+ // Apple-style micro-interactions (only when not disabled)
211
+ "transition-all duration-150 ease-out",
212
+ effectiveDisabled ? "" : "active:scale-[0.97] active:opacity-90",
213
+ "select-none",
214
+ sizeClass,
215
+ variantClass,
216
+ effectiveDisabled ? "cursor-not-allowed" : "cursor-pointer",
217
+ className
218
+ ));
219
+ </script>
220
+
221
+ {#if href}
222
+ <a
223
+ {href}
224
+ class="{buttonClasses} {loading ? 'loading-pulse' : ''}"
225
+ {onclick}
226
+ {...restProps}
227
+ >
228
+ {#if loading}
229
+ <span class="shimmer-overlay"></span>
230
+ {/if}
231
+ <span class="inline-flex items-center gap-1.5" class:invisible={loading || success}>
232
+ {#if typeof children === 'function'}{@render children()}{:else if children}{children}{/if}
233
+ </span>
234
+ {#if typeof trailing === 'function'}{@render trailing()}{:else if trailing}{trailing}{/if}
235
+ </a>
236
+ {:else}
237
+ <button
238
+ {type}
239
+ class="{buttonClasses} {loading ? 'loading-pulse' : ''}"
240
+ disabled={effectiveDisabled}
241
+ {onclick}
242
+ {...restProps}
243
+ >
244
+ {#if loading}
245
+ <span class="shimmer-overlay"></span>
246
+ {/if}
247
+ <span class="inline-flex items-center gap-1.5" class:invisible={loading || success}>
248
+ {#if typeof children === 'function'}{@render children()}{:else if children}{children}{/if}
249
+ </span>
250
+ {#if typeof trailing === 'function'}{@render trailing()}{:else if trailing}{trailing}{/if}
251
+ {#if success}
252
+ <span class="absolute inset-0 flex items-center justify-center gap-2">
253
+ <CheckOutline class="w-5 h-5" />
254
+ {#if successText}<span class="font-medium">{successText}</span>{/if}
255
+ </span>
256
+ {/if}
257
+ </button>
258
+ {/if}
259
+
260
+ <style>
261
+ .loading-pulse {
262
+ animation: pulse-bg 2s ease-in-out infinite;
263
+ }
264
+
265
+ @keyframes pulse-bg {
266
+ 0%, 100% { filter: brightness(1); }
267
+ 50% { filter: brightness(1.15); }
268
+ }
269
+
270
+ .shimmer-overlay {
271
+ position: absolute;
272
+ inset: 0;
273
+ overflow: hidden;
274
+ border-radius: inherit;
275
+ }
276
+
277
+ .shimmer-overlay::after {
278
+ content: '';
279
+ position: absolute;
280
+ top: 0;
281
+ left: -100%;
282
+ width: 100%;
283
+ height: 100%;
284
+ background: linear-gradient(
285
+ 90deg,
286
+ transparent 0%,
287
+ rgba(255, 255, 255, 0.2) 50%,
288
+ transparent 100%
289
+ );
290
+ animation: shimmer 1s ease-in-out infinite;
291
+ }
292
+
293
+ @keyframes shimmer {
294
+ 0% {
295
+ left: -100%;
296
+ }
297
+ 100% {
298
+ left: 100%;
299
+ }
300
+ }
301
+ </style>