@gait-financial/react 0.1.10 → 0.1.12

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/index.cjs CHANGED
@@ -87,6 +87,54 @@ if (typeof window !== "undefined" && typeof customElements !== "undefined") {
87
87
  }
88
88
 
89
89
  // src/components/GaitButton.tsx
90
+ function setAttributes(el, props) {
91
+ const {
92
+ "data-id": dataId,
93
+ disabled,
94
+ size,
95
+ style,
96
+ items,
97
+ priceBreakdown,
98
+ totalCost,
99
+ customer,
100
+ 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
+ }
90
138
  var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
91
139
  "data-id": dataId,
92
140
  disabled,
@@ -108,8 +156,25 @@ var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
108
156
  id,
109
157
  ...rest
110
158
  }, ref) {
159
+ const containerRef = (0, import_react.useRef)(null);
111
160
  const elementRef = (0, import_react.useRef)(null);
112
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 = {
171
+ onClick,
172
+ onLoaded,
173
+ onDataProcessed,
174
+ onSplitFeedback,
175
+ onMerchantIdError,
176
+ onConfirm
177
+ };
113
178
  (0, import_react.useImperativeHandle)(ref, () => elementRef.current, []);
114
179
  (0, import_react.useEffect)(() => {
115
180
  if (typeof customElements !== "undefined" && customElements.get("gait-button")) {
@@ -119,95 +184,51 @@ var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
119
184
  defineCustomElements().then(() => setIsReady(true)).catch(() => setIsReady(false));
120
185
  }, []);
121
186
  (0, import_react.useEffect)(() => {
122
- const el = elementRef.current;
123
- if (!el) return;
124
- if (dataId !== void 0) el.setAttribute("data-id", dataId);
125
- if (size !== void 0) el.setAttribute("size", size);
126
- if (disabled) {
127
- el.setAttribute("disabled", "");
128
- } else {
129
- el.removeAttribute("disabled");
130
- }
131
- if (style !== void 0) {
132
- const styleStr = typeof style === "string" ? style : Object.entries(style).map(
133
- ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
134
- ).join("; ");
135
- el.setAttribute("style", styleStr);
136
- }
137
- if (items !== void 0) {
138
- el.setAttribute(
139
- "items",
140
- typeof items === "string" ? items : JSON.stringify(items)
141
- );
142
- }
143
- if (priceBreakdown !== void 0) {
144
- el.setAttribute(
145
- "price-breakdown",
146
- typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown)
147
- );
148
- }
149
- if (totalCost !== void 0) {
150
- el.setAttribute("total-cost", String(totalCost));
151
- }
152
- if (customer !== void 0) {
153
- el.setAttribute(
154
- "customer",
155
- typeof customer === "string" ? customer : JSON.stringify(customer)
156
- );
157
- }
158
- if (webhook !== void 0) {
159
- el.setAttribute(
160
- "webhook",
161
- typeof webhook === "string" ? webhook : JSON.stringify(webhook)
162
- );
163
- }
164
- if (merchantStoreUrl !== void 0) {
165
- el.setAttribute("merchant-store-url", merchantStoreUrl);
166
- }
167
- }, [
168
- dataId,
169
- disabled,
170
- size,
171
- style,
172
- items,
173
- priceBreakdown,
174
- totalCost,
175
- customer,
176
- webhook,
177
- merchantStoreUrl
178
- ]);
179
- (0, import_react.useEffect)(() => {
180
- const el = elementRef.current;
181
- if (!el) return;
187
+ if (!isReady || !containerRef.current || typeof document === "undefined")
188
+ return;
189
+ const container = containerRef.current;
190
+ 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
+ });
203
+ if (className) el.className = className;
204
+ if (id) el.id = id;
205
+ Object.entries(rest).forEach(([k, v]) => {
206
+ if (v != null && typeof v !== "function") el.setAttribute(k, String(v));
207
+ });
208
+ const cbs = callbackRef.current;
182
209
  const handleClick = (e) => {
183
- if (onClick && e instanceof CustomEvent && e.detail) {
184
- onClick(e.detail);
185
- }
210
+ if (cbs.onClick && e instanceof CustomEvent && e.detail)
211
+ cbs.onClick(e.detail);
186
212
  };
187
213
  const handleLoaded = (e) => {
188
- if (onLoaded && e instanceof CustomEvent && e.detail) {
189
- onLoaded(e.detail);
190
- }
214
+ if (cbs.onLoaded && e instanceof CustomEvent && e.detail)
215
+ cbs.onLoaded(e.detail);
191
216
  };
192
217
  const handleDataProcessed = (e) => {
193
- if (onDataProcessed && e instanceof CustomEvent && e.detail) {
194
- onDataProcessed(e.detail);
195
- }
218
+ if (cbs.onDataProcessed && e instanceof CustomEvent && e.detail)
219
+ cbs.onDataProcessed(e.detail);
196
220
  };
197
221
  const handleSplitFeedback = (e) => {
198
- if (onSplitFeedback && e instanceof CustomEvent && e.detail) {
199
- onSplitFeedback(e.detail);
200
- }
222
+ if (cbs.onSplitFeedback && e instanceof CustomEvent && e.detail)
223
+ cbs.onSplitFeedback(e.detail);
201
224
  };
202
225
  const handleMerchantIdError = (e) => {
203
- if (onMerchantIdError && e instanceof CustomEvent && e.detail) {
204
- onMerchantIdError(e.detail);
205
- }
226
+ if (cbs.onMerchantIdError && e instanceof CustomEvent && e.detail)
227
+ cbs.onMerchantIdError(e.detail);
206
228
  };
207
229
  const handleConfirm = (e) => {
208
- if (onConfirm && e instanceof CustomEvent && e.detail) {
209
- onConfirm(e.detail);
210
- }
230
+ if (cbs.onConfirm && e instanceof CustomEvent && e.detail)
231
+ cbs.onConfirm(e.detail);
211
232
  };
212
233
  el.addEventListener("gait-click", handleClick);
213
234
  el.addEventListener("gait-loaded", handleLoaded);
@@ -215,6 +236,8 @@ var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
215
236
  el.addEventListener("gait-split-feedback", handleSplitFeedback);
216
237
  el.addEventListener("gait-merchant-id-error", handleMerchantIdError);
217
238
  el.addEventListener("gait-confirm", handleConfirm);
239
+ elementRef.current = el;
240
+ container.appendChild(el);
218
241
  return () => {
219
242
  el.removeEventListener("gait-click", handleClick);
220
243
  el.removeEventListener("gait-loaded", handleLoaded);
@@ -222,49 +245,45 @@ var GaitButton = (0, import_react.forwardRef)(function GaitButton2({
222
245
  el.removeEventListener("gait-split-feedback", handleSplitFeedback);
223
246
  el.removeEventListener("gait-merchant-id-error", handleMerchantIdError);
224
247
  el.removeEventListener("gait-confirm", handleConfirm);
248
+ container.removeChild(el);
249
+ elementRef.current = null;
225
250
  };
251
+ }, [isReady]);
252
+ (0, import_react.useEffect)(() => {
253
+ const el = elementRef.current;
254
+ 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
+ });
267
+ if (className) el.className = className;
268
+ else el.removeAttribute("class");
269
+ if (id) el.id = id;
270
+ else el.removeAttribute("id");
226
271
  }, [
227
- onClick,
228
- onLoaded,
229
- onDataProcessed,
230
- onSplitFeedback,
231
- onMerchantIdError,
232
- onConfirm
272
+ dataId,
273
+ disabled,
274
+ size,
275
+ style,
276
+ items,
277
+ priceBreakdown,
278
+ totalCost,
279
+ customer,
280
+ webhook,
281
+ merchantStoreUrl,
282
+ className,
283
+ id
233
284
  ]);
