@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,353 @@
1
+ <script>
2
+ export const products = $props.products || []
3
+ export const stripePublicKey = $props.stripePublicKey || ''
4
+ export const apiUrl = $props.apiUrl || ''
5
+ export const onSubmit = $props.onSubmit
6
+ export const onRemoveProduct = $props.onRemoveProduct
7
+ export const shipping = $props.shipping || 0
8
+ export const taxes = $props.taxes || 0
9
+ export const className = $props.className || ''
10
+
11
+ // Calculate totals
12
+ export const calculateSubtotal = () => {
13
+ return products.reduce((sum, product) => sum + (product.price * (product.quantity || 1)), 0)
14
+ }
15
+
16
+ export const total = calculateSubtotal() + shipping + taxes
17
+
18
+ export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.trim()
19
+ </script>
20
+
21
+ <div class="{{ containerClasses }}">
22
+ <div class="mx-auto max-w-2xl px-4 pb-24 pt-16 lg:max-w-7xl lg:px-8 sm:px-6">
23
+ <h2 class="sr-only">Checkout</h2>
24
+
25
+ <form class="lg:grid lg:grid-cols-2 lg:gap-x-12 xl:gap-x-16" onsubmit="event.preventDefault(); handleCheckoutSubmit(event)">
26
+ <!-- Left Column - Contact & Payment -->
27
+ <div>
28
+ <!-- Contact Information -->
29
+ <div>
30
+ <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
31
+ Contact information
32
+ </h2>
33
+
34
+ <div class="mt-4">
35
+ <label for="email-address" class="block text-sm text-gray-700 dark:text-gray-300 font-medium">
36
+ Email address
37
+ </label>
38
+ <div class="mt-1">
39
+ <input
40
+ id="email-address"
41
+ type="email"
42
+ name="email-address"
43
+ autocomplete="email"
44
+ required
45
+ class="block w-full border border-gray-300 dark:border-gray-600 rounded-md p-2 shadow-sm bg-white dark:bg-blue-gray-700 text-gray-900 dark:text-gray-100 focus:border-indigo-500 dark:focus:border-indigo-400 sm:text-sm focus:ring-indigo-500 dark:focus:ring-indigo-400"
46
+ />
47
+ </div>
48
+ </div>
49
+ </div>
50
+
51
+ <!-- Billing Information -->
52
+ <div class="mt-10 border-t border-gray-200 dark:border-gray-700 pt-10">
53
+ <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
54
+ Billing information
55
+ </h2>
56
+
57
+ <div class="mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
58
+ <div class="sm:col-span-2">
59
+ <label for="card-name" class="block text-sm text-gray-700 dark:text-gray-300 font-medium">
60
+ Name on card
61
+ </label>
62
+ <div class="mt-1">
63
+ <input
64
+ type="text"
65
+ id="card-name"
66
+ name="card-name"
67
+ autocomplete="cc-name"
68
+ required
69
+ class="block w-full border border-gray-300 dark:border-gray-600 rounded-md p-2 shadow-sm bg-white dark:bg-blue-gray-700 text-gray-900 dark:text-gray-100 focus:border-indigo-500 sm:text-sm focus:ring-indigo-500"
70
+ />
71
+ </div>
72
+ </div>
73
+
74
+ <!-- Stripe Element Mount Point -->
75
+ <div class="sm:col-span-2">
76
+ <div id="payment-element" class="mt-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 p-3"></div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Payment Type -->
82
+ <div class="mt-10 border-t border-gray-200 dark:border-gray-700 pt-10">
83
+ <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
84
+ Payment
85
+ </h2>
86
+
87
+ <fieldset class="mt-4">
88
+ <legend class="sr-only">Payment type</legend>
89
+ <div class="sm:flex sm:items-center space-y-4 sm:space-x-10 sm:space-y-0">
90
+ <div class="flex items-center">
91
+ <input
92
+ id="credit-card"
93
+ name="payment-type"
94
+ type="radio"
95
+ checked
96
+ class="h-4 w-4 border-gray-300 dark:border-gray-600 text-indigo-600 dark:text-indigo-400 focus:ring-indigo-500 dark:focus:ring-indigo-400"
97
+ />
98
+ <label for="credit-card" class="ml-3 block text-sm text-gray-700 dark:text-gray-300 font-medium">
99
+ Credit card
100
+ </label>
101
+ </div>
102
+ </div>
103
+ </fieldset>
104
+ </div>
105
+ </div>
106
+
107
+ <!-- Right Column - Order Summary -->
108
+ <div class="mt-10 lg:mt-0">
109
+ <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
110
+ Order summary
111
+ </h2>
112
+
113
+ <div class="mt-4 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-blue-gray-800 shadow-sm">
114
+ <h3 class="sr-only">Items in your cart</h3>
115
+
116
+ <!-- Product List -->
117
+ <ul role="list" class="divide-y divide-gray-200 dark:divide-gray-700">
118
+ @foreach(product in products)
119
+ <li class="flex px-4 py-6 sm:px-6">
120
+ <div class="flex-shrink-0">
121
+ @if(product.images)
122
+ <img src="{{ product.images }}" alt="{{ product.name }}" class="w-20 rounded-md" />
123
+ @else
124
+ <div class="w-20 h-20 bg-gray-200 dark:bg-gray-700 rounded-md flex items-center justify-center">
125
+ <svg class="h-8 w-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
126
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
127
+ </svg>
128
+ </div>
129
+ @endif
130
+ </div>
131
+
132
+ <div class="ml-6 flex flex-1 flex-col">
133
+ <div class="flex">
134
+ <div class="min-w-0 flex-1">
135
+ <h4 class="text-sm">
136
+ <a href="#" class="text-gray-700 dark:text-gray-300 font-medium hover:text-gray-800 dark:hover:text-gray-200">
137
+ {{ product.name }}
138
+ </a>
139
+ </h4>
140
+ </div>
141
+
142
+ @if(onRemoveProduct)
143
+ <div class="ml-4 flow-root flex-shrink-0">
144
+ <button
145
+ type="button"
146
+ class="flex items-center justify-center bg-white dark:bg-blue-gray-700 p-2.5 text-gray-400 dark:text-gray-500 -m-2.5 hover:text-gray-500 dark:hover:text-gray-400"
147
+ onclick="handleRemoveProduct('{{ product.id }}')"
148
+ title="Remove"
149
+ >
150
+ <span class="sr-only">Remove</span>
151
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
152
+ <path fill-rule="evenodd" d="M8.75 1A2.75 2.75 0 0 0 6 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 1 0 .23 1.482l.149-.022.841 10.518A2.75 2.75 0 0 0 7.596 19h4.807a2.75 2.75 0 0 0 2.742-2.53l.841-10.52.149.023a.75.75 0 0 0 .23-1.482A41.03 41.03 0 0 0 14 4.193V3.75A2.75 2.75 0 0 0 11.25 1h-2.5ZM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4ZM8.58 7.72a.75.75 0 0 0-1.5.06l.3 7.5a.75.75 0 1 0 1.5-.06l-.3-7.5Zm4.34.06a.75.75 0 1 0-1.5-.06l-.3 7.5a.75.75 0 1 0 1.5.06l.3-7.5Z" clip-rule="evenodd" />
153
+ </svg>
154
+ </button>
155
+ </div>
156
+ @endif
157
+ </div>
158
+
159
+ <div class="flex flex-1 items-end justify-between pt-2">
160
+ <p class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-medium">
161
+ ${{ (product.price / 100).toFixed(2) }}
162
+ </p>
163
+
164
+ <div class="ml-4">
165
+ <label for="quantity-{{ product.id }}" class="sr-only">Quantity</label>
166
+ <select
167
+ id="quantity-{{ product.id }}"
168
+ name="quantity"
169
+ class="border border-gray-300 dark:border-gray-600 rounded-md text-left text-base text-gray-700 dark:text-gray-300 font-medium shadow-sm bg-white dark:bg-blue-gray-700 focus:border-indigo-500 sm:text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
170
+ >
171
+ <option value="1" {{ (product.quantity || 1) === 1 ? 'selected' : '' }}>1</option>
172
+ <option value="2" {{ (product.quantity || 1) === 2 ? 'selected' : '' }}>2</option>
173
+ <option value="3" {{ (product.quantity || 1) === 3 ? 'selected' : '' }}>3</option>
174
+ <option value="4" {{ (product.quantity || 1) === 4 ? 'selected' : '' }}>4</option>
175
+ <option value="5" {{ (product.quantity || 1) === 5 ? 'selected' : '' }}>5</option>
176
+ <option value="6" {{ (product.quantity || 1) === 6 ? 'selected' : '' }}>6</option>
177
+ <option value="7" {{ (product.quantity || 1) === 7 ? 'selected' : '' }}>7</option>
178
+ <option value="8" {{ (product.quantity || 1) === 8 ? 'selected' : '' }}>8</option>
179
+ </select>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </li>
184
+ @endforeach
185
+ </ul>
186
+
187
+ <!-- Summary Totals -->
188
+ <dl class="border-t border-gray-200 dark:border-gray-700 px-4 py-6 space-y-6 sm:px-6">
189
+ <div class="flex items-center justify-between">
190
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Subtotal</dt>
191
+ <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
192
+ ${{ (calculateSubtotal() / 100).toFixed(2) }}
193
+ </dd>
194
+ </div>
195
+
196
+ @if(shipping > 0)
197
+ <div class="flex items-center justify-between">
198
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Shipping</dt>
199
+ <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
200
+ ${{ (shipping / 100).toFixed(2) }}
201
+ </dd>
202
+ </div>
203
+ @endif
204
+
205
+ @if(taxes > 0)
206
+ <div class="flex items-center justify-between">
207
+ <dt class="text-sm text-gray-600 dark:text-gray-400">Taxes</dt>
208
+ <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
209
+ ${{ (taxes / 100).toFixed(2) }}
210
+ </dd>
211
+ </div>
212
+ @endif
213
+
214
+ <div class="flex items-center justify-between border-t border-gray-200 dark:border-gray-700 pt-6">
215
+ <dt class="text-base text-gray-900 dark:text-gray-100 font-medium">Total</dt>
216
+ <dd class="text-base text-gray-900 dark:text-gray-100 font-medium">
217
+ ${{ (total / 100).toFixed(2) }}
218
+ </dd>
219
+ </div>
220
+ </dl>
221
+
222
+ <!-- Submit Button -->
223
+ <div class="border-t border-gray-200 dark:border-gray-700 px-4 py-6 sm:px-6">
224
+ <button
225
+ type="submit"
226
+ class="w-full border border-transparent rounded-md bg-indigo-600 dark:bg-indigo-500 px-4 py-3 text-base text-white font-medium shadow-sm hover:bg-indigo-700 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900"
227
+ >
228
+ Confirm order
229
+ </button>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </form>
234
+ </div>
235
+ </div>
236
+
237
+ <script>
238
+ // Stripe integration
239
+ let stripe = null
240
+ let elements = null
241
+ let paymentElement = null
242
+
243
+ function handleRemoveProduct(productId) {
244
+ if (onRemoveProduct) {
245
+ onRemoveProduct(productId)
246
+ }
247
+ }
248
+
249
+ async function initializeStripe() {
250
+ if (!stripePublicKey) {
251
+ console.error('Stripe public key is required')
252
+ return
253
+ }
254
+
255
+ try {
256
+ // Load Stripe.js
257
+ if (typeof window !== 'undefined' && window.Stripe) {
258
+ stripe = window.Stripe(stripePublicKey)
259
+ } else {
260
+ // Dynamically load Stripe.js
261
+ const script = document.createElement('script')
262
+ script.src = 'https://js.stripe.com/v3/'
263
+ script.async = true
264
+ script.onload = () => {
265
+ stripe = window.Stripe(stripePublicKey)
266
+ setupStripeElements()
267
+ }
268
+ document.head.appendChild(script)
269
+ }
270
+
271
+ if (stripe) {
272
+ await setupStripeElements()
273
+ }
274
+ } catch (error) {
275
+ console.error('Error initializing Stripe:', error)
276
+ }
277
+ }
278
+
279
+ async function setupStripeElements() {
280
+ if (!stripe || !apiUrl) return
281
+
282
+ try {
283
+ // Create payment intent for subscription
284
+ const response = await fetch(`${apiUrl}/stripe/create-subscription`, {
285
+ method: 'POST',
286
+ headers: {
287
+ 'Content-Type': 'application/json',
288
+ 'Accept': 'application/json',
289
+ },
290
+ body: JSON.stringify({
291
+ amount: total,
292
+ quantity: products.reduce((sum, p) => sum + (p.quantity || 1), 0),
293
+ }),
294
+ })
295
+
296
+ const paymentIntent = await response.json()
297
+ const { clientSecret } = paymentIntent
298
+ if (!clientSecret) {
299
+ console.error('No client secret returned from API')
300
+ return
301
+ }
302
+
303
+ // Create elements instance
304
+ elements = stripe.elements({
305
+ clientSecret,
306
+ appearance: {
307
+ theme: document.documentElement.classList.contains('dark') ? 'night' : 'stripe',
308
+ },
309
+ })
310
+
311
+ // Create and mount payment element
312
+ paymentElement = elements.create('payment')
313
+ paymentElement.mount('#payment-element')
314
+ } catch (error) {
315
+ console.error('Error setting up Stripe elements:', error)
316
+ }
317
+ }
318
+
319
+ async function handleCheckoutSubmit(event) {
320
+ if (!stripe || !elements) {
321
+ console.error('Stripe not initialized')
322
+ return
323
+ }
324
+
325
+ try {
326
+ const { error } = await stripe.confirmPayment({
327
+ elements,
328
+ redirect: 'if_required',
329
+ confirmParams: {
330
+ return_url: window.location.href,
331
+ },
332
+ })
333
+
334
+ if (error) {
335
+ console.error('Payment error:', error)
336
+ alert(error.message)
337
+ } else {
338
+ // Success!
339
+ if (onSubmit) {
340
+ onSubmit({ success: true })
341
+ }
342
+ }
343
+ } catch (error) {
344
+ console.error('Checkout error:', error)
345
+ alert('An error occurred during checkout')
346
+ }
347
+ }
348
+
349
+ // Initialize on component mount
350
+ if (typeof window !== 'undefined') {
351
+ initializeStripe()
352
+ }
353
+ </script>
@@ -0,0 +1,59 @@
1
+ export { default as Checkout } from './Checkout.stx'
2
+ export { default as DefaultPaymentMethod } from './DefaultPaymentMethod.stx'
3
+ export { default as PaymentMethods } from './PaymentMethods.stx'
4
+ export { default as SubscriptionCheckout } from './SubscriptionCheckout.stx'
5
+
6
+ export interface PaymentMethod {
7
+ id: string | number
8
+ brand: string
9
+ last_four: string
10
+ exp_month: number
11
+ exp_year: number
12
+ is_default?: boolean
13
+ }
14
+
15
+ export interface PaymentMethodsProps {
16
+ paymentMethods: PaymentMethod[]
17
+ onDeletePaymentMethod?: (id: string | number) => void
18
+ onMakeDefault?: (id: string | number) => void
19
+ isLoading?: boolean
20
+ className?: string
21
+ }
22
+
23
+ export interface DefaultPaymentMethodProps {
24
+ paymentMethod: PaymentMethod | null
25
+ isLoading?: boolean
26
+ className?: string
27
+ }
28
+
29
+ export interface Product {
30
+ id: string | number
31
+ name: string
32
+ price: number // in cents
33
+ images?: string
34
+ quantity?: number
35
+ }
36
+
37
+ export interface SubscriptionCheckoutProps {
38
+ products: Product[]
39
+ stripePublicKey: string
40
+ apiUrl: string
41
+ onSubmit?: (result: { success: boolean }) => void
42
+ onRemoveProduct?: (id: string | number) => void
43
+ shipping?: number
44
+ taxes?: number
45
+ className?: string
46
+ }
47
+
48
+ export interface CheckoutProps {
49
+ products: Product[]
50
+ redirectUrl: string
51
+ mode: 'one-time' | 'subscription'
52
+ stripePublicKey: string
53
+ apiUrl: string
54
+ onSubmit?: (result: { success: boolean }) => void
55
+ onRemoveProduct?: (id: string | number) => void
56
+ shipping?: number
57
+ taxes?: number
58
+ className?: string
59
+ }
@@ -0,0 +1,24 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const as = $props.as || 'div'
4
+ export let isOpen = false
5
+
6
+ export function toggle() {
7
+ isOpen = !isOpen
8
+ }
9
+
10
+ export function close() {
11
+ isOpen = false
12
+ }
13
+
14
+ export function open() {
15
+ isOpen = true
16
+ }
17
+
18
+ const baseClasses = 'relative'
19
+ const popoverClasses = `${baseClasses} ${className}`.trim()
20
+ </script>
21
+
22
+ <component :is="{{ as }}" class="{{ popoverClasses }}">
23
+ <slot :isOpen="isOpen" :toggle="toggle" :close="close" :open="open" />
24
+ </component>
@@ -0,0 +1,23 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const as = $props.as || 'button'
4
+ export const disabled = $props.disabled || false
5
+ export const onClick = $props.onClick
6
+
7
+ const baseClasses = 'inline-flex items-center gap-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75'
8
+ const buttonClasses = `${baseClasses} ${className}`.trim()
9
+
10
+ export function handleClick(e) {
11
+ if (disabled) return
12
+ onClick?.(e)
13
+ }
14
+ </script>
15
+
16
+ <component
17
+ :is="{{ as }}"
18
+ class="{{ buttonClasses }}"
19
+ @if(disabled)disabled aria-disabled="true"@endif
20
+ @click="handleClick"
21
+ >
22
+ <slot />
23
+ </component>
@@ -0,0 +1,23 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const as = $props.as || 'div'
4
+ export const static_ = $props.static || false
5
+ export const position = $props.position || 'bottom'
6
+
7
+ const positionClasses = {
8
+ 'top': 'bottom-full mb-2',
9
+ 'bottom': 'top-full mt-2',
10
+ 'left': 'right-full mr-2',
11
+ 'right': 'left-full ml-2',
12
+ }
13
+
14
+ const baseClasses = 'absolute z-10 rounded-lg bg-white dark:bg-neutral-800 p-4 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'
15
+ const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
16
+ </script>
17
+
18
+ <component
19
+ :is="{{ as }}"
20
+ class="{{ panelClasses }}"
21
+ >
22
+ <slot />
23
+ </component>
@@ -0,0 +1,95 @@
1
+ # Popover Component
2
+
3
+ A floating panel that displays rich content anchored to a button or element.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ @component('Popover')
15
+ @component('PopoverButton', { className: 'px-4 py-2 bg-blue-500 text-white rounded' })
16
+ Solutions
17
+ @endcomponent
18
+
19
+ @component('PopoverPanel', { className: 'w-64' })
20
+ <div class="grid gap-4">
21
+ <a href="/analytics">Analytics</a>
22
+ <a href="/engagement">Engagement</a>
23
+ <a href="/security">Security</a>
24
+ </div>
25
+ @endcomponent
26
+ @endcomponent
27
+ ```
28
+
29
+ ## Props
30
+
31
+ ### Popover
32
+ | Prop | Type | Default | Description |
33
+ |------|------|---------|-------------|
34
+ | `className` | `string` | `''` | Additional CSS classes |
35
+ | `as` | `string` | `'div'` | HTML element to render |
36
+
37
+ ### PopoverButton
38
+ | Prop | Type | Default | Description |
39
+ |------|------|---------|-------------|
40
+ | `className` | `string` | `''` | Additional CSS classes |
41
+ | `as` | `string` | `'button'` | HTML element to render |
42
+ | `disabled` | `boolean` | `false` | Disable the button |
43
+ | `onClick` | `(e: Event) => void` | - | Click handler |
44
+
45
+ ### PopoverPanel
46
+ | Prop | Type | Default | Description |
47
+ |------|------|---------|-------------|
48
+ | `className` | `string` | `''` | Additional CSS classes |
49
+ | `as` | `string` | `'div'` | HTML element to render |
50
+ | `static` | `boolean` | `false` | Keep visible regardless of state |
51
+ | `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Panel position |
52
+
53
+ ## Examples
54
+
55
+ ### Solutions Menu
56
+
57
+ ```stx
58
+ @component('PopoverPanel', { className: 'w-80' })
59
+ <div class="grid gap-8">
60
+ <a href="/analytics" class="flex items-start gap-4 group">
61
+ <div class="shrink-0 text-blue-500">
62
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
63
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
64
+ </svg>
65
+ </div>
66
+ <div>
67
+ <p class="font-semibold text-neutral-900 dark:text-white group-hover:text-blue-500">Analytics</p>
68
+ <p class="text-sm text-neutral-500 dark:text-neutral-400">Get a better understanding of your traffic</p>
69
+ </div>
70
+ </a>
71
+ </div>
72
+ @endcomponent
73
+ ```
74
+
75
+ ### Different Positions
76
+
77
+ ```stx
78
+ @component('PopoverPanel', { position: 'top' })
79
+ Content appears above
80
+ @endcomponent
81
+
82
+ @component('PopoverPanel', { position: 'left' })
83
+ Content appears to the left
84
+ @endcomponent
85
+ ```
86
+
87
+ ## Features
88
+
89
+ - Multiple positioning options
90
+ - Auto-positioning
91
+ - Dark mode support
92
+ - Accessible (WAI-ARIA)
93
+ - Keyboard support
94
+ - Focus management
95
+ - Customizable styling
@@ -0,0 +1,22 @@
1
+ export { default as Popover } from './Popover.stx'
2
+ export { default as PopoverButton } from './PopoverButton.stx'
3
+ export { default as PopoverPanel } from './PopoverPanel.stx'
4
+
5
+ export interface PopoverProps {
6
+ className?: string
7
+ as?: string
8
+ }
9
+
10
+ export interface PopoverButtonProps {
11
+ className?: string
12
+ as?: string
13
+ disabled?: boolean
14
+ onClick?: (e: Event) => void
15
+ }
16
+
17
+ export interface PopoverPanelProps {
18
+ className?: string
19
+ as?: string
20
+ static?: boolean
21
+ position?: 'top' | 'bottom' | 'left' | 'right'
22
+ }
@@ -0,0 +1,74 @@
1
+ <script>
2
+ export const target = $props.target || 'body'
3
+ export const disabled = $props.disabled || false
4
+
5
+ let portalContainer = null
6
+ let mounted = false
7
+
8
+ // Create portal container
9
+ function createPortal() {
10
+ if (typeof window === 'undefined' || disabled)
11
+ return
12
+
13
+ // Get target element
14
+ const targetElement = typeof target === 'string'
15
+ ? document.querySelector(target)
16
+ : target
17
+
18
+ if (!targetElement) {
19
+ console.warn(`Portal target "${target}" not found`)
20
+ return
21
+ }
22
+
23
+ // Create portal container
24
+ portalContainer = document.createElement('div')
25
+ portalContainer.setAttribute('data-portal', 'true')
26
+
27
+ // Move portal content to target
28
+ const portalContent = document.getElementById('portal-content')
29
+ if (portalContent) {
30
+ while (portalContent.firstChild) {
31
+ portalContainer.appendChild(portalContent.firstChild)
32
+ }
33
+ }
34
+
35
+ targetElement.appendChild(portalContainer)
36
+ mounted = true
37
+ }
38
+
39
+ // Cleanup portal
40
+ function cleanupPortal() {
41
+ if (portalContainer && portalContainer.parentNode) {
42
+ portalContainer.parentNode.removeChild(portalContainer)
43
+ portalContainer = null
44
+ mounted = false
45
+ }
46
+ }
47
+
48
+ // Initialize portal on mount
49
+ if (typeof window !== 'undefined') {
50
+ window.addEventListener('DOMContentLoaded', () => {
51
+ setTimeout(createPortal, 0)
52
+ })
53
+
54
+ // Cleanup on page unload
55
+ window.addEventListener('beforeunload', cleanupPortal)
56
+ }
57
+ </script>
58
+
59
+ @if(disabled)
60
+ <!-- Render in place if disabled -->
61
+ <slot />
62
+ @else
63
+ <!-- Hidden container that will be moved to portal -->
64
+ <div id="portal-content" style="display: none;">
65
+ <slot />
66
+ </div>
67
+ @endif
68
+
69
+ <script>
70
+ // Trigger portal creation after render
71
+ if (typeof window !== 'undefined' && document.readyState === 'complete') {
72
+ setTimeout(createPortal, 0)
73
+ }
74
+ </script>
@@ -0,0 +1,6 @@
1
+ export { default as Portal } from './Portal.stx'
2
+
3
+ export interface PortalProps {
4
+ target?: string | HTMLElement
5
+ disabled?: boolean
6
+ }