@snap-pay/react 1.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ Initial public release.
6
+
7
+ - `<SnapProvider>` — context provider carrying the `Snap` instance, Elements handle, and client secret.
8
+ - Hooks: `useSnap`, `useElements`, `useClientSecret`.
9
+ - React wrappers for every Element: `PaymentElement`, `WalletElement`, `CardElement`, `ExpressCheckoutElement`, `PaymentMethodSelector`, `BillingAddressElement`, `CustomerElement`, `OtpElement`, `QrElement`.
10
+ - Shared `useElementLifecycle` / `useElementEvent` hooks keep event wiring stable across re-renders.
11
+ - Peer dependency `react >= 18` — supports React 18 and 19.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @snap-pay/react
2
+
3
+ React bindings for SNAP Elements — provider, hooks, and component wrappers.
4
+
5
+ ```bash
6
+ bun add @snap-pay/js @snap-pay/elements @snap-pay/react
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import { Snap } from "@snap-pay/js";
13
+ import "@snap-pay/elements"; // side effect: registers every element
14
+ import {
15
+ SnapProvider,
16
+ PaymentElement,
17
+ BillingAddressElement,
18
+ CustomerElement,
19
+ useSnap,
20
+ useElements,
21
+ useClientSecret,
22
+ } from "@snap-pay/react";
23
+
24
+ const snap = new Snap(process.env.NEXT_PUBLIC_SNAP_PK!);
25
+
26
+ export function CheckoutPage({ clientSecret }: { clientSecret: string }) {
27
+ return (
28
+ <SnapProvider snap={snap} clientSecret={clientSecret}>
29
+ <CustomerElement />
30
+ <BillingAddressElement defaultCountry="Somalia" />
31
+ <PaymentElement />
32
+ <ConfirmButton />
33
+ </SnapProvider>
34
+ );
35
+ }
36
+
37
+ function ConfirmButton() {
38
+ const snap = useSnap();
39
+ const elements = useElements();
40
+ const clientSecret = useClientSecret();
41
+ return (
42
+ <button
43
+ onClick={async () => {
44
+ const result = await snap.confirmPayment({ elements, clientSecret });
45
+ if (result.status === "succeeded") window.location.href = "/thanks";
46
+ }}
47
+ >
48
+ Pay
49
+ </button>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ## Components
55
+
56
+ Every wrapper accepts the standard event props (`onReady`, `onChange`, `onFocus`, `onBlur`, `onFailed`) plus `style` and `className` for the outer container.
57
+
58
+ | Component | Notes |
59
+ |---|---|
60
+ | `<PaymentElement />` | Umbrella — auto-picks Wallet + Card |
61
+ | `<WalletElement />` | Mobile-wallet radio buttons |
62
+ | `<CardElement />` | Card fields inside a cross-origin iframe |
63
+ | `<ExpressCheckoutElement />` | Prominent one-tap buttons; adds `onSubmit` for the click event |
64
+ | `<PaymentMethodSelector />` | Tabbed Wallet/Card switcher |
65
+ | `<BillingAddressElement defaultCountry={...} />` | Address form |
66
+ | `<CustomerElement />` | Name / email / phone |
67
+ | `<OtpElement length={6} />` | Multi-cell OTP input |
68
+ | `<QrElement />` | Placeholder — emits `failed` on mount until the real QR handshake ships |
69
+
70
+ ## Hooks
71
+
72
+ - `useSnap()` — the `Snap` instance from the provider
73
+ - `useElements()` — the shared Elements handle
74
+ - `useClientSecret()` — the client secret
75
+
76
+ All three throw if called outside a `<SnapProvider>`.
77
+
78
+ ## License
79
+
80
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ require('@snap-pay/js');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/provider.tsx
8
+ var SnapContext = react.createContext(null);
9
+ function SnapProvider(props) {
10
+ const { snap, clientSecret, appearance, children } = props;
11
+ const appearanceKey = appearance ? JSON.stringify(appearance) : "";
12
+ const value = react.useMemo(
13
+ () => ({
14
+ snap,
15
+ elements: snap.elements({ clientSecret, appearance }),
16
+ clientSecret
17
+ }),
18
+ // Intentional: appearanceKey stands in for appearance identity.
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ [snap, clientSecret, appearanceKey]
21
+ );
22
+ return /* @__PURE__ */ jsxRuntime.jsx(SnapContext.Provider, { value, children });
23
+ }
24
+ function useSnap() {
25
+ const ctx = react.useContext(SnapContext);
26
+ if (!ctx) {
27
+ throw new Error(
28
+ "useSnap must be called inside a <SnapProvider>."
29
+ );
30
+ }
31
+ return ctx.snap;
32
+ }
33
+ function useElements() {
34
+ const ctx = react.useContext(SnapContext);
35
+ if (!ctx) {
36
+ throw new Error(
37
+ "useElements must be called inside a <SnapProvider>."
38
+ );
39
+ }
40
+ return ctx.elements;
41
+ }
42
+ function useClientSecret() {
43
+ const ctx = react.useContext(SnapContext);
44
+ if (!ctx) {
45
+ throw new Error(
46
+ "useClientSecret must be called inside a <SnapProvider>."
47
+ );
48
+ }
49
+ return ctx.clientSecret;
50
+ }
51
+ function WalletElement(props) {
52
+ const elements = useElements();
53
+ const containerRef = react.useRef(null);
54
+ const elementRef = react.useRef(null);
55
+ react.useEffect(() => {
56
+ const container = containerRef.current;
57
+ if (!container) return;
58
+ const el = elements.create("wallet");
59
+ elementRef.current = el;
60
+ el.mount(container);
61
+ return () => {
62
+ el.destroy();
63
+ elementRef.current = null;
64
+ };
65
+ }, [elements]);
66
+ useElementEvent(elementRef, "ready", props.onReady);
67
+ useElementEvent(elementRef, "change", props.onChange);
68
+ useElementEvent(elementRef, "focus", props.onFocus);
69
+ useElementEvent(elementRef, "blur", props.onBlur);
70
+ useElementEvent(elementRef, "failed", props.onFailed);
71
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
72
+ }
73
+ function useElementEvent(elementRef, event, handler, _unused) {
74
+ react.useEffect(() => {
75
+ const el = elementRef.current;
76
+ if (!el) return;
77
+ if (!handler) return;
78
+ el.on(event, handler);
79
+ return () => el.off(event, handler);
80
+ }, [elementRef, event, handler]);
81
+ }
82
+ function CardElement(props) {
83
+ const elements = useElements();
84
+ const containerRef = react.useRef(null);
85
+ const elementRef = react.useRef(null);
86
+ react.useEffect(() => {
87
+ const container = containerRef.current;
88
+ if (!container) return;
89
+ const el = elements.create("card");
90
+ elementRef.current = el;
91
+ el.mount(container);
92
+ return () => {
93
+ el.destroy();
94
+ elementRef.current = null;
95
+ };
96
+ }, [elements]);
97
+ useElementEvent2(elementRef, "ready", props.onReady);
98
+ useElementEvent2(elementRef, "change", props.onChange);
99
+ useElementEvent2(elementRef, "focus", props.onFocus);
100
+ useElementEvent2(elementRef, "blur", props.onBlur);
101
+ useElementEvent2(elementRef, "failed", props.onFailed);
102
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
103
+ }
104
+ function useElementEvent2(elementRef, event, handler) {
105
+ react.useEffect(() => {
106
+ const el = elementRef.current;
107
+ if (!el) return;
108
+ if (!handler) return;
109
+ el.on(event, handler);
110
+ return () => el.off(event, handler);
111
+ }, [elementRef, event, handler]);
112
+ }
113
+ function useElementEvent3(elementRef, event, handler) {
114
+ react.useEffect(() => {
115
+ const el = elementRef.current;
116
+ if (!el) return;
117
+ if (!handler) return;
118
+ el.on(event, handler);
119
+ return () => el.off(event, handler);
120
+ }, [elementRef, event, handler]);
121
+ }
122
+ function useElementLifecycle(type, options) {
123
+ const elements = useElements();
124
+ const containerRef = react.useRef(null);
125
+ const elementRef = react.useRef(null);
126
+ const optionsJson = options ? JSON.stringify(options) : "";
127
+ react.useEffect(() => {
128
+ const container = containerRef.current;
129
+ if (!container) return;
130
+ const el = elements.create(type, options ?? {});
131
+ elementRef.current = el;
132
+ el.mount(container);
133
+ return () => {
134
+ el.destroy();
135
+ elementRef.current = null;
136
+ };
137
+ }, [elements, type, optionsJson]);
138
+ return { containerRef, elementRef };
139
+ }
140
+ function PaymentElement(props) {
141
+ const { containerRef, elementRef } = useElementLifecycle("payment");
142
+ useElementEvent3(elementRef, "ready", props.onReady);
143
+ useElementEvent3(elementRef, "change", props.onChange);
144
+ useElementEvent3(elementRef, "focus", props.onFocus);
145
+ useElementEvent3(elementRef, "blur", props.onBlur);
146
+ useElementEvent3(elementRef, "failed", props.onFailed);
147
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
148
+ }
149
+ function ExpressCheckoutElement(props) {
150
+ const { containerRef, elementRef } = useElementLifecycle("express");
151
+ useElementEvent3(elementRef, "ready", props.onReady);
152
+ useElementEvent3(elementRef, "change", props.onChange);
153
+ useElementEvent3(elementRef, "focus", props.onFocus);
154
+ useElementEvent3(elementRef, "blur", props.onBlur);
155
+ useElementEvent3(elementRef, "failed", props.onFailed);
156
+ useElementEvent3(elementRef, "submit", props.onSubmit);
157
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
158
+ }
159
+ function PaymentMethodSelector(props) {
160
+ const { containerRef, elementRef } = useElementLifecycle("paymentMethodSelector");
161
+ useElementEvent3(elementRef, "ready", props.onReady);
162
+ useElementEvent3(elementRef, "change", props.onChange);
163
+ useElementEvent3(elementRef, "failed", props.onFailed);
164
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
165
+ }
166
+ function BillingAddressElement(props) {
167
+ const { containerRef, elementRef } = useElementLifecycle(
168
+ "billingAddress",
169
+ { defaultCountry: props.defaultCountry }
170
+ );
171
+ useElementEvent3(elementRef, "ready", props.onReady);
172
+ useElementEvent3(elementRef, "change", props.onChange);
173
+ useElementEvent3(elementRef, "focus", props.onFocus);
174
+ useElementEvent3(elementRef, "blur", props.onBlur);
175
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
176
+ }
177
+ function CustomerElement(props) {
178
+ const { containerRef, elementRef } = useElementLifecycle("customer");
179
+ useElementEvent3(elementRef, "ready", props.onReady);
180
+ useElementEvent3(elementRef, "change", props.onChange);
181
+ useElementEvent3(elementRef, "focus", props.onFocus);
182
+ useElementEvent3(elementRef, "blur", props.onBlur);
183
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
184
+ }
185
+ function OtpElement(props) {
186
+ const { containerRef, elementRef } = useElementLifecycle("otp", {
187
+ otpLength: props.length
188
+ });
189
+ useElementEvent3(elementRef, "ready", props.onReady);
190
+ useElementEvent3(elementRef, "change", props.onChange);
191
+ useElementEvent3(elementRef, "focus", props.onFocus);
192
+ useElementEvent3(elementRef, "blur", props.onBlur);
193
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
194
+ }
195
+ function QrElement(props) {
196
+ const { containerRef, elementRef } = useElementLifecycle("qr");
197
+ useElementEvent3(elementRef, "ready", props.onReady);
198
+ useElementEvent3(elementRef, "failed", props.onFailed);
199
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: props.className, style: props.style });
200
+ }
201
+
202
+ exports.BillingAddressElement = BillingAddressElement;
203
+ exports.CardElement = CardElement;
204
+ exports.CustomerElement = CustomerElement;
205
+ exports.ExpressCheckoutElement = ExpressCheckoutElement;
206
+ exports.OtpElement = OtpElement;
207
+ exports.PaymentElement = PaymentElement;
208
+ exports.PaymentMethodSelector = PaymentMethodSelector;
209
+ exports.QrElement = QrElement;
210
+ exports.SnapProvider = SnapProvider;
211
+ exports.WalletElement = WalletElement;
212
+ exports.useClientSecret = useClientSecret;
213
+ exports.useElements = useElements;
214
+ exports.useSnap = useSnap;
@@ -0,0 +1,75 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+ import { Snap, SnapElements } from '@snap-pay/js';
4
+ import { Appearance, ElementEventMap } from '@snap-pay/types';
5
+
6
+ interface SnapProviderProps {
7
+ snap: Snap;
8
+ clientSecret: string;
9
+ appearance?: Appearance;
10
+ children: ReactNode;
11
+ }
12
+ declare function SnapProvider(props: SnapProviderProps): react.JSX.Element;
13
+ declare function useSnap(): Snap;
14
+ declare function useElements(): SnapElements;
15
+ declare function useClientSecret(): string;
16
+
17
+ interface WalletElementProps {
18
+ onReady?: () => void;
19
+ onChange?: (payload: ElementEventMap["change"]) => void;
20
+ onFocus?: () => void;
21
+ onBlur?: () => void;
22
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
23
+ style?: CSSProperties;
24
+ className?: string;
25
+ }
26
+ declare function WalletElement(props: WalletElementProps): react.JSX.Element;
27
+
28
+ interface CardElementProps {
29
+ onReady?: () => void;
30
+ onChange?: (payload: ElementEventMap["change"]) => void;
31
+ onFocus?: () => void;
32
+ onBlur?: () => void;
33
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
34
+ style?: CSSProperties;
35
+ className?: string;
36
+ }
37
+ declare function CardElement(props: CardElementProps): react.JSX.Element;
38
+
39
+ interface CommonElementProps {
40
+ onReady?: () => void;
41
+ onChange?: (payload: ElementEventMap["change"]) => void;
42
+ onFocus?: () => void;
43
+ onBlur?: () => void;
44
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
45
+ onSubmit?: () => void;
46
+ style?: CSSProperties;
47
+ className?: string;
48
+ }
49
+
50
+ type PaymentElementProps = CommonElementProps;
51
+ declare function PaymentElement(props: PaymentElementProps): react.JSX.Element;
52
+
53
+ type ExpressCheckoutElementProps = CommonElementProps;
54
+ declare function ExpressCheckoutElement(props: ExpressCheckoutElementProps): react.JSX.Element;
55
+
56
+ type PaymentMethodSelectorProps = CommonElementProps;
57
+ declare function PaymentMethodSelector(props: PaymentMethodSelectorProps): react.JSX.Element;
58
+
59
+ interface BillingAddressElementProps extends CommonElementProps {
60
+ defaultCountry?: string;
61
+ }
62
+ declare function BillingAddressElement(props: BillingAddressElementProps): react.JSX.Element;
63
+
64
+ type CustomerElementProps = CommonElementProps;
65
+ declare function CustomerElement(props: CustomerElementProps): react.JSX.Element;
66
+
67
+ interface OtpElementProps extends CommonElementProps {
68
+ length?: number;
69
+ }
70
+ declare function OtpElement(props: OtpElementProps): react.JSX.Element;
71
+
72
+ type QrElementProps = CommonElementProps;
73
+ declare function QrElement(props: QrElementProps): react.JSX.Element;
74
+
75
+ export { BillingAddressElement, type BillingAddressElementProps, CardElement, type CardElementProps, CustomerElement, type CustomerElementProps, ExpressCheckoutElement, type ExpressCheckoutElementProps, OtpElement, type OtpElementProps, PaymentElement, type PaymentElementProps, PaymentMethodSelector, type PaymentMethodSelectorProps, QrElement, type QrElementProps, SnapProvider, type SnapProviderProps, WalletElement, type WalletElementProps, useClientSecret, useElements, useSnap };
@@ -0,0 +1,75 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+ import { Snap, SnapElements } from '@snap-pay/js';
4
+ import { Appearance, ElementEventMap } from '@snap-pay/types';
5
+
6
+ interface SnapProviderProps {
7
+ snap: Snap;
8
+ clientSecret: string;
9
+ appearance?: Appearance;
10
+ children: ReactNode;
11
+ }
12
+ declare function SnapProvider(props: SnapProviderProps): react.JSX.Element;
13
+ declare function useSnap(): Snap;
14
+ declare function useElements(): SnapElements;
15
+ declare function useClientSecret(): string;
16
+
17
+ interface WalletElementProps {
18
+ onReady?: () => void;
19
+ onChange?: (payload: ElementEventMap["change"]) => void;
20
+ onFocus?: () => void;
21
+ onBlur?: () => void;
22
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
23
+ style?: CSSProperties;
24
+ className?: string;
25
+ }
26
+ declare function WalletElement(props: WalletElementProps): react.JSX.Element;
27
+
28
+ interface CardElementProps {
29
+ onReady?: () => void;
30
+ onChange?: (payload: ElementEventMap["change"]) => void;
31
+ onFocus?: () => void;
32
+ onBlur?: () => void;
33
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
34
+ style?: CSSProperties;
35
+ className?: string;
36
+ }
37
+ declare function CardElement(props: CardElementProps): react.JSX.Element;
38
+
39
+ interface CommonElementProps {
40
+ onReady?: () => void;
41
+ onChange?: (payload: ElementEventMap["change"]) => void;
42
+ onFocus?: () => void;
43
+ onBlur?: () => void;
44
+ onFailed?: (payload: ElementEventMap["failed"]) => void;
45
+ onSubmit?: () => void;
46
+ style?: CSSProperties;
47
+ className?: string;
48
+ }
49
+
50
+ type PaymentElementProps = CommonElementProps;
51
+ declare function PaymentElement(props: PaymentElementProps): react.JSX.Element;
52
+
53
+ type ExpressCheckoutElementProps = CommonElementProps;
54
+ declare function ExpressCheckoutElement(props: ExpressCheckoutElementProps): react.JSX.Element;
55
+
56
+ type PaymentMethodSelectorProps = CommonElementProps;
57
+ declare function PaymentMethodSelector(props: PaymentMethodSelectorProps): react.JSX.Element;
58
+
59
+ interface BillingAddressElementProps extends CommonElementProps {
60
+ defaultCountry?: string;
61
+ }
62
+ declare function BillingAddressElement(props: BillingAddressElementProps): react.JSX.Element;
63
+
64
+ type CustomerElementProps = CommonElementProps;
65
+ declare function CustomerElement(props: CustomerElementProps): react.JSX.Element;
66
+
67
+ interface OtpElementProps extends CommonElementProps {
68
+ length?: number;
69
+ }
70
+ declare function OtpElement(props: OtpElementProps): react.JSX.Element;
71
+
72
+ type QrElementProps = CommonElementProps;
73
+ declare function QrElement(props: QrElementProps): react.JSX.Element;
74
+
75
+ export { BillingAddressElement, type BillingAddressElementProps, CardElement, type CardElementProps, CustomerElement, type CustomerElementProps, ExpressCheckoutElement, type ExpressCheckoutElementProps, OtpElement, type OtpElementProps, PaymentElement, type PaymentElementProps, PaymentMethodSelector, type PaymentMethodSelectorProps, QrElement, type QrElementProps, SnapProvider, type SnapProviderProps, WalletElement, type WalletElementProps, useClientSecret, useElements, useSnap };
package/dist/index.js ADDED
@@ -0,0 +1,200 @@
1
+ import { createContext, useMemo, useContext, useRef, useEffect } from 'react';
2
+ import '@snap-pay/js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/provider.tsx
6
+ var SnapContext = createContext(null);
7
+ function SnapProvider(props) {
8
+ const { snap, clientSecret, appearance, children } = props;
9
+ const appearanceKey = appearance ? JSON.stringify(appearance) : "";
10
+ const value = useMemo(
11
+ () => ({
12
+ snap,
13
+ elements: snap.elements({ clientSecret, appearance }),
14
+ clientSecret
15
+ }),
16
+ // Intentional: appearanceKey stands in for appearance identity.
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ [snap, clientSecret, appearanceKey]
19
+ );
20
+ return /* @__PURE__ */ jsx(SnapContext.Provider, { value, children });
21
+ }
22
+ function useSnap() {
23
+ const ctx = useContext(SnapContext);
24
+ if (!ctx) {
25
+ throw new Error(
26
+ "useSnap must be called inside a <SnapProvider>."
27
+ );
28
+ }
29
+ return ctx.snap;
30
+ }
31
+ function useElements() {
32
+ const ctx = useContext(SnapContext);
33
+ if (!ctx) {
34
+ throw new Error(
35
+ "useElements must be called inside a <SnapProvider>."
36
+ );
37
+ }
38
+ return ctx.elements;
39
+ }
40
+ function useClientSecret() {
41
+ const ctx = useContext(SnapContext);
42
+ if (!ctx) {
43
+ throw new Error(
44
+ "useClientSecret must be called inside a <SnapProvider>."
45
+ );
46
+ }
47
+ return ctx.clientSecret;
48
+ }
49
+ function WalletElement(props) {
50
+ const elements = useElements();
51
+ const containerRef = useRef(null);
52
+ const elementRef = useRef(null);
53
+ useEffect(() => {
54
+ const container = containerRef.current;
55
+ if (!container) return;
56
+ const el = elements.create("wallet");
57
+ elementRef.current = el;
58
+ el.mount(container);
59
+ return () => {
60
+ el.destroy();
61
+ elementRef.current = null;
62
+ };
63
+ }, [elements]);
64
+ useElementEvent(elementRef, "ready", props.onReady);
65
+ useElementEvent(elementRef, "change", props.onChange);
66
+ useElementEvent(elementRef, "focus", props.onFocus);
67
+ useElementEvent(elementRef, "blur", props.onBlur);
68
+ useElementEvent(elementRef, "failed", props.onFailed);
69
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
70
+ }
71
+ function useElementEvent(elementRef, event, handler, _unused) {
72
+ useEffect(() => {
73
+ const el = elementRef.current;
74
+ if (!el) return;
75
+ if (!handler) return;
76
+ el.on(event, handler);
77
+ return () => el.off(event, handler);
78
+ }, [elementRef, event, handler]);
79
+ }
80
+ function CardElement(props) {
81
+ const elements = useElements();
82
+ const containerRef = useRef(null);
83
+ const elementRef = useRef(null);
84
+ useEffect(() => {
85
+ const container = containerRef.current;
86
+ if (!container) return;
87
+ const el = elements.create("card");
88
+ elementRef.current = el;
89
+ el.mount(container);
90
+ return () => {
91
+ el.destroy();
92
+ elementRef.current = null;
93
+ };
94
+ }, [elements]);
95
+ useElementEvent2(elementRef, "ready", props.onReady);
96
+ useElementEvent2(elementRef, "change", props.onChange);
97
+ useElementEvent2(elementRef, "focus", props.onFocus);
98
+ useElementEvent2(elementRef, "blur", props.onBlur);
99
+ useElementEvent2(elementRef, "failed", props.onFailed);
100
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
101
+ }
102
+ function useElementEvent2(elementRef, event, handler) {
103
+ useEffect(() => {
104
+ const el = elementRef.current;
105
+ if (!el) return;
106
+ if (!handler) return;
107
+ el.on(event, handler);
108
+ return () => el.off(event, handler);
109
+ }, [elementRef, event, handler]);
110
+ }
111
+ function useElementEvent3(elementRef, event, handler) {
112
+ useEffect(() => {
113
+ const el = elementRef.current;
114
+ if (!el) return;
115
+ if (!handler) return;
116
+ el.on(event, handler);
117
+ return () => el.off(event, handler);
118
+ }, [elementRef, event, handler]);
119
+ }
120
+ function useElementLifecycle(type, options) {
121
+ const elements = useElements();
122
+ const containerRef = useRef(null);
123
+ const elementRef = useRef(null);
124
+ const optionsJson = options ? JSON.stringify(options) : "";
125
+ useEffect(() => {
126
+ const container = containerRef.current;
127
+ if (!container) return;
128
+ const el = elements.create(type, options ?? {});
129
+ elementRef.current = el;
130
+ el.mount(container);
131
+ return () => {
132
+ el.destroy();
133
+ elementRef.current = null;
134
+ };
135
+ }, [elements, type, optionsJson]);
136
+ return { containerRef, elementRef };
137
+ }
138
+ function PaymentElement(props) {
139
+ const { containerRef, elementRef } = useElementLifecycle("payment");
140
+ useElementEvent3(elementRef, "ready", props.onReady);
141
+ useElementEvent3(elementRef, "change", props.onChange);
142
+ useElementEvent3(elementRef, "focus", props.onFocus);
143
+ useElementEvent3(elementRef, "blur", props.onBlur);
144
+ useElementEvent3(elementRef, "failed", props.onFailed);
145
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
146
+ }
147
+ function ExpressCheckoutElement(props) {
148
+ const { containerRef, elementRef } = useElementLifecycle("express");
149
+ useElementEvent3(elementRef, "ready", props.onReady);
150
+ useElementEvent3(elementRef, "change", props.onChange);
151
+ useElementEvent3(elementRef, "focus", props.onFocus);
152
+ useElementEvent3(elementRef, "blur", props.onBlur);
153
+ useElementEvent3(elementRef, "failed", props.onFailed);
154
+ useElementEvent3(elementRef, "submit", props.onSubmit);
155
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
156
+ }
157
+ function PaymentMethodSelector(props) {
158
+ const { containerRef, elementRef } = useElementLifecycle("paymentMethodSelector");
159
+ useElementEvent3(elementRef, "ready", props.onReady);
160
+ useElementEvent3(elementRef, "change", props.onChange);
161
+ useElementEvent3(elementRef, "failed", props.onFailed);
162
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
163
+ }
164
+ function BillingAddressElement(props) {
165
+ const { containerRef, elementRef } = useElementLifecycle(
166
+ "billingAddress",
167
+ { defaultCountry: props.defaultCountry }
168
+ );
169
+ useElementEvent3(elementRef, "ready", props.onReady);
170
+ useElementEvent3(elementRef, "change", props.onChange);
171
+ useElementEvent3(elementRef, "focus", props.onFocus);
172
+ useElementEvent3(elementRef, "blur", props.onBlur);
173
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
174
+ }
175
+ function CustomerElement(props) {
176
+ const { containerRef, elementRef } = useElementLifecycle("customer");
177
+ useElementEvent3(elementRef, "ready", props.onReady);
178
+ useElementEvent3(elementRef, "change", props.onChange);
179
+ useElementEvent3(elementRef, "focus", props.onFocus);
180
+ useElementEvent3(elementRef, "blur", props.onBlur);
181
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
182
+ }
183
+ function OtpElement(props) {
184
+ const { containerRef, elementRef } = useElementLifecycle("otp", {
185
+ otpLength: props.length
186
+ });
187
+ useElementEvent3(elementRef, "ready", props.onReady);
188
+ useElementEvent3(elementRef, "change", props.onChange);
189
+ useElementEvent3(elementRef, "focus", props.onFocus);
190
+ useElementEvent3(elementRef, "blur", props.onBlur);
191
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
192
+ }
193
+ function QrElement(props) {
194
+ const { containerRef, elementRef } = useElementLifecycle("qr");
195
+ useElementEvent3(elementRef, "ready", props.onReady);
196
+ useElementEvent3(elementRef, "failed", props.onFailed);
197
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: props.className, style: props.style });
198
+ }
199
+
200
+ export { BillingAddressElement, CardElement, CustomerElement, ExpressCheckoutElement, OtpElement, PaymentElement, PaymentMethodSelector, QrElement, SnapProvider, WalletElement, useClientSecret, useElements, useSnap };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@snap-pay/react",
3
+ "version": "1.0.0",
4
+ "description": "React bindings for SNAP Elements — provider, hooks, and component wrappers.",
5
+ "license": "MIT",
6
+ "author": "SNAP",
7
+ "homepage": "https://github.com/khaledhussein957/SNAP/tree/main/packages/react",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/khaledhussein957/SNAP.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/khaledhussein957/SNAP/issues"
15
+ },
16
+ "keywords": ["snap", "payments", "react", "elements", "checkout", "hooks"],
17
+ "sideEffects": false,
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "type": "module",
22
+ "main": "./dist/index.cjs",
23
+ "module": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "files": ["dist", "README.md", "CHANGELOG.md"],
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "test": "vitest run",
36
+ "typecheck": "tsc --noEmit"
37
+ },
38
+ "dependencies": {
39
+ "@snap-pay/elements": "1.0.0",
40
+ "@snap-pay/js": "1.0.0",
41
+ "@snap-pay/types": "1.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=18.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@testing-library/react": "^16.3.0",
48
+ "@types/react": "^19",
49
+ "@vitejs/plugin-react": "^5.0.5",
50
+ "happy-dom": "^15.11.7",
51
+ "react": "19.2.4",
52
+ "react-dom": "19.2.4",
53
+ "tsup": "^8.3.5",
54
+ "typescript": "^5.7.2",
55
+ "vitest": "^4.1.9"
56
+ }
57
+ }