234
- if (!isReady) {
235
- return null;
236
- }
237
- const elementProps = {
238
- ref: elementRef
239
- };
240
- if (className) elementProps.className = className;
241
- if (id) elementProps.id = id;
242
- if (dataId !== void 0) elementProps["data-id"] = dataId;
243
- if (size !== void 0) elementProps.size = size;
244
- if (disabled) elementProps.disabled = "";
245
- if (style !== void 0) {
246
- elementProps.style = typeof style === "string" ? style : Object.entries(style).map(
247
- ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
248
- ).join("; ");
249
- }
250
- if (items !== void 0) {
251
- elementProps.items = typeof items === "string" ? items : JSON.stringify(items);
252
- }
253
- if (priceBreakdown !== void 0) {
254
- elementProps["price-breakdown"] = typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown);
255
- }
256
- if (totalCost !== void 0) elementProps["total-cost"] = String(totalCost);
257
- if (customer !== void 0) {
258
- elementProps.customer = typeof customer === "string" ? customer : JSON.stringify(customer);
259
- }
260
- if (webhook !== void 0) {
261
- elementProps.webhook = typeof webhook === "string" ? webhook : JSON.stringify(webhook);
262
- }
263
- if (merchantStoreUrl !== void 0) {
264
- elementProps["merchant-store-url"] = merchantStoreUrl;
265
- }
266
- Object.assign(elementProps, rest);
267
- return import_react.default.createElement("gait-button", elementProps);
285
+ if (!isReady) return null;
286
+ return import_react.default.createElement("div", { ref: containerRef });
268
287
  });
