@funnelsgrove/payments 0.7.13 → 0.7.14

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.
@@ -104,6 +104,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
104
104
  const [committedEmail, setCommittedEmail] = useState(normalizedCustomerEmail);
105
105
  const [emailError, setEmailError] = useState(null);
106
106
  const [emailSubmitting, setEmailSubmitting] = useState(false);
107
+ const [mountedPaymentAttemptId, setMountedPaymentAttemptId] = useState(null);
107
108
  const prepareAbortRef = useRef(null);
108
109
  const statusAbortRef = useRef(null);
109
110
  const successAttemptRef = useRef(null);
@@ -192,6 +193,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
192
193
  prepareAbortRef.current = controller;
193
194
  successAttemptRef.current = null;
194
195
  paymentInfoAttemptRef.current = null;
196
+ setMountedPaymentAttemptId(null);
195
197
  queueMicrotask(() => {
196
198
  if (!controller.signal.aborted) {
197
199
  setState(initialView);
@@ -213,6 +215,9 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
213
215
  });
214
216
  return () => controller.abort();
215
217
  }, [committedEmail, normalizedRequest, open, requiresEmailCommit, setState]);
218
+ const paymentFormMounted = Boolean(view.checkout && mountedPaymentAttemptId === view.checkout.attemptId);
219
+ const showPreparingLoader = view.state === 'loading'
220
+ || (view.state === 'ready' && !paymentFormMounted);
216
221
  useEffect(() => {
217
222
  var _a;
218
223
  if (!open) {
@@ -227,12 +232,14 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
227
232
  }
228
233
  };
229
234
  window.addEventListener('keydown', handleKeyDown);
230
- (_a = closeButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
235
+ if (!showPreparingLoader) {
236
+ (_a = closeButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
237
+ }
231
238
  return () => {
232
239
  window.removeEventListener('keydown', handleKeyDown);
233
240
  previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus();
234
241
  };
235
- }, [open]);
242
+ }, [open, showPreparingLoader]);
236
243
  useEffect(() => () => {
237
244
  var _a, _b;
238
245
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
@@ -330,25 +337,24 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
330
337
  : view.state === 'recoverable_error'
331
338
  ? copy.error
332
339
  : null;
333
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'solidgate-checkout-overlay', children: _jsxs("div", { role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'solidgate-checkout-title', "data-checkout-state": view.state, className: 'solidgate-checkout-dialog', children: [_jsxs("div", { className: 'solidgate-checkout-header', children: [_jsx("h2", { id: 'solidgate-checkout-title', className: 'solidgate-checkout-title', children: copy.title }), _jsx("button", { ref: closeButtonRef, type: 'button', onClick: onClose, "aria-label": copy.closeAriaLabel, className: 'solidgate-checkout-close', children: "\u00D7" })] }), view.state === 'collecting_email' ? (_jsxs("form", { className: 'solidgate-checkout-email-form', onSubmit: (event) => void commitCustomerEmail(event), children: [_jsxs("label", { className: 'solidgate-checkout-email-label', children: [copy.customerEmailLabel, _jsx("input", { type: 'email', autoComplete: 'email', inputMode: 'email', maxLength: 320, required: true, value: emailInput, onChange: (event) => {
334
- setEmailInput(event.target.value);
335
- setEmailError(null);
336
- }, placeholder: copy.customerEmailPlaceholder, "aria-invalid": Boolean(emailError), className: 'solidgate-checkout-email-input' })] }), emailError ? (_jsx("p", { role: 'alert', className: 'solidgate-checkout-alert', children: emailError })) : null, _jsx("button", { type: 'submit', disabled: emailSubmitting, className: 'solidgate-checkout-action', children: copy.customerEmailContinue })] })) : null, view.checkout ? (_jsx(SolidgatePaymentForm, { checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
337
- if (view.state === 'loading') {
338
- setState(Object.assign(Object.assign({}, view), { state: 'ready' }));
339
- }
340
- }, onSubmit: () => {
341
- var _a, _b, _c;
342
- if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
343
- paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
344
- void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
345
- }
346
- setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
347
- }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
348
- var _a;
349
- setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
350
- (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, copyRef.current.error);
351
- } }, view.checkout.attemptId)) : null, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'solidgate-checkout-status', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, view.state === 'verifying' && view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: () => void verifyCanonicalStatus(), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.resumeVerification })) : null, (view.state === 'recoverable_error' || view.state === 'expired') && onRetry ? (_jsx("button", { type: 'button', onClick: onRetry, className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] }) }), _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
340
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: 'solidgate-checkout-overlay', children: [showPreparingLoader ? (_jsxs("div", { className: 'solidgate-checkout-loader', role: 'status', "aria-live": 'polite', children: [_jsx("span", { className: 'solidgate-checkout-spinner', "aria-hidden": 'true' }), _jsx("span", { className: 'solidgate-checkout-visually-hidden', children: copy.loading })] })) : null, view.state !== 'loading' || view.checkout ? (_jsxs("div", { role: 'dialog', "aria-modal": 'true', "aria-hidden": showPreparingLoader || undefined, "aria-labelledby": 'solidgate-checkout-title', "data-checkout-state": view.state, className: `solidgate-checkout-dialog${showPreparingLoader ? ' is-preparing' : ''}`, children: [_jsxs("div", { className: 'solidgate-checkout-header', children: [_jsx("h2", { id: 'solidgate-checkout-title', className: 'solidgate-checkout-title', children: copy.title }), _jsx("button", { ref: closeButtonRef, type: 'button', onClick: onClose, "aria-label": copy.closeAriaLabel, className: 'solidgate-checkout-close', children: "\u00D7" })] }), view.state === 'collecting_email' ? (_jsxs("form", { className: 'solidgate-checkout-email-form', onSubmit: (event) => void commitCustomerEmail(event), children: [_jsxs("label", { className: 'solidgate-checkout-email-label', children: [copy.customerEmailLabel, _jsx("input", { type: 'email', autoComplete: 'email', inputMode: 'email', maxLength: 320, required: true, value: emailInput, onChange: (event) => {
341
+ setEmailInput(event.target.value);
342
+ setEmailError(null);
343
+ }, placeholder: copy.customerEmailPlaceholder, "aria-invalid": Boolean(emailError), className: 'solidgate-checkout-email-input' })] }), emailError ? (_jsx("p", { role: 'alert', className: 'solidgate-checkout-alert', children: emailError })) : null, _jsx("button", { type: 'submit', disabled: emailSubmitting, className: 'solidgate-checkout-action', children: copy.customerEmailContinue })] })) : null, view.checkout ? (_jsx(SolidgatePaymentForm, { checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
344
+ var _a;
345
+ setMountedPaymentAttemptId(((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || null);
346
+ }, onSubmit: () => {
347
+ var _a, _b, _c;
348
+ if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
349
+ paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
350
+ void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
351
+ }
352
+ setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
353
+ }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
354
+ var _a;
355
+ setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
356
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, copyRef.current.error);
357
+ } }, view.checkout.attemptId)) : null, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'solidgate-checkout-status', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, view.state === 'verifying' && view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: () => void verifyCanonicalStatus(), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.resumeVerification })) : null, (view.state === 'recoverable_error' || view.state === 'expired') && onRetry ? (_jsx("button", { type: 'button', onClick: onRetry, className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] })) : null] }), _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
352
358
  }
