@stacksjs/components 0.2.35 → 0.2.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/dist/Accordion-19x8q4rj.stx +88 -0
  2. package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
  3. package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
  4. package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
  5. package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
  6. package/dist/Calendar-reny0et7.stx +189 -0
  7. package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
  8. package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
  9. package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
  10. package/dist/Combobox-fdkr6cdq.stx +96 -0
  11. package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
  12. package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
  13. package/dist/ComboboxOption-hmxt3gte.stx +49 -0
  14. package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
  15. package/dist/CommandPalette-36mxf05w.stx +81 -0
  16. package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
  17. package/dist/Dialog-hczpexs4.stx +37 -0
  18. package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
  19. package/dist/Dropdown-vvhemtc0.stx +82 -0
  20. package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
  21. package/dist/DropdownItem-c7rz08e5.stx +35 -0
  22. package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
  23. package/dist/EmailInput-vak3kkmk.stx +30 -0
  24. package/dist/Form-mas630hy.stx +95 -0
  25. package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
  26. package/dist/Listbox-q49tnr90.stx +101 -0
  27. package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
  28. package/dist/ListboxOption-e0dtv1gc.stx +49 -0
  29. package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
  30. package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
  31. package/dist/Navigator-rk2a5wvk.stx +113 -0
  32. package/dist/Notification-tb9b525s.stx +78 -0
  33. package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
  34. package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
  35. package/dist/PasswordInput-n02x6053.stx +142 -0
  36. package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
  37. package/dist/Popover-et2q44m7.stx +60 -0
  38. package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
  39. package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
  40. package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
  41. package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
  42. package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
  43. package/dist/RadioGroup-qxmnm3xm.stx +60 -0
  44. package/dist/RadioGroupOption-s1d6r221.stx +58 -0
  45. package/dist/SearchInput-pdx7agn0.stx +44 -0
  46. package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
  47. package/dist/Sidebar-73vc78as.stx +236 -0
  48. package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
  49. package/dist/SidebarHeader-7v174a9c.stx +122 -0
  50. package/dist/SidebarItem-3y3e8g2t.stx +92 -0
  51. package/dist/SidebarSection-z5ey6awe.stx +113 -0
  52. package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
  53. package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
  54. package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
  55. package/dist/Switch-r5t5w04w.stx +70 -0
  56. package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
  57. package/dist/Tabs-bzvvn4ke.stx +126 -0
  58. package/dist/Teleport-tsx4bt4n.stx +65 -0
  59. package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
  60. package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
  61. package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
  62. package/dist/Transition-7smenaz8.stx +102 -0
  63. package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
  64. package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
  65. package/dist/VirtualList-fa7v5a3h.stx +67 -0
  66. package/dist/VirtualTable-f9crbfjr.stx +103 -0
  67. package/dist/components/CodeBlock.stx +1 -6
  68. package/dist/index.js +13 -13
  69. package/dist/ui/accordion/Accordion.stx +1 -3
  70. package/dist/ui/auth/Login.stx +0 -1
  71. package/dist/ui/auth/Signup.stx +0 -1
  72. package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
  73. package/dist/ui/avatar/Avatar.stx +0 -2
  74. package/dist/ui/badge/Badge.stx +0 -1
  75. package/dist/ui/button/Button.stx +1 -2
  76. package/dist/ui/calendar/Calendar.stx +12 -2
  77. package/dist/ui/card/Card.stx +0 -2
  78. package/dist/ui/checkbox/Checkbox.stx +2 -2
  79. package/dist/ui/combobox/Combobox.stx +2 -1
  80. package/dist/ui/combobox/ComboboxButton.stx +1 -0
  81. package/dist/ui/combobox/ComboboxInput.stx +1 -4
  82. package/dist/ui/combobox/ComboboxOption.stx +2 -9
  83. package/dist/ui/combobox/ComboboxOptions.stx +1 -1
  84. package/dist/ui/command-palette/CommandPalette.stx +3 -6
  85. package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
  86. package/dist/ui/dialog/Dialog.stx +1 -3
  87. package/dist/ui/drawer/Drawer.stx +1 -3
  88. package/dist/ui/dropdown/Dropdown.stx +2 -5
  89. package/dist/ui/dropdown/DropdownButton.stx +1 -3
  90. package/dist/ui/dropdown/DropdownItem.stx +2 -7
  91. package/dist/ui/dropdown/DropdownItems.stx +1 -1
  92. package/dist/ui/form/Form.stx +0 -5
  93. package/dist/ui/image/Image.stx +1 -6
  94. package/dist/ui/input/EmailInput.stx +1 -1
  95. package/dist/ui/input/NumberInput.stx +0 -1
  96. package/dist/ui/input/PasswordInput.stx +1 -4
  97. package/dist/ui/input/SearchInput.stx +2 -2
  98. package/dist/ui/input/TextInput.stx +1 -2
  99. package/dist/ui/listbox/Listbox.stx +2 -1
  100. package/dist/ui/listbox/ListboxButton.stx +1 -0
  101. package/dist/ui/listbox/ListboxOption.stx +2 -9
  102. package/dist/ui/listbox/ListboxOptions.stx +1 -1
  103. package/dist/ui/navigator/Navigator.stx +4 -4
  104. package/dist/ui/notification/Notification.stx +1 -5
  105. package/dist/ui/pagination/Pagination.stx +1 -2
  106. package/dist/ui/payment/Checkout.stx +0 -4
  107. package/dist/ui/payment/PaymentMethods.stx +15 -2
  108. package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
  109. package/dist/ui/popover/Popover.stx +2 -5
  110. package/dist/ui/popover/PopoverButton.stx +1 -3
  111. package/dist/ui/popover/PopoverPanel.stx +1 -1
  112. package/dist/ui/portal/Portal.stx +1 -0
  113. package/dist/ui/progress/Progress.stx +6 -1
  114. package/dist/ui/radio/Radio.stx +1 -1
  115. package/dist/ui/radio-group/RadioGroup.stx +2 -1
  116. package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
  117. package/dist/ui/select/Select.stx +1 -1
  118. package/dist/ui/sidebar/Sidebar.stx +9 -26
  119. package/dist/ui/sidebar/SidebarFooter.stx +6 -21
  120. package/dist/ui/sidebar/SidebarHeader.stx +7 -18
  121. package/dist/ui/sidebar/SidebarItem.stx +4 -20
  122. package/dist/ui/sidebar/SidebarSection.stx +5 -18
  123. package/dist/ui/sidebar/index.d.ts +13 -1
  124. package/dist/ui/stepper/StepperStep.stx +1 -5
  125. package/dist/ui/switch/Switch.stx +4 -10
  126. package/dist/ui/table/TableHeader.stx +1 -1
  127. package/dist/ui/tabs/Tabs.stx +0 -1
  128. package/dist/ui/teleport/Teleport.stx +1 -0
  129. package/dist/ui/textarea/Textarea.stx +1 -1
  130. package/dist/ui/tooltip/Tooltip.stx +1 -2
  131. package/dist/ui/transition/Transition.stx +9 -16
  132. package/dist/ui/virtual-list/VirtualList.stx +2 -4
  133. package/dist/ui/virtual-table/VirtualTable.stx +1 -1
  134. package/package.json +3 -3
  135. package/src/components/CodeBlock.stx +26 -29
  136. package/src/components/Footer.stx +1 -1
  137. package/src/components/Hero.stx +1 -1
  138. package/src/components/Installation.stx +5 -7
  139. package/src/ui/accordion/Accordion.stx +48 -68
  140. package/src/ui/audio/Audio.stx +4 -4
  141. package/src/ui/auth/Login.stx +17 -19
  142. package/src/ui/auth/Signup.stx +26 -28
  143. package/src/ui/auth/TwoFactorChallenge.stx +49 -63
  144. package/src/ui/avatar/Avatar.stx +23 -34
  145. package/src/ui/badge/Badge.stx +11 -19
  146. package/src/ui/button/Button.stx +18 -33
  147. package/src/ui/calendar/Calendar.stx +93 -136
  148. package/src/ui/card/Card.stx +27 -33
  149. package/src/ui/checkbox/Checkbox.stx +36 -44
  150. package/src/ui/combobox/Combobox.stx +87 -4
  151. package/src/ui/combobox/ComboboxButton.stx +14 -2
  152. package/src/ui/combobox/ComboboxInput.stx +12 -5
  153. package/src/ui/combobox/ComboboxOption.stx +29 -32
  154. package/src/ui/combobox/ComboboxOptions.stx +7 -3
  155. package/src/ui/command-palette/CommandPalette.stx +70 -28
  156. package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
  157. package/src/ui/dialog/Dialog.stx +28 -10
  158. package/src/ui/drawer/Drawer.stx +29 -49
  159. package/src/ui/dropdown/Dropdown.stx +69 -11
  160. package/src/ui/dropdown/DropdownButton.stx +12 -6
  161. package/src/ui/dropdown/DropdownItem.stx +18 -26
  162. package/src/ui/dropdown/DropdownItems.stx +2 -2
  163. package/src/ui/form/Form.stx +50 -164
  164. package/src/ui/heatmap/Heatmap.stx +16 -37
  165. package/src/ui/image/Image.stx +68 -100
  166. package/src/ui/input/EmailInput.stx +16 -27
  167. package/src/ui/input/NumberInput.stx +40 -53
  168. package/src/ui/input/PasswordInput.stx +64 -77
  169. package/src/ui/input/SearchInput.stx +27 -46
  170. package/src/ui/input/TextInput.stx +42 -59
  171. package/src/ui/listbox/Listbox.stx +91 -4
  172. package/src/ui/listbox/ListboxButton.stx +12 -1
  173. package/src/ui/listbox/ListboxOption.stx +30 -38
  174. package/src/ui/listbox/ListboxOptions.stx +2 -2
  175. package/src/ui/navigator/Navigator.stx +52 -77
  176. package/src/ui/notification/Notification.stx +46 -39
  177. package/src/ui/pagination/Pagination.stx +73 -108
  178. package/src/ui/payment/Checkout.stx +8 -16
  179. package/src/ui/payment/PaymentMethods.stx +21 -30
  180. package/src/ui/payment/SubscriptionCheckout.stx +99 -129
  181. package/src/ui/popover/Popover.stx +50 -14
  182. package/src/ui/popover/PopoverButton.stx +12 -6
  183. package/src/ui/popover/PopoverPanel.stx +6 -6
  184. package/src/ui/portal/Portal.stx +20 -37
  185. package/src/ui/progress/Progress.stx +36 -48
  186. package/src/ui/radio/Radio.stx +29 -34
  187. package/src/ui/radio-group/RadioGroup.stx +51 -4
  188. package/src/ui/radio-group/RadioGroupOption.stx +37 -30
  189. package/src/ui/select/Select.stx +23 -31
  190. package/src/ui/sidebar/Sidebar.stx +136 -188
  191. package/src/ui/sidebar/SidebarFooter.stx +49 -35
  192. package/src/ui/sidebar/SidebarHeader.stx +52 -30
  193. package/src/ui/sidebar/SidebarItem.stx +52 -78
  194. package/src/ui/sidebar/SidebarSection.stx +56 -51
  195. package/src/ui/sidebar/index.ts +21 -1
  196. package/src/ui/stepper/StepperStep.stx +9 -5
  197. package/src/ui/switch/Switch.stx +38 -42
  198. package/src/ui/table/TableHeader.stx +10 -3
  199. package/src/ui/tabs/Tabs.stx +68 -87
  200. package/src/ui/teleport/Teleport.stx +23 -59
  201. package/src/ui/textarea/Textarea.stx +42 -54
  202. package/src/ui/tooltip/Tooltip.stx +34 -40
  203. package/src/ui/transition/Transition.stx +76 -148
  204. package/src/ui/video/Video.stx +4 -4
  205. package/src/ui/virtual-list/VirtualList.stx +45 -61
  206. package/src/ui/virtual-table/VirtualTable.stx +62 -72
  207. package/dist/Accordion-sbfra0mz.stx +0 -108
  208. package/dist/Calendar-58nnkdh8.stx +0 -232
  209. package/dist/Combobox-th5ebw62.stx +0 -13
  210. package/dist/ComboboxOption-an9zyan6.stx +0 -52
  211. package/dist/CommandPalette-kwmdf2np.stx +0 -39
  212. package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
  213. package/dist/Dialog-rgmj8q2j.stx +0 -19
  214. package/dist/Dropdown-jz4jy0n3.stx +0 -24
  215. package/dist/DropdownItem-wagdspb8.stx +0 -43
  216. package/dist/EmailInput-ajsp9vw6.stx +0 -41
  217. package/dist/Form-1mdmx9b6.stx +0 -209
  218. package/dist/Listbox-pqqh72g8.stx +0 -14
  219. package/dist/ListboxOption-7nxzaexq.stx +0 -57
  220. package/dist/Navigator-aay8bjab.stx +0 -138
  221. package/dist/Notification-gk33gv7b.stx +0 -71
  222. package/dist/PasswordInput-zv4hm43j.stx +0 -155
  223. package/dist/Popover-469gbt6r.stx +0 -24
  224. package/dist/RadioGroup-bvb1mrhd.stx +0 -13
  225. package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
  226. package/dist/SearchInput-2521m475.stx +0 -63
  227. package/dist/Sidebar-40ckbzh8.stx +0 -288
  228. package/dist/SidebarFooter-hnth56mg.stx +0 -95
  229. package/dist/SidebarHeader-3wrte2p2.stx +0 -100
  230. package/dist/SidebarItem-zqtvvzpc.stx +0 -118
  231. package/dist/SidebarSection-3etwnbjr.stx +0 -108
  232. package/dist/Switch-036j9vm1.stx +0 -74
  233. package/dist/Tabs-svh721m0.stx +0 -145
  234. package/dist/Teleport-qfch3w0b.stx +0 -101
  235. package/dist/Transition-0hmr6zxb.stx +0 -174
  236. package/dist/VirtualList-cndhjejh.stx +0 -83
  237. package/dist/VirtualTable-ng0b48yp.stx +0 -113
