@gait-financial/react 0.1.12 → 0.1.15

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/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # @gait-financial/react
2
2
 
3
- React wrappers for Gait Web Components. Thin, type-safe bindings that map React props to the underlying custom elements.
3
+ React wrapper for Gait Web Components. Use `<GaitButton>` with `data-id` and optional props for checkout/split payment.
4
4
 
5
- The button always shows **"Gait"** with the Gait logo icon and uses brand color **#4147BF**. No `label`, `theme`, or `brandColor` props.
6
-
7
- ## Installation
5
+ ## Install
8
6
 
9
7
  ```bash
10
8
  npm install @gait-financial/react
@@ -12,103 +10,46 @@ npm install @gait-financial/react
12
10
 
13
11
  ## Setup
14
12
 
15
- ### 1. Register the Web Component
16
-
17
- Import the registration module once (e.g. in `_app.tsx`, `layout.tsx`, or your root component):
13
+ **1. Register once** (e.g. in your root or layout):
18
14
 
19
15
  ```tsx
20
16
  import '@gait-financial/react/register';
21
17
  ```
22
18
 
23
- This loads and registers the Gait Web Components in the browser. Safe for SSR—no-op when `window`/`customElements` are unavailable.
24
-
25
- **Alternative:** Load the Web Component via script tag and skip the register import:
26
-
27
- ```html
28
- <script src="https://cdn.usegait.com/button.js"></script>
29
- ```
30
-
31
- ### 2. Use the Components
19
+ **2. Use the component:**
32
20
 
33
21
  ```tsx
34
22
  import { GaitButton } from '@gait-financial/react';
35
23
 
36
- function Checkout() {
37
- return (
38
- <GaitButton
39
- data-id="your-checkout-id"
40
- totalCost={99.99}
41
- onClick={(detail) => console.log('Clicked', detail)}
42
- onSplitFeedback={(detail) => {
43
- if (detail.success) console.log('Split sent!');
44
- else console.error('Failed', detail.error);
45
- }}
46
- />
47
- );
48
- }
24
+ <GaitButton
25
+ data-id="your-checkout-id"
26
+ totalCost={99.99}
27
+ onSplitFeedback={(d) => console.log(d.success ? 'OK' : d.error)}
28
+ />
49
29
  ```
50
30
 
51
- ## GaitButton Props
52
-
53
- | Prop | Type | Description |
54
- |------|------|-------------|
55
- | `data-id` | `string` | Checkout/data identity (required for payments) |
56
- | `disabled` | `boolean` | Disable the button |
57
- | `size` | `'small' \| 'medium' \| 'large'` | Button size |
58
- | `style` | `string \| React.CSSProperties` | Inline styles |
59
- | `items` | `GaitButtonItem[] \| string` | Cart items for split payment modal |
60
- | `priceBreakdown` | `GaitPriceBreakdownItem[] \| string` | Price breakdown |
61
- | `totalCost` | `number \| string` | Total cost |
62
- | `customer` | `GaitCustomer \| string` | Customer info for split payment |
63
- | `webhook` | `GaitWebhook \| string` | Webhook config for split callbacks |
64
- | `merchantStoreUrl` | `string` | Merchant store URL |
65
- | `onClick` | `(detail) => void` | Button clicked |
66
- | `onLoaded` | `(detail) => void` | Component loaded |
67
- | `onDataProcessed` | `(detail) => void` | Data processed |
68
- | `onSplitFeedback` | `(detail) => void` | Split payment API result |
69
- | `onMerchantIdError` | `(detail) => void` | Merchant ID lookup failed |
70
- | `onConfirm` | `(detail) => void` | Pay remaining confirmed |
71
-
72
- The button label is always **"Gait"** (with icon). Brand color is fixed to **#4147BF**; split-option styling uses **#F3F2FF**.
73
-
74
31
  ## Vite
75
32
 
76
- If using Vite, add this to `vite.config.ts` so the web component script loads correctly:
33
+ Add to `vite.config.ts` so the web component script loads:
77
34
 
78
35
  ```ts
