@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,36 @@
1
1
  <script server>
2
2
  export const headingText = $props.headingText || 'Sign up for Stacks'
3
3
  export const showSocialSignup = $props.showSocialSignup ?? true
4
- export const onSubmit = $props.onSubmit || (() => {})
5
4
  export const className = $props.className || ''
6
5
 
7
6
  export const containerClasses = `min-h-full flex flex-col justify-center py-12 lg:px-8 sm:px-6 dark:bg-blue-gray-900 ${className}`.trim()
8
7
  </script>
9
8
 
9
+ <script client>
10
+ const emit = defineEmits()
11
+
12
+ function onSubmit(event) {
13
+ event.preventDefault()
14
+ const formData = new FormData(event.target)
15
+ const name = formData.get('name')
16
+ const email = formData.get('email')
17
+ const confirmEmail = formData.get('confirm_email')
18
+ const password = formData.get('password')
19
+
20
+ if (email !== confirmEmail) {
21
+ emit('error', { message: 'Emails do not match' })
22
+ return
23
+ }
24
+
25
+ emit('submit', { name, email, password })
26
+
27
+ setTimeout(() => {
28
+ event.target.querySelector('#password')?.setAttribute('value', '')
29
+ event.target.querySelector('#confirm_email')?.setAttribute('value', '')
30
+ }, 1000)
31
+ }
32
+ </script>
33
+
10
34
  <div class="{{ containerClasses }}">
11
35
  <div class="sm:mx-auto sm:max-w-md sm:w-full">
12
36
  <h2 class="mt-6 text-center text-2xl text-gray-900 dark:text-gray-100 font-bold leading-9 tracking-tight">
@@ -16,7 +40,7 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
16
40
 
17
41
  <div class="mt-10 sm:mx-auto sm:max-w-[480px] sm:w-full">
18
42
  <div class="bg-white dark:bg-blue-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
19
- <form class="space-y-6" onsubmit="event.preventDefault(); handleSubmit(event)">
43
+ <form class="space-y-6" @submit="onSubmit($event)">
20
44
  <div>
21
45
  <label for="name" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
22
46
  Full Name
@@ -133,29 +157,3 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
133
157
  </div>
134
158
  </div>
135
159
  </div>
136
-
137
- <script>
138
- function handleSubmit(event) {
139
- const formData = new FormData(event.target)
140
- const name = formData.get('name')
141
- const email = formData.get('email')
142
- const confirmEmail = formData.get('confirm_email')
143
- const password = formData.get('password')
144
-
145
- // Basic email confirmation validation
146
- if (email !== confirmEmail) {
147
- alert('Emails do not match!')
148
- return
149
- }
150
-
151
- if (onSubmit) {
152
- onSubmit({ name, email, password })
153
- }
154
-
155
- // Clear sensitive fields after submission
156
- setTimeout(() => {
157
- event.target.querySelector('#password').value = ''
158
- event.target.querySelector('#confirm_email').value = ''
159
- }, 1000)
160
- }
161
- </script>
@@ -5,24 +5,28 @@ export const label = $props.label || ''
5
5
  export const description = $props.description || ''
6
6
  export const className = $props.className || ''
7
7
  export const as = $props.as || 'div'
8
- export const onClick = $props.onClick
9
8
 
10
9
  export const isComplete = stepNumber < currentStep
11
10
  export const isCurrent = stepNumber === currentStep
12
11
  export const isUpcoming = stepNumber > currentStep
13
12
 
14
13
  const baseClasses = 'flex items-center'
15
- const stepClasses = `${baseClasses} ${className}`.trim()
14
+ export const stepClasses = `${baseClasses} ${className}`.trim()
15
+ </script>
16
+
17
+ <script client>
18
+ const emit = defineEmits()
19
+ const stepNumber = {{ stepNumber }}
16
20
 
