@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,36 @@
1
+ # Stepper Component
2
+
3
+ A step-by-step progress indicator for multi-step processes.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ <script>
15
+ export let currentStep = 0
16
+
17
+ export function handleStepClick(step) {
18
+ currentStep = step
19
+ }
20
+ </script>
21
+
22
+ @component('Stepper', { currentStep })
23
+ @component('StepperStep', { stepNumber: 0, currentStep, label: 'Account', onClick: handleStepClick })
24
+ @component('StepperStep', { stepNumber: 1, currentStep, label: 'Profile', onClick: handleStepClick })
25
+ @component('StepperStep', { stepNumber: 2, currentStep, label: 'Confirmation', onClick: handleStepClick })
26
+ @endcomponent
27
+ ```
28
+
29
+ ## Features
30
+
31
+ - Horizontal and vertical orientation
32
+ - Clickable steps
33
+ - Complete/current/upcoming states
34
+ - Dark mode support
35
+ - Accessible
36
+ - Customizable styling
@@ -0,0 +1,17 @@
1
+ <script>
2
+ export const currentStep = $props.currentStep || 0
3
+ export const orientation = $props.orientation || 'horizontal'
4
+ export const className = $props.className || ''
5
+ export const as = $props.as || 'div'
6
+
7
+ const orientationClasses = orientation === 'horizontal'
8
+ ? 'flex flex-row'
9
+ : 'flex flex-col'
10
+
11
+ const baseClasses = 'w-full'
12
+ const stepperClasses = `${baseClasses} ${orientationClasses} ${className}`.trim()
13
+ </script>
14
+
15
+ <component :is="{{ as }}" class="{{ stepperClasses }}">
16
+ <slot :currentStep="currentStep" />
17
+ </component>
@@ -0,0 +1,53 @@
1
+ <script>
2
+ export const stepNumber = $props.stepNumber
3
+ export const currentStep = $props.currentStep || 0
4
+ export const label = $props.label || ''
5
+ export const description = $props.description || ''
6
+ export const className = $props.className || ''
7
+ export const as = $props.as || 'div'
8
+ export const onClick = $props.onClick
9
+
10
+ export const isComplete = stepNumber < currentStep
11
+ export const isCurrent = stepNumber === currentStep
12
+ export const isUpcoming = stepNumber > currentStep
13
+
14
+ const baseClasses = 'flex items-center'
15
+ const stepClasses = `${baseClasses} ${className}`.trim()
16
+
17
+ export function handleClick() {
18
+ onClick?.(stepNumber)
19
+ }
20
+ </script>
21
+
22
+ <component :is="{{ as }}" class="{{ stepClasses }}">
23
+ <button
24
+ type="button"
25
+ @click="handleClick"
26
+ class="flex items-center gap-3 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 rounded-lg p-2"
27
+ >
28
+ <div class="{{ isComplete ? 'bg-blue-600 dark:bg-blue-500' : isCurrent ? 'bg-blue-600 dark:bg-blue-500 ring-4 ring-blue-200 dark:ring-blue-800' : 'bg-neutral-300 dark:bg-neutral-600' }} w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold transition-all">
29
+ @if(isComplete)
30
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
31
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
32
+ </svg>
33
+ @else
34
+ {{ stepNumber + 1 }}
35
+ @endif
36
+ </div>
37
+
38
+ @if(label)
39
+ <div class="text-left">
40
+ <div class="text-sm font-medium {{ isCurrent ? 'text-blue-600 dark:text-blue-400' : 'text-neutral-900 dark:text-white' }}">
41
+ {{ label }}
42
+ </div>
43
+ @if(description)
44
+ <div class="text-xs text-neutral-500 dark:text-neutral-400">
45
+ {{ description }}
46
+ </div>
47
+ @endif
48
+ </div>
49
+ @endif
50
+ </button>
51
+
52
+ <slot :isComplete="isComplete" :isCurrent="isCurrent" :isUpcoming="isUpcoming" />
53
+ </component>
@@ -0,0 +1,19 @@
1
+ export { default as Stepper } from './Stepper.stx'
2
+ export { default as StepperStep } from './StepperStep.stx'
3
+
4
+ export interface StepperProps {
5
+ currentStep?: number
6
+ orientation?: 'horizontal' | 'vertical'
7
+ className?: string
8
+ as?: string
9
+ }
10
+
11
+ export interface StepperStepProps {
12
+ stepNumber: number
13
+ currentStep?: number
14
+ label?: string
15
+ description?: string
16
+ className?: string
17
+ as?: string
18
+ onClick?: (step: number) => void
19
+ }
@@ -0,0 +1,193 @@
1
+ # Storage Utilities
2
+
3
+ Type-safe utilities for localStorage and sessionStorage with expiration support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Usage
14
+
15
+ ```typescript
16
+ import { localStorage, sessionStorage } from '@stacksjs/components'
17
+
18
+ // Set a value
19
+ localStorage.set('user', { name: 'John', age: 30 })
20
+
21
+ // Get a value
22
+ const user = localStorage.get('user')
23
+ console.log(user) // { name: 'John', age: 30 }
24
+
25
+ // Remove a value
26
+ localStorage.remove('user')
27
+
28
+ // Clear all
29
+ localStorage.clear()
30
+ ```
31
+
32
+ ### With Expiration
33
+
34
+ ```typescript
35
+ // Expire after 1 hour (3600000 ms)
36
+ localStorage.set('token', 'abc123', { expire: 3600000 })
37
+
38
+ // After 1 hour, get() returns null
39
+ const token = localStorage.get('token') // null if expired
40
+ ```
41
+
42
+ ### With Namespace
43
+
44
+ ```typescript
45
+ import { createStorage } from '@stacksjs/components'
46
+
47
+ const userStorage = createStorage('local', 'user')
48
+ const appStorage = createStorage('local', 'app')
49
+
50
+ userStorage.set('preferences', { theme: 'dark' })
51
+ appStorage.set('preferences', { language: 'en' })
52
+
53
+ // These are stored as 'user:preferences' and 'app:preferences'
54
+ ```
55
+
56
+ ### Session Storage
57
+
58
+ ```typescript
59
+ import { sessionStorage } from '@stacksjs/components'
60
+
61
+ // Same API as localStorage
62
+ sessionStorage.set('tempData', { value: 123 })
63
+ const data = sessionStorage.get('tempData')
64
+ ```
65
+
66
+ ## API
67
+
68
+ ### Storage Methods
69
+
70
+ #### `set(key: string, value: any, options?: StorageOptions): void`
71
+ Store a value with optional expiration.
72
+
73
+ #### `get<T>(key: string): T | null`
74
+ Retrieve a value. Returns null if expired or not found.
75
+
76
+ #### `remove(key: string): void`
77
+ Remove a specific key.
78
+
79
+ #### `clear(): void`
80
+ Clear all items (or all namespaced items).
81
+
82
+ #### `has(key: string): boolean`
83
+ Check if a key exists and hasn't expired.
84
+
85
+ #### `keys(): string[]`
86
+ Get all keys (without namespace prefix).
87
+
88
+ #### `size(): number`
89
+ Get the number of stored items.
90
+
91
+ ## StorageOptions
92
+
93
+ ```typescript
94
+ interface StorageOptions {
95
+ expire?: number // Expiration in milliseconds
96
+ namespace?: string // Prefix for keys (in createStorage)
97
+ }
98
+ ```
99
+
100
+ ## Features
101
+
102
+ - **Type-safe** - Full TypeScript support
103
+ - **Expiration** - Automatic expiration handling
104
+ - **Namespacing** - Prevent key collisions
105
+ - **Error handling** - Graceful error handling
106
+ - **JSON serialization** - Automatic serialization/deserialization
107
+ - **Both storages** - localStorage and sessionStorage support
108
+
109
+ ## Examples
110
+
111
+ ### User Authentication Token
112
+
113
+ ```typescript
114
+ import { localStorage } from '@stacksjs/components'
115
+
116
+ // Store token with 24-hour expiration
117
+ function login(token: string) {
118
+ localStorage.set('auth_token', token, {
119
+ expire: 24 * 60 * 60 * 1000, // 24 hours
120
+ })
121
+ }
122
+
123
+ // Get token (returns null if expired)
124
+ function getToken(): string | null {
125
+ return localStorage.get('auth_token')
126
+ }
127
+
128
+ // Check if logged in
129
+ function isLoggedIn(): boolean {
130
+ return localStorage.has('auth_token')
131
+ }
132
+
133
+ // Logout
134
+ function logout() {
135
+ localStorage.remove('auth_token')
136
+ }
137
+ ```
138
+
139
+ ### User Preferences
140
+
141
+ ```typescript
142
+ import { createStorage } from '@stacksjs/components'
143
+
144
+ const prefs = createStorage('local', 'preferences')
145
+
146
+ interface UserPreferences {
147
+ theme: 'light' | 'dark'
148
+ language: string
149
+ notifications: boolean
150
+ }
151
+
152
+ // Save preferences
153
+ function savePreferences(preferences: UserPreferences) {
154
+ prefs.set('user', preferences)
155
+ }
156
+
157
+ // Load preferences with defaults
158
+ function loadPreferences(): UserPreferences {
159
+ const saved = prefs.get<UserPreferences>('user')
160
+ return saved || {
161
+ theme: 'light',
162
+ language: 'en',
163
+ notifications: true,
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Shopping Cart
169
+
170
+ ```typescript
171
+ import { localStorage } from '@stacksjs/components'
172
+
173
+ interface CartItem {
174
+ id: string
175
+ name: string
176
+ quantity: number
177
+ price: number
178
+ }
179
+
180
+ function addToCart(item: CartItem) {
181
+ const cart = localStorage.get<CartItem[]>('cart') || []
182
+ cart.push(item)
183
+ localStorage.set('cart', cart)
184
+ }
185
+
186
+ function getCart(): CartItem[] {
187
+ return localStorage.get<CartItem[]>('cart') || []
188
+ }
189
+
190
+ function clearCart() {
191
+ localStorage.remove('cart')
192
+ }
193
+ ```
@@ -0,0 +1,347 @@
1
+ // Storage utilities for localStorage and sessionStorage
2
+
3
+ export interface StorageOptions {
4
+ expire?: number // Expiration in milliseconds
5
+ namespace?: string // Prefix for keys
6
+ encrypt?: boolean // Encrypt data (basic XOR encryption)
7
+ compress?: boolean // Compress large objects
8
+ }
9
+
10
+ type StorageWatchCallback = (newValue: any, oldValue: any, key: string) => void
11
+
12
+ export class StorageManager {
13
+ private storage: globalThis.Storage
14
+ private namespace: string
15
+ private watchers: Map<string, Set<StorageWatchCallback>> = new Map()
16
+ private encryptionKey = 'stx-storage-key' // Simple encryption key
17
+
18
+ constructor(type: 'local' | 'session' = 'local', namespace = '') {
19
+ this.storage = type === 'local' ? globalThis.localStorage : globalThis.sessionStorage
20
+ this.namespace = namespace
21
+
22
+ // Listen for storage events from other tabs/windows
23
+ if (typeof window !== 'undefined') {
24
+ window.addEventListener('storage', this.handleStorageEvent.bind(this))
25
+ }
26
+ }
27
+
28
+ private handleStorageEvent(event: StorageEvent): void {
29
+ if (!event.key)
30
+ return
31
+
32
+ const key = this.namespace && event.key.startsWith(`${this.namespace}:`)
33
+ ? event.key.slice(this.namespace.length + 1)
34
+ : event.key
35
+
36
+ const callbacks = this.watchers.get(key)
37
+ if (callbacks) {
38
+ const oldValue = event.oldValue ? JSON.parse(event.oldValue).value : null
39
+ const newValue = event.newValue ? JSON.parse(event.newValue).value : null
40
+ callbacks.forEach(callback => callback(newValue, oldValue, key))
41
+ }
42
+ }
43
+
44
+ private encrypt(data: string): string {
45
+ // Simple XOR encryption (not cryptographically secure, just obfuscation)
46
+ let result = ''
47
+ for (let i = 0; i < data.length; i++) {
48
+ result += String.fromCharCode(data.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length))
49
+ }
50
+ return btoa(result) // Base64 encode
51
+ }
52
+
53
+ private decrypt(data: string): string {
54
+ try {
55
+ const decoded = atob(data) // Base64 decode
56
+ let result = ''
57
+ for (let i = 0; i < decoded.length; i++) {
58
+ result += String.fromCharCode(decoded.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length))
59
+ }
60
+ return result
61
+ }
62
+ catch {
63
+ return data // Return as-is if decryption fails
64
+ }
65
+ }
66
+
67
+ private compress(data: string): string {
68
+ // Simple LZ-based compression for large strings
69
+ if (data.length < 100)
70
+ return data // Don't compress small strings
71
+
72
+ const dict: Record<string, number> = {}
73
+ let dictSize = 256
74
+ const result: number[] = []
75
+ let w = ''
76
+
77
+ for (let i = 0; i < data.length; i++) {
78
+ const c = data.charAt(i)
79
+ const wc = w + c
80
+
81
+ if (dict[wc] !== undefined) {
82
+ w = wc
83
+ }
84
+ else {
85
+ result.push(w.length > 1 ? dict[w] : w.charCodeAt(0))
86
+ dict[wc] = dictSize++
87
+ w = c
88
+ }
89
+ }
90
+
91
+ if (w !== '') {
92
+ result.push(w.length > 1 ? dict[w] : w.charCodeAt(0))
93
+ }
94
+
95
+ return JSON.stringify(result)
96
+ }
97
+
98
+ private decompress(data: string): string {
99
+ try {
100
+ const compressed = JSON.parse(data)
101
+ if (!Array.isArray(compressed))
102
+ return data
103
+
104
+ const dict: Record<number, string> = {}
105
+ let dictSize = 256
106
+ let w = String.fromCharCode(compressed[0])
107
+ let result = w
108
+
109
+ for (let i = 1; i < compressed.length; i++) {
110
+ const k = compressed[i]
111
+ let entry: string
112
+
113
+ if (dict[k] !== undefined) {
114
+ entry = dict[k]
115
+ }
116
+ else if (k === dictSize) {
117
+ entry = w + w.charAt(0)
118
+ }
119
+ else {
120
+ return data // Decompression failed
121
+ }
122
+
123
+ result += entry
124
+ dict[dictSize++] = w + entry.charAt(0)
125
+ w = entry
126
+ }
127
+
128
+ return result
129
+ }
130
+ catch {
131
+ return data // Return as-is if decompression fails
132
+ }
133
+ }
134
+
135
+ private getKey(key: string): string {
136
+ return this.namespace ? `${this.namespace}:${key}` : key
137
+ }
138
+
139
+ set(key: string, value: any, options?: StorageOptions): void {
140
+ const fullKey = this.getKey(key)
141
+ const oldValue = this.get(key)
142
+
143
+ const data = {
144
+ value,
145
+ timestamp: Date.now(),
146
+ expire: options?.expire,
147
+ encrypted: options?.encrypt || false,
148
+ compressed: options?.compress || false,
149
+ }
150
+
151
+ try {
152
+ let serialized = JSON.stringify(data)
153
+
154
+ // Compress if enabled
155
+ if (options?.compress) {
156
+ serialized = this.compress(serialized)
157
+ }
158
+
159
+ // Encrypt if enabled
160
+ if (options?.encrypt) {
161
+ serialized = this.encrypt(serialized)
162
+ }
163
+
164
+ this.storage.setItem(fullKey, serialized)
165
+
166
+ // Notify watchers
167
+ this.notifyWatchers(key, value, oldValue)
168
+ }
169
+ catch (error) {
170
+ console.error('Storage set error:', error)
171
+ }
172
+ }
173
+
174
+ get<T = any>(key: string): T | null {
175
+ const fullKey = this.getKey(key)
176
+
177
+ try {
178
+ let item = this.storage.getItem(fullKey)
179
+ if (!item)
180
+ return null
181
+
182
+ // Try to decrypt if it looks encrypted (base64)
183
+ if (item.match(/^[A-Z0-9+/]+=*$/i)) {
184
+ try {
185
+ item = this.decrypt(item)
186
+ }
187
+ catch {
188
+ // Not encrypted, continue
189
+ }
190
+ }
191
+
192
+ // Try to decompress if it looks compressed (starts with [)
193
+ if (item.startsWith('[')) {
194
+ try {
195
+ item = this.decompress(item)
196
+ }
197
+ catch {
198
+ // Not compressed, continue
199
+ }
200
+ }
201
+
202
+ const data = JSON.parse(item)
203
+
204
+ // Check expiration
205
+ if (data.expire && Date.now() - data.timestamp > data.expire) {
206
+ this.remove(key)
207
+ return null
208
+ }
209
+
210
+ return data.value as T
211
+ }
212
+ catch (error) {
213
+ console.error('Storage get error:', error)
214
+ return null
215
+ }
216
+ }
217
+
218
+ watch(key: string, callback: StorageWatchCallback): () => void {
219
+ if (!this.watchers.has(key)) {
220
+ this.watchers.set(key, new Set())
221
+ }
222
+
223
+ this.watchers.get(key)!.add(callback)
224
+
225
+ // Return unwatch function
226
+ return () => {
227
+ const callbacks = this.watchers.get(key)
228
+ if (callbacks) {
229
+ callbacks.delete(callback)
230
+ if (callbacks.size === 0) {
231
+ this.watchers.delete(key)
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ private notifyWatchers(key: string, newValue: any, oldValue: any): void {
238
+ const callbacks = this.watchers.get(key)
239
+ if (callbacks) {
240
+ callbacks.forEach(callback => callback(newValue, oldValue, key))
241
+ }
242
+ }
243
+
244
+ remove(key: string): void {
245
+ const fullKey = this.getKey(key)
246
+ const oldValue = this.get(key)
247
+ this.storage.removeItem(fullKey)
248
+
249
+ // Notify watchers
250
+ this.notifyWatchers(key, null, oldValue)
251
+ }
252
+
253
+ clear(): void {
254
+ if (this.namespace) {
255
+ // Clear only namespaced items
256
+ const keys = this.keys()
257
+ keys.forEach(key => this.remove(key))
258
+ }
259
+ else {
260
+ this.storage.clear()
261
+ }
262
+ }
263
+
264
+ has(key: string): boolean {
265
+ return this.get(key) !== null
266
+ }
267
+
268
+ keys(): string[] {
269
+ const allKeys = Object.keys(this.storage)
270
+
271
+ if (this.namespace) {
272
+ const prefix = `${this.namespace}:`
273
+ return allKeys
274
+ .filter(key => key.startsWith(prefix))
275
+ .map(key => key.slice(prefix.length))
276
+ }
277
+
278
+ return allKeys
279
+ }
280
+
281
+ size(): number {
282
+ return this.keys().length
283
+ }
284
+
285
+ // Get storage quota information (if available)
286
+ getQuota(): { usage: number, quota: number, percentage: number } | null {
287
+ if ('storage' in navigator && 'estimate' in navigator.storage) {
288
+ // Use async/await pattern but return promise
289
+ return null // This would need to be async, but keeping sync API
290
+ }
291
+
292
+ // Fallback: estimate based on current data
293
+ try {
294
+ let totalSize = 0
295
+ for (let i = 0; i < this.storage.length; i++) {
296
+ const key = this.storage.key(i)
297
+ if (key) {
298
+ const value = this.storage.getItem(key) || ''
299
+ totalSize += key.length + value.length
300
+ }
301
+ }
302
+
303
+ // Typical localStorage quota is 5-10MB
304
+ const estimatedQuota = 5 * 1024 * 1024 // 5MB
305
+ return {
306
+ usage: totalSize * 2, // Multiply by 2 for UTF-16 encoding
307
+ quota: estimatedQuota,
308
+ percentage: (totalSize * 2 / estimatedQuota) * 100,
309
+ }
310
+ }
311
+ catch {
312
+ return null
313
+ }
314
+ }
315
+
316
+ // Batch operations
317
+ setMany(items: Record<string, any>, options?: StorageOptions): void {
318
+ Object.entries(items).forEach(([key, value]) => {
319
+ this.set(key, value, options)
320
+ })
321
+ }
322
+
323
+ getMany<T = any>(keys: string[]): Record<string, T | null> {
324
+ const result: Record<string, T | null> = {}
325
+ keys.forEach((key) => {
326
+ result[key] = this.get<T>(key)
327
+ })
328
+ return result
329
+ }
330
+
331
+ removeMany(keys: string[]): void {
332
+ keys.forEach(key => this.remove(key))
333
+ }
334
+ }
335
+
336
+ // Convenience exports
337
+ export const storage: {
338
+ local: StorageManager
339
+ session: StorageManager
340
+ } = {
341
+ local: new StorageManager('local'),
342
+ session: new StorageManager('session'),
343
+ }
344
+
345
+ export function createStorage(type: 'local' | 'session' = 'local', namespace?: string): StorageManager {
346
+ return new StorageManager(type, namespace)
347
+ }