|
37 | 37 | let submitting = $state(false); |
38 | 38 | let loading = $state(true); |
39 | 39 |
|
40 | | - // untrack pins the setup to once-per-mount: the callback props are usually inline arrows, |
41 | | - // so their identity changes on every parent render and must not re-create the setup intent. |
42 | | - const mountPayment: Attachment<HTMLDivElement> = (node) => { |
43 | | - let paymentEl: { unmount(): void } | null = null; |
44 | | - untrack(() => |
45 | | - Promise.all([createSetupIntent(), loadStripe(stripeKey)]) |
46 | | - .then(([{ clientSecret, customerSessionClientSecret }, s]) => { |
47 | | - if (!s || !clientSecret) throw new Error('Failed to initialize payment'); |
48 | | - const isDark = document.documentElement.classList.contains('dark'); |
49 | | - stripe = s; |
50 | | - elements = s.elements({ |
51 | | - clientSecret, |
52 | | - customerSessionClientSecret, |
53 | | - appearance: { |
54 | | - theme: isDark ? 'night' : 'stripe', |
55 | | - variables: { |
56 | | - colorPrimary: isDark ? 'hsl(41, 69%, 62%)' : 'hsl(40, 91%, 45%)', |
57 | | - tabIconColor: isDark ? '#a3a3a3' : '#737373', |
58 | | - tabIconHoverColor: isDark ? '#ffffff' : '#000000', |
59 | | - tabIconSelectedColor: '#1f1f1f', |
60 | | - colorBackground: isDark ? 'hsl(80 2.1% 12.4%)' : '#ffffff', |
61 | | - logoColor: isDark ? 'light' : 'dark', |
62 | | - }, |
63 | | - }, |
64 | | - }); |
65 | | - const el = elements.create('payment', { |
66 | | - defaultValues: { |
67 | | - billingDetails: { |
68 | | - name: billingName || undefined, |
69 | | - email: email || undefined, |
70 | | - }, |
71 | | - }, |
72 | | - }); |
73 | | - el.mount(node); |
74 | | - el.on('ready', () => (loading = false)); |
75 | | - paymentEl = el; |
76 | | - }) |
77 | | - .catch(() => (loading = false)), |
78 | | - ); |
79 | | - return () => paymentEl?.unmount(); |
80 | | - }; |
81 | | -
|
82 | 40 | const submit = () => { |
83 | 41 | if (!stripe || !elements) return; |
84 | 42 | submitting = true; |
|
104 | 62 | {#if title} |
105 | 63 | <h3 class="text-base font-medium mb-4">{title}</h3> |
106 | 64 | {/if} |
107 | | - <div {@attach mountPayment} class="w-full"></div> |
| 65 | + <div |
| 66 | + {@attach (node) => { |
| 67 | + // untrack pins the setup to once-per-mount: the callback props are usually inline arrows, |
| 68 | + // so their identity changes on every parent render and must not re-create the setup intent. |
| 69 | + let paymentEl: { unmount(): void } | null = null; |
| 70 | + untrack(() => |
| 71 | + Promise.all([createSetupIntent(), loadStripe(stripeKey)]) |
| 72 | + .then(([{ clientSecret, customerSessionClientSecret }, s]) => { |
| 73 | + if (!s || !clientSecret) throw new Error('Failed to initialize payment'); |
| 74 | + const isDark = document.documentElement.classList.contains('dark'); |
| 75 | + stripe = s; |
| 76 | + elements = s.elements({ |
| 77 | + clientSecret, |
| 78 | + customerSessionClientSecret, |
| 79 | + appearance: { |
| 80 | + theme: isDark ? 'night' : 'stripe', |
| 81 | + variables: { |
| 82 | + colorPrimary: isDark ? 'hsl(41, 69%, 62%)' : 'hsl(40, 91%, 45%)', |
| 83 | + tabIconColor: isDark ? '#a3a3a3' : '#737373', |
| 84 | + tabIconHoverColor: isDark ? '#ffffff' : '#000000', |
| 85 | + tabIconSelectedColor: '#1f1f1f', |
| 86 | + colorBackground: isDark ? 'hsl(80 2.1% 12.4%)' : '#ffffff', |
| 87 | + logoColor: isDark ? 'light' : 'dark', |
| 88 | + }, |
| 89 | + }, |
| 90 | + }); |
| 91 | + const el = elements.create('payment', { |
| 92 | + defaultValues: { |
| 93 | + billingDetails: { |
| 94 | + name: billingName || undefined, |
| 95 | + email: email || undefined, |
| 96 | + }, |
| 97 | + }, |
| 98 | + }); |
| 99 | + el.mount(node); |
| 100 | + el.on('ready', () => (loading = false)); |
| 101 | + paymentEl = el; |
| 102 | + }) |
| 103 | + .catch(() => (loading = false)), |
| 104 | + ); |
| 105 | + return () => paymentEl?.unmount(); |
| 106 | + }} |
| 107 | + class="w-full"> |
| 108 | + </div> |
108 | 109 | <div class="flex justify-end gap-2 mt-4"> |
109 | 110 | {#if oncancel} |
110 | 111 | <Button variant="outline" disabled={submitting} onclick={oncancel}>Cancel</Button> |
|
0 commit comments