@@ -1,12 +1,11 @@
1
1
  <script server>
2
2
  export const paymentMethods = $props.paymentMethods || []
3
- export const onDeletePaymentMethod = $props.onDeletePaymentMethod
4
- export const onMakeDefault = $props.onMakeDefault
5
3
  export const isLoading = $props.isLoading || false
4
+ export const showMakeDefault = $props.showMakeDefault ?? true
5
+ export const showDelete = $props.showDelete ?? true
6
6
  export const className = $props.className || ''
7
7
 
8
- // Card brand icon mapping
9
- const brandIcons = {
8
+ export const brandIcons = {
10
9
  visa: '💳',
11
10
  mastercard: '💳',
12
11
  amex: '💳',
@@ -17,9 +16,22 @@ const brandIcons = {
17
16
  export const containerClasses = `mt-4 ${className}`.trim()
18
17
  </script>
19
18
 
19
+ <script client>
20
+ const emit = defineEmits()
21
+
22
+ function onMakeDefault(methodId) {
23
+ emit('makeDefault', methodId)
24
+ }
25
+
26
+ function onDelete(methodId) {
27
+ if (confirm('Are you sure you want to delete this payment method?')) {
28
+ emit('deletePaymentMethod', methodId)
29
+ }
30
+ }
31
+ </script>
32
+
20
33
  <div class="{{ containerClasses }}">
21
34
  @if(isLoading)
22
- <!-- Loading state -->
23
35
  <div class="animate-pulse">
24
36
  <div class="h-24 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
25
37
  </div>
@@ -31,12 +43,10 @@ export const containerClasses = `mt-4 ${className}`.trim()
31
43
  <div class="w-full p-3">
32
44
  <div class="flex items-center justify-between">
33
45
  <div class="flex items-center space-x-4">
34
- <!-- Card Brand Icon -->
35
46
  <div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-md">
36
47
  <span class="text-2xl">{{ brandIcons[method.brand.toLowerCase()] || brandIcons.default }}</span>
37
48
  </div>
38
49
 
39
- <!-- Card Details -->
40
50
  <div>
41
51
  <h2 class="text-sm text-gray-600 dark:text-gray-300">
42
52
  {{ method.brand }} •••• {{ method.last_four }}
@@ -48,12 +58,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
48
58
  </div>
49
59
 
50
60
  <div class="flex justify-end space-x-4">
51
- <!-- Make Default Button -->
52
- @if(onMakeDefault)
61
+ @if(showMakeDefault)
53
62
  <button
54
63
  type="button"
55
64
  class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-gray-700 dark:text-gray-300 font-semibold shadow-sm hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
56
- @click="handleMakeDefault('{{ method.id }}')"
65
+ @click="onMakeDefault('{{ method.id }}')"
57
66
  title="Make default"
58
67
  >
59
68
  <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -62,12 +71,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
62
71
  </button>
63
72
  @endif
64
73
 
65
- <!-- Delete Button -->
66
- @if(onDeletePaymentMethod)
74
+ @if(showDelete)
67
75
  <button
68
76
  type="button"
69
77
  class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-red-600 dark:text-red-400 font-semibold shadow-sm hover:bg-red-50 dark:hover:bg-red-900/20 focus-visible:outline-2 focus-visible:outline-red-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
70
- @click="handleDelete('{{ method.id }}')"
78
+ @click="onDelete('{{ method.id }}')"
71
79
  title="Delete payment method"
72
80
  >
73
81
  <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -82,7 +90,6 @@ export const containerClasses = `mt-4 ${className}`.trim()
82
90
  @endforeach
83
91
  </ul>
84
92
  @else
85
- <!-- Empty state -->
86
93
  <div class="text-center py-12 bg-white dark:bg-blue-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
87
94
  <svg class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
88
95
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
@@ -95,19 +102,3 @@ export const containerClasses = `mt-4 ${className}`.trim()
95
102
  @endif
96
103
  @endif
97
104
  </div>
98
-
99
- <script>
100
- function handleMakeDefault(methodId) {
101
- if (onMakeDefault) {
102
- onMakeDefault(methodId)
103
- }
104
- }
105
-
106
- function handleDelete(methodId) {
107
- if (onDeletePaymentMethod) {
108
- if (confirm('Are you sure you want to delete this payment method?')) {
109
- onDeletePaymentMethod(methodId)
110
- }
111
- }
112
- }
113
- </script>
@@ -2,27 +2,115 @@
2
2
  export const products = $props.products || []
3
3
  export const stripePublicKey = $props.stripePublicKey || ''
4
4
  export const apiUrl = $props.apiUrl || ''
5
- export const onSubmit = $props.onSubmit
6
- export const onRemoveProduct = $props.onRemoveProduct
5
+ export const showRemoveProduct = $props.showRemoveProduct ?? true
7
6
  export const shipping = $props.shipping || 0
8
7
  export const taxes = $props.taxes || 0
9
8
  export const className = $props.className || ''
10
9
 
11
- // Calculate totals
12
- export const calculateSubtotal = () => {
13
- return products.reduce((sum, product) => sum + (product.price * (product.quantity || 1)), 0)
10
+ export const subtotal = products.reduce((sum, product) => sum + (product.price * (product.quantity || 1)), 0)
11
+ export const total = subtotal + shipping + taxes
12
+
13
+ export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.trim()
14
+ </script>
15
+
16
+ <script client>
17
+ const emit = defineEmits()
18
+ const stripePublicKey = {{ stripePublicKey }}
19
+ const apiUrl = {{ apiUrl }}
20
+ const products = {{ products }}
21
+ const total = {{ total }}
22
+
23
+ let stripe = null
24
+ let elements = null
25
+ let paymentElement = null
26
+
27
+ function onRemoveProduct(productId) {
28
+ emit('removeProduct', productId)
29
+ }
30
+
31
+ async function setupStripeElements() {
32
+ if (!stripe || !apiUrl) return
33
+ try {
34
+ const response = await fetch(`${apiUrl}/stripe/create-subscription`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
37
+ body: JSON.stringify({
38
+ amount: total,
39
+ quantity: products.reduce((sum, p) => sum + (p.quantity || 1), 0),
40
+ }),
41
+ })
42
+ const paymentIntent = await response.json()
43
+ const { clientSecret } = paymentIntent
44
+ if (!clientSecret) {
45
+ console.error('No client secret returned from API')
46
+ return
47
+ }
48
+ elements = stripe.elements({
49
+ clientSecret,
50
+ appearance: {
51
+ theme: document.documentElement.classList.contains('dark') ? 'night' : 'stripe',
52
+ },
53
+ })
54
+ paymentElement = elements.create('payment')
55
+ paymentElement.mount('#payment-element')
56
+ }
57
+ catch (error) {
58
+ console.error('Error setting up Stripe elements:', error)
59
+ }
14
60
  }
15
61
 
16
- export const total = calculateSubtotal() + shipping + taxes
62
+ async function initializeStripe() {
63
+ if (!stripePublicKey) {
64
+ console.error('Stripe public key is required')
65
+ return
66
+ }
67
+ if (window.Stripe) {
68
+ stripe = window.Stripe(stripePublicKey)
69
+ await setupStripeElements()
70
+ return
71
+ }
72
+ const script = document.createElement('script')
73
+ script.src = 'https://js.stripe.com/v3/'
74
+ script.async = true
75
+ script.onload = async () => {
76
+ stripe = window.Stripe(stripePublicKey)
77
+ await setupStripeElements()
78
+ }
79
+ document.head.appendChild(script)
80
+ }
17
81
 
18
- export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.trim()
82
+ async function onSubmit(event) {
83
+ event.preventDefault()
84
+ if (!stripe || !elements) {
85
+ emit('error', { message: 'Stripe not initialized' })
86
+ return
87
+ }
88
+ try {
89
+ const { error } = await stripe.confirmPayment({
90
+ elements,
91
+ redirect: 'if_required',
92
+ confirmParams: { return_url: window.location.href },
93
+ })
94
+ if (error) {
95
+ emit('error', error)
96
+ }
97
+ else {
98
+ emit('submit', { success: true })
99
+ }
100
+ }
101
+ catch (error) {
102
+ emit('error', error)
103
+ }
104
+ }
105
+
106
+ onMount(() => initializeStripe())
19
107
  </script>
20
108
 
21
109
  <div class="{{ containerClasses }}">
22
110
  <div class="mx-auto max-w-2xl px-4 pb-24 pt-16 lg:max-w-7xl lg:px-8 sm:px-6">
23
111
  <h2 class="sr-only">Checkout</h2>
24
112
 
25
- <form class="lg:grid lg:grid-cols-2 lg:gap-x-12 xl:gap-x-16" onsubmit="event.preventDefault(); handleCheckoutSubmit(event)">
113
+ <form class="lg:grid lg:grid-cols-2 lg:gap-x-12 xl:gap-x-16" @submit="onSubmit($event)">
26
114
  <!-- Left Column - Contact & Payment -->
27
115
  <div>
28
116
  <!-- Contact Information -->
@@ -139,12 +227,12 @@ export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.
139
227
  </h4>
140
228
  </div>
141
229
 
142
- @if(onRemoveProduct)
230
+ @if(showRemoveProduct)
143
231
  <div class="ml-4 flow-root flex-shrink-0">
144
232
  <button
145
233
  type="button"
146
234
  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
- @click="handleRemoveProduct('{{ product.id }}')"
235
+ @click="onRemoveProduct('{{ product.id }}')"
148
236
  title="Remove"
149
237
  >
150
238
  <span class="sr-only">Remove</span>
@@ -189,7 +277,7 @@ export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.
189
277
  <div class="flex items-center justify-between">
190
278
  <dt class="text-sm text-gray-600 dark:text-gray-400">Subtotal</dt>
191
279
  <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
192
- ${{ (calculateSubtotal() / 100).toFixed(2) }}
280
+ ${{ (subtotal / 100).toFixed(2) }}
193
281
  </dd>
194
282
  </div>
195
283
 
@@ -233,121 +321,3 @@ export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.
233
321
  </form>
234
322
  </div>
235
323
  </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>
@@ -1,24 +1,60 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'div'
4
- export let isOpen = false
5
4
 
6
- export function toggle() {
7
- isOpen = !isOpen
8
- }
5
+ const baseClasses = 'relative'
6
+ export const popoverClasses = `${baseClasses} ${className}`.trim()
7
+ export const popoverInstanceId = `stx-pop-${Math.random().toString(36).slice(2, 11)}`
8
+ </script>
9
9
 
10
- export function close() {
11
- isOpen = false
12
- }
10
+ <script client>
11
+ const emit = defineEmits()
12
+ const popoverInstanceId = {{ popoverInstanceId }}
13
+ const isOpen = state(false)
14
+ let rootEl = null
15
+ let outsideHandler = null
13
16
 
14
- export function open() {
15
- isOpen = true
16
- }
17
+ function open() { if (!isOpen()) { isOpen.set(true); emit('change', true) } }
18
+ function close() { if (isOpen()) { isOpen.set(false); emit('change', false) } }
19
+ function toggle() { isOpen() ? close() : open() }
17
20
 
18
- const baseClasses = 'relative'
19
- const popoverClasses = `${baseClasses} ${className}`.trim()
21
+ function onToggle(event) { event.stopPropagation(); toggle() }
22
+ function onClose(event) { event.stopPropagation(); close() }
23
+ function onOpen(event) { event.stopPropagation(); open() }
24
+
25
+ onMount(() => {
26
+ rootEl = document.querySelector(`[data-popover-id="${popoverInstanceId}"]`)
27
+ outsideHandler = (event) => {
28
+ if (!isOpen()) return
29
+ if (rootEl && !rootEl.contains(event.target)) close()
30
+ }
31
+ document.addEventListener('click', outsideHandler, true)
32
+ })
33
+
34
+ onDestroy(() => {
35
+ if (outsideHandler) document.removeEventListener('click', outsideHandler, true)
36
+ })
37
+
38
+ const dataState = derived(() => isOpen() ? 'open' : 'closed')
39
+
40
+ defineExpose({ isOpen, toggle, open, close })
20
41
  </script>
21
42
 
22
- <component :is="{{ as }}" class="{{ popoverClasses }}">
23
- <slot :isOpen="isOpen" :toggle="toggle" :close="close" :open="open" />
43
+ <component
44
+ :is="{{ as }}"
45
+ class="{{ popoverClasses }}"
46
+ data-stx-popover
47
+ data-popover-id="{{ popoverInstanceId }}"
48
+ :data-state="dataState()"
49
+ @stx:popover-toggle="onToggle($event)"
50
+ @stx:popover-open="onOpen($event)"
51
+ @stx:popover-close="onClose($event)"
52
+ >
53
+ <slot />
24
54
  </component>
55
+
56
+ <style>
57
+ [data-stx-popover][data-state="closed"] [data-stx-popover-panel] {
58
+ display: none;
59
+ }
60
+ </style>
@@ -2,22 +2,28 @@
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'button'
4
4
  export const disabled = $props.disabled || false
5
- export const onClick = $props.onClick
6
5
 
7
6
  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()
7
+ export const buttonClasses = `${baseClasses} ${className}`.trim()
8
+ </script>
9
+
10
+ <script client>
11
+ const emit = defineEmits()
12
+ const isDisabled = {{ disabled }}
9
13
 
10
- export function handleClick(e) {
11
- if (disabled) return
12
- onClick?.(e)
14
+ function onClick(event) {
15
+ if (isDisabled) return
16
+ emit('click', event)
17
+ event.currentTarget.dispatchEvent(new CustomEvent('stx:popover-toggle', { bubbles: true }))
13
18
  }
14
19
  </script>
15
20
 
16
21
  <component
17
22
  :is="{{ as }}"
18
23
  class="{{ buttonClasses }}"
24
+ data-stx-popover-button
19
25
  @if(disabled)disabled aria-disabled="true"@endif
20
- @click="handleClick"
26
+ @click="onClick($event)"
21
27
  >
22
28
  <slot />
23
29
  </component>
@@ -1,23 +1,23 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'div'
4
- export const static_ = $props.static || false
5
4
  export const position = $props.position || 'bottom'
6
5
 
7
6
  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',
7
+ top: 'bottom-full mb-2',
8
+ bottom: 'top-full mt-2',
9
+ left: 'right-full mr-2',
10
+ right: 'left-full ml-2',
12
11
  }
13
12
 
14
13
  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()
14
+ export const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
16
15
  </script>
17
16
 
18
17
  <component
19
18
  :is="{{ as }}"
20
19
  class="{{ panelClasses }}"
20
+ data-stx-popover-panel
21
21
  >
22
22
  <slot />
23
23
  </component>
@@ -2,73 +2,56 @@
2
2
  export const target = $props.target || 'body'
3
3
  export const disabled = $props.disabled || false
4
4
 
5
- let portalContainer = null
6
- let mounted = false
5
+ export const portalId = `portal-content-${Math.random().toString(36).slice(2, 11)}`
6
+ </script>
7
7
 
8
- // Create portal container
9
- function createPortal() {
10
- if (typeof window === 'undefined' || disabled)
11
- return
8
+ <script client>
9
+ const target = {{ target }}
10
+ const isDisabled = {{ disabled }}
11
+ const portalId = {{ portalId }}
12
12
 
13
- // Get target element
14
- const targetElement = typeof target === 'string'
15
- ? document.querySelector(target)
16
- : target
13
+ let portalContainer = null
17
14
 
15
+ function createPortal() {
16
+ if (isDisabled) return
17
+ const targetElement = typeof target === 'string' ? document.querySelector(target) : target
18
18
  if (!targetElement) {
19
19
  console.warn(`Portal target "${target}" not found`)
20
20
  return
21
21
  }
22
-
23
- // Create portal container
24
22
  portalContainer = document.createElement('div')
25
23
  portalContainer.setAttribute('data-portal', 'true')
26
-
27
- // Move portal content to target
28
- const portalContent = document.getElementById('portal-content')
24
+ const portalContent = document.getElementById(portalId)
29
25
  if (portalContent) {
30
26
  while (portalContent.firstChild) {
31
27
  portalContainer.appendChild(portalContent.firstChild)
32
28
  }
33
29
  }
34
-
35
30
  targetElement.appendChild(portalContainer)
36
- mounted = true
37
31
  }
38
32
 
39
- // Cleanup portal
40
33
  function cleanupPortal() {
41
34
  if (portalContainer && portalContainer.parentNode) {
42
35
  portalContainer.parentNode.removeChild(portalContainer)
43
36
  portalContainer = null
44
- mounted = false
45
37
  }
46
38
  }
47
39
 
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
40
+ onMount(() => {
41
+ setTimeout(createPortal, 0)
55
42
  window.addEventListener('beforeunload', cleanupPortal)
56
- }
43
+ })
44
+
45
+ onDestroy(() => {
46
+ cleanupPortal()
47
+ window.removeEventListener('beforeunload', cleanupPortal)
48
+ })
57
49
  </script>
58
50
 
59
51
  @if(disabled)
60
- <!-- Render in place if disabled -->
61
52
  <slot />
62
53
  @else
63
- <!-- Hidden container that will be moved to portal -->
64
- <div id="portal-content" style="display: none;">
54
+ <div id="{{ portalId }}" style="display: none;">
65
55
  <slot />
66
56
  </div>
67
57
  @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>