269
288
  GaitButton.displayName = "GaitButton";
270
289
  // Annotate the CommonJS export names for ESM import in node:
@@ -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 * Maps React props to element attributes (WC uses attributes as source of truth).\n * Uses ref + useEffect for binding - no direct JSX attribute spread to avoid React's\n * string coercion of booleans/objects.\n * Waits for WC registration before rendering to avoid invisible element (race condition).\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\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 elementRef = useRef<HTMLElement>(null);\n const [isReady, setIsReady] = useState(false);\n\n useImperativeHandle(ref, () => elementRef.current as HTMLElement, []);\n\n // Wait for WC to be registered before rendering (avoids invisible element)\n useEffect(() => {\n if (typeof customElements !== 'undefined' && customElements.get('gait-button')) {\n setIsReady(true);\n return;\n }\n defineCustomElements().then(() => setIsReady(true)).catch(() => setIsReady(false));\n }, []);\n\n // Sync props to element attributes via ref (WC reads attributes) - must run every render (hooks order)\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n\n if (dataId !== undefined) el.setAttribute('data-id', dataId);\n if (size !== undefined) el.setAttribute('size', size);\n\n if (disabled) {\n el.setAttribute('disabled', '');\n } else {\n el.removeAttribute('disabled');\n }\n\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\n if (items !== undefined) {\n el.setAttribute(\n 'items',\n typeof items === 'string' ? items : JSON.stringify(items)\n );\n }\n\n if (priceBreakdown !== undefined) {\n el.setAttribute(\n 'price-breakdown',\n typeof priceBreakdown === 'string'\n ? priceBreakdown\n : JSON.stringify(priceBreakdown)\n );\n }\n\n if (totalCost !== undefined) {\n el.setAttribute('total-cost', String(totalCost));\n }\n\n if (customer !== undefined) {\n el.setAttribute(\n 'customer',\n typeof customer === 'string' ? customer : JSON.stringify(customer)\n );\n }\n\n if (webhook !== undefined) {\n el.setAttribute(\n 'webhook',\n typeof webhook === 'string' ? webhook : JSON.stringify(webhook)\n );\n }\n\n if (merchantStoreUrl !== undefined) {\n el.setAttribute('merchant-store-url', merchantStoreUrl);\n }\n }, [\n dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n ]);\n\n // Event listeners\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n\n const handleClick = (e: Event) => {\n if (onClick && e instanceof CustomEvent && e.detail) {\n onClick(e.detail as GaitClickDetail);\n }\n };\n const handleLoaded = (e: Event) => {\n if (onLoaded && e instanceof CustomEvent && e.detail) {\n onLoaded(e.detail as GaitLoadedDetail);\n }\n };\n const handleDataProcessed = (e: Event) => {\n if (onDataProcessed && e instanceof CustomEvent && e.detail) {\n onDataProcessed(e.detail as GaitDataProcessedDetail);\n }\n };\n const handleSplitFeedback = (e: Event) => {\n if (onSplitFeedback && e instanceof CustomEvent && e.detail) {\n onSplitFeedback(e.detail as GaitSplitFeedbackDetail);\n }\n };\n const handleMerchantIdError = (e: Event) => {\n if (onMerchantIdError && e instanceof CustomEvent && e.detail) {\n onMerchantIdError(e.detail as GaitMerchantIdErrorDetail);\n }\n };\n const handleConfirm = (e: Event) => {\n if (onConfirm && e instanceof CustomEvent && e.detail) {\n onConfirm(e.detail as GaitConfirmDetail);\n }\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 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 };\n }, [\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n ]);\n\n // Conditional render AFTER all hooks - required by Rules of Hooks\n if (!isReady) {\n return null;\n }\n\n // Pass attributes on initial render so WC sees them in connectedCallback (useEffect runs after mount)\n const elementProps: Record<string, unknown> = {\n ref: elementRef,\n };\n if (className) elementProps.className = className;\n if (id) elementProps.id = id;\n if (dataId !== undefined) elementProps['data-id'] = dataId;\n if (size !== undefined) elementProps.size = size;\n if (disabled) elementProps.disabled = '';\n if (style !== undefined) {\n elementProps.style =\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 }\n if (items !== undefined) {\n elementProps.items =\n typeof items === 'string' ? items : JSON.stringify(items);\n }\n if (priceBreakdown !== undefined) {\n elementProps['price-breakdown'] =\n typeof priceBreakdown === 'string'\n ? priceBreakdown\n : JSON.stringify(priceBreakdown);\n }\n if (totalCost !== undefined) elementProps['total-cost'] = String(totalCost);\n if (customer !== undefined) {\n elementProps.customer =\n typeof customer === 'string' ? customer : JSON.stringify(customer);\n }\n if (webhook !== undefined) {\n elementProps.webhook =\n typeof webhook === 'string' ? webhook : JSON.stringify(webhook);\n }\n if (merchantStoreUrl !== undefined) {\n elementProps['merchant-store-url'] = merchantStoreUrl;\n }\n Object.assign(elementProps, rest);\n\n return React.createElement('gait-button', elementProps);\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\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"]}
package/dist/index.d.cts CHANGED
@@ -70,10 +70,9 @@ interface GaitConfirmDetail {
70
70
 
71
71
  /**
72
72
  * React wrapper for GaitButton Web Component
73
- * Maps React props to element attributes (WC uses attributes as source of truth).
74
- * Uses ref + useEffect for binding - no direct JSX attribute spread to avoid React's
75
- * string coercion of booleans/objects.
76
- * Waits for WC registration before rendering to avoid invisible element (race condition).
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.
77
76
  */
78
77
 
79
78
  interface GaitButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {
package/dist/index.d.ts CHANGED
@@ -70,10 +70,9 @@ interface GaitConfirmDetail {
70
70
 
71
71
  /**
72
72
  * React wrapper for GaitButton Web Component
73
- * Maps React props to element attributes (WC uses attributes as source of truth).
74
- * Uses ref + useEffect for binding - no direct JSX attribute spread to avoid React's
75
- * string coercion of booleans/objects.
76
- * Waits for WC registration before rendering to avoid invisible element (race condition).
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.
77
76
  */
78
77
 
79
78
  interface GaitButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClick' | 'style'> {
package/dist/index.js CHANGED
@@ -57,6 +57,54 @@ if (typeof window !== "undefined" && typeof customElements !== "undefined") {
57
57
  }
58
58
 
59
59
  // src/components/GaitButton.tsx
60
+ function setAttributes(el, props) {
61
+ const {
62
+ "data-id": dataId,
63
+ disabled,
64
+ size,
65
+ style,
66
+ items,
67
+ priceBreakdown,
68
+ totalCost,
69
+ customer,
70
+ webhook,
71
+ merchantStoreUrl
72
+ } = props;
73
+ if (dataId !== void 0) el.setAttribute("data-id", dataId);
74
+ if (size !== void 0) el.setAttribute("size", size);
75
+ if (disabled) el.setAttribute("disabled", "");
76
+ else el.removeAttribute("disabled");
77
+ if (style !== void 0) {
78
+ const styleStr = typeof style === "string" ? style : Object.entries(style).map(
79
+ ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
80
+ ).join("; ");
81
+ el.setAttribute("style", styleStr);
82
+ }
83
+ if (items !== void 0)
84
+ el.setAttribute(
85
+ "items",
86
+ typeof items === "string" ? items : JSON.stringify(items)
87
+ );
88
+ if (priceBreakdown !== void 0)
89
+ el.setAttribute(
90
+ "price-breakdown",
91
+ typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown)
92
+ );
93
+ if (totalCost !== void 0)
94
+ el.setAttribute("total-cost", String(totalCost));
95
+ if (customer !== void 0)
96
+ el.setAttribute(
97
+ "customer",
98
+ typeof customer === "string" ? customer : JSON.stringify(customer)
99
+ );
100
+ if (webhook !== void 0)
101
+ el.setAttribute(
102
+ "webhook",
103
+ typeof webhook === "string" ? webhook : JSON.stringify(webhook)
104
+ );
105
+ if (merchantStoreUrl !== void 0)
106
+ el.setAttribute("merchant-store-url", merchantStoreUrl);
107
+ }
60
108
  var GaitButton = forwardRef(function GaitButton2({
61
109
  "data-id": dataId,
62
110
  disabled,
@@ -78,8 +126,25 @@ var GaitButton = forwardRef(function GaitButton2({
78
126
  id,
79
127
  ...rest
80
128
  }, ref) {
129
+ const containerRef = useRef(null);
81
130
  const elementRef = useRef(null);
82
131
  const [isReady, setIsReady] = useState(false);
132
+ const callbackRef = useRef({
133
+ onClick,
134
+ onLoaded,
135
+ onDataProcessed,
136
+ onSplitFeedback,
137
+ onMerchantIdError,
138
+ onConfirm
139
+ });
140
+ callbackRef.current = {
141
+ onClick,
142
+ onLoaded,
143
+ onDataProcessed,
144
+ onSplitFeedback,
145
+ onMerchantIdError,
146
+ onConfirm
147
+ };
83
148
  useImperativeHandle(ref, () => elementRef.current, []);
84
149
  useEffect(() => {
85
150
  if (typeof customElements !== "undefined" && customElements.get("gait-button")) {
@@ -89,95 +154,51 @@ var GaitButton = forwardRef(function GaitButton2({
89
154
  defineCustomElements().then(() => setIsReady(true)).catch(() => setIsReady(false));
90
155
  }, []);
91
156
  useEffect(() => {
92
- const el = elementRef.current;
93
- if (!el) return;
94
- if (dataId !== void 0) el.setAttribute("data-id", dataId);
95
- if (size !== void 0) el.setAttribute("size", size);
96
- if (disabled) {
97
- el.setAttribute("disabled", "");
98
- } else {
99
- el.removeAttribute("disabled");
100
- }
101
- if (style !== void 0) {
102
- const styleStr = typeof style === "string" ? style : Object.entries(style).map(
103
- ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
104
- ).join("; ");
105
- el.setAttribute("style", styleStr);
106
- }
107
- if (items !== void 0) {
108
- el.setAttribute(
109
- "items",
110
- typeof items === "string" ? items : JSON.stringify(items)
111
- );
112
- }
113
- if (priceBreakdown !== void 0) {
114
- el.setAttribute(
115
- "price-breakdown",
116
- typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown)
117
- );
118
- }
119
- if (totalCost !== void 0) {
120
- el.setAttribute("total-cost", String(totalCost));
121
- }
122
- if (customer !== void 0) {
123
- el.setAttribute(
124
- "customer",
125
- typeof customer === "string" ? customer : JSON.stringify(customer)
126
- );
127
- }
128
- if (webhook !== void 0) {
129
- el.setAttribute(
130
- "webhook",
131
- typeof webhook === "string" ? webhook : JSON.stringify(webhook)
132
- );
133
- }
134
- if (merchantStoreUrl !== void 0) {
135
- el.setAttribute("merchant-store-url", merchantStoreUrl);
136
- }
137
- }, [
138
- dataId,
139
- disabled,
140
- size,
141
- style,
142
- items,
143
- priceBreakdown,
144
- totalCost,
145
- customer,
146
- webhook,
147
- merchantStoreUrl
148
- ]);
149
- useEffect(() => {
150
- const el = elementRef.current;
151
- if (!el) return;
157
+ if (!isReady || !containerRef.current || typeof document === "undefined")
158
+ return;
159
+ const container = containerRef.current;
160
+ const el = document.createElement("gait-button");
161
+ setAttributes(el, {
162
+ "data-id": dataId,
163
+ disabled,
164
+ size,
165
+ style,
166
+ items,
167
+ priceBreakdown,
168
+ totalCost,
169
+ customer,
170
+ webhook,
171
+ merchantStoreUrl
172
+ });
173
+ if (className) el.className = className;
174
+ if (id) el.id = id;
175
+ Object.entries(rest).forEach(([k, v]) => {
176
+ if (v != null && typeof v !== "function") el.setAttribute(k, String(v));
177
+ });
178
+ const cbs = callbackRef.current;
152
179
  const handleClick = (e) => {
153
- if (onClick && e instanceof CustomEvent && e.detail) {
154
- onClick(e.detail);
155
- }
180
+ if (cbs.onClick && e instanceof CustomEvent && e.detail)
181
+ cbs.onClick(e.detail);
156
182
  };
157
183
  const handleLoaded = (e) => {
158
- if (onLoaded && e instanceof CustomEvent && e.detail) {
159
- onLoaded(e.detail);
160
- }
184
+ if (cbs.onLoaded && e instanceof CustomEvent && e.detail)
185
+ cbs.onLoaded(e.detail);
161
186
  };
162
187
  const handleDataProcessed = (e) => {
163
- if (onDataProcessed && e instanceof CustomEvent && e.detail) {
164
- onDataProcessed(e.detail);
165
- }
188
+ if (cbs.onDataProcessed && e instanceof CustomEvent && e.detail)
189
+ cbs.onDataProcessed(e.detail);
166
190
  };
167
191
  const handleSplitFeedback = (e) => {
168
- if (onSplitFeedback && e instanceof CustomEvent && e.detail) {
169
- onSplitFeedback(e.detail);
170
- }
192
+ if (cbs.onSplitFeedback && e instanceof CustomEvent && e.detail)
193
+ cbs.onSplitFeedback(e.detail);
171
194
  };
172
195
  const handleMerchantIdError = (e) => {
173
- if (onMerchantIdError && e instanceof CustomEvent && e.detail) {
174
- onMerchantIdError(e.detail);
175
- }
196
+ if (cbs.onMerchantIdError && e instanceof CustomEvent && e.detail)
197
+ cbs.onMerchantIdError(e.detail);
176
198
  };
177
199
  const handleConfirm = (e) => {
178
- if (onConfirm && e instanceof CustomEvent && e.detail) {
179
- onConfirm(e.detail);
180
- }
200
+ if (cbs.onConfirm && e instanceof CustomEvent && e.detail)
201
+ cbs.onConfirm(e.detail);
181
202
  };
182
203
  el.addEventListener("gait-click", handleClick);
183
204
  el.addEventListener("gait-loaded", handleLoaded);
@@ -185,6 +206,8 @@ var GaitButton = forwardRef(function GaitButton2({
185
206
  el.addEventListener("gait-split-feedback", handleSplitFeedback);
186
207
  el.addEventListener("gait-merchant-id-error", handleMerchantIdError);
187
208
  el.addEventListener("gait-confirm", handleConfirm);
209
+ elementRef.current = el;
210
+ container.appendChild(el);
188
211
  return () => {
189
212
  el.removeEventListener("gait-click", handleClick);
190
213
  el.removeEventListener("gait-loaded", handleLoaded);
@@ -192,49 +215,45 @@ var GaitButton = forwardRef(function GaitButton2({
192
215
  el.removeEventListener("gait-split-feedback", handleSplitFeedback);
193
216
  el.removeEventListener("gait-merchant-id-error", handleMerchantIdError);
194
217
  el.removeEventListener("gait-confirm", handleConfirm);
218
+ container.removeChild(el);
219
+ elementRef.current = null;
195
220
  };
221
+ }, [isReady]);
222
+ useEffect(() => {
223
+ const el = elementRef.current;
224
+ if (!el) return;
225
+ setAttributes(el, {
226
+ "data-id": dataId,
227
+ disabled,
228
+ size,
229
+ style,
230
+ items,
231
+ priceBreakdown,
232
+ totalCost,
233
+ customer,
234
+ webhook,
235
+ merchantStoreUrl
236
+ });
237
+ if (className) el.className = className;
238
+ else el.removeAttribute("class");
239
+ if (id) el.id = id;
240
+ else el.removeAttribute("id");
196
241
  }, [
197
- onClick,
198
- onLoaded,
199
- onDataProcessed,
200
- onSplitFeedback,
201
- onMerchantIdError,
202
- onConfirm
242
+ dataId,
243
+ disabled,
244
+ size,
245
+ style,
246
+ items,
247
+ priceBreakdown,
248
+ totalCost,
249
+ customer,
250
+ webhook,
251
+ merchantStoreUrl,
252
+ className,
253
+ id
203
254
  ]);
204
- if (!isReady) {
205
- return null;
206
- }
207
- const elementProps = {
208
- ref: elementRef
209
- };
210
- if (className) elementProps.className = className;
211
- if (id) elementProps.id = id;
212
- if (dataId !== void 0) elementProps["data-id"] = dataId;
213
- if (size !== void 0) elementProps.size = size;
214
- if (disabled) elementProps.disabled = "";
215
- if (style !== void 0) {
216
- elementProps.style = typeof style === "string" ? style : Object.entries(style).map(
217
- ([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`
218
- ).join("; ");
219
- }
220
- if (items !== void 0) {
221
- elementProps.items = typeof items === "string" ? items : JSON.stringify(items);
222
- }
223
- if (priceBreakdown !== void 0) {
224
- elementProps["price-breakdown"] = typeof priceBreakdown === "string" ? priceBreakdown : JSON.stringify(priceBreakdown);
225
- }
226
- if (totalCost !== void 0) elementProps["total-cost"] = String(totalCost);
227
- if (customer !== void 0) {
228
- elementProps.customer = typeof customer === "string" ? customer : JSON.stringify(customer);
229
- }
230
- if (webhook !== void 0) {
231
- elementProps.webhook = typeof webhook === "string" ? webhook : JSON.stringify(webhook);
232
- }
233
- if (merchantStoreUrl !== void 0) {
234
- elementProps["merchant-store-url"] = merchantStoreUrl;
235
- }
236
- Object.assign(elementProps, rest);
237
- return React.createElement("gait-button", elementProps);
255
+ if (!isReady) return null;
256
+ return React.createElement("div", { ref: containerRef });
238
257
  });
239
258
  GaitButton.displayName = "GaitButton";
240
259
  export {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/GaitButton.tsx","../src/register.ts"],"sourcesContent":["'use client';\n\n/**\n * React wrapper for GaitButton Web Component\n * Maps React props to element attributes (WC uses attributes as source of truth).\n * Uses ref + useEffect for binding - no direct JSX attribute spread to avoid React's\n * string coercion of booleans/objects.\n * Waits for WC registration before rendering to avoid invisible element (race condition).\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\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 elementRef = useRef<HTMLElement>(null);\n const [isReady, setIsReady] = useState(false);\n\n useImperativeHandle(ref, () => elementRef.current as HTMLElement, []);\n\n // Wait for WC to be registered before rendering (avoids invisible element)\n useEffect(() => {\n if (typeof customElements !== 'undefined' && customElements.get('gait-button')) {\n setIsReady(true);\n return;\n }\n defineCustomElements().then(() => setIsReady(true)).catch(() => setIsReady(false));\n }, []);\n\n // Sync props to element attributes via ref (WC reads attributes) - must run every render (hooks order)\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n\n if (dataId !== undefined) el.setAttribute('data-id', dataId);\n if (size !== undefined) el.setAttribute('size', size);\n\n if (disabled) {\n el.setAttribute('disabled', '');\n } else {\n el.removeAttribute('disabled');\n }\n\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\n if (items !== undefined) {\n el.setAttribute(\n 'items',\n typeof items === 'string' ? items : JSON.stringify(items)\n );\n }\n\n if (priceBreakdown !== undefined) {\n el.setAttribute(\n 'price-breakdown',\n typeof priceBreakdown === 'string'\n ? priceBreakdown\n : JSON.stringify(priceBreakdown)\n );\n }\n\n if (totalCost !== undefined) {\n el.setAttribute('total-cost', String(totalCost));\n }\n\n if (customer !== undefined) {\n el.setAttribute(\n 'customer',\n typeof customer === 'string' ? customer : JSON.stringify(customer)\n );\n }\n\n if (webhook !== undefined) {\n el.setAttribute(\n 'webhook',\n typeof webhook === 'string' ? webhook : JSON.stringify(webhook)\n );\n }\n\n if (merchantStoreUrl !== undefined) {\n el.setAttribute('merchant-store-url', merchantStoreUrl);\n }\n }, [\n dataId,\n disabled,\n size,\n style,\n items,\n priceBreakdown,\n totalCost,\n customer,\n webhook,\n merchantStoreUrl,\n ]);\n\n // Event listeners\n useEffect(() => {\n const el = elementRef.current;\n if (!el) return;\n\n const handleClick = (e: Event) => {\n if (onClick && e instanceof CustomEvent && e.detail) {\n onClick(e.detail as GaitClickDetail);\n }\n };\n const handleLoaded = (e: Event) => {\n if (onLoaded && e instanceof CustomEvent && e.detail) {\n onLoaded(e.detail as GaitLoadedDetail);\n }\n };\n const handleDataProcessed = (e: Event) => {\n if (onDataProcessed && e instanceof CustomEvent && e.detail) {\n onDataProcessed(e.detail as GaitDataProcessedDetail);\n }\n };\n const handleSplitFeedback = (e: Event) => {\n if (onSplitFeedback && e instanceof CustomEvent && e.detail) {\n onSplitFeedback(e.detail as GaitSplitFeedbackDetail);\n }\n };\n const handleMerchantIdError = (e: Event) => {\n if (onMerchantIdError && e instanceof CustomEvent && e.detail) {\n onMerchantIdError(e.detail as GaitMerchantIdErrorDetail);\n }\n };\n const handleConfirm = (e: Event) => {\n if (onConfirm && e instanceof CustomEvent && e.detail) {\n onConfirm(e.detail as GaitConfirmDetail);\n }\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 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 };\n }, [\n onClick,\n onLoaded,\n onDataProcessed,\n onSplitFeedback,\n onMerchantIdError,\n onConfirm,\n ]);\n\n // Conditional render AFTER all hooks - required by Rules of Hooks\n if (!isReady) {\n return null;\n }\n\n // Pass attributes on initial render so WC sees them in connectedCallback (useEffect runs after mount)\n const elementProps: Record<string, unknown> = {\n ref: elementRef,\n };\n if (className) elementProps.className = className;\n if (id) elementProps.id = id;\n if (dataId !== undefined) elementProps['data-id'] = dataId;\n if (size !== undefined) elementProps.size = size;\n if (disabled) elementProps.disabled = '';\n if (style !== undefined) {\n elementProps.style =\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 }\n if (items !== undefined) {\n elementProps.items =\n typeof items === 'string' ? items : JSON.stringify(items);\n }\n if (priceBreakdown !== undefined) {\n elementProps['price-breakdown'] =\n typeof priceBreakdown === 'string'\n ? priceBreakdown\n : JSON.stringify(priceBreakdown);\n }\n if (totalCost !== undefined) elementProps['total-cost'] = String(totalCost);\n if (customer !== undefined) {\n elementProps.customer =\n typeof customer === 'string' ? customer : JSON.stringify(customer);\n }\n if (webhook !== undefined) {\n elementProps.webhook =\n typeof webhook === 'string' ? webhook : JSON.stringify(webhook);\n }\n if (merchantStoreUrl !== undefined) {\n elementProps['merchant-store-url'] = merchantStoreUrl;\n }\n Object.assign(elementProps, rest);\n\n return React.createElement('gait-button', elementProps);\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"]}
1
+ {"version":3,"sources":["../src/components/GaitButton.tsx","../src/register.ts"],"sourcesContent":["'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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gait-financial/react",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "private": false,
5
5
  "description": "React wrappers for Gait Web Components",
6
6
  "type": "module",