79
- export default defineConfig({
80
- optimizeDeps: {
81
- exclude: ['@gait-financial/react'],
82
- },
83
- // ...
84
- });
36
+ optimizeDeps: { exclude: ['@gait-financial/react'] },
85
37
  ```
86
38
 
87
- ## Next.js
88
-
89
- The package uses `"use client"` directives. Import and use in Client Components:
90
-
91
- ```tsx
92
- 'use client';
93
-
94
- import '@gait-financial/react/register';
95
- import { GaitButton } from '@gait-financial/react';
96
-
97
- export default function CheckoutPage() {
98
- return <GaitButton data-id="your-checkout-id" />;
99
- }
100
- ```
101
-
102
- ## Architecture
103
-
104
- This package is a thin wrapper. The Web Component implementation is the source of truth. The structure allows adding other framework packages (e.g. `@gait/vue`, `@gait/svelte`) later without refactoring the core.
39
+ ## Builds (from repo root)
105
40
 
106
- ### Adding @gait/vue later
41
+ - **Production:** `npm run publish:react` — builds WC + React package, publishes to `latest`
42
+ - **Staging:** `npm run publish:react:staging` — builds with `.env.staging`, publishes with tag `staging`
43
+ From `packages/react`: `npm run publish:staging`
107
44
 
108
- Copy the `packages/react` structure to `packages/vue`:
45
+ ## Props
109
46
 
110
- 1. **register.ts** Same logic: load the bundled WC script, guard for SSR.
111
- 2. **components/GaitButton.vue** – Vue wrapper: use `ref` + `onMounted`/`watch` to sync props to the element, map DOM events to emits.
112
- 3. **types.ts** Copy event detail types from the Web Component (source of truth).
113
- 4. **Build** Use Vite or similar to output ESM, CJS, and `.d.ts`.
114
- 5. **Entry** Export components and types; keep registration as a separate import.
47
+ | Prop | Description |
48
+ |------|-------------|
49
+ | `data-id` | Checkout identity (required for payments) |
50
+ | `totalCost` | Total amount |
51
+ | `items` | Cart items for split payment |
52
+ | `customer` | `{ email, name? }` |
53
+ | `webhook` | `{ url, body? }` for split callbacks |
54
+ | `disabled`, `size`, `style` | Button options |
55
+ | `onClick`, `onLoaded`, `onDataProcessed`, `onSplitFeedback`, `onMerchantIdError`, `onConfirm` | Event callbacks |
package/dist/index.cjs CHANGED
@@ -41,45 +41,28 @@ var import_react = __toESM(require("react"), 1);
41
41
  // src/register.ts
42
42
  var import_meta = {};
43
43
  var loadPromise = null;
44
- async function defineCustomElements(scriptUrl) {
45
- if (typeof window === "undefined" || typeof customElements === "undefined") {
46
- return;
47
- }
48
- if (customElements.get("gait-button")) {
49
- return;
50
- }
51
- if (loadPromise) {
52
- return loadPromise;
53
- }
54
- const tryLoad = (url) => new Promise((resolve, reject) => {
44
+ function defineCustomElements(scriptUrl) {
45
+ if (typeof window === "undefined" || typeof customElements === "undefined") return Promise.resolve();
46
+ if (customElements.get("gait-button")) return Promise.resolve();
47
+ if (loadPromise) return loadPromise;
48
+ loadPromise = new Promise((resolve, reject) => {
49
+ let src;
50
+ if (scriptUrl) src = scriptUrl;
51
+ else {
52
+ try {
53
+ src = new URL("./wc/button.js", import_meta.url).href;
54
+ } catch {
55
+ reject(new Error('[@gait-financial/react] Could not resolve script. With Vite, add optimizeDeps: { exclude: ["@gait-financial/react"] } to vite.config.'));
56
+ return;
57
+ }
58
+ }
55
59
  const script = document.createElement("script");
56
60
  script.async = true;
57
- script.src = url;
61
+ script.src = src;
58
62
  script.onload = () => resolve();
59
- script.onerror = () => reject();
63
+ script.onerror = () => reject(new Error("[@gait-financial/react] Failed to load web component."));
60
64
  document.head.appendChild(script);
61
65
  });
62
- loadPromise = (async () => {
63
- const fallbackUrl = `${typeof window !== "undefined" ? window.location.origin : ""}/node_modules/@gait-financial/react/dist/wc/button.js`;
64
- if (scriptUrl) {
65
- await tryLoad(scriptUrl);
66
- return;
67
- }
68
- try {
69
- const url = new URL("./wc/button.js", import_meta.url).href;
70
- await tryLoad(url);
71
- return;
72
- } catch {
73
- }
74
- try {
75
- await tryLoad(fallbackUrl);
76
- return;
77
- } catch {
78
- throw new Error(
79
- '[@gait-financial/react] Failed to load web component script. If using Vite, add optimizeDeps: { exclude: ["@gait-financial/react"] } to vite.config.'
80
- );
81
- }
82
- })();
83
66
  return loadPromise;
84
67
  }
85
68
  if (typeof window !== "undefined" && typeof customElements !== "undefined") {
@@ -87,7 +70,31 @@ if (typeof window !== "undefined" && typeof customElements !== "undefined") {
87
70
  }
88
71
 
89
72
  // src/components/GaitButton.tsx
90
- function setAttributes(el, props) {
73
+ var EVENT_HANDLERS = [
74
+ { event: "gait-click", key: "onClick" },
75
+ { event: "gait-loaded", key: "onLoaded" },
76
+ { event: "gait-data-processed", key: "onDataProcessed" },
77
+ { event: "gait-split-feedback", key: "onSplitFeedback" },
78
+ { event: "gait-merchant-id-error", key: "onMerchantIdError" },
79
+ { event: "gait-confirm", key: "onConfirm" }
80
+ ];
81
+ function applyAttributes(el, props) {
82
+ const { "data-id": dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl } = props;
83
+ if (dataId !== void 0) el.setAttribute("data-id", dataId);
84
+ if (size !== void 0) el.setAttribute("size", size);
85
+ disabled ? el.setAttribute("disabled", "") : el.removeAttribute("disabled");
86
+ if (style !== void 0) {
87
+ const s = typeof style === "string" ? style : Object.entries(style).map(([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`).join("; ");
88
+ el.setAttribute("style", s);
89
+ }
90
+ if (items !== void 0) el.setAttribute("items", typeof items === "string" ? items : JSON.stringify(items));
91
+ if (priceBreakdown !== void 0) el.setAttribute("price-breakdown", typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown));
92
+ if (totalCost !== void 0) el.setAttribute("total-cost", String(totalCost));
93
+ if (customer !== void 0) el.setAttribute("customer", typeof customer === "string" ? customer : JSON.stringify(customer));
94
+ if (webhook !== void 0) el.setAttribute("webhook", typeof webhook === "string" ? webhook : JSON.stringify(webhook));
95
+ if (merchantStoreUrl !== void 0) el.setAttribute("merchant-store-url", merchantStoreUrl);
96
+ }
97
+ var GaitButton = (0, import_react.forwardRef)(function GaitButton2(props, ref) {
91
98
  const {
92
99
  "data-id": dataId,
93
100
  disabled,
@@ -98,191 +105,63 @@ function setAttributes(el, props) {
98
105
  totalCost,
99
106
  customer,
100
107
  webhook,
101
- merchantStoreUrl
102
- } = props;
103
- if (dataId !== void 0) el.setAttribute("data-id", dataId);
104
- if (size !== void 0) el.setAttribute("size", size);
105
- if (disabled) el.setAttribute("disabled", "");
106
- else el.removeAttribute("disabled");
107
- if (style !== void 0) {
108
- const styleStr = typeof style === "string" ? style : Object.entries(style).map(
109
- ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
110
- ).join("; ");
111
- el.setAttribute("style", styleStr);
112
- }
113
- if (items !== void 0)
114
- el.setAttribute(
115
- "items",
116
- typeof items === "string" ? items : JSON.stringify(items)
117
- );
118
- if (priceBreakdown !== void 0)
119
- el.setAttribute(
120
- "price-breakdown",
121
- typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown)
122
- );
123
- if (totalCost !== void 0)
124
- el.setAttribute("total-cost", String(totalCost));
125
- if (customer !== void 0)
126
- el.setAttribute(
127
- "customer",
128
- typeof customer === "string" ? customer : JSON.stringify(customer)
129
- );
130
- if (webhook !== void 0)
131
- el.setAttribute(
132
- "webhook",
133
- typeof webhook === "string" ? webhook : JSON.stringify(webhook)
134
- );
135
- if (merchantStoreUrl !== void 0)
136
- el.setAttribute("merchant-store-url", merchantStoreUrl);
137
- }
138
- var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
139
- "data-id": dataId,
140
- disabled,
141
- size,
142
- style,
143
- items,
144
- priceBreakdown,
145
- totalCost,
146
- customer,
147
- webhook,
148
- merchantStoreUrl,
149
- onClick,
150
- onLoaded,
151
- onDataProcessed,
152
- onSplitFeedback,
153
- onMerchantIdError,
154
- onConfirm,
155
- className,
156
- id,
157
- ...rest
158
- }, ref) {
159
- const containerRef = (0, import_react.useRef)(null);
160
- const elementRef = (0, import_react.useRef)(null);
161
- const [isReady, setIsReady] = (0, import_react.useState)(false);
162
- const callbackRef = (0, import_react.useRef)({
163
- onClick,
164
- onLoaded,
165
- onDataProcessed,
166
- onSplitFeedback,
167
- onMerchantIdError,
168
- onConfirm
169
- });
170
- callbackRef.current = {
108
+ merchantStoreUrl,
171
109
  onClick,
172
110
  onLoaded,
173
111
  onDataProcessed,
174
112
  onSplitFeedback,
175
113
  onMerchantIdError,
176
- onConfirm
177
- };
114
+ onConfirm,
115
+ className,
116
+ id
117
+ } = props;
118
+ const containerRef = (0, import_react.useRef)(null);
119
+ const elementRef = (0, import_react.useRef)(null);
120
+ const [ready, setReady] = (0, import_react.useState)(false);
121
+ const callbacksRef = (0, import_react.useRef)({ onClick, onLoaded, onDataProcessed, onSplitFeedback, onMerchantIdError, onConfirm });
122
+ callbacksRef.current = { onClick, onLoaded, onDataProcessed, onSplitFeedback, onMerchantIdError, onConfirm };
178
123
  (0, import_react.useImperativeHandle)(ref, () => elementRef.current, []);