17
- export function handleClick() {
18
- onClick?.(stepNumber)
21
+ function onClick() {
22
+ emit('click', stepNumber)
19
23
  }
20
24
  </script>
21
25
 
22
26
  <component :is="{{ as }}" class="{{ stepClasses }}">
23
27
  <button
24
28
  type="button"
25
- @click="handleClick"
29
+ @click="onClick()"
26
30
  class="flex items-center gap-3 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 rounded-lg p-2"
27
31
  >
28
32
  <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">
@@ -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>
@@ -0,0 +1,70 @@
1
+ <script server>
2
+ export const checked = $props.checked || false
3
+ export const disabled = $props.disabled || false
4
+ export const size = $props.size || 'md'
5
+ export const className = $props.className || ''
6
+ export const label = $props.label || ''
7
+
8
+ const sizeConfig = {
9
+ sm: { track: 'h-5 w-9', thumb: 'h-4 w-4', translate: 'translate-x-4' },
10
+ md: { track: 'h-[38px] w-[74px]', thumb: 'h-[34px] w-[34px]', translate: 'translate-x-9' },
11
+ lg: { track: 'h-12 w-24', thumb: 'h-11 w-11', translate: 'translate-x-12' },
12
+ }
13
+
14
+ export const config = sizeConfig[size]
15
+
16
+ export const trackBaseClasses = 'relative inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-300 dark:focus-visible:ring-offset-neutral-700'
17
+ export const trackDisabledClasses = disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''
18
+ export const thumbBaseClasses = 'pointer-events-none inline-block transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out'
19
+ </script>
20
+
21
+ <script client>
22
+ const emit = defineEmits()
23
+ const isDisabled = {{ disabled }}
24
+ const config = {{ config }}
25
+ const trackBase = {{ trackBaseClasses }}
26
+ const trackDisabled = {{ trackDisabledClasses }}
27
+ const thumbBase = {{ thumbBaseClasses }}
28
+ const extraClass = {{ className }}
29
+ const isChecked = state({{ checked }})
30
+
31
+ function toggle() {
32
+ if (isDisabled) return
33
+ isChecked.set(!isChecked())
34
+ emit('change', isChecked())
35
+ }
36
+
37
+ function trackClasses() {
38
+ const stateClass = isChecked()
39
+ ? 'bg-blue-500 dark:bg-blue-600'
40
+ : 'bg-neutral-400 dark:bg-neutral-600'
41
+ return `${trackBase} ${stateClass} ${trackDisabled} ${config.track} ${extraClass}`.trim()
42
+ }
43
+
44
+ function thumbClasses() {
45
+ const pos = isChecked() ? config.translate : 'translate-x-0'
46
+ return `${thumbBase} ${pos} ${config.thumb}`.trim()
47
+ }
48
+
49
+ defineExpose({ isChecked, toggle })
50
+ </script>
51
+
52
+ <label class="inline-flex items-center gap-3 cursor-pointer">
53
+ @if(label)
54
+ <span class="text-sm font-medium text-neutral-700 dark:text-neutral-300">
55
+ {{ label }}
56
+ </span>
57
+ @endif
58
+
59
+ <button
60
+ type="button"
61
+ role="switch"
62
+ :aria-checked="isChecked() ? 'true' : 'false'"
63
+ :class="trackClasses()"
64
+ @if(disabled)disabled aria-disabled="true"@endif
65
+ @click="toggle()"
66
+ >
67
+ <span class="sr-only">@if(label){{ label }}@else Toggle switch@endif</span>
68
+ <span aria-hidden="true" :class="thumbClasses()"></span>
69
+ </button>
70
+ </label>
@@ -1,13 +1,20 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const sortable = $props.sortable || false
4
- export const onClick = $props.onClick
5
4
 
6
5
  const baseClasses = 'px-6 py-3 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider dark:text-neutral-400'
7
6
  const sortableClasses = sortable ? 'cursor-pointer hover:text-neutral-700 dark:hover:text-neutral-200' : ''
8
- const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
7
+ export const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
9
8
  </script>
10
9
 
11
- <th class="{{ headerClasses }}" @if(sortable)@click="onClick"@endif>
10
+ <script client>
11
+ const emit = defineEmits()
12
+
13
+ function onClick() {
14
+ emit('click')
15
+ }
16
+ </script>
17
+
18
+ <th class="{{ headerClasses }}" @if(sortable)@click="onClick()"@endif>
12
19
  <slot />
13
20
  </th>
@@ -0,0 +1,126 @@
1
+ <script server>
2
+ export const tabs = $props.tabs || []
3
+ export const defaultTab = $props.defaultTab || 0
4
+ export const orientation = $props.orientation || 'horizontal'
5
+ export const variant = $props.variant || 'line'
6
+ export const className = $props.className || ''
7
+
8
+ const orientationClasses = {
9
+ horizontal: 'flex-col',
10
+ vertical: 'flex-row',
11
+ }
12
+
13
+ const tabListOrientationClasses = {
14
+ horizontal: 'flex-row border-b',
15
+ vertical: 'flex-col border-r',
16
+ }
17
+
18
+ const variantClasses = {
19
+ line: 'border-gray-200 dark:border-gray-700',
20
+ pills: 'gap-2 border-0 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg',
21
+ enclosed: 'border-gray-200 dark:border-gray-700',
22
+ }
23
+
24
+ export const containerClasses = `flex ${orientationClasses[orientation]} ${className}`.trim()
25
+ export const tabListClasses = `flex ${tabListOrientationClasses[orientation]} ${variantClasses[variant]}`.trim()
26
+ export const tabPanelClasses = `p-4 focus:outline-none`.trim()
27
+ </script>
28
+
29
+ <script client>
30
+ const emit = defineEmits()
31
+ const tabs = {{ tabs }}
32
+ const variant = {{ variant }}
33
+ const orientation = {{ orientation }}
34
+ const activeTab = state({{ defaultTab }})
35
+
36
+ function selectTab(index) {
37
+ activeTab.set(index)
38
+ emit('change', { index, tab: tabs[index] })
39
+ }
40
+
41
+ function isActive(index) {
42
+ return activeTab() === index
43
+ }
44
+
45
+ function getTabClasses(index) {
46
+ const isActiveTab = isActive(index)
47
+ const base = 'px-4 py-2 font-medium text-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900'
48
+ if (variant === 'line') {
49
+ const border = orientation === 'horizontal' ? 'border-b-2' : 'border-r-2'
50
+ return `${base} ${border} ${isActiveTab ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600'}`
51
+ }
52
+ if (variant === 'pills') {
53
+ return `${base} rounded-md ${isActiveTab ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'}`
54
+ }
55
+ if (variant === 'enclosed') {
56
+ const borders = orientation === 'horizontal' ? 'border-t border-l border-r rounded-t-md' : 'border-t border-l border-b rounded-l-md'
57
+ return `${base} ${borders} ${isActiveTab ? 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 -mb-px' : 'bg-gray-50 dark:bg-gray-900 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`
58
+ }
59
+ return base
60
+ }
61
+
62
+ function onTabKey(event, index) {
63
+ const tabCount = tabs.length
64
+ let next = index
65
+ if (orientation === 'horizontal') {
66
+ if (event.key === 'ArrowRight') { event.preventDefault(); next = (index + 1) % tabCount }
67
+ else if (event.key === 'ArrowLeft') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
68
+ }
69
+ else {
70
+ if (event.key === 'ArrowDown') { event.preventDefault(); next = (index + 1) % tabCount }
71
+ else if (event.key === 'ArrowUp') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
72
+ }
73
+ if (event.key === 'Home') { event.preventDefault(); next = 0 }
74
+ else if (event.key === 'End') { event.preventDefault(); next = tabCount - 1 }
75
+ if (next !== index) {
76
+ selectTab(next)
77
+ document.getElementById(`tab-${next}`)?.focus()
78
+ }
79
+ }
80
+
81
+ defineExpose({ activeTab, selectTab })
82
+ </script>
83
+
84
+ <div class="{{ containerClasses }}">
85
+ <div
86
+ class="{{ tabListClasses }}"
87
+ role="tablist"
88
+ aria-orientation="{{ orientation }}"
89
+ >
90
+ @foreach(tab in tabs)
91
+ <button
92
+ type="button"
93
+ :class="getTabClasses({{ $loop.index }})"
94
+ role="tab"
95
+ :aria-selected="isActive({{ $loop.index }}) ? 'true' : 'false'"
96
+ aria-controls="tab-panel-{{ $loop.index }}"
97
+ id="tab-{{ $loop.index }}"
98
+ :tabindex="isActive({{ $loop.index }}) ? '0' : '-1'"
99
+ @click="selectTab({{ $loop.index }})"
100
+ @keydown="onTabKey($event, {{ $loop.index }})"
101
+ >
102
+ @if(tab.icon)
103
+ <span class="inline-flex items-center gap-2">
104
+ <span x-html="{!! JSON.stringify(tab.icon) !!}"></span>
105
+ <span>{{ tab.label }}</span>
106
+ </span>
107
+ @else
108
+ {{ tab.label }}
109
+ @endif
110
+ </button>
111
+ @endforeach
112
+ </div>
113
+
114
+ @foreach(tab in tabs)
115
+ <div
116
+ :show="isActive({{ $loop.index }})"
117
+ id="tab-panel-{{ $loop.index }}"
118
+ class="{{ tabPanelClasses }}"
119
+ role="tabpanel"
120
+ aria-labelledby="tab-{{ $loop.index }}"
121
+ tabindex="0"
122
+ >
123
+ {!! tab.content !!}
124
+ </div>
125
+ @endforeach
126
+ </div>
@@ -0,0 +1,65 @@
1
+ <script server>
2
+ export const to = $props.to || 'body'
3
+ export const disabled = $props.disabled || false
4
+ export const defer = $props.defer || false
5
+
6
+ export const teleportId = `teleport-${Math.random().toString(36).slice(2, 11)}`
7
+ </script>
8
+
9
+ <script client>
10
+ const to = {{ to }}
11
+ const isDisabled = {{ disabled }}
12
+ const shouldDefer = {{ defer }}
13
+ const teleportId = {{ teleportId }}
14
+
15
+ let teleportContainer = null
16
+
17
+ function teleport() {
18
+ if (isDisabled) return
19
+ const targetElement = typeof to === 'string' ? document.querySelector(to) : to
20
+ if (!targetElement) {
21
+ console.warn(`Teleport target "${to}" not found`)
22
+ return
23
+ }
24
+ const sourceElement = document.getElementById(teleportId)
25
+ if (!sourceElement) return
26
+ if (!teleportContainer) {
27
+ teleportContainer = document.createElement('div')
28
+ teleportContainer.setAttribute('data-teleport-id', teleportId)
29
+ }
30
+ while (sourceElement.firstChild) {
31
+ teleportContainer.appendChild(sourceElement.firstChild)
32
+ }
33
+ targetElement.appendChild(teleportContainer)
34
+ }
35
+
36
+ function returnContent() {
37
+ if (!teleportContainer || !teleportContainer.parentNode) return
38
+ const sourceElement = document.getElementById(teleportId)
39
+ if (!sourceElement) return
40
+ while (teleportContainer.firstChild) {
41
+ sourceElement.appendChild(teleportContainer.firstChild)
42
+ }
43
+ teleportContainer.parentNode.removeChild(teleportContainer)
44
+ teleportContainer = null
45
+ }
46
+
47
+ onMount(() => {
48
+ if (shouldDefer) setTimeout(teleport, 0)
49
+ else teleport()
50
+ window.addEventListener('beforeunload', returnContent)
51
+ })
52
+
53
+ onDestroy(() => {
54
+ returnContent()
55
+ window.removeEventListener('beforeunload', returnContent)
56
+ })
57
+ </script>
58
+
59
+ @if(disabled)
60
+ <slot />
61
+ @else
62
+ <div id="{{ teleportId }}" data-teleport-source="true">
63
+ <slot />
64
+ </div>
65
+ @endif