fontdue-js 2.17.2 → 2.18.2

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/__generated__/CartItemProduct_product.graphql.d.ts +1 -1
  3. package/dist/__generated__/CartItemProduct_product.graphql.js +16 -10
  4. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.d.ts +1 -1
  5. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.js +16 -10
  6. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.d.ts +1 -1
  7. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.js +16 -10
  8. package/dist/__generated__/CartOrderUpdateMutation.graphql.d.ts +1 -1
  9. package/dist/__generated__/CartOrderUpdateMutation.graphql.js +16 -10
  10. package/dist/__generated__/CartQuery.graphql.d.ts +1 -1
  11. package/dist/__generated__/CartQuery.graphql.js +16 -10
  12. package/dist/__generated__/CartStateRemoveDiscountMutation.graphql.d.ts +1 -1
  13. package/dist/__generated__/CartStateRemoveDiscountMutation.graphql.js +37 -29
  14. package/dist/__generated__/CartStateUpdateMutation.graphql.d.ts +1 -1
  15. package/dist/__generated__/CartStateUpdateMutation.graphql.js +79 -66
  16. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +2 -1
  17. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +16 -10
  18. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.d.ts +1 -1
  19. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.js +16 -10
  20. package/dist/__generated__/CouponCodeInputApplyCouponMutation.graphql.d.ts +1 -1
  21. package/dist/__generated__/CouponCodeInputApplyCouponMutation.graphql.js +37 -29
  22. package/dist/__generated__/NewsletterSignupQuery.graphql.d.ts +3 -1
  23. package/dist/__generated__/NewsletterSignupQuery.graphql.js +16 -4
  24. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.d.ts +2 -1
  25. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.js +1 -1
  26. package/dist/__generated__/PrecartAddToCartMutation.graphql.d.ts +1 -1
  27. package/dist/__generated__/PrecartAddToCartMutation.graphql.js +21 -15
  28. package/dist/__generated__/StoreModalCartQuery.graphql.d.ts +1 -1
  29. package/dist/__generated__/StoreModalCartQuery.graphql.js +27 -21
  30. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
  31. package/dist/__generated__/StoreModalProductQuery.graphql.js +117 -79
  32. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.d.ts +1 -1
  33. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.js +39 -4
  34. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.d.ts +13 -1
  35. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.js +37 -2
  36. package/dist/__generated__/StoreModalUnifiedCheckoutCompleteOrderMutation.graphql.d.ts +1 -1
  37. package/dist/__generated__/StoreModalUnifiedCheckoutCompleteOrderMutation.graphql.js +10 -4
  38. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.d.ts +2 -1
  39. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.js +10 -4
  40. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateOrderMutation.graphql.d.ts +1 -1
  41. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateOrderMutation.graphql.js +36 -28
  42. package/dist/__generated__/StoreModalUnifiedCheckout_order.graphql.d.ts +2 -1
  43. package/dist/__generated__/StoreModalUnifiedCheckout_order.graphql.js +9 -3
  44. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +2 -1
  45. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +16 -10
  46. package/dist/__generated__/TestFontsForm_Query.graphql.d.ts +3 -1
  47. package/dist/__generated__/TestFontsForm_Query.graphql.js +16 -4
  48. package/dist/__generated__/productState_Query.graphql.d.ts +29 -1
  49. package/dist/__generated__/productState_Query.graphql.js +24 -5
  50. package/dist/__generated__/productState_collectionBundle.graphql.d.ts +41 -0
  51. package/dist/__generated__/productState_collectionBundle.graphql.js +88 -0
  52. package/dist/__generated__/productState_node.graphql.d.ts +29 -1
  53. package/dist/__generated__/productState_node.graphql.js +20 -2
  54. package/dist/components/Cart/CartItem/CartItemProduct.js +3 -3
  55. package/dist/components/CharacterViewer/index.js +22 -2
  56. package/dist/components/NewsletterSignup/index.js +62 -19
  57. package/dist/components/Root/productState.js +35 -7
  58. package/dist/components/StoreModalProductSummary/index.js +21 -9
  59. package/dist/components/StripeProvider/index.d.ts +1 -2
  60. package/dist/components/StripeProvider/index.js +7 -10
  61. package/dist/components/TestFontsForm/index.js +60 -12
  62. package/dist/components/elements/StoreModalUnifiedCheckout.js +4 -4
  63. package/dist/relay/environment.js +5 -1
  64. package/package.json +3 -1