179
124
  (0, import_react.useEffect)(() => {
180
125
  if (typeof customElements !== "undefined" && customElements.get("gait-button")) {
181
- setIsReady(true);
126
+ setReady(true);
182
127
  return;
183
128
  }
184
- defineCustomElements().then(() => setIsReady(true)).catch(() => setIsReady(false));
129
+ defineCustomElements().then(() => setReady(true), () => setReady(false));
185
130
  }, []);
186
131
  (0, import_react.useEffect)(() => {
187
- if (!isReady || !containerRef.current || typeof document === "undefined")
188
- return;
132
+ if (!ready || !containerRef.current) return;
189
133
  const container = containerRef.current;
190
134
  const el = document.createElement("gait-button");
191
- setAttributes(el, {
192
- "data-id": dataId,
193
- disabled,
194
- size,
195
- style,
196
- items,
197
- priceBreakdown,
198
- totalCost,
199
- customer,
200
- webhook,
201
- merchantStoreUrl
202
- });
135
+ applyAttributes(el, { "data-id": dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl });
203
136
  if (className) el.className = className;
204
137
  if (id) el.id = id;
205
- Object.entries(rest).forEach(([k, v]) => {
206
- if (v != null && typeof v !== "function") el.setAttribute(k, String(v));
138
+ const listeners = [];
139
+ EVENT_HANDLERS.forEach(({ event, key }) => {
140
+ const handler = (e) => {
141
+ const cb = callbacksRef.current[key];
142
+ if (cb && e instanceof CustomEvent && e.detail) cb(e.detail);
143
+ };
144
+ el.addEventListener(event, handler);
145
+ listeners.push(() => el.removeEventListener(event, handler));
207
146
  });
208
- const cbs = callbackRef.current;
209
- const handleClick = (e) => {
210
- if (cbs.onClick && e instanceof CustomEvent && e.detail)
211
- cbs.onClick(e.detail);
212
- };
213
- const handleLoaded = (e) => {
214
- if (cbs.onLoaded && e instanceof CustomEvent && e.detail)
215
- cbs.onLoaded(e.detail);
216
- };
217
- const handleDataProcessed = (e) => {
218
- if (cbs.onDataProcessed && e instanceof CustomEvent && e.detail)
219
- cbs.onDataProcessed(e.detail);
220
- };
221
- const handleSplitFeedback = (e) => {
222
- if (cbs.onSplitFeedback && e instanceof CustomEvent && e.detail)
223
- cbs.onSplitFeedback(e.detail);
224
- };
225
- const handleMerchantIdError = (e) => {
226
- if (cbs.onMerchantIdError && e instanceof CustomEvent && e.detail)
227
- cbs.onMerchantIdError(e.detail);
228
- };
229
- const handleConfirm = (e) => {
230
- if (cbs.onConfirm && e instanceof CustomEvent && e.detail)
231
- cbs.onConfirm(e.detail);
232
- };
233
- el.addEventListener("gait-click", handleClick);
234
- el.addEventListener("gait-loaded", handleLoaded);
235
- el.addEventListener("gait-data-processed", handleDataProcessed);
236
- el.addEventListener("gait-split-feedback", handleSplitFeedback);
237
- el.addEventListener("gait-merchant-id-error", handleMerchantIdError);
238
- el.addEventListener("gait-confirm", handleConfirm);
239
147
  elementRef.current = el;
240
148
  container.appendChild(el);
241
149
  return () => {
242
- el.removeEventListener("gait-click", handleClick);
243
- el.removeEventListener("gait-loaded", handleLoaded);
244
- el.removeEventListener("gait-data-processed", handleDataProcessed);
245
- el.removeEventListener("gait-split-feedback", handleSplitFeedback);
246
- el.removeEventListener("gait-merchant-id-error", handleMerchantIdError);
247
- el.removeEventListener("gait-confirm", handleConfirm);
248
- container.removeChild(el);
150
+ listeners.forEach((off) => off());
151
+ if (el.parentNode) el.parentNode.removeChild(el);
249
152
  elementRef.current = null;
250
153
  };
251
- }, [isReady]);
154
+ }, [ready]);
252
155
  (0, import_react.useEffect)(() => {
253
156
  const el = elementRef.current;
254
157
  if (!el) return;
255
- setAttributes(el, {
256
- "data-id": dataId,
257
- disabled,
258
- size,
259
- style,
260
- items,
261
- priceBreakdown,
262
- totalCost,
263
- customer,
264
- webhook,
265
- merchantStoreUrl
266
- });
158
+ applyAttributes(el, { "data-id": dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl });
267
159
  if (className) el.className = className;
268
160
  else el.removeAttribute("class");
269
161
  if (id) el.id = id;
270
162
  else el.removeAttribute("id");
271
- }, [
272
- dataId,
273
- disabled,
274
- size,
275
- style,
276
- items,
277
- priceBreakdown,
278
- totalCost,
279
- customer,
280
- webhook,
281
- merchantStoreUrl,
282
- className,
283
- id
284
- ]);
285
- if (!isReady) return null;
163
+ }, [dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl, className, id]);
164
+ if (!ready) return null;
286
165
  return import_react.default.createElement("div", { ref: containerRef });
