@stacksjs/components 0.2.27 → 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.
- package/dist/Accordion-19x8q4rj.stx +88 -0
- package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
- package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
- package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
- package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
- package/dist/Calendar-reny0et7.stx +189 -0
- package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
- package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
- package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
- package/dist/Combobox-fdkr6cdq.stx +96 -0
- package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
- package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
- package/dist/ComboboxOption-hmxt3gte.stx +49 -0
- package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
- package/dist/CommandPalette-36mxf05w.stx +81 -0
- package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
- package/dist/Dialog-hczpexs4.stx +37 -0
- package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
- package/dist/Dropdown-vvhemtc0.stx +82 -0
- package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
- package/dist/DropdownItem-c7rz08e5.stx +35 -0
- package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
- package/dist/EmailInput-vak3kkmk.stx +30 -0
- package/dist/Form-mas630hy.stx +95 -0
- package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
- package/dist/Listbox-q49tnr90.stx +101 -0
- package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
- package/dist/ListboxOption-e0dtv1gc.stx +49 -0
- package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
- package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
- package/dist/Navigator-rk2a5wvk.stx +113 -0
- package/dist/Notification-tb9b525s.stx +78 -0
- package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
- package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
- package/dist/PasswordInput-n02x6053.stx +142 -0
- package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
- package/dist/Popover-et2q44m7.stx +60 -0
- package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
- package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
- package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
- package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
- package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
- package/dist/RadioGroup-qxmnm3xm.stx +60 -0
- package/dist/RadioGroupOption-s1d6r221.stx +58 -0
- package/dist/SearchInput-pdx7agn0.stx +44 -0
- package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
- package/dist/Sidebar-73vc78as.stx +236 -0
- package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
- package/dist/SidebarHeader-7v174a9c.stx +122 -0
- package/dist/SidebarItem-3y3e8g2t.stx +92 -0
- package/dist/SidebarSection-z5ey6awe.stx +113 -0
- package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
- package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
- package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
- package/dist/Switch-r5t5w04w.stx +70 -0
- package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
- package/dist/Tabs-bzvvn4ke.stx +126 -0
- package/dist/Teleport-tsx4bt4n.stx +65 -0
- package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
- package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
- package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
- package/dist/Transition-7smenaz8.stx +102 -0
- package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
- package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
- package/dist/VirtualList-fa7v5a3h.stx +67 -0
- package/dist/VirtualTable-f9crbfjr.stx +103 -0
- package/dist/components/CodeBlock.stx +1 -6
- package/dist/index.js +13 -13
- package/dist/ui/accordion/Accordion.stx +1 -3
- package/dist/ui/auth/Login.stx +0 -1
- package/dist/ui/auth/Signup.stx +0 -1
- package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
- package/dist/ui/avatar/Avatar.stx +0 -2
- package/dist/ui/badge/Badge.stx +0 -1
- package/dist/ui/button/Button.stx +1 -2
- package/dist/ui/calendar/Calendar.stx +12 -2
- package/dist/ui/card/Card.stx +0 -2
- package/dist/ui/checkbox/Checkbox.stx +2 -2
- package/dist/ui/combobox/Combobox.stx +2 -1
- package/dist/ui/combobox/ComboboxButton.stx +1 -0
- package/dist/ui/combobox/ComboboxInput.stx +1 -4
- package/dist/ui/combobox/ComboboxOption.stx +2 -9
- package/dist/ui/combobox/ComboboxOptions.stx +1 -1
- package/dist/ui/command-palette/CommandPalette.stx +3 -6
- package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
- package/dist/ui/dialog/Dialog.stx +1 -3
- package/dist/ui/drawer/Drawer.stx +1 -3
- package/dist/ui/dropdown/Dropdown.stx +2 -5
- package/dist/ui/dropdown/DropdownButton.stx +1 -3
- package/dist/ui/dropdown/DropdownItem.stx +2 -7
- package/dist/ui/dropdown/DropdownItems.stx +1 -1
- package/dist/ui/form/Form.stx +0 -5
- package/dist/ui/image/Image.stx +1 -6
- package/dist/ui/input/EmailInput.stx +1 -1
- package/dist/ui/input/NumberInput.stx +0 -1
- package/dist/ui/input/PasswordInput.stx +1 -4
- package/dist/ui/input/SearchInput.stx +2 -2
- package/dist/ui/input/TextInput.stx +1 -2
- package/dist/ui/listbox/Listbox.stx +2 -1
- package/dist/ui/listbox/ListboxButton.stx +1 -0
- package/dist/ui/listbox/ListboxOption.stx +2 -9
- package/dist/ui/listbox/ListboxOptions.stx +1 -1
- package/dist/ui/navigator/Navigator.stx +4 -4
- package/dist/ui/notification/Notification.stx +1 -5
- package/dist/ui/pagination/Pagination.stx +1 -2
- package/dist/ui/payment/Checkout.stx +0 -4
- package/dist/ui/payment/PaymentMethods.stx +15 -2
- package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
- package/dist/ui/popover/Popover.stx +2 -5
- package/dist/ui/popover/PopoverButton.stx +1 -3
- package/dist/ui/popover/PopoverPanel.stx +1 -1
- package/dist/ui/portal/Portal.stx +1 -0
- package/dist/ui/progress/Progress.stx +6 -1
- package/dist/ui/radio/Radio.stx +1 -1
- package/dist/ui/radio-group/RadioGroup.stx +2 -1
- package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
- package/dist/ui/select/Select.stx +1 -1
- package/dist/ui/sidebar/Sidebar.stx +9 -26
- package/dist/ui/sidebar/SidebarFooter.stx +6 -21
- package/dist/ui/sidebar/SidebarHeader.stx +7 -18
- package/dist/ui/sidebar/SidebarItem.stx +4 -20
- package/dist/ui/sidebar/SidebarSection.stx +5 -18
- package/dist/ui/sidebar/index.d.ts +13 -1
- package/dist/ui/stepper/StepperStep.stx +1 -5
- package/dist/ui/switch/Switch.stx +4 -10
- package/dist/ui/table/TableHeader.stx +1 -1
- package/dist/ui/tabs/Tabs.stx +0 -1
- package/dist/ui/teleport/Teleport.stx +1 -0
- package/dist/ui/textarea/Textarea.stx +1 -1
- package/dist/ui/tooltip/Tooltip.stx +1 -2
- package/dist/ui/transition/Transition.stx +9 -16
- package/dist/ui/virtual-list/VirtualList.stx +2 -4
- package/dist/ui/virtual-table/VirtualTable.stx +1 -1
- package/package.json +3 -3
- package/src/components/CodeBlock.stx +26 -29
- package/src/components/Footer.stx +1 -1
- package/src/components/Hero.stx +1 -1
- package/src/components/Installation.stx +5 -7
- package/src/ui/accordion/Accordion.stx +48 -68
- package/src/ui/audio/Audio.stx +4 -4
- package/src/ui/auth/Login.stx +17 -19
- package/src/ui/auth/Signup.stx +26 -28
- package/src/ui/auth/TwoFactorChallenge.stx +49 -63
- package/src/ui/avatar/Avatar.stx +23 -34
- package/src/ui/badge/Badge.stx +11 -19
- package/src/ui/button/Button.stx +18 -33
- package/src/ui/calendar/Calendar.stx +93 -136
- package/src/ui/card/Card.stx +27 -33
- package/src/ui/checkbox/Checkbox.stx +36 -44
- package/src/ui/combobox/Combobox.stx +87 -4
- package/src/ui/combobox/ComboboxButton.stx +14 -2
- package/src/ui/combobox/ComboboxInput.stx +12 -5
- package/src/ui/combobox/ComboboxOption.stx +29 -32
- package/src/ui/combobox/ComboboxOptions.stx +7 -3
- package/src/ui/command-palette/CommandPalette.stx +70 -28
- package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
- package/src/ui/dialog/Dialog.stx +28 -10
- package/src/ui/drawer/Drawer.stx +29 -49
- package/src/ui/dropdown/Dropdown.stx +69 -11
- package/src/ui/dropdown/DropdownButton.stx +12 -6
- package/src/ui/dropdown/DropdownItem.stx +18 -26
- package/src/ui/dropdown/DropdownItems.stx +2 -2
- package/src/ui/form/Form.stx +50 -164
- package/src/ui/heatmap/Heatmap.stx +16 -37
- package/src/ui/image/Image.stx +68 -100
- package/src/ui/input/EmailInput.stx +16 -27
- package/src/ui/input/NumberInput.stx +40 -53
- package/src/ui/input/PasswordInput.stx +64 -77
- package/src/ui/input/SearchInput.stx +27 -46
- package/src/ui/input/TextInput.stx +42 -59
- package/src/ui/listbox/Listbox.stx +91 -4
- package/src/ui/listbox/ListboxButton.stx +12 -1
- package/src/ui/listbox/ListboxOption.stx +30 -38
- package/src/ui/listbox/ListboxOptions.stx +2 -2
- package/src/ui/navigator/Navigator.stx +52 -77
- package/src/ui/notification/Notification.stx +46 -39
- package/src/ui/pagination/Pagination.stx +73 -108
- package/src/ui/payment/Checkout.stx +8 -16
- package/src/ui/payment/PaymentMethods.stx +21 -30
- package/src/ui/payment/SubscriptionCheckout.stx +99 -129
- package/src/ui/popover/Popover.stx +50 -14
- package/src/ui/popover/PopoverButton.stx +12 -6
- package/src/ui/popover/PopoverPanel.stx +6 -6
- package/src/ui/portal/Portal.stx +20 -37
- package/src/ui/progress/Progress.stx +36 -48
- package/src/ui/radio/Radio.stx +29 -34
- package/src/ui/radio-group/RadioGroup.stx +51 -4
- package/src/ui/radio-group/RadioGroupOption.stx +37 -30
- package/src/ui/select/Select.stx +23 -31
- package/src/ui/sidebar/Sidebar.stx +136 -188
- package/src/ui/sidebar/SidebarFooter.stx +49 -35
- package/src/ui/sidebar/SidebarHeader.stx +52 -30
- package/src/ui/sidebar/SidebarItem.stx +52 -78
- package/src/ui/sidebar/SidebarSection.stx +56 -51
- package/src/ui/sidebar/index.ts +21 -1
- package/src/ui/stepper/StepperStep.stx +9 -5
- package/src/ui/switch/Switch.stx +38 -42
- package/src/ui/table/TableHeader.stx +10 -3
- package/src/ui/tabs/Tabs.stx +68 -87
- package/src/ui/teleport/Teleport.stx +23 -59
- package/src/ui/textarea/Textarea.stx +42 -54
- package/src/ui/tooltip/Tooltip.stx +34 -40
- package/src/ui/transition/Transition.stx +76 -148
- package/src/ui/video/Video.stx +4 -4
- package/src/ui/virtual-list/VirtualList.stx +45 -61
- package/src/ui/virtual-table/VirtualTable.stx +62 -72
- package/dist/Accordion-sbfra0mz.stx +0 -108
- package/dist/Calendar-58nnkdh8.stx +0 -232
- package/dist/Combobox-th5ebw62.stx +0 -13
- package/dist/ComboboxOption-an9zyan6.stx +0 -52
- package/dist/CommandPalette-kwmdf2np.stx +0 -39
- package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
- package/dist/Dialog-rgmj8q2j.stx +0 -19
- package/dist/Dropdown-jz4jy0n3.stx +0 -24
- package/dist/DropdownItem-wagdspb8.stx +0 -43
- package/dist/EmailInput-ajsp9vw6.stx +0 -41
- package/dist/Form-1mdmx9b6.stx +0 -209
- package/dist/Listbox-pqqh72g8.stx +0 -14
- package/dist/ListboxOption-7nxzaexq.stx +0 -57
- package/dist/Navigator-aay8bjab.stx +0 -138
- package/dist/Notification-gk33gv7b.stx +0 -71
- package/dist/PasswordInput-zv4hm43j.stx +0 -155
- package/dist/Popover-469gbt6r.stx +0 -24
- package/dist/RadioGroup-bvb1mrhd.stx +0 -13
- package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
- package/dist/SearchInput-2521m475.stx +0 -63
- package/dist/Sidebar-40ckbzh8.stx +0 -288
- package/dist/SidebarFooter-hnth56mg.stx +0 -95
- package/dist/SidebarHeader-3wrte2p2.stx +0 -100
- package/dist/SidebarItem-zqtvvzpc.stx +0 -118
- package/dist/SidebarSection-3etwnbjr.stx +0 -108
- package/dist/Switch-036j9vm1.stx +0 -74
- package/dist/Tabs-svh721m0.stx +0 -145
- package/dist/Teleport-qfch3w0b.stx +0 -101
- package/dist/Transition-0hmr6zxb.stx +0 -174
- package/dist/VirtualList-cndhjejh.stx +0 -83
- 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
|
-
|
|
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
|
-
|
|
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="
|
|
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
|
-
|
|
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="
|
|
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
|
|
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
|
-
|
|
12
|
-
export const
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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(
|
|
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="
|
|
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
|
-
${{ (
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
|
23
|
-
|
|
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
|
-
|
|
11
|
-
if (
|
|
12
|
-
|
|
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="
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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>
|
package/src/ui/portal/Portal.stx
CHANGED
|
@@ -2,73 +2,56 @@
|
|
|
2
2
|
export const target = $props.target || 'body'
|
|
3
3
|
export const disabled = $props.disabled || false
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export const portalId = `portal-content-${Math.random().toString(36).slice(2, 11)}`
|
|
6
|
+
</script>
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
<script client>
|
|
9
|
+
const target = {{ target }}
|
|
10
|
+
const isDisabled = {{ disabled }}
|
|
11
|
+
const portalId = {{ portalId }}
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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>
|