@stacksjs/components 0.2.3

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 (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Keyframe animation utilities using Web Animations API
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { animate, KeyframeAnimations } from '@stacksjs/components'
7
+ *
8
+ * // Use predefined animation
9
+ * animate(element, 'fadeIn', { duration: 500 })
10
+ *
11
+ * // Custom keyframes
12
+ * createKeyframeAnimation(element, [
13
+ * { offset: 0, opacity: 0, transform: 'scale(0.5)' },
14
+ * { offset: 1, opacity: 1, transform: 'scale(1)' }
15
+ * ], { duration: 300 })
16
+ * ```
17
+ */
18
+
19
+ /**
20
+ * Single keyframe definition
21
+ */
22
+ export interface Keyframe {
23
+ /** Keyframe position (0 to 1) */
24
+ offset: number
25
+ /** CSS properties to animate */
26
+ [property: string]: any
27
+ }
28
+
29
+ /**
30
+ * Animation configuration options
31
+ */
32
+ export interface AnimationOptions {
33
+ /** Animation duration in milliseconds */
34
+ duration?: number
35
+ /** Easing function (CSS easing string) */
36
+ easing?: string
37
+ /** Delay before animation starts in milliseconds */
38
+ delay?: number
39
+ /** Number of iterations (Infinity for infinite) */
40
+ iterations?: number
41
+ /** Animation direction */
42
+ direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'
43
+ /** Fill mode */
44
+ fill?: 'none' | 'forwards' | 'backwards' | 'both'
45
+ }
46
+
47
+ /**
48
+ * Create a keyframe animation using Web Animations API
49
+ *
50
+ * @param element - Target HTML element
51
+ * @param keyframes - Array of keyframe definitions
52
+ * @param options - Animation options
53
+ * @returns Animation instance or null if not supported
54
+ */
55
+ export function createKeyframeAnimation(
56
+ element: HTMLElement,
57
+ keyframes: readonly Keyframe[] | Keyframe[],
58
+ options: AnimationOptions = {},
59
+ ): Animation | null {
60
+ if (typeof element.animate !== 'function') {
61
+ console.warn('Web Animations API not supported')
62
+ return null
63
+ }
64
+
65
+ const {
66
+ duration = 1000,
67
+ easing = 'ease',
68
+ delay = 0,
69
+ iterations = 1,
70
+ direction = 'normal',
71
+ fill = 'both',
72
+ } = options
73
+
74
+ return element.animate([...keyframes], {
75
+ duration,
76
+ easing,
77
+ delay,
78
+ iterations,
79
+ direction,
80
+ fill,
81
+ })
82
+ }
83
+
84
+ /**
85
+ * Predefined keyframe animation presets
86
+ *
87
+ * Available animations:
88
+ * - fadeIn/fadeOut
89
+ * - slideInUp/Down/Left/Right, slideOutUp/Down/Left/Right
90
+ * - zoomIn/Out
91
+ * - rotate, bounce, pulse, shake, swing
92
+ * - flip, rubberBand, heartbeat
93
+ */
94
+ export const KeyframeAnimations = {
95
+ fadeIn: [
96
+ { offset: 0, opacity: 0 },
97
+ { offset: 1, opacity: 1 },
98
+ ],
99
+ fadeOut: [
100
+ { offset: 0, opacity: 1 },
101
+ { offset: 1, opacity: 0 },
102
+ ],
103
+ slideInUp: [
104
+ { offset: 0, transform: 'translateY(100%)', opacity: 0 },
105
+ { offset: 1, transform: 'translateY(0)', opacity: 1 },
106
+ ],
107
+ slideInDown: [
108
+ { offset: 0, transform: 'translateY(-100%)', opacity: 0 },
109
+ { offset: 1, transform: 'translateY(0)', opacity: 1 },
110
+ ],
111
+ slideInLeft: [
112
+ { offset: 0, transform: 'translateX(-100%)', opacity: 0 },
113
+ { offset: 1, transform: 'translateX(0)', opacity: 1 },
114
+ ],
115
+ slideInRight: [
116
+ { offset: 0, transform: 'translateX(100%)', opacity: 0 },
117
+ { offset: 1, transform: 'translateX(0)', opacity: 1 },
118
+ ],
119
+ slideOutUp: [
120
+ { offset: 0, transform: 'translateY(0)', opacity: 1 },
121
+ { offset: 1, transform: 'translateY(-100%)', opacity: 0 },
122
+ ],
123
+ slideOutDown: [
124
+ { offset: 0, transform: 'translateY(0)', opacity: 1 },
125
+ { offset: 1, transform: 'translateY(100%)', opacity: 0 },
126
+ ],
127
+ slideOutLeft: [
128
+ { offset: 0, transform: 'translateX(0)', opacity: 1 },
129
+ { offset: 1, transform: 'translateX(-100%)', opacity: 0 },
130
+ ],
131
+ slideOutRight: [
132
+ { offset: 0, transform: 'translateX(0)', opacity: 1 },
133
+ { offset: 1, transform: 'translateX(100%)', opacity: 0 },
134
+ ],
135
+ zoomIn: [
136
+ { offset: 0, transform: 'scale(0)', opacity: 0 },
137
+ { offset: 0.5, opacity: 1 },
138
+ { offset: 1, transform: 'scale(1)' },
139
+ ],
140
+ zoomOut: [
141
+ { offset: 0, transform: 'scale(1)', opacity: 1 },
142
+ { offset: 0.5, opacity: 1 },
143
+ { offset: 1, transform: 'scale(0)', opacity: 0 },
144
+ ],
145
+ rotate: [
146
+ { offset: 0, transform: 'rotate(0deg)' },
147
+ { offset: 1, transform: 'rotate(360deg)' },
148
+ ],
149
+ bounce: [
150
+ { offset: 0, transform: 'translateY(0)' },
151
+ { offset: 0.2, transform: 'translateY(0)' },
152
+ { offset: 0.4, transform: 'translateY(-30px)' },
153
+ { offset: 0.43, transform: 'translateY(-30px)' },
154
+ { offset: 0.53, transform: 'translateY(0)' },
155
+ { offset: 0.7, transform: 'translateY(-15px)' },
156
+ { offset: 0.8, transform: 'translateY(0)' },
157
+ { offset: 0.9, transform: 'translateY(-4px)' },
158
+ { offset: 1, transform: 'translateY(0)' },
159
+ ],
160
+ pulse: [
161
+ { offset: 0, transform: 'scale(1)' },
162
+ { offset: 0.5, transform: 'scale(1.05)' },
163
+ { offset: 1, transform: 'scale(1)' },
164
+ ],
165
+ shake: [
166
+ { offset: 0, transform: 'translateX(0)' },
167
+ { offset: 0.1, transform: 'translateX(-10px)' },
168
+ { offset: 0.2, transform: 'translateX(10px)' },
169
+ { offset: 0.3, transform: 'translateX(-10px)' },
170
+ { offset: 0.4, transform: 'translateX(10px)' },
171
+ { offset: 0.5, transform: 'translateX(-10px)' },
172
+ { offset: 0.6, transform: 'translateX(10px)' },
173
+ { offset: 0.7, transform: 'translateX(-10px)' },
174
+ { offset: 0.8, transform: 'translateX(10px)' },
175
+ { offset: 0.9, transform: 'translateX(-10px)' },
176
+ { offset: 1, transform: 'translateX(0)' },
177
+ ],
178
+ swing: [
179
+ { offset: 0, transform: 'rotate(0deg)' },
180
+ { offset: 0.2, transform: 'rotate(15deg)' },
181
+ { offset: 0.4, transform: 'rotate(-10deg)' },
182
+ { offset: 0.6, transform: 'rotate(5deg)' },
183
+ { offset: 0.8, transform: 'rotate(-5deg)' },
184
+ { offset: 1, transform: 'rotate(0deg)' },
185
+ ],
186
+ flip: [
187
+ { offset: 0, transform: 'perspective(400px) rotateY(0)', animationTimingFunction: 'ease-out' },
188
+ { offset: 0.4, transform: 'perspective(400px) translateZ(150px) rotateY(170deg)', animationTimingFunction: 'ease-out' },
189
+ { offset: 0.5, transform: 'perspective(400px) translateZ(150px) rotateY(190deg)', animationTimingFunction: 'ease-in' },
190
+ { offset: 0.8, transform: 'perspective(400px) rotateY(360deg)', animationTimingFunction: 'ease-in' },
191
+ { offset: 1, transform: 'perspective(400px)', animationTimingFunction: 'ease-in' },
192
+ ],
193
+ rubberBand: [
194
+ { offset: 0, transform: 'scale(1)' },
195
+ { offset: 0.3, transform: 'scaleX(1.25) scaleY(0.75)' },
196
+ { offset: 0.4, transform: 'scaleX(0.75) scaleY(1.25)' },
197
+ { offset: 0.5, transform: 'scaleX(1.15) scaleY(0.85)' },
198
+ { offset: 0.65, transform: 'scaleX(0.95) scaleY(1.05)' },
199
+ { offset: 0.75, transform: 'scaleX(1.05) scaleY(0.95)' },
200
+ { offset: 1, transform: 'scale(1)' },
201
+ ],
202
+ heartbeat: [
203
+ { offset: 0, transform: 'scale(1)' },
204
+ { offset: 0.14, transform: 'scale(1.3)' },
205
+ { offset: 0.28, transform: 'scale(1)' },
206
+ { offset: 0.42, transform: 'scale(1.3)' },
207
+ { offset: 0.7, transform: 'scale(1)' },
208
+ ],
209
+ } as const
210
+
211
+ /**
212
+ * Type for predefined animation names
213
+ */
214
+ export type KeyframeAnimationName = keyof typeof KeyframeAnimations
215
+
216
+ /**
217
+ * Animate an element with a predefined animation
218
+ *
219
+ * @param element - Target HTML element
220
+ * @param animationName - Name of predefined animation
221
+ * @param options - Animation options
222
+ * @returns Animation instance or null if not supported
223
+ */
224
+ export function animate(
225
+ element: HTMLElement,
226
+ animationName: KeyframeAnimationName,
227
+ options: AnimationOptions = {},
228
+ ): Animation | null {
229
+ return createKeyframeAnimation(element, KeyframeAnimations[animationName], options)
230
+ }
@@ -0,0 +1,473 @@
1
+ /**
2
+ * Error handling utilities for @stacksjs/components
3
+ *
4
+ * Provides clear, actionable error messages with links to documentation
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { ComponentError, handleError } from '@stacksjs/components'
9
+ *
10
+ * throw new ComponentError('Button', 'Invalid variant provided', {
11
+ * received: 'invalid',
12
+ * expected: ['primary', 'secondary', 'outline']
13
+ * })
14
+ * ```
15
+ */
16
+
17
+ import process from 'node:process'
18
+
19
+ /**
20
+ * Base URL for documentation
21
+ */
22
+ const DOCS_BASE_URL = 'https://stacksjs.com/components'
23
+
24
+ /**
25
+ * Error context information
26
+ */
27
+ export interface ErrorContext {
28
+ /** The component name where the error occurred */
29
+ component?: string
30
+ /** The prop name that caused the error */
31
+ prop?: string
32
+ /** The value that was received */
33
+ received?: any
34
+ /** The expected value(s) */
35
+ expected?: any
36
+ /** Additional details about the error */
37
+ details?: string
38
+ /** Link to relevant documentation */
39
+ docsUrl?: string
40
+ }
41
+
42
+ /**
43
+ * Custom error class for component errors
44
+ */
45
+ export class ComponentError extends Error {
46
+ component: string
47
+ context: ErrorContext
48
+
49
+ constructor(component: string, message: string, context: ErrorContext = {}) {
50
+ const fullMessage = ComponentError.formatMessage(component, message, context)
51
+ super(fullMessage)
52
+
53
+ this.name = 'ComponentError'
54
+ this.component = component
55
+ this.context = { component, ...context }
56
+
57
+ // Maintains proper stack trace for where error was thrown (only available on V8)
58
+ if (Error.captureStackTrace) {
59
+ Error.captureStackTrace(this, ComponentError)
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Format error message with context
65
+ */
66
+ static formatMessage(component: string, message: string, context: ErrorContext): string {
67
+ let formatted = `[${component}] ${message}`
68
+
69
+ if (context.prop) {
70
+ formatted += `\n Prop: "${context.prop}"`
71
+ }
72
+
73
+ if (context.received !== undefined) {
74
+ formatted += `\n Received: ${JSON.stringify(context.received)}`
75
+ }
76
+
77
+ if (context.expected !== undefined) {
78
+ const expected = Array.isArray(context.expected)
79
+ ? context.expected.map(v => JSON.stringify(v)).join(', ')
80
+ : JSON.stringify(context.expected)
81
+ formatted += `\n Expected: ${expected}`
82
+ }
83
+
84
+ if (context.details) {
85
+ formatted += `\n Details: ${context.details}`
86
+ }
87
+
88
+ if (context.docsUrl) {
89
+ formatted += `\n Documentation: ${context.docsUrl}`
90
+ }
91
+ else if (component) {
92
+ formatted += `\n Documentation: ${DOCS_BASE_URL}/${component.toLowerCase()}`
93
+ }
94
+
95
+ return formatted
96
+ }
97
+
98
+ /**
99
+ * Get error as plain object
100
+ */
101
+ toJSON(): { name: string, message: string, component: string, context: ErrorContext | undefined, stack: string | undefined } {
102
+ return {
103
+ name: this.name,
104
+ message: this.message,
105
+ component: this.component,
106
+ context: this.context,
107
+ stack: this.stack,
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Error types for common scenarios
114
+ */
115
+ export const ErrorTypes = {
116
+ /**
117
+ * Invalid prop value error
118
+ */
119
+ invalidProp(component: string, prop: string, received: any, expected: any): ComponentError {
120
+ return new ComponentError(
121
+ component,
122
+ `Invalid prop value for "${prop}"`,
123
+ {
124
+ prop,
125
+ received,
126
+ expected,
127
+ details: Array.isArray(expected)
128
+ ? `The "${prop}" prop must be one of the allowed values`
129
+ : `The "${prop}" prop does not match the expected type`,
130
+ },
131
+ )
132
+ },
133
+
134
+ /**
135
+ * Required prop missing error
136
+ */
137
+ requiredProp(component: string, prop: string): ComponentError {
138
+ return new ComponentError(
139
+ component,
140
+ `Required prop "${prop}" is missing`,
141
+ {
142
+ prop,
143
+ details: `The "${prop}" prop is required for ${component} to function correctly`,
144
+ },
145
+ )
146
+ },
147
+
148
+ /**
149
+ * Invalid prop type error
150
+ */
151
+ invalidType(component: string, prop: string, received: any, expected: string): ComponentError {
152
+ return new ComponentError(
153
+ component,
154
+ `Invalid type for prop "${prop}"`,
155
+ {
156
+ prop,
157
+ received: typeof received,
158
+ expected,
159
+ details: `Expected "${prop}" to be of type ${expected}, but received ${typeof received}`,
160
+ },
161
+ )
162
+ },
163
+
164
+ /**
165
+ * Component not found error
166
+ */
167
+ componentNotFound(componentName: string): ComponentError {
168
+ return new ComponentError(
169
+ 'ComponentLoader',
170
+ `Component "${componentName}" not found`,
171
+ {
172
+ component: componentName,
173
+ details: 'Make sure the component is properly imported and registered',
174
+ },
175
+ )
176
+ },
177
+
178
+ /**
179
+ * Invalid children error
180
+ */
181
+ invalidChildren(component: string, received: any, expected: string): ComponentError {
182
+ return new ComponentError(
183
+ component,
184
+ 'Invalid children',
185
+ {
186
+ received: typeof received,
187
+ expected,
188
+ details: `${component} expects ${expected} as children`,
189
+ },
190
+ )
191
+ },
192
+
193
+ /**
194
+ * State update error
195
+ */
196
+ stateUpdate(component: string, stateName: string, error: Error): ComponentError {
197
+ return new ComponentError(
198
+ component,
199
+ `Failed to update state "${stateName}"`,
200
+ {
201
+ details: error.message,
202
+ },
203
+ )
204
+ },
205
+
206
+ /**
207
+ * Event handler error
208
+ */
209
+ eventHandler(component: string, eventName: string, error: Error): ComponentError {
210
+ return new ComponentError(
211
+ component,
212
+ `Error in "${eventName}" event handler`,
213
+ {
214
+ details: error.message,
215
+ },
216
+ )
217
+ },
218
+
219
+ /**
220
+ * Render error
221
+ */
222
+ render(component: string, error: Error): ComponentError {
223
+ return new ComponentError(
224
+ component,
225
+ 'Failed to render component',
226
+ {
227
+ details: error.message,
228
+ },
229
+ )
230
+ },
231
+ }
232
+
233
+ /**
234
+ * Global error handler
235
+ */
236
+ let errorHandler: ((error: Error) => void) | null = null
237
+
238
+ /**
239
+ * Set global error handler
240
+ *
241
+ * @param handler - Error handler function
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * setErrorHandler((error) => {
246
+ * console.error('Component error:', error)
247
+ * // Send to error tracking service
248
+ * })
249
+ * ```
250
+ */
251
+ export function setErrorHandler(handler: (error: Error) => void): void {
252
+ errorHandler = handler
253
+ }
254
+
255
+ /**
256
+ * Clear the global error handler (useful for testing)
257
+ */
258
+ export function clearErrorHandler(): void {
259
+ errorHandler = null
260
+ }
261
+
262
+ /**
263
+ * Handle an error
264
+ *
265
+ * @param error - Error to handle
266
+ * @param options - Handling options
267
+ * @param options.throwError - Whether to re-throw the error after handling
268
+ * @param options.logError - Whether to log the error to console
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * try {
273
+ * // Component code
274
+ * } catch (error) {
275
+ * handleError(error, { throwError: false })
276
+ * }
277
+ * ```
278
+ */
279
+ export function handleError(
280
+ error: Error,
281
+ options: {
282
+ /** Whether to re-throw the error after handling */
283
+ throwError?: boolean
284
+ /** Whether to log the error to console */
285
+ logError?: boolean
286
+ } = {},
287
+ ): void {
288
+ const { throwError = true, logError = true } = options
289
+
290
+ // Log to console in development
291
+ if (logError && process.env.NODE_ENV !== 'production') {
292
+ console.error('Component Error:', error)
293
+
294
+ if (error instanceof ComponentError) {
295
+ console.group('Error Details')
296
+ console.log('Component:', error.component)
297
+ console.log('Context:', error.context)
298
+ console.groupEnd()
299
+ }
300
+ }
301
+
302
+ // Call global error handler if set
303
+ if (errorHandler) {
304
+ try {
305
+ errorHandler(error)
306
+ }
307
+ catch (handlerError) {
308
+ console.error('Error in error handler:', handlerError)
309
+ }
310
+ }
311
+
312
+ // Re-throw if requested
313
+ if (throwError) {
314
+ throw error
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Create a wrapped function that catches and handles errors
320
+ *
321
+ * @param fn - Function to wrap
322
+ * @param component - Component name for error context
323
+ * @param eventName - Event name for error context
324
+ * @returns Wrapped function
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * const safeHandler = wrapErrorHandler(
329
+ * () => { /* ... * / },
330
+ * 'Button',
331
+ * 'onClick'
332
+ * )
333
+ * ```
334
+ */
335
+ export function wrapErrorHandler<T extends (...args: any[]) => any>(
336
+ fn: T,
337
+ component: string,
338
+ eventName: string,
339
+ ): T {
340
+ return ((...args: any[]) => {
341
+ try {
342
+ const result = fn(...args)
343
+
344
+ // Handle async functions
345
+ if (result instanceof Promise) {
346
+ return result.catch((error) => {
347
+ const wrappedError = ErrorTypes.eventHandler(component, eventName, error)
348
+ handleError(wrappedError, { throwError: false })
349
+ throw wrappedError
350
+ })
351
+ }
352
+
353
+ return result
354
+ }
355
+ catch (error) {
356
+ const wrappedError = ErrorTypes.eventHandler(component, eventName, error as Error)
357
+ handleError(wrappedError, { throwError: false })
358
+ throw wrappedError
359
+ }
360
+ }) as T
361
+ }
362
+
363
+ /**
364
+ * Assert a condition with a custom error
365
+ *
366
+ * @param condition - Condition to check
367
+ * @param error - Error to throw if condition is false
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * assert(
372
+ * props.variant !== undefined,
373
+ * ErrorTypes.requiredProp('Button', 'variant')
374
+ * )
375
+ * ```
376
+ */
377
+ export function assert(condition: any, error: Error): asserts condition {
378
+ if (!condition) {
379
+ throw error
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Warning message handler
385
+ */
386
+ export function warn(component: string, message: string, context: Partial<ErrorContext> = {}): void {
387
+ if (process.env.NODE_ENV === 'production') {
388
+ return
389
+ }
390
+
391
+ const fullMessage = ComponentError.formatMessage(component, message, context as ErrorContext)
392
+
393
+ console.warn(`⚠️ ${fullMessage}`)
394
+ }
395
+
396
+ /**
397
+ * Development-only assertion
398
+ */
399
+ export function devAssert(condition: any, error: Error): asserts condition {
400
+ if (process.env.NODE_ENV === 'production') {
401
+ return
402
+ }
403
+
404
+ assert(condition, error)
405
+ }
406
+
407
+ /**
408
+ * Create a debug logger for a component
409
+ *
410
+ * @param component - Component name
411
+ * @returns Debug logger functions
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * const debug = createDebugger('Button')
416
+ * debug.log('Button rendered')
417
+ * debug.error('Invalid prop')
418
+ * ```
419
+ */
420
+ export function createDebugger(component: string): {
421
+ log: (...args: any[]) => void
422
+ warn: (message: string, context?: Partial<ErrorContext>) => void
423
+ error: (message: string, context?: ErrorContext) => void
424
+ } {
425
+ const isDebug = typeof process !== 'undefined' && process.env.DEBUG === 'true'
426
+
427
+ return {
428
+ log(...args: any[]): void {
429
+ if (isDebug) {
430
+ console.log(`[${component}]`, ...args)
431
+ }
432
+ },
433
+ warn(message: string, context?: Partial<ErrorContext>): void {
434
+ warn(component, message, context)
435
+ },
436
+ error(message: string, context?: ErrorContext): void {
437
+ const error = new ComponentError(component, message, context)
438
+ handleError(error, { throwError: false })
439
+ },
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Error boundary helper for catching render errors
445
+ *
446
+ * @param component - Component name
447
+ * @param renderFn - Render function
448
+ * @param fallback - Fallback content on error
449
+ * @returns Rendered content or fallback
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const content = errorBoundary(
454
+ * 'MyComponent',
455
+ * () => renderComponent(),
456
+ * '<div>Error loading component</div>'
457
+ * )
458
+ * ```
459
+ */
460
+ export function errorBoundary<T>(
461
+ component: string,
462
+ renderFn: () => T,
463
+ fallback: T,
464
+ ): T {
465
+ try {
466
+ return renderFn()
467
+ }
468
+ catch (error) {
469
+ const wrappedError = ErrorTypes.render(component, error as Error)
470
+ handleError(wrappedError, { throwError: false })
471
+ return fallback
472
+ }
473
+ }