287
166
  });
288
167
  GaitButton.displayName = "GaitButton";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/GaitButton.tsx","../src/register.ts"],"sourcesContent":["'use client';\n\n/**\n * @gait-financial/react - React wrappers for Gait Web Components\n *\n * Usage:\n * 1. Register the Web Component (run once, e.g. in _app.tsx or layout):\n * import '@gait-financial/react/register';\n *\n * 2. Use the React components:\n * import { GaitButton } from '@gait-financial/react';\n */\n\nexport { GaitButton } from './components/GaitButton';\nexport type { GaitButtonProps } from './components/GaitButton';\n\nexport type {\n ButtonSize,\n GaitButtonItem,\n GaitPriceBreakdownItem,\n GaitCustomer,\n GaitWebhook,\n GaitClickDetail,\n GaitLoadedDetail,\n GaitDataProcessedDetail,\n GaitSplitFeedbackDetail,\n GaitMerchantIdErrorDetail,\n GaitConfirmDetail,\n} from './types';\n","'use client';\n\n/**\n * React wrapper for GaitButton Web Component\n * Creates the element imperatively with attributes set BEFORE appending,\n * so connectedCallback sees data-id and other attrs immediately.\n * Avoids React's prop handling which causes \"readonly property\" errors.\n */\n\nimport React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport { defineCustomElements } from '../register';\nimport type {\n GaitButtonItem,\n GaitPriceBreakdownItem,\n GaitCustomer,\n GaitWebhook,\n GaitClickDetail,\n GaitLoadedDetail,\n GaitDataProcessedDetail,\n GaitSplitFeedbackDetail,\n GaitMerchantIdErrorDetail,\n GaitConfirmDetail,\n} from '../types';\n\nexport interface GaitButtonProps\n extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {\n /** Checkout/data identity (required for payment flow) */\n 'data-id'?: string;\n /** Disable the button */\n disabled?: boolean;\n /** Button size */\n size?: 'small' | 'medium' | 'large';\n /** Inline styles - string (for WC) or React.CSSProperties */\n style?: React.CSSProperties | string;\n /** Cart/order items for split payment modal */\n items?: GaitButtonItem[] | string;\n /** Price breakdown items */\n priceBreakdown?: GaitPriceBreakdownItem[] | string;\n /** Total cost */\n totalCost?: number | string;\n /** Customer info for split payment */\n customer?: GaitCustomer | string;\n /** Webhook config for split payment callbacks */\n webhook?: GaitWebhook | string;\n /** Merchant store URL (defaults to window.location.origin) */\n merchantStoreUrl?: string;\n\n /** Button clicked */\n onClick?: (detail: GaitClickDetail) => void;\n /** Component loaded/rendered */\n onLoaded?: (detail: GaitLoadedDetail) => void;\n /** Data processed (e.g. checkoutId resolved) */\n onDataProcessed?: (detail: GaitDataProcessedDetail) => void;\n /** Split payment API feedback (success/failure) */\n onSplitFeedback?: (detail: GaitSplitFeedbackDetail) => void;\n /** Merchant ID lookup failed */\n onMerchantIdError?: (detail: GaitMerchantIdErrorDetail) => void;\n /** Pay remaining confirmed */\n onConfirm?: (detail: GaitConfirmDetail) => void;\n}\n\nfunction setAttributes(\n el: HTMLElement,\n props: Pick<\n GaitButtonProps,\n | 'data-id'\n | 'disabled'\n | 'size'\n | 'style'\n | 'items'\n | 'priceBreakdown'\n | 'totalCost'\n | 'customer'\n | 'webhook'\n | 'merchantStoreUrl'\n >\n) {\n const {\n 'data-id': dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n } = props;\n\n if (dataId !== undefined) el.setAttribute('data-id', dataId);\n if (size !== undefined) el.setAttribute('size', size);\n if (disabled) el.setAttribute('disabled', '');\n else el.removeAttribute('disabled');\n if (style !== undefined) {\n const styleStr =\n typeof style === 'string'\n ? style\n : Object.entries(style)\n .map(\n ([k, v]) =>\n `${k.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${v}`\n )\n .join('; ');\n el.setAttribute('style', styleStr);\n }\n if (items !== undefined)\n el.setAttribute(\n 'items',\n typeof items === 'string' ? items : JSON.stringify(items)\n );\n if (priceBreakdown !== undefined)\n el.setAttribute(\n 'price-breakdown',\n typeof priceBreakdown === 'string'\n ? priceBreakdown\n : JSON.stringify(priceBreakdown)\n );\n if (totalCost !== undefined)\n el.setAttribute('total-cost', String(totalCost));\n if (customer !== undefined)\n el.setAttribute(\n 'customer',\n typeof customer === 'string' ? customer : JSON.stringify(customer)\n );\n if (webhook !== undefined)\n el.setAttribute(\n 'webhook',\n typeof webhook === 'string' ? webhook : JSON.stringify(webhook)\n );\n if (merchantStoreUrl !== undefined)\n el.setAttribute('merchant-store-url', merchantStoreUrl);\n}\n\nconst GaitButton = forwardRef<HTMLElement, GaitButtonProps>(function GaitButton(\n {\n 'data-id': dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n className,\n id,\n ...rest\n },\n ref\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const elementRef = useRef<HTMLElement | null>(null);\n const [isReady, setIsReady] = useState(false);\n\n const callbackRef = useRef({\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n });\n callbackRef.current = {\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n };\n\n useImperativeHandle(ref, () => elementRef.current as HTMLElement, []);\n\n // Wait for WC registration\n useEffect(() => {\n if (\n typeof customElements !== 'undefined' &&\n customElements.get('gait-button')\n ) {\n setIsReady(true);\n return;\n }\n defineCustomElements()\n .then(() => setIsReady(true))\n .catch(() => setIsReady(false));\n }, []);\n\n // Create gait-button imperatively with attrs BEFORE append (so connectedCallback sees data-id)\n useEffect(() => {\n if (!isReady || !containerRef.current || typeof document === 'undefined')\n return;\n\n const container = containerRef.current;\n const el = document.createElement('gait-button') as HTMLElement;\n\n // Set attributes BEFORE appending - connectedCallback fires on append and will see them\n setAttributes(el, {\n 'data-id': dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n });\n\n if (className) el.className = className;\n if (id) el.id = id;\n Object.entries(rest).forEach(([k, v]) => {\n if (v != null && typeof v !== 'function') el.setAttribute(k, String(v));\n });\n\n const cbs = callbackRef.current;\n const handleClick = (e: Event) => {\n if (cbs.onClick && e instanceof CustomEvent && e.detail)\n cbs.onClick(e.detail as GaitClickDetail);\n };\n const handleLoaded = (e: Event) => {\n if (cbs.onLoaded && e instanceof CustomEvent && e.detail)\n cbs.onLoaded(e.detail as GaitLoadedDetail);\n };\n const handleDataProcessed = (e: Event) => {\n if (cbs.onDataProcessed && e instanceof CustomEvent && e.detail)\n cbs.onDataProcessed(e.detail as GaitDataProcessedDetail);\n };\n const handleSplitFeedback = (e: Event) => {\n if (cbs.onSplitFeedback && e instanceof CustomEvent && e.detail)\n cbs.onSplitFeedback(e.detail as GaitSplitFeedbackDetail);\n };\n const handleMerchantIdError = (e: Event) => {\n if (cbs.onMerchantIdError && e instanceof CustomEvent && e.detail)\n cbs.onMerchantIdError(e.detail as GaitMerchantIdErrorDetail);\n };\n const handleConfirm = (e: Event) => {\n if (cbs.onConfirm && e instanceof CustomEvent && e.detail)\n cbs.onConfirm(e.detail as GaitConfirmDetail);\n };\n\n el.addEventListener('gait-click', handleClick as EventListener);\n el.addEventListener('gait-loaded', handleLoaded as EventListener);\n el.addEventListener('gait-data-processed', handleDataProcessed as EventListener);\n el.addEventListener('gait-split-feedback', handleSplitFeedback as EventListener);\n el.addEventListener('gait-merchant-id-error', handleMerchantIdError as EventListener);\n el.addEventListener('gait-confirm', handleConfirm as EventListener);\n\n elementRef.current = el;\n container.appendChild(el);\n\n return () => {\n el.removeEventListener('gait-click', handleClick as EventListener);\n el.removeEventListener('gait-loaded', handleLoaded as EventListener);\n el.removeEventListener('gait-data-processed', handleDataProcessed as EventListener);\n el.removeEventListener('gait-split-feedback', handleSplitFeedback as EventListener);\n el.removeEventListener('gait-merchant-id-error', handleMerchantIdError as EventListener);\n el.removeEventListener('gait-confirm', handleConfirm as EventListener);\n container.removeChild(el);\n elementRef.current = null;\n };\n }, [isReady]); // Only run when isReady changes - create once\n\n // Update attributes when props change\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n\n setAttributes(el, {\n 'data-id': dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n });\n\n if (className) el.className = className;\n else el.removeAttribute('class');\n if (id) el.id = id;\n else el.removeAttribute('id');\n }, [\n dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n className,\n id,\n ]);\n\n if (!isReady) return null;\n\n return React.createElement('div', { ref: containerRef });\n});\n\nGaitButton.displayName = 'GaitButton';\n\nexport { GaitButton };\n","/**\n * Registers Gait Web Components in the browser.\n * Safe to call in SSR environments - no-op when window/customElements unavailable.\n *\n * Usage:\n * import '@gait-financial/react/register'; // Side-effect: registers when in browser\n * // or\n * import { defineCustomElements } from '@gait-financial/react/register';\n * await defineCustomElements(); // Optional: load from custom URL\n */\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads and registers Gait Web Components.\n * In browser: loads the WC script if not already registered.\n * In SSR: no-op.\n * Deduplicated: multiple calls return the same promise.\n *\n * @param scriptUrl - Optional URL to the button.js script. If omitted, uses the bundled script.\n */\nexport async function defineCustomElements(\n scriptUrl?: string\n): Promise<void> {\n if (typeof window === 'undefined' || typeof customElements === 'undefined') {\n return;\n }\n if (customElements.get('gait-button')) {\n return;\n }\n if (loadPromise) {\n return loadPromise;\n }\n\n const tryLoad = (url: string): Promise<void> =>\n new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.async = true;\n script.src = url;\n script.onload = () => resolve();\n script.onerror = () => reject();\n document.head.appendChild(script);\n });\n\n loadPromise = (async () => {\n const fallbackUrl = `${typeof window !== 'undefined' ? window.location.origin : ''}/node_modules/@gait-financial/react/dist/wc/button.js`;\n\n if (scriptUrl) {\n await tryLoad(scriptUrl);\n return;\n }\n\n // Try relative to module first (works when not pre-bundled)\n try {\n const url = new URL('./wc/button.js', import.meta.url).href;\n await tryLoad(url);\n return;\n } catch {\n // ignore\n }\n\n // Fallback: direct path (works when Vite pre-bundles and breaks import.meta.url)\n try {\n await tryLoad(fallbackUrl);\n return;\n } catch {\n throw new Error(\n '[@gait-financial/react] Failed to load web component script. If using Vite, add optimizeDeps: { exclude: [\"@gait-financial/react\"] } to vite.config.'\n );\n }\n })();\n return loadPromise;\n}\n\n// Side-effect: auto-register when imported in browser (SSR-safe)\nif (typeof window !== 'undefined' && typeof customElements !== 'undefined') {\n void defineCustomElements();\n}\n"],"mappings":"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","names":["GaitButton","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/GaitButton.tsx","../src/register.ts"],"sourcesContent":["'use client';\n\nexport { GaitButton } from './components/GaitButton';\nexport type { GaitButtonProps } from './components/GaitButton';\nexport type {\n ButtonSize,\n GaitButtonItem,\n GaitPriceBreakdownItem,\n GaitCustomer,\n GaitWebhook,\n GaitClickDetail,\n GaitLoadedDetail,\n GaitDataProcessedDetail,\n GaitSplitFeedbackDetail,\n GaitMerchantIdErrorDetail,\n GaitConfirmDetail,\n} from './types';\n","'use client';\n\nimport React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { defineCustomElements } from '../register';\nimport type {\n GaitButtonItem,\n GaitPriceBreakdownItem,\n GaitCustomer,\n GaitWebhook,\n GaitClickDetail,\n GaitLoadedDetail,\n GaitDataProcessedDetail,\n GaitSplitFeedbackDetail,\n GaitMerchantIdErrorDetail,\n GaitConfirmDetail,\n} from '../types';\n\nexport interface GaitButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {\n 'data-id'?: string;\n disabled?: boolean;\n size?: 'small' | 'medium' | 'large';\n style?: React.CSSProperties | string;\n items?: GaitButtonItem[] | string;\n priceBreakdown?: GaitPriceBreakdownItem[] | string;\n totalCost?: number | string;\n customer?: GaitCustomer | string;\n webhook?: GaitWebhook | string;\n merchantStoreUrl?: string;\n onClick?: (detail: GaitClickDetail) => void;\n onLoaded?: (detail: GaitLoadedDetail) => void;\n onDataProcessed?: (detail: GaitDataProcessedDetail) => void;\n onSplitFeedback?: (detail: GaitSplitFeedbackDetail) => void;\n onMerchantIdError?: (detail: GaitMerchantIdErrorDetail) => void;\n onConfirm?: (detail: GaitConfirmDetail) => void;\n}\n\nconst EVENT_HANDLERS: Array<{ event: string; key: keyof Pick<GaitButtonProps, 'onClick' | 'onLoaded' | 'onDataProcessed' | 'onSplitFeedback' | 'onMerchantIdError' | 'onConfirm'> }> = [\n { event: 'gait-click', key: 'onClick' },\n { event: 'gait-loaded', key: 'onLoaded' },\n { event: 'gait-data-processed', key: 'onDataProcessed' },\n { event: 'gait-split-feedback', key: 'onSplitFeedback' },\n { event: 'gait-merchant-id-error', key: 'onMerchantIdError' },\n { event: 'gait-confirm', key: 'onConfirm' },\n];\n\nfunction applyAttributes(\n el: HTMLElement,\n props: Pick<GaitButtonProps, 'data-id' | 'disabled' | 'size' | 'style' | 'items' | 'priceBreakdown' | 'totalCost' | 'customer' | 'webhook' | 'merchantStoreUrl'>\n) {\n const { 'data-id': dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl } = props;\n if (dataId !== undefined) el.setAttribute('data-id', dataId);\n if (size !== undefined) el.setAttribute('size', size);\n disabled ? el.setAttribute('disabled', '') : el.removeAttribute('disabled');\n if (style !== undefined) {\n const s = typeof style === 'string' ? style : Object.entries(style).map(([k, v]) => `${k.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${v}`).join('; ');\n el.setAttribute('style', s);\n }\n if (items !== undefined) el.setAttribute('items', typeof items === 'string' ? items : JSON.stringify(items));\n if (priceBreakdown !== undefined) el.setAttribute('price-breakdown', typeof priceBreakdown === 'string' ? priceBreakdown : JSON.stringify(priceBreakdown));\n if (totalCost !== undefined) el.setAttribute('total-cost', String(totalCost));\n if (customer !== undefined) el.setAttribute('customer', typeof customer === 'string' ? customer : JSON.stringify(customer));\n if (webhook !== undefined) el.setAttribute('webhook', typeof webhook === 'string' ? webhook : JSON.stringify(webhook));\n if (merchantStoreUrl !== undefined) el.setAttribute('merchant-store-url', merchantStoreUrl);\n}\n\nconst GaitButton = forwardRef<HTMLElement, GaitButtonProps>(function GaitButton(props, ref) {\n const {\n 'data-id': dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n className,\n id,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const elementRef = useRef<HTMLElement | null>(null);\n const [ready, setReady] = useState(false);\n const callbacksRef = useRef({ onClick, onLoaded, onDataProcessed, onSplitFeedback, onMerchantIdError, onConfirm });\n callbacksRef.current = { onClick, onLoaded, onDataProcessed, onSplitFeedback, onMerchantIdError, onConfirm };\n\n useImperativeHandle(ref, () => elementRef.current as HTMLElement, []);\n\n useEffect(() => {\n if (typeof customElements !== 'undefined' && customElements.get('gait-button')) {\n setReady(true);\n return;\n }\n defineCustomElements().then(() => setReady(true), () => setReady(false));\n }, []);\n\n useEffect(() => {\n if (!ready || !containerRef.current) return;\n\n const container = containerRef.current;\n const el = document.createElement('gait-button') as HTMLElement;\n\n applyAttributes(el, { 'data-id': dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl });\n if (className) el.className = className;\n if (id) el.id = id;\n\n const listeners: Array<() => void> = [];\n EVENT_HANDLERS.forEach(({ event, key }) => {\n const handler = (e: Event) => {\n const cb = callbacksRef.current[key];\n if (cb && e instanceof CustomEvent && e.detail) cb(e.detail as never);\n };\n el.addEventListener(event, handler as EventListener);\n listeners.push(() => el.removeEventListener(event, handler as EventListener));\n });\n\n elementRef.current = el;\n container.appendChild(el);\n\n return () => {\n listeners.forEach((off) => off());\n if (el.parentNode) el.parentNode.removeChild(el);\n elementRef.current = null;\n };\n }, [ready]);\n\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n applyAttributes(el, { 'data-id': dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl });\n if (className) el.className = className;\n else el.removeAttribute('class');\n if (id) el.id = id;\n else el.removeAttribute('id');\n }, [dataId, disabled, size, style, items, priceBreakdown, totalCost, customer, webhook, merchantStoreUrl, className, id]);\n\n if (!ready) return null;\n return React.createElement('div', { ref: containerRef });\n});\n\nGaitButton.displayName = 'GaitButton';\nexport { GaitButton };\n","/**\n * Loads and registers Gait Web Components. Safe for SSR (no-op when no window).\n * Import once: import '@gait-financial/react/register';\n */\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function defineCustomElements(scriptUrl?: string): Promise<void> {\n if (typeof window === 'undefined' || typeof customElements === 'undefined') return Promise.resolve();\n if (customElements.get('gait-button')) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise((resolve, reject) => {\n let src: string;\n if (scriptUrl) src = scriptUrl;\n else {\n try {\n src = new URL('./wc/button.js', import.meta.url).href;\n } catch {\n reject(new Error('[@gait-financial/react] Could not resolve script. With Vite, add optimizeDeps: { exclude: [\"@gait-financial/react\"] } to vite.config.'));\n return;\n }\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = src;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('[@gait-financial/react] Failed to load web component.'));\n document.head.appendChild(script);\n });\n return loadPromise;\n}\n\nif (typeof window !== 'undefined' && typeof customElements !== 'undefined') {\n void defineCustomElements();\n}\n"],"mappings":"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","names":["GaitButton","React"]}
package/dist/index.d.cts CHANGED
@@ -68,45 +68,22 @@ interface GaitConfirmDetail {
68
68
  paymentId: string;
69
69
  }
70
70
 
71
- /**
72
- * React wrapper for GaitButton Web Component
73
- * Creates the element imperatively with attributes set BEFORE appending,
74
- * so connectedCallback sees data-id and other attrs immediately.
75
- * Avoids React's prop handling which causes "readonly property" errors.
76
- */
77
-
78
71
  interface GaitButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {
79
- /** Checkout/data identity (required for payment flow) */
80
72
  'data-id'?: string;
81
- /** Disable the button */
82
73
  disabled?: boolean;
83
- /** Button size */
84
74
  size?: 'small' | 'medium' | 'large';
85
- /** Inline styles - string (for WC) or React.CSSProperties */
86
75
  style?: React.CSSProperties | string;
87
- /** Cart/order items for split payment modal */
88
76
  items?: GaitButtonItem[] | string;
89
- /** Price breakdown items */
90
77
  priceBreakdown?: GaitPriceBreakdownItem[] | string;
91
- /** Total cost */
92
78
  totalCost?: number | string;
93
- /** Customer info for split payment */
94
79
  customer?: GaitCustomer | string;
95
- /** Webhook config for split payment callbacks */
96
80
  webhook?: GaitWebhook | string;
97
- /** Merchant store URL (defaults to window.location.origin) */
98
81
  merchantStoreUrl?: string;
99
- /** Button clicked */
100
82
  onClick?: (detail: GaitClickDetail) => void;
101
- /** Component loaded/rendered */
102
83
  onLoaded?: (detail: GaitLoadedDetail) => void;
103
- /** Data processed (e.g. checkoutId resolved) */
104
84
  onDataProcessed?: (detail: GaitDataProcessedDetail) => void;
105
- /** Split payment API feedback (success/failure) */
106
85
  onSplitFeedback?: (detail: GaitSplitFeedbackDetail) => void;
107
- /** Merchant ID lookup failed */
108
86
  onMerchantIdError?: (detail: GaitMerchantIdErrorDetail) => void;
109
- /** Pay remaining confirmed */
110
87
  onConfirm?: (detail: GaitConfirmDetail) => void;
111
88
  }
112
89
  declare const GaitButton: React.ForwardRefExoticComponent<GaitButtonProps & React.RefAttributes<HTMLElement>>;
package/dist/index.d.ts CHANGED
@@ -68,45 +68,22 @@ interface GaitConfirmDetail {
68
68
  paymentId: string;
69
69
  }
70
70
 
71
- /**
72
- * React wrapper for GaitButton Web Component
73
- * Creates the element imperatively with attributes set BEFORE appending,
74
- * so connectedCallback sees data-id and other attrs immediately.
75
- * Avoids React's prop handling which causes "readonly property" errors.
76
- */
77
-
78
71
  interface GaitButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {
79
- /** Checkout/data identity (required for payment flow) */
80
72
  'data-id'?: string;
81
- /** Disable the button */
82
73
  disabled?: boolean;
83
- /** Button size */
84
74
  size?: 'small' | 'medium' | 'large';
85
- /** Inline styles - string (for WC) or React.CSSProperties */
86
75
  style?: React.CSSProperties | string;
87
- /** Cart/order items for split payment modal */
88
76
  items?: GaitButtonItem[] | string;
89
- /** Price breakdown items */
90
77
  priceBreakdown?: GaitPriceBreakdownItem[] | string;
91
- /** Total cost */
92
78
  totalCost?: number | string;
93
- /** Customer info for split payment */
94
79
  customer?: GaitCustomer | string;
95
- /** Webhook config for split payment callbacks */
96
80
  webhook?: GaitWebhook | string;
97
- /** Merchant store URL (defaults to window.location.origin) */
98
81
  merchantStoreUrl?: string;
99
- /** Button clicked */
100
82
  onClick?: (detail: GaitClickDetail) => void;
101
- /** Component loaded/rendered */
102
83
  onLoaded?: (detail: GaitLoadedDetail) => void;
103
- /** Data processed (e.g. checkoutId resolved) */
104
84
  onDataProcessed?: (detail: GaitDataProcessedDetail) => void;
105
- /** Split payment API feedback (success/failure) */
106
85
  onSplitFeedback?: (detail: GaitSplitFeedbackDetail) => void;
107
- /** Merchant ID lookup failed */
108
86
  onMerchantIdError?: (detail: GaitMerchantIdErrorDetail) => void;
109
- /** Pay remaining confirmed */
110
87
  onConfirm?: (detail: GaitConfirmDetail) => void;
111
88
  }
112
89
  declare const GaitButton: React.ForwardRefExoticComponent<GaitButtonProps & React.RefAttributes<HTMLElement>>;