@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,132 @@
1
+ /**
2
+ * Reactive localStorage hook with cross-tab synchronization
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const { value, setValue, removeValue } = useLocalStorage('user', { name: 'John' })
7
+ *
8
+ * setValue({ name: 'Jane' })
9
+ * setValue(prev => ({ ...prev, age: 30 }))
10
+ * removeValue()
11
+ * ```
12
+ */
13
+
14
+ /**
15
+ * Options for useLocalStorage hook
16
+ */
17
+ export interface UseLocalStorageOptions<T> {
18
+ /** Custom serializer function (defaults to JSON.stringify) */
19
+ serializer?: (value: T) => string
20
+ /** Custom deserializer function (defaults to JSON.parse) */
21
+ deserializer?: (value: string) => T
22
+ /** Error handler function */
23
+ onError?: (error: Error) => void
24
+ }
25
+
26
+ /**
27
+ * Hook for reactive localStorage with cross-tab sync
28
+ *
29
+ * @param key - Storage key
30
+ * @param initialValue - Initial value if key doesn't exist
31
+ * @param options - Configuration options
32
+ * @returns Object with value, setValue, and removeValue
33
+ */
34
+ export function useLocalStorage<T>(
35
+ key: string,
36
+ initialValue: T,
37
+ options: UseLocalStorageOptions<T> = {},
38
+ ): {
39
+ value: T
40
+ setValue: (newValue: T | ((prev: T) => T)) => void
41
+ removeValue: () => void
42
+ } {
43
+ const {
44
+ serializer = JSON.stringify,
45
+ deserializer = JSON.parse,
46
+ onError = console.error,
47
+ } = options
48
+
49
+ // Get initial value from storage
50
+ let storedValue: T = initialValue
51
+
52
+ try {
53
+ if (typeof window !== 'undefined') {
54
+ const item = window.localStorage.getItem(key)
55
+ if (item !== null) {
56
+ storedValue = deserializer(item)
57
+ }
58
+ }
59
+ }
60
+ catch (error) {
61
+ onError(error as Error)
62
+ }
63
+
64
+ let value = storedValue
65
+ const listeners = new Set<(newValue: T) => void>()
66
+
67
+ // Set value
68
+ function setValue(newValue: T | ((prev: T) => T)) {
69
+ try {
70
+ // Allow value to be a function for same API as useState
71
+ const valueToStore = typeof newValue === 'function' ? (newValue as (prev: T) => T)(value) : newValue
72
+
73
+ value = valueToStore
74
+
75
+ // Save to localStorage
76
+ if (typeof window !== 'undefined') {
77
+ window.localStorage.setItem(key, serializer(valueToStore))
78
+ }
79
+
80
+ // Notify listeners
81
+ listeners.forEach(listener => listener(valueToStore))
82
+ }
83
+ catch (error) {
84
+ onError(error as Error)
85
+ }
86
+ }
87
+
88
+ // Remove value
89
+ function removeValue() {
90
+ try {
91
+ value = initialValue
92
+
93
+ if (typeof window !== 'undefined') {
94
+ window.localStorage.removeItem(key)
95
+ }
96
+
97
+ // Notify listeners
98
+ listeners.forEach(listener => listener(initialValue))
99
+ }
100
+ catch (error) {
101
+ onError(error as Error)
102
+ }
103
+ }
104
+
105
+ // Listen for storage events (changes from other tabs/windows)
106
+ if (typeof window !== 'undefined') {
107
+ const handleStorageChange = (event: StorageEvent) => {
108
+ if (event.key === key && event.newValue !== null) {
109
+ try {
110
+ const newValue = deserializer(event.newValue)
111
+ value = newValue
112
+ listeners.forEach(listener => listener(newValue))
113
+ }
114
+ catch (error) {
115
+ onError(error as Error)
116
+ }
117
+ }
118
+ else if (event.key === key && event.newValue === null) {
119
+ value = initialValue
120
+ listeners.forEach(listener => listener(initialValue))
121
+ }
122
+ }
123
+
124
+ window.addEventListener('storage', handleStorageChange)
125
+ }
126
+
127
+ return {
128
+ value,
129
+ setValue,
130
+ removeValue,
131
+ }
132
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Hook for responsive media queries
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const { matches } = useMediaQuery('(min-width: 768px)')
7
+ * const { matches: isDark } = useIsDarkMode()
8
+ * const { matches: isMobile } = useIsMobile()
9
+ * ```
10
+ */
11
+
12
+ /**
13
+ * Options for useMediaQuery hook
14
+ */
15
+ export interface UseMediaQueryOptions {
16
+ /** Default value when window.matchMedia is not available */
17
+ defaultValue?: boolean
18
+ }
19
+
20
+ /**
21
+ * Hook for responsive media queries
22
+ *
23
+ * @param query - Media query string
24
+ * @param options - Configuration options
25
+ * @returns Object with matches boolean and updateMatches function
26
+ */
27
+ export function useMediaQuery(
28
+ query: string,
29
+ options: UseMediaQueryOptions = {},
30
+ ): { matches: boolean, updateMatches: () => void } {
31
+ const { defaultValue = false } = options
32
+
33
+ let matches = defaultValue
34
+ let mediaQueryList: MediaQueryList | null = null
35
+
36
+ // Initialize
37
+ if (typeof window !== 'undefined' && 'matchMedia' in window) {
38
+ mediaQueryList = window.matchMedia(query)
39
+ matches = mediaQueryList.matches
40
+
41
+ // Listen for changes
42
+ const handleChange = (event: MediaQueryListEvent) => {
43
+ matches = event.matches
44
+ }
45
+
46
+ if (mediaQueryList.addEventListener) {
47
+ mediaQueryList.addEventListener('change', handleChange)
48
+ }
49
+ else {
50
+ // Fallback for older browsers
51
+ mediaQueryList.addListener(handleChange)
52
+ }
53
+ }
54
+
55
+ function updateMatches() {
56
+ if (mediaQueryList) {
57
+ matches = mediaQueryList.matches
58
+ }
59
+ }
60
+
61
+ return { matches, updateMatches }
62
+ }
63
+
64
+ /**
65
+ * Common responsive breakpoints
66
+ */
67
+ export const Breakpoints = {
68
+ 'xs': '(min-width: 320px)',
69
+ 'sm': '(min-width: 640px)',
70
+ 'md': '(min-width: 768px)',
71
+ 'lg': '(min-width: 1024px)',
72
+ 'xl': '(min-width: 1280px)',
73
+ '2xl': '(min-width: 1536px)',
74
+ 'mobile': '(max-width: 767px)',
75
+ 'tablet': '(min-width: 768px) and (max-width: 1023px)',
76
+ 'desktop': '(min-width: 1024px)',
77
+ 'dark': '(prefers-color-scheme: dark)',
78
+ 'light': '(prefers-color-scheme: light)',
79
+ 'reducedMotion': '(prefers-reduced-motion: reduce)',
80
+ 'portrait': '(orientation: portrait)',
81
+ 'landscape': '(orientation: landscape)',
82
+ } as const
83
+
84
+ /**
85
+ * Use a predefined breakpoint
86
+ * @param breakpoint - Breakpoint name from Breakpoints constant
87
+ */
88
+ export function useBreakpoint(breakpoint: keyof typeof Breakpoints): { matches: boolean, updateMatches: () => void } {
89
+ return useMediaQuery(Breakpoints[breakpoint])
90
+ }
91
+
92
+ /**
93
+ * Check if viewport is mobile (max-width: 767px)
94
+ */
95
+ export function useIsMobile(): { matches: boolean, updateMatches: () => void } {
96
+ return useMediaQuery(Breakpoints.mobile)
97
+ }
98
+
99
+ /**
100
+ * Check if viewport is desktop (min-width: 1024px)
101
+ */
102
+ export function useIsDesktop(): { matches: boolean, updateMatches: () => void } {
103
+ return useMediaQuery(Breakpoints.desktop)
104
+ }
105
+
106
+ /**
107
+ * Check if user prefers dark color scheme
108
+ */
109
+ export function useIsDarkMode(): { matches: boolean, updateMatches: () => void } {
110
+ return useMediaQuery(Breakpoints.dark)
111
+ }
112
+
113
+ /**
114
+ * Check if user prefers reduced motion
115
+ */
116
+ export function usePrefersReducedMotion(): { matches: boolean, updateMatches: () => void } {
117
+ return useMediaQuery(Breakpoints.reducedMotion)
118
+ }
package/src/index.ts ADDED
@@ -0,0 +1,99 @@
1
+ // Export shared components (these will be imported as .stx files)
2
+ export { default as CodeBlock } from './components/CodeBlock.stx'
3
+
4
+ export { default as Footer } from './components/Footer.stx'
5
+ export { default as Hero } from './components/Hero.stx'
6
+ export { default as Installation } from './components/Installation.stx'
7
+ // Export composables
8
+ export * from './composables'
9
+ // Export UI components
10
+ export * from './ui/accordion'
11
+ export * from './ui/audio'
12
+ export * from './ui/auth'
13
+ export * from './ui/avatar'
14
+ export * from './ui/badge'
15
+
16
+ export * from './ui/breadcrumb'
17
+ export * from './ui/button'
18
+ export * from './ui/calendar'
19
+ export * from './ui/card'
20
+
21
+ export * from './ui/checkbox'
22
+ export * from './ui/combobox'
23
+ export * from './ui/command-palette'
24
+ export * from './ui/dialog'
25
+ export * from './ui/drawer'
26
+ export * from './ui/dropdown'
27
+ export * from './ui/form'
28
+ export * from './ui/image'
29
+ export * from './ui/input'
30
+ export * from './ui/listbox'
31
+ export * from './ui/navigator'
32
+ export * from './ui/notification'
33
+ export * from './ui/pagination'
34
+ export * from './ui/payment'
35
+ export * from './ui/popover'
36
+ export * from './ui/portal'
37
+ export * from './ui/progress'
38
+ export * from './ui/radio'
39
+ export * from './ui/radio-group'
40
+ export * from './ui/select'
41
+ export * from './ui/sidebar'
42
+ export * from './ui/skeleton'
43
+ export * from './ui/spinner'
44
+ export * from './ui/stepper'
45
+ export * from './ui/storage'
46
+ export * from './ui/switch'
47
+ export * from './ui/table'
48
+ export * from './ui/tabs'
49
+ export * from './ui/teleport'
50
+ export * from './ui/textarea'
51
+ export * from './ui/tooltip'
52
+ export * from './ui/transition'
53
+ export * from './ui/video'
54
+ export * from './ui/virtual-list'
55
+ export * from './ui/virtual-table'
56
+ export * from './utils/accessibility'
57
+ export * from './utils/animation'
58
+ export * from './utils/error-handling'
59
+ // Export utilities
60
+ export * from './utils/highlighter'
61
+ export * from './utils/i18n'
62
+ export * from './utils/keep-alive'
63
+ export * from './utils/lazy-loading'
64
+ export * from './utils/prop-validation'
65
+ export * from './utils/theme'
66
+
67
+ // Types
68
+ export interface ComponentProps {
69
+ className?: string
70
+ [key: string]: any
71
+ }
72
+
73
+ export interface CodeBlockProps extends ComponentProps {
74
+ code: string
75
+ language?: string
76
+ theme?: 'light' | 'dark' | 'auto'
77
+ lineNumbers?: boolean
78
+ copyable?: boolean
79
+ }
80
+
81
+ export interface HeroProps extends ComponentProps {
82
+ title?: string
83
+ description?: string
84
+ githubUrl?: string
85
+ }
86
+
87
+ export interface FooterProps extends ComponentProps {
88
+ author?: string
89
+ authorUrl?: string
90
+ project?: string
91
+ projectUrl?: string
92
+ twitterUrl?: string
93
+ sponsorUrl?: string
94
+ coffeeUrl?: string
95
+ }
96
+
97
+ export interface InstallationProps extends ComponentProps {
98
+ packageName?: string
99
+ }
package/src/stx.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare module '*.stx' {
2
+ const component: any
3
+ export default component
4
+ }
@@ -0,0 +1,108 @@
1
+ <script>
2
+ export const items = $props.items || []
3
+ export const allowMultiple = $props.allowMultiple || false
4
+ export const defaultOpen = $props.defaultOpen || []
5
+ export const className = $props.className || ''
6
+ export const onChange = $props.onChange
7
+
8
+ // Track which items are open
9
+ let openItems = defaultOpen
10
+
11
+ // Toggle an item
12
+ function toggleItem(index) {
13
+ if (allowMultiple) {
14
+ // Multiple items can be open
15
+ if (openItems.includes(index)) {
16
+ openItems = openItems.filter(i => i !== index)
17
+ } else {
18
+ openItems = [...openItems, index]
19
+ }
20
+ } else {
21
+ // Only one item can be open
22
+ if (openItems.includes(index)) {
23
+ openItems = []
24
+ } else {
25
+ openItems = [index]
26
+ }
27
+ }
28
+
29
+ if (onChange) {
30
+ onChange(openItems)
31
+ }
32
+ }
33
+
34
+ // Check if item is open
35
+ function isOpen(index) {
36
+ return openItems.includes(index)
37
+ }
38
+
39
+ export const accordionClasses = `divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg ${className}`.trim()
40
+ export const itemClasses = ``.trim()
41
+ export const headerClasses = `w-full flex items-center justify-between px-4 py-3 text-left font-medium text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
42
+ export const iconClasses = `w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200`.trim()
43
+ export const contentClasses = `px-4 py-3 text-gray-700 dark:text-gray-300`.trim()
44
+ </script>
45
+
46
+ <div class="{{ accordionClasses }}" role="region">
47
+ @foreach(item in items)
48
+ <div class="{{ itemClasses }}">
49
+ <button
50
+ type="button"
51
+ class="{{ headerClasses }}"
52
+ onclick="toggleItem({{ $loop.index }})"
53
+ aria-expanded="{{ isOpen($loop.index) ? 'true' : 'false' }}"
54
+ aria-controls="accordion-content-{{ $loop.index }}"
55
+ >
56
+ <span>{{ item.title }}</span>
57
+ <svg
58
+ class="{{ iconClasses }}"
59
+ style="transform: rotate({{ isOpen($loop.index) ? '180' : '0' }}deg)"
60
+ fill="none"
61
+ viewBox="0 0 24 24"
62
+ stroke="currentColor"
63
+ >
64
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
65
+ </svg>
66
+ </button>
67
+
68
+ @if(isOpen($loop.index))
69
+ <div
70
+ id="accordion-content-{{ $loop.index }}"
71
+ class="{{ contentClasses }}"
72
+ role="region"
73
+ >
74
+ {!! item.content !!}
75
+ </div>
76
+ @endif
77
+ </div>
78
+ @endforeach
79
+ </div>
80
+
81
+ <script>
82
+ // Keyboard navigation
83
+ if (typeof window !== 'undefined') {
84
+ window.addEventListener('DOMContentLoaded', () => {
85
+ const buttons = document.querySelectorAll('[aria-expanded]')
86
+
87
+ buttons.forEach((button, index) => {
88
+ button.addEventListener('keydown', (event) => {
89
+ if (event.key === 'ArrowDown') {
90
+ event.preventDefault()
91
+ const nextButton = buttons[index + 1]
92
+ if (nextButton) nextButton.focus()
93
+ } else if (event.key === 'ArrowUp') {
94
+ event.preventDefault()
95
+ const prevButton = buttons[index - 1]
96
+ if (prevButton) prevButton.focus()
97
+ } else if (event.key === 'Home') {
98
+ event.preventDefault()
99
+ buttons[0].focus()
100
+ } else if (event.key === 'End') {
101
+ event.preventDefault()
102
+ buttons[buttons.length - 1].focus()
103
+ }
104
+ })
105
+ })
106
+ })
107
+ }
108
+ </script>
@@ -0,0 +1,14 @@
1
+ export { default as Accordion } from './Accordion.stx'
2
+
3
+ export interface AccordionItem {
4
+ title: string
5
+ content: string
6
+ }
7
+
8
+ export interface AccordionProps {
9
+ items: AccordionItem[]
10
+ allowMultiple?: boolean
11
+ defaultOpen?: number[]
12
+ onChange?: (openItems: number[]) => void
13
+ className?: string
14
+ }
@@ -0,0 +1,50 @@
1
+ <script>
2
+ export const src = $props.src || ''
3
+ export const autoplay = $props.autoplay || false
4
+ export const loop = $props.loop || false
5
+ export const muted = $props.muted || false
6
+ export const controls = $props.controls ?? true
7
+ export const preload = $props.preload || 'metadata'
8
+ export const className = $props.className || ''
9
+ export const showWaveform = $props.showWaveform || false
10
+ export const title = $props.title || ''
11
+
12
+ export const containerClasses = `relative w-full ${className}`.trim()
13
+ export const audioClasses = 'w-full rounded-md bg-gray-100 dark:bg-gray-800'
14
+ </script>
15
+
16
+ <div class="{{ containerClasses }}">
17
+ @if(title)
18
+ <div class="mb-2 px-1">
19
+ <h3 class="text-sm font-medium text-gray-900 dark:text-gray-100">
20
+ {{ title }}
21
+ </h3>
22
+ </div>
23
+ @endif
24
+
25
+ <audio
26
+ class="{{ audioClasses }}"
27
+ src="{{ src }}"
28
+ controls="{{ controls }}"
29
+ autoplay="{{ autoplay }}"
30
+ loop="{{ loop }}"
31
+ muted="{{ muted }}"
32
+ preload="{{ preload }}"
33
+ >
34
+ Your browser does not support the audio element.
35
+ </audio>
36
+
37
+ @if(showWaveform)
38
+ <div class="mt-2 h-16 bg-gray-100 dark:bg-gray-800 rounded-md flex items-center justify-center">
39
+ <svg class="w-full h-full px-4" viewBox="0 0 100 40" preserveAspectRatio="none">
40
+ <path
41
+ d="M 0,20 L 10,15 L 20,10 L 30,5 L 40,15 L 50,20 L 60,25 L 70,30 L 80,20 L 90,15 L 100,20"
42
+ fill="none"
43
+ stroke="currentColor"
44
+ stroke-width="2"
45
+ class="text-indigo-500 dark:text-indigo-400"
46
+ />
47
+ </svg>
48
+ </div>
49
+ @endif
50
+ </div>
@@ -0,0 +1,42 @@
1
+ # Audio Component
2
+
3
+ A styled audio player with optional waveform visualization.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ @component('Audio', {
15
+ src: '/audio/podcast.mp3',
16
+ title: 'Episode 42: Building with STX',
17
+ controls: true
18
+ })
19
+ @endcomponent
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `src` | `string` | - | Audio file URL (required) |
27
+ | `autoplay` | `boolean` | `false` | Auto-play on load |
28
+ | `loop` | `boolean` | `false` | Loop playback |
29
+ | `muted` | `boolean` | `false` | Start muted |
30
+ | `controls` | `boolean` | `true` | Show native controls |
31
+ | `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | Preload strategy |
32
+ | `className` | `string` | `''` | Additional CSS classes |
33
+ | `showWaveform` | `boolean` | `false` | Show waveform visualization |
34
+ | `title` | `string` | `''` | Optional title above player |
35
+
36
+ ## Features
37
+
38
+ - **Native controls** - Uses browser's native audio controls
39
+ - **Dark mode** - Styled for dark mode
40
+ - **Waveform** - Optional visual waveform display
41
+ - **Accessible** - Proper fallback text
42
+ - **Responsive** - Full-width design
@@ -0,0 +1,13 @@
1
+ export { default as Audio } from './Audio.stx'
2
+
3
+ export interface AudioProps {
4
+ src: string
5
+ autoplay?: boolean
6
+ loop?: boolean
7
+ muted?: boolean
8
+ controls?: boolean
9
+ preload?: 'none' | 'metadata' | 'auto'
10
+ className?: string
11
+ showWaveform?: boolean
12
+ title?: string
13
+ }