353
359
  const solidgateCheckoutDialogStyles = `
354
360
  .solidgate-checkout-overlay {
@@ -378,6 +384,49 @@ const solidgateCheckoutDialogStyles = `
378
384
  box-shadow: 0 24px 80px rgb(17 24 39 / 22%);
379
385
  }
380
386
 
387
+ .solidgate-checkout-dialog.is-preparing {
388
+ position: absolute;
389
+ visibility: hidden;
390
+ pointer-events: none;
391
+ }
392
+
393
+ .solidgate-checkout-loader {
394
+ display: grid;
395
+ place-items: center;
396
+ }
397
+
398
+ .solidgate-checkout-spinner {
399
+ box-sizing: border-box;
400
+ width: 44px;
401
+ height: 44px;
402
+ border: 4px solid var(--color-border, #d1d5db);
403
+ border-top-color: var(--color-primary, #22c55e);
404
+ border-radius: 50%;
405
+ animation: solidgate-checkout-spin 720ms linear infinite;
406
+ }
407
+
408
+ .solidgate-checkout-visually-hidden {
409
+ position: absolute;
410
+ width: 1px;
411
+ height: 1px;
412
+ overflow: hidden;
413
+ clip: rect(0 0 0 0);
414
+ clip-path: inset(50%);
415
+ white-space: nowrap;
416
+ }
417
+
418
+ @keyframes solidgate-checkout-spin {
419
+ to {
420
+ transform: rotate(360deg);
421
+ }
422
+ }
423
+
424
+ @media (prefers-reduced-motion: reduce) {
425
+ .solidgate-checkout-spinner {
426
+ animation-duration: 1.6s;
427
+ }
428
+ }
429
+
381
430
  .solidgate-checkout-header {
382
431
  display: flex;
383
432
  align-items: center;
@@ -984,12 +984,13 @@ const sharedStripeCheckoutV2Styles = `
984
984
  .shared-checkout-special-offer-gift--image {
985
985
  position: relative;
986
986
  width: min(100%, 250px);
987
+ aspect-ratio: 1;
987
988
  }
988
989
 
989
990
  .shared-checkout-special-offer-gift-image {
990
991
  display: block;
991
992
  width: 100%;
992
- aspect-ratio: 1;
993
+ height: 100%;
993
994
  background-position: center;
994
995
  background-repeat: no-repeat;
995
996
  background-size: contain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.7.13",
3
+ "version": "0.7.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",