@@ -10,6 +10,7 @@ var _NewsletterSignupQuery2 = _interopRequireDefault(require("../../__generated_
10
10
  var _NewsletterSignupUpdateCustomerMutation2 = _interopRequireDefault(require("../../__generated__/NewsletterSignupUpdateCustomerMutation.graphql"));
11
11
  var _react = _interopRequireWildcard(require("react"));
12
12
  var _reactRelay = require("react-relay");
13
+ var _reactGoogleRecaptcha = _interopRequireDefault(require("react-google-recaptcha"));
13
14
  var _TextField = _interopRequireDefault(require("../TextField"));
14
15
  var _Check = _interopRequireDefault(require("../Icons/Check"));
15
16
  var _useSerializablePreloadedQuery = _interopRequireDefault(require("../../relay/useSerializablePreloadedQuery"));
@@ -18,9 +19,9 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
20
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
20
21
  const updateCustomerMutation = (_NewsletterSignupUpdateCustomerMutation2.default.hash && _NewsletterSignupUpdateCustomerMutation2.default.hash !== "769087891b6f263122bbb630b3f2ca6c" && console.error("The definition of 'NewsletterSignupUpdateCustomerMutation' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NewsletterSignupUpdateCustomerMutation2.default);
21
- const query = (_NewsletterSignupQuery2.default.hash && _NewsletterSignupQuery2.default.hash !== "16f0b6d7ed420ec5857c972b77805e4c" && console.error("The definition of 'NewsletterSignupQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NewsletterSignupQuery2.default);
22
+ const query = (_NewsletterSignupQuery2.default.hash && _NewsletterSignupQuery2.default.hash !== "24b303198a6038318723fc0124548862" && console.error("The definition of 'NewsletterSignupQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NewsletterSignupQuery2.default);
22
23
  function NewsletterSignupComponent(_ref) {
23
- var _data$viewer, _data$viewer$settings, _data$viewer2, _data$viewer2$setting;
24
+ var _data$viewer, _data$viewer$settings, _data$viewer2, _data$viewer2$setting, _data$viewer3, _data$viewer3$setting, _data$viewer4, _data$viewer4$setting;
24
25
  let {
25
26
  optInLabel: optInLabelProp,
26
27
  successLabel: successLabelProp,
@@ -36,17 +37,34 @@ function NewsletterSignupComponent(_ref) {
36
37
  const [error, setError] = (0, _react.useState)(null);
37
38
  const [submitting, setSubmitting] = (0, _react.useState)(false);
38
39
  const [submitted, setSubmitted] = (0, _react.useState)(false);
40
+ const [recaptchaToken, setRecaptchaToken] = (0, _react.useState)(null);
41
+ const [pendingSubmit, setPendingSubmit] = (0, _react.useState)(false);
42
+ const recaptchaRef = (0, _react.useRef)(null);
39
43
  const environment = (0, _reactRelay.useRelayEnvironment)();
40
- const submitMutation = (mutation, input) => {
44
+ const recaptchaEnabled = ((_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : (_data$viewer$settings = _data$viewer.settings) === null || _data$viewer$settings === void 0 ? void 0 : _data$viewer$settings.recaptchaEnabled) ?? false;
45
+ const recaptchaSiteKey = (_data$viewer2 = data.viewer) === null || _data$viewer2 === void 0 ? void 0 : (_data$viewer2$setting = _data$viewer2.settings) === null || _data$viewer2$setting === void 0 ? void 0 : _data$viewer2$setting.recaptchaSiteKey;
46
+ const submitForm = (0, _react.useCallback)(token => {
47
+ setSubmitting(true);
48
+ setPendingSubmit(false);
49
+ setError(null);
41
50
  (0, _reactRelay.commitMutation)(environment, {
42
- mutation,
51
+ mutation: updateCustomerMutation,
43
52
  variables: {
44
- input
53
+ input: {
54
+ name,
55
+ email,
56
+ newsletterOptIn,
57
+ recaptchaToken: token ?? undefined
58
+ }
45
59
  },
46
60
  onCompleted: (_res, errors) => {
47
61
  if (errors && errors.length > 0) {
62
+ var _recaptchaRef$current;
48
63
  setError(errors[0].message);
49
64
  setSubmitting(false);
65
+ // Reset reCAPTCHA on error so user can retry
66
+ (_recaptchaRef$current = recaptchaRef.current) === null || _recaptchaRef$current === void 0 ? void 0 : _recaptchaRef$current.reset();
67
+ setRecaptchaToken(null);
50
68
  } else {
51
69
  setSubmitted(true);
52
70
  setSubmitting(false);
@@ -54,27 +72,46 @@ function NewsletterSignupComponent(_ref) {
54
72
  }
55
73
  },
56
74
  onError: _ref2 => {
75
+ var _recaptchaRef$current2;
57
76
  let {
58
77
  message
59
78
  } = _ref2;
60
79
  setError(message);
61
80
  setSubmitting(false);
81
+ // Reset reCAPTCHA on error so user can retry
82
+ (_recaptchaRef$current2 = recaptchaRef.current) === null || _recaptchaRef$current2 === void 0 ? void 0 : _recaptchaRef$current2.reset();
83
+ setRecaptchaToken(null);
62
84
  }
63
85
  });
64
- };
86
+ }, [environment, name, email, newsletterOptIn]);
87
+ const handleRecaptchaChange = (0, _react.useCallback)(token => {
88
+ setRecaptchaToken(token);
89
+ // If we were waiting for a token to submit, do it now
90
+ if (token && pendingSubmit) {
91
+ submitForm(token);
92
+ }
93
+ }, [pendingSubmit, submitForm]);
94
+ const handleRecaptchaExpired = (0, _react.useCallback)(() => {
95
+ setRecaptchaToken(null);
96
+ }, []);
65
97
  const handleSubmit = e => {
66
98
  e.preventDefault();
67
- setSubmitting(true);
68
- setError(null);
69
- submitMutation(updateCustomerMutation, {
70
- name,
71
- email,
72
- newsletterOptIn
73
- });
99
+
100
+ // If reCAPTCHA is enabled but no token, execute it and wait for callback
101
+ if (recaptchaEnabled && recaptchaSiteKey && !recaptchaToken) {
102
+ var _recaptchaRef$current3;
103
+ setPendingSubmit(true);
104
+ setError(null);
105
+ (_recaptchaRef$current3 = recaptchaRef.current) === null || _recaptchaRef$current3 === void 0 ? void 0 : _recaptchaRef$current3.execute();
106
+ return;
107
+ }
108
+ submitForm(recaptchaToken);
74
109
  };
75
- const disabled = submitting || !newsletterOptIn || !email || !name;
76
- const optInLabel = optInLabelProp || ((_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : (_data$viewer$settings = _data$viewer.settings) === null || _data$viewer$settings === void 0 ? void 0 : _data$viewer$settings.newsletterOptInLabel);
77
- const successLabel = (successLabelProp || ((_data$viewer2 = data.viewer) === null || _data$viewer2 === void 0 ? void 0 : (_data$viewer2$setting = _data$viewer2.settings) === null || _data$viewer2$setting === void 0 ? void 0 : _data$viewer2$setting.newsletterSuccessLabel)) ?? 'Success!';
110
+
111
+ // Button is disabled only while actively submitting or waiting for reCAPTCHA
112
+ const disabled = !newsletterOptIn || !email || !name;
113
+ const optInLabel = optInLabelProp || ((_data$viewer3 = data.viewer) === null || _data$viewer3 === void 0 ? void 0 : (_data$viewer3$setting = _data$viewer3.settings) === null || _data$viewer3$setting === void 0 ? void 0 : _data$viewer3$setting.newsletterOptInLabel);
114
+ const successLabel = (successLabelProp || ((_data$viewer4 = data.viewer) === null || _data$viewer4 === void 0 ? void 0 : (_data$viewer4$setting = _data$viewer4.settings) === null || _data$viewer4$setting === void 0 ? void 0 : _data$viewer4$setting.newsletterSuccessLabel)) ?? 'Success!';
78
115
  if (submitted) {
79
116
  return /*#__PURE__*/_react.default.createElement("div", {
80
117
  className: "newsletter-signup"
@@ -128,12 +165,18 @@ function NewsletterSignupComponent(_ref) {
128
165
  dangerouslySetInnerHTML: {
129
166
  __html: optInLabel ?? ''
130
167
  }
131
- })), /*#__PURE__*/_react.default.createElement("div", {
168
+ })), recaptchaEnabled && recaptchaSiteKey && /*#__PURE__*/_react.default.createElement(_reactGoogleRecaptcha.default, {
169
+ ref: recaptchaRef,
170
+ sitekey: recaptchaSiteKey,
171
+ size: "invisible",
172
+ onChange: handleRecaptchaChange,
173
+ onExpired: handleRecaptchaExpired
174
+ }), /*#__PURE__*/_react.default.createElement("div", {
132
175
  className: "newsletter-signup__section"
133
176
  }, /*#__PURE__*/_react.default.createElement("button", {
134
177
  className: "submit-button",
135
- disabled: disabled
136
- }, submitting ? 'Submitting...' : buttonLabel, ' ', /*#__PURE__*/_react.default.createElement("span", {
178
+ disabled: submitting || pendingSubmit || disabled
179
+ }, submitting || pendingSubmit ? 'Submitting...' : buttonLabel, ' ', /*#__PURE__*/_react.default.createElement("span", {
137
180
  className: "submit-button__arrow"
138
181
  }, ' →')))));
139
182
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.populateProductStateReducer = exports.populateProductState = void 0;
7
7
  var _productState_Query2 = _interopRequireDefault(require("../../__generated__/productState_Query.graphql"));
8
8
  var _productState_node2 = _interopRequireDefault(require("../../__generated__/productState_node.graphql"));
9
+ var _productState_collectionBundle2 = _interopRequireDefault(require("../../__generated__/productState_collectionBundle.graphql"));
9
10
  var _productState_bundle2 = _interopRequireDefault(require("../../__generated__/productState_bundle.graphql"));
10
11
  var _productState_collection2 = _interopRequireDefault(require("../../__generated__/productState_collection.graphql"));
11
12
  var _productState_sku2 = _interopRequireDefault(require("../../__generated__/productState_sku.graphql"));
@@ -15,7 +16,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
15
16
  _productState_sku2.default.hash && _productState_sku2.default.hash !== "2cee6ddddc85699f0c90e2607c0634ef" && console.error("The definition of 'productState_sku' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_sku2.default;
16
17
  _productState_collection2.default.hash && _productState_collection2.default.hash !== "90e56933f956c6773c0958284de81884" && console.error("The definition of 'productState_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_collection2.default;
17
18
  _productState_bundle2.default.hash && _productState_bundle2.default.hash !== "fa38bbc9f2062a727da4f5062b362054" && console.error("The definition of 'productState_bundle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_bundle2.default;
18
- _productState_node2.default.hash && _productState_node2.default.hash !== "aab1ef07e0192a0d22a4c300084d9d05" && console.error("The definition of 'productState_node' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_node2.default;
19
+ _productState_collectionBundle2.default.hash && _productState_collectionBundle2.default.hash !== "6744aa8f57d14b0111a0c49f488d7b03" && console.error("The definition of 'productState_collectionBundle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_collectionBundle2.default;
20
+ _productState_node2.default.hash && _productState_node2.default.hash !== "a1e172db45ca550a254819d877497d12" && console.error("The definition of 'productState_node' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_node2.default;
19
21
  const query = (_productState_Query2.default.hash && _productState_Query2.default.hash !== "ac3292bae9a274139d5a525fd0162711" && console.error("The definition of 'productState_Query' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_Query2.default);
20
22
  const populateProductState = (store, environment, id) => {
21
23
  const existingState = store.getState().fetchedCollectionIds;
@@ -39,12 +41,25 @@ const allSkus = function (collection) {
39
41
  var _collection$bundles;
40
42
  bundleSkus = (collection === null || collection === void 0 ? void 0 : (_collection$bundles = collection.bundles) === null || _collection$bundles === void 0 ? void 0 : _collection$bundles.flatMap(bundle => allSkus(bundle, includeCollections, includeFontStyles))) ?? [];
41
43
  }
44
+ let collectionBundleSkus = [];
45
+ if ('collectionBundles' in collection && collection.collectionBundles) {
46
+ collectionBundleSkus = collection.collectionBundles.flatMap(collBundle => allSkus(collBundle, includeCollections, includeFontStyles));
47
+ }
48
+
49
+ // For collection bundles, get SKUs from member collections
50
+ let memberCollectionSkus = [];
51
+ if ('memberCollections' in collection && collection.memberCollections) {
52
+ memberCollectionSkus = collection.memberCollections.flatMap(member => {
53
+ var _member$fontStyles;
54
+ return [includeCollections ? member.sku : null, ...(includeFontStyles ? ((_member$fontStyles = member.fontStyles) === null || _member$fontStyles === void 0 ? void 0 : _member$fontStyles.map(style => style.sku)) ?? [] : [])];
55
+ }).filter(_utils.notEmpty);
56
+ }
42
57
  let childrenSkus = [];
43
58
  if ('children' in collection) {
44
59
  var _collection$children;
45
60
  childrenSkus = ((_collection$children = collection.children) === null || _collection$children === void 0 ? void 0 : _collection$children.flatMap(child => allSkus(child, includeCollections, includeFontStyles))) ?? [];
46
61
  }
47
- return [includeCollections ? collection === null || collection === void 0 ? void 0 : collection.sku : null, ...(includeFontStyles ? (collection === null || collection === void 0 ? void 0 : (_collection$fontStyle = collection.fontStyles) === null || _collection$fontStyle === void 0 ? void 0 : _collection$fontStyle.map(style => style.sku)) ?? [] : []), ...bundleSkus, ...childrenSkus].filter(_utils.notEmpty);
62
+ return [includeCollections ? collection === null || collection === void 0 ? void 0 : collection.sku : null, ...(includeFontStyles && 'fontStyles' in collection ? (collection === null || collection === void 0 ? void 0 : (_collection$fontStyle = collection.fontStyles) === null || _collection$fontStyle === void 0 ? void 0 : _collection$fontStyle.map(style => style.sku)) ?? [] : []), ...bundleSkus, ...collectionBundleSkus, ...memberCollectionSkus, ...childrenSkus].filter(_utils.notEmpty);
48
63
  };
49
64
  const flattenSkuData = collection => {
50
65
  let bundles = {};
@@ -54,6 +69,13 @@ const flattenSkuData = collection => {
54
69
  ...flattenSkuData(bundle)
55
70
  }), {});
56
71
  }
72
+ let collectionBundles = {};
73
+ if ('collectionBundles' in collection && collection.collectionBundles) {
74
+ collectionBundles = collection.collectionBundles.reduce((res, collBundle) => ({
75
+ ...res,
76
+ ...flattenSkuData(collBundle)
77
+ }), {});
78
+ }
57
79
  let children = {};
58
80
  if ('children' in collection && collection.children) {
59
81
  children = collection.children.reduce((res, child) => ({
@@ -64,12 +86,17 @@ const flattenSkuData = collection => {
64
86
  let collectionData = {};
65
87
  if (collection.sku) {
66
88
  var _collection$fontStyle2, _collection$children2;
89
+ // For collection bundles, get font style IDs from member collections
90
+ const fontStyleIds = 'memberCollections' in collection && collection.memberCollections ? collection.memberCollections.flatMap(member => {
91
+ var _member$fontStyles2;
92
+ return ((_member$fontStyles2 = member.fontStyles) === null || _member$fontStyles2 === void 0 ? void 0 : _member$fontStyles2.map(style => style.id)) ?? [];
93
+ }) : 'fontStyles' in collection ? (((_collection$fontStyle2 = collection.fontStyles) === null || _collection$fontStyle2 === void 0 ? void 0 : _collection$fontStyle2.map(style => style.id)) ?? []).concat('children' in collection ? ((_collection$children2 = collection.children) === null || _collection$children2 === void 0 ? void 0 : _collection$children2.flatMap(child => {
94
+ var _child$fontStyles;
95
+ return ((_child$fontStyles = child.fontStyles) === null || _child$fontStyles === void 0 ? void 0 : _child$fontStyles.map(style => style.id)) ?? [];
96
+ })) ?? [] : []) : [];
67
97
  collectionData = {
68
98
  [collection.sku.id]: {
69
- fontStyleIds: (((_collection$fontStyle2 = collection.fontStyles) === null || _collection$fontStyle2 === void 0 ? void 0 : _collection$fontStyle2.map(style => style.id)) ?? []).concat('children' in collection ? ((_collection$children2 = collection.children) === null || _collection$children2 === void 0 ? void 0 : _collection$children2.flatMap(child => {
70
- var _child$fontStyles;
71
- return ((_child$fontStyles = child.fontStyles) === null || _child$fontStyles === void 0 ? void 0 : _child$fontStyles.map(style => style.id)) ?? [];
72
- })) ?? [] : []),
99
+ fontStyleIds,
73
100
  fontStyleSkuIds: allSkus(collection, false, true).map(sku => sku.id),
74
101
  childrenSkuIds: allSkus(collection, true, false).map(sku => sku.id).filter(skuId => {
75
102
  var _collection$sku;
@@ -83,7 +110,8 @@ const flattenSkuData = collection => {
83
110
  return {
84
111
  ...collectionData,
85
112
  ...children,
86
- ...bundles
113
+ ...bundles,
114
+ ...collectionBundles
87
115
  };
88
116
  };
89
117
  const flattenSkuPrices = collection => {
@@ -63,7 +63,7 @@ const StoreModalProductSummary = _ref => {
63
63
  const refetchVariables = (0, _react.useMemo)(() => ({
64
64
  selectedSkuIds: selectedSkuIdsArray
65
65
  }), [selectedSkuIdsArray]);
66
- const [viewer, refetch] = (0, _reactRelay.useRefetchableFragment)((_StoreModalProductSummary_viewer2.default.hash && _StoreModalProductSummary_viewer2.default.hash !== "e21762fcdb76ac693bcdf7b98effc40b" && console.error("The definition of 'StoreModalProductSummary_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductSummary_viewer2.default), viewerKey);
66
+ const [viewer, refetch] = (0, _reactRelay.useRefetchableFragment)((_StoreModalProductSummary_viewer2.default.hash && _StoreModalProductSummary_viewer2.default.hash !== "81a5f120d76cbb84753d158a7ff38c19" && console.error("The definition of 'StoreModalProductSummary_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductSummary_viewer2.default), viewerKey);
67
67
  (0, _useRefetchOnLicenseChanges.useRefetchOnLicenseChanges)({
68
68
  environment,
69
69
  refetch,
@@ -124,14 +124,26 @@ const StoreModalProductSummary = _ref => {
124
124
  const anySkusSelected = Object.keys(selectedSkuIds).filter(skuId => selectedSkuIds[skuId]).length > 0;
125
125
  const stylesPrice = countStylesPrice(viewer);
126
126
  let taxLabel = null;
127
- if (viewer.taxCollections.some(_ref3 => {
128
- let {
129
- taxName,
130
- inclusive
131
- } = _ref3;
132
- return taxName === 'VAT' && !inclusive;
133
- })) {
134
- taxLabel = ' excl. VAT';
127
+ const {
128
+ taxSystem,
129
+ stripeTaxSettings
130
+ } = viewer.settings ?? {};
131
+ if (taxSystem === 'stripe') {
132
+ // For Stripe Tax, use the explicit tax behavior setting
133
+ if ((stripeTaxSettings === null || stripeTaxSettings === void 0 ? void 0 : stripeTaxSettings.taxBehavior) === 'exclusive') {
134
+ taxLabel = ' excl. Tax';
135
+ }
136
+ // No label for 'inclusive' or 'inferred_by_currency'
137
+ } else {
138
+ // Legacy fontdue tax system - show label if any tax is not inclusive
139
+ if (viewer.taxCollections.some(_ref3 => {
140
+ let {
141
+ inclusive
142
+ } = _ref3;
143
+ return !inclusive;
144
+ })) {
145
+ taxLabel = ' excl. Tax';
146
+ }
135
147
  }
136
148
  let message = null;
137
149
  const requiredOrderVariablesNotYetSelected = (_viewer$orderVariable = viewer.orderVariables) === null || _viewer$orderVariable === void 0 ? void 0 : _viewer$orderVariable.filter(variable => variable.variableType === 'select').find(variable => !orderVariableSelections.find(selection => selection.orderVariableId === variable.id));
@@ -4,7 +4,6 @@ interface StripeProvider_props {
4
4
  viewer: StripeProvider_viewer$key;
5
5
  children: JSX.Element;
6
6
  providerType: 'checkout' | 'elements';
7
- requireClientSecret?: boolean;
8
7
  }
9
- declare const StripeProvider: ({ viewer, providerType, children, requireClientSecret, }: StripeProvider_props) => JSX.Element | null;
8
+ declare const StripeProvider: ({ viewer, providerType, children, }: StripeProvider_props) => JSX.Element | null;
10
9
  export default StripeProvider;
@@ -20,8 +20,7 @@ const StripeProvider = _ref => {
20
20
  let {
21
21
  viewer,
22
22
  providerType,
23
- children,
24
- requireClientSecret
23
+ children
25
24
  } = _ref;
26
25
  const data = (0, _reactRelay.useFragment)((_StripeProvider_viewer2.default.hash && _StripeProvider_viewer2.default.hash !== "dd5fd4cdd75d727050b8d1d6f4269fa8" && console.error("The definition of 'StripeProvider_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StripeProvider_viewer2.default), viewer);
27
26
  const stripeKey = data.stripePublishableKey;
@@ -29,6 +28,12 @@ const StripeProvider = _ref => {
29
28
  const stripePromiseRef = (0, _react.useRef)(stripeKey && stripeAccount ? (0, _stripeJs.loadStripe)(stripeKey, {
30
29
  stripeAccount
31
30
  }) : null);
31
+
32
+ // All hooks must be called before any conditional returns (React rules of hooks)
33
+ const environment = (0, _reactRelay.useRelayEnvironment)();
34
+ const {
35
+ stripe: config
36
+ } = (0, _react.useContext)(_ConfigContext.default);
32
37
  let customFontSource;
33
38
  const woffSrc = (_data$settings = data.settings) === null || _data$settings === void 0 ? void 0 : (_data$settings$uiFont = _data$settings.uiFontStyle) === null || _data$settings$uiFont === void 0 ? void 0 : (_data$settings$uiFont2 = _data$settings$uiFont.webfontSources) === null || _data$settings$uiFont2 === void 0 ? void 0 : (_data$settings$uiFont3 = _data$settings$uiFont2.find(source => (source === null || source === void 0 ? void 0 : source.format) === 'woff2')) === null || _data$settings$uiFont3 === void 0 ? void 0 : _data$settings$uiFont3.url;
34
39
  let fontFamily = (_data$settings2 = data.settings) !== null && _data$settings2 !== void 0 && _data$settings2.uiFontStyle ? `${data.settings.uiFontStyle.cssFamily} ${data.settings.uiFontStyle.name}` : undefined;
@@ -46,13 +51,8 @@ const StripeProvider = _ref => {
46
51
  weight: '400'
47
52
  };
48
53
  }
49
-
50
- // the stripe provider may not be rendered, so we have to conditonally call
51
- // useStripe down in the children
52
54
  const clientSecret = ((_data$currentOrder = data.currentOrder) === null || _data$currentOrder === void 0 ? void 0 : (_data$currentOrder$st = _data$currentOrder.stripePaymentIntent) === null || _data$currentOrder$st === void 0 ? void 0 : _data$currentOrder$st.clientSecret) ?? undefined;
53
- if (requireClientSecret && !clientSecret) return children;
54
55
  const customProperties = (0, _utils.parseCustomProperties)((_data$themeConfig = data.themeConfig) === null || _data$themeConfig === void 0 ? void 0 : _data$themeConfig.customProperties);
55
- const environment = (0, _reactRelay.useRelayEnvironment)();
56
56
  const fetchClientSecret = (0, _react.useCallback)(() => {
57
57
  return new Promise((resolve, reject) => {
58
58
  (0, _reactRelay.commitMutation)(environment, {
@@ -73,9 +73,6 @@ const StripeProvider = _ref => {
73
73
  });
74
74
  });
75
75
  }, [environment]);
76
- const {
77
- stripe: config
78
- } = (0, _react.useContext)(_ConfigContext.default);
79
76
  const options = {
80
77
  appearance: config.appearance ?? {
81
78
  theme: 'flat',
@@ -10,6 +10,7 @@ var _TestFontsForm_Query2 = _interopRequireDefault(require("../../__generated__/
10
10
  var _TestFontsFormUpdateCustomerMutation2 = _interopRequireDefault(require("../../__generated__/TestFontsFormUpdateCustomerMutation.graphql"));
11
11
  var _react = _interopRequireWildcard(require("react"));
12
12
  var _reactRelay = require("react-relay");
13
+ var _reactGoogleRecaptcha = _interopRequireDefault(require("react-google-recaptcha"));
13
14
  var _TextField = _interopRequireDefault(require("../TextField"));
14
15
  var _Icons = require("../Icons");
15
16
  var _Checkbox = _interopRequireDefault(require("../Checkbox"));
@@ -32,7 +33,7 @@ const TestFontsDownloading = _ref => {
32
33
  }, "here"), ".")));
33
34
  };
34
35
  const TestFontsFormComponent = _ref2 => {
35
- var _data$viewer$testFont;
36
+ var _data$viewer$settings, _data$viewer$settings2, _data$viewer$testFont;
36
37
  let {
37
38
  data,
38
39
  agreementLabel,
@@ -47,28 +48,36 @@ const TestFontsFormComponent = _ref2 => {
47
48
  const [eulaFail, setEulaFail] = (0, _react.useState)(false);
48
49
  const [submitting, setSubmitting] = (0, _react.useState)(false);
49
50
  const [submitted, setSubmitted] = (0, _react.useState)(false);
51
+ const [recaptchaToken, setRecaptchaToken] = (0, _react.useState)(null);
52
+ const [pendingSubmit, setPendingSubmit] = (0, _react.useState)(false);
50
53
  const downloadForm = (0, _react.useRef)(null);
54
+ const recaptchaRef = (0, _react.useRef)(null);
51
55
  const environment = (0, _reactRelay.useRelayEnvironment)();
52
56
  if (!data.viewer) return null;
53
- const handleSubmit = e => {
54
- e.preventDefault();
55
- if (!eulaAgreed) {
56
- setEulaFail(true);
57
- return;
58
- }
57
+ const recaptchaEnabled = ((_data$viewer$settings = data.viewer.settings) === null || _data$viewer$settings === void 0 ? void 0 : _data$viewer$settings.recaptchaEnabled) ?? false;
58
+ const recaptchaSiteKey = (_data$viewer$settings2 = data.viewer.settings) === null || _data$viewer$settings2 === void 0 ? void 0 : _data$viewer$settings2.recaptchaSiteKey;
59
+ const submitForm = (0, _react.useCallback)(token => {
60
+ setSubmitting(true);
61
+ setPendingSubmit(false);
62
+ setError(null);
59
63
  (0, _reactRelay.commitMutation)(environment, {
60
64
  mutation: updateCustomerMutation,
61
65
  variables: {
62
66
  input: {
63
67
  name,
64
68
  email,
65
- newsletterOptIn
69
+ newsletterOptIn,
70
+ recaptchaToken: token ?? undefined
66
71
  }
67
72
  },
68
73
  onCompleted: (res, errors) => {
69
74
  if (errors && errors.length > 0) {
75
+ var _recaptchaRef$current;
70
76
  setError(errors[0].message);
71
77
  setSubmitting(false);
78
+ // Reset reCAPTCHA on error so user can retry
79
+ (_recaptchaRef$current = recaptchaRef.current) === null || _recaptchaRef$current === void 0 ? void 0 : _recaptchaRef$current.reset();
80
+ setRecaptchaToken(null);
72
81
  } else {
73
82
  var _downloadForm$current;
74
83
  (_downloadForm$current = downloadForm.current) === null || _downloadForm$current === void 0 ? void 0 : _downloadForm$current.submit();
@@ -78,14 +87,47 @@ const TestFontsFormComponent = _ref2 => {
78
87
  }
79
88
  },
80
89
  onError: _ref3 => {
90
+ var _recaptchaRef$current2;
81
91
  let {
82
92
  message
83
93
  } = _ref3;
84
94
  setError(message);
85
95
  setSubmitting(false);
96
+ // Reset reCAPTCHA on error so user can retry
97
+ (_recaptchaRef$current2 = recaptchaRef.current) === null || _recaptchaRef$current2 === void 0 ? void 0 : _recaptchaRef$current2.reset();
98
+ setRecaptchaToken(null);
86
99
  }
87
100
  });
101
+ }, [environment, name, email, newsletterOptIn]);
102
+ const handleRecaptchaChange = (0, _react.useCallback)(token => {
103
+ setRecaptchaToken(token);
104
+ // If we were waiting for a token to submit, do it now
105
+ if (token && pendingSubmit) {
106
+ submitForm(token);
107
+ }
108
+ }, [pendingSubmit, submitForm]);
109
+ const handleRecaptchaExpired = (0, _react.useCallback)(() => {
110
+ setRecaptchaToken(null);
111
+ }, []);
112
+ const handleSubmit = e => {
113
+ e.preventDefault();
114
+ if (!eulaAgreed) {
115
+ setEulaFail(true);
116
+ return;
117
+ }
118
+
119
+ // If reCAPTCHA is enabled but no token, execute it and wait for callback
120
+ if (recaptchaEnabled && recaptchaSiteKey && !recaptchaToken) {
121
+ var _recaptchaRef$current3;
122
+ setPendingSubmit(true);
123
+ setError(null);
124
+ (_recaptchaRef$current3 = recaptchaRef.current) === null || _recaptchaRef$current3 === void 0 ? void 0 : _recaptchaRef$current3.execute();
125
+ return;
126
+ }
127
+ submitForm(recaptchaToken);
88
128
  };
129
+
130
+ // Button is disabled only while actively submitting or waiting for reCAPTCHA
89
131
  const disabled = !eulaAgreed || !email || !name;
90
132
  const settings = data.viewer.settings;
91
133
  const newsletterOptInLabel = settings === null || settings === void 0 ? void 0 : settings.newsletterOptInLabel;
@@ -140,18 +182,24 @@ const TestFontsFormComponent = _ref2 => {
140
182
  dangerouslySetInnerHTML: {
141
183
  __html: newsletterOptInLabel
142
184
  }
143
- })), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("button", {
185
+ })), recaptchaEnabled && recaptchaSiteKey && /*#__PURE__*/_react.default.createElement(_reactGoogleRecaptcha.default, {
186
+ ref: recaptchaRef,
187
+ sitekey: recaptchaSiteKey,
188
+ size: "invisible",
189
+ onChange: handleRecaptchaChange,
190
+ onExpired: handleRecaptchaExpired
191
+ }), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("button", {
144
192
  className: "submit-button",
145
193
  type: "submit",
146
- disabled: submitting || disabled
147
- }, downloadLabel, /*#__PURE__*/_react.default.createElement("span", {
194
+ disabled: submitting || pendingSubmit || disabled
195
+ }, submitting || pendingSubmit ? 'Submitting...' : downloadLabel, /*#__PURE__*/_react.default.createElement("span", {
148
196
  className: "submit-button__arrow"
149
197
  }, " \u2192")))), /*#__PURE__*/_react.default.createElement("form", {
150
198
  action: archiveUrl ?? undefined,
151
199
  ref: downloadForm
152
200
  }));
153
201
  };
154
- const query = (_TestFontsForm_Query2.default.hash && _TestFontsForm_Query2.default.hash !== "b8838ac445fa9addb19af5a2d653e979" && console.error("The definition of 'TestFontsForm_Query' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TestFontsForm_Query2.default);
202
+ const query = (_TestFontsForm_Query2.default.hash && _TestFontsForm_Query2.default.hash !== "cd43f0cacc4dcf01cf94fb1ff97197ca" && console.error("The definition of 'TestFontsForm_Query' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TestFontsForm_Query2.default);
155
203
  function TestFontsFormPreloadedQueryRenderer(_ref4) {
156
204
  let {
157
205
  preloadedQuery,
@@ -168,7 +168,7 @@ function EditableSection(_ref) {
168
168
  }, "Change")));
169
169
  }
170
170
  function StoreModalUnifiedCheckout(_ref2) {
171
- var _order$orderVariableS, _viewer$settings, _viewer$settings2, _viewer$settings3, _order$stripePaymentI, _order$stripePaymentI2;
171
+ var _order$orderVariableS, _viewer$settings, _viewer$settings2, _order$stripePaymentI, _viewer$settings3, _order$stripePaymentI2, _order$stripePaymentI3;
172
172
  let {
173
173
  order: orderKey,
174
174
  viewer: viewerKey,
@@ -176,7 +176,7 @@ function StoreModalUnifiedCheckout(_ref2) {
176
176
  onUpdateOrderVariableSelections
177
177
  } = _ref2;
178
178
  const viewer = (0, _reactRelay.useFragment)((_StoreModalUnifiedCheckout_viewer2.default.hash && _StoreModalUnifiedCheckout_viewer2.default.hash !== "83cc5e6f8d508fb1c2356a72b040f9b0" && console.error("The definition of 'StoreModalUnifiedCheckout_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalUnifiedCheckout_viewer2.default), viewerKey);
179
- const order = (0, _reactRelay.useFragment)((_StoreModalUnifiedCheckout_order2.default.hash && _StoreModalUnifiedCheckout_order2.default.hash !== "ea43fb766c4dd1183b46ddf449aa85bd" && console.error("The definition of 'StoreModalUnifiedCheckout_order' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalUnifiedCheckout_order2.default), orderKey);
179
+ const order = (0, _reactRelay.useFragment)((_StoreModalUnifiedCheckout_order2.default.hash && _StoreModalUnifiedCheckout_order2.default.hash !== "7c5503876a562732ebaead9b2ae97180" && console.error("The definition of 'StoreModalUnifiedCheckout_order' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalUnifiedCheckout_order2.default), orderKey);
180
180
 
181
181
  // Check if order has a country-type order variable with a different country than detected
182
182
  // This is important for order snapshot/quote functionality where a quote was created for
@@ -664,7 +664,7 @@ function StoreModalUnifiedCheckout(_ref2) {
664
664
  "data-disabled": false
665
665
  }, /*#__PURE__*/_react.default.createElement("form", {
666
666
  onSubmit: handleComplete
667
- }, !zeroOrder && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("h3", {
667
+ }, !zeroOrder && ((_order$stripePaymentI = order.stripePaymentIntent) === null || _order$stripePaymentI === void 0 ? void 0 : _order$stripePaymentI.clientSecret) && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("h3", {
668
668
  className: "store-modal__cart__checkout-section-title"
669
669
  }, "Payment information", /*#__PURE__*/_react.default.createElement("span", {
670
670
  className: "store-modal__cart__checkout-section-subtitle"
@@ -708,7 +708,7 @@ function StoreModalUnifiedCheckout(_ref2) {
708
708
  type: "submit",
709
709
  className: "store-modal__cart__button",
710
710
  disabled: completing || !acceptedEULA
711
- }, completing ? 'Submitting...' : zeroOrder ? 'Complete order' : /*#__PURE__*/_react.default.createElement("span", null, "Pay", ' ', (_order$stripePaymentI = order.stripePaymentIntent) !== null && _order$stripePaymentI !== void 0 && _order$stripePaymentI.amount && (_order$stripePaymentI2 = order.stripePaymentIntent) !== null && _order$stripePaymentI2 !== void 0 && _order$stripePaymentI2.currency ? /*#__PURE__*/_react.default.createElement(_Price.Price, {
711
+ }, completing ? 'Submitting...' : zeroOrder ? 'Complete order' : /*#__PURE__*/_react.default.createElement("span", null, "Pay", ' ', (_order$stripePaymentI2 = order.stripePaymentIntent) !== null && _order$stripePaymentI2 !== void 0 && _order$stripePaymentI2.amount && (_order$stripePaymentI3 = order.stripePaymentIntent) !== null && _order$stripePaymentI3 !== void 0 && _order$stripePaymentI3.currency ? /*#__PURE__*/_react.default.createElement(_Price.Price, {
712
712
  price: {
713
713
  amount: order.stripePaymentIntent.amount,
714
714
  currency: order.stripePaymentIntent.currency
@@ -8,6 +8,9 @@ exports.responseCache = exports.networkFetch = void 0;
8
8
  exports.useCurrentEnvironment = useCurrentEnvironment;
9
9
  var _react = require("react");
10
10
  var _relayRuntime = require("relay-runtime");
11
+ var _package = require("../../package.json");
12
+ // @ts-ignore - JSON import
13
+
11
14
  const IS_SERVER = typeof window === typeof undefined;
12
15
  const FONTDUE_URL = typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FONTDUE_URL : undefined;
13
16
  if (IS_SERVER && (FONTDUE_URL == null || FONTDUE_URL === '')) {
@@ -25,7 +28,8 @@ function createNetworkFetch(options) {
25
28
  headers: {
26
29
  Accept: 'application/json',
27
30
  'Content-Type': 'application/json',
28
- 'fontdue-stripe-integration': STRIPE_INTEGRATION ?? ((options === null || options === void 0 ? void 0 : options.stripeIntegration) || 'dynamic')
31
+ 'fontdue-stripe-integration': STRIPE_INTEGRATION ?? ((options === null || options === void 0 ? void 0 : options.stripeIntegration) || 'dynamic'),
32
+ 'fontdue-client-version': _package.version
29
33
  },
30
34
  body: JSON.stringify({
31
35
  query: request.text,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "2.17.2",
3
+ "version": "2.18.2",
4
4
  "scripts": {
5
5
  "build": "npm run relay && run-p build-js build-css build-ts",
6
6
  "build-js": "babel src --out-dir dist --extensions .ts,.tsx,.js,.jsx",
@@ -30,6 +30,7 @@
30
30
  "fontfaceobserver": "^2.0.13",
31
31
  "react-device-detect": "^2.2.3",
32
32
  "react-error-boundary": "^4.1.2",
33
+ "react-google-recaptcha": "^3.1.0",
33
34
  "react-redux": "^9",
34
35
  "react-relay": "^20.0.0",
35
36
  "redux": "^5.0.0",
@@ -48,6 +49,7 @@
48
49
  "@types/node": "^24.1.0",
49
50
  "@types/react": "19.0.0",
50
51
  "@types/react-dom": "19.0.0",
52
+ "@types/react-google-recaptcha": "^2.1.8",
51
53
  "@types/react-relay": "^18.0.0",
52
54
  "@types/relay-runtime": "^19.0.0",
53
55
  "@types/uuid": "^8.3.3",