fontdue-js 2.17.1 → 2.18.1

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 (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +2 -1
  3. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +1 -1
  4. package/dist/__generated__/NewsletterSignupQuery.graphql.d.ts +3 -1
  5. package/dist/__generated__/NewsletterSignupQuery.graphql.js +16 -4
  6. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.d.ts +2 -1
  7. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.js +1 -1
  8. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
  9. package/dist/__generated__/StoreModalProductQuery.graphql.js +112 -79
  10. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.d.ts +1 -1
  11. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.js +34 -4
  12. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.d.ts +7 -1
  13. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.js +32 -2
  14. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.d.ts +2 -1
  15. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.js +1 -1
  16. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +2 -1
  17. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +1 -1
  18. package/dist/__generated__/TestFontsForm_Query.graphql.d.ts +3 -1
  19. package/dist/__generated__/TestFontsForm_Query.graphql.js +16 -4
  20. package/dist/components/CharacterViewer/index.js +22 -2
  21. package/dist/components/ConfigContext.d.ts +9 -4
  22. package/dist/components/NewsletterSignup/index.js +62 -19
  23. package/dist/components/Select/index.d.ts +1 -1
  24. package/dist/components/StoreModal/StoreModalCart.js +3 -2
  25. package/dist/components/StoreModalProductSummary/index.js +21 -9
  26. package/dist/components/TestFontsForm/index.js +60 -12
  27. package/dist/components/TypeTester/useFeaturesData.d.ts +3 -3
  28. package/dist/components/TypeTester/useTypeTesterStyler.d.ts +0 -1
  29. package/dist/fontdue.css +1 -1
  30. package/dist/hooks.d.ts +0 -1
  31. package/dist/relay/environment.js +5 -1
  32. package/package.json +4 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @generated SignedSource<<7183fbedeafbebe5a87197cb023bfc40>>
2
+ * @generated SignedSource<<77143b56d76cde92647b41b80c14a88a>>
3
3
  * @lightSyntaxTransform
4
4
  * @nogrep
5
5
  */
@@ -38,6 +38,12 @@ export type StoreModalProductSummary_viewer$data = {
38
38
  readonly " $fragmentSpreads": FragmentRefs<"Price_price">;
39
39
  } | null;
40
40
  } | null;
41
+ readonly settings: {
42
+ readonly stripeTaxSettings: {
43
+ readonly taxBehavior: string | null;
44
+ } | null;
45
+ readonly taxSystem: string;
46
+ } | null;
41
47
  readonly taxCollections: ReadonlyArray<{
42
48
  readonly inclusive: boolean;
43
49
  readonly taxName: string;
@@ -7,7 +7,7 @@ exports.default = void 0;
7
7
  var _StoreModalProductSummaryRefetchQuery = _interopRequireDefault(require("./StoreModalProductSummaryRefetchQuery.graphql"));
8
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
9
  /**
10
- * @generated SignedSource<<7183fbedeafbebe5a87197cb023bfc40>>
10
+ * @generated SignedSource<<77143b56d76cde92647b41b80c14a88a>>
11
11
  * @lightSyntaxTransform
12
12
  * @nogrep
13
13
  */
@@ -76,6 +76,36 @@ const node = function () {
76
76
  },
77
77
  "name": "StoreModalProductSummary_viewer",
78
78
  "selections": [{
79
+ "alias": null,
80
+ "args": null,
81
+ "concreteType": "Settings",
82
+ "kind": "LinkedField",
83
+ "name": "settings",
84
+ "plural": false,
85
+ "selections": [{
86
+ "alias": null,
87
+ "args": null,
88
+ "kind": "ScalarField",
89
+ "name": "taxSystem",
90
+ "storageKey": null
91
+ }, {
92
+ "alias": null,
93
+ "args": null,
94
+ "concreteType": "StripeTaxSettings",
95
+ "kind": "LinkedField",
96
+ "name": "stripeTaxSettings",
97
+ "plural": false,
98
+ "selections": [{
99
+ "alias": null,
100
+ "args": null,
101
+ "kind": "ScalarField",
102
+ "name": "taxBehavior",
103
+ "storageKey": null
104
+ }],
105
+ "storageKey": null
106
+ }],
107
+ "storageKey": null
108
+ }, {
79
109
  "alias": null,
80
110
  "args": null,
81
111
  "concreteType": "TaxCollection",
@@ -203,6 +233,6 @@ const node = function () {
203
233
  "abstractKey": null
204
234
  };
205
235
  }();
206
- node.hash = "e21762fcdb76ac693bcdf7b98effc40b";
236
+ node.hash = "d95602902697c0bb7ca4be2089649198";
207
237
  var _default = node;
208
238
  exports.default = _default;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @generated SignedSource<<d68089e8bc657b1cc594c4cbc03a25bc>>
2
+ * @generated SignedSource<<d997fbe505c7bf6f8ab6e05db335721b>>
3
3
  * @lightSyntaxTransform
4
4
  * @nogrep
5
5
  */
@@ -9,6 +9,7 @@ export type UpdateCustomerInput = {
9
9
  email?: string | null;
10
10
  name?: string | null;
11
11
  newsletterOptIn?: boolean | null;
12
+ recaptchaToken?: string | null;
12
13
  };
13
14
  export type StoreModalUnifiedCheckoutUpdateCustomerMutation$variables = {
14
15
  input: UpdateCustomerInput;
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  /**
8
- * @generated SignedSource<<d68089e8bc657b1cc594c4cbc03a25bc>>
8
+ * @generated SignedSource<<d997fbe505c7bf6f8ab6e05db335721b>>
9
9
  * @lightSyntaxTransform
10
10
  * @nogrep
11
11
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @generated SignedSource<<955383975fd3e6c3d1aead410f172705>>
2
+ * @generated SignedSource<<410f5861dc700f9d280fe855abbc8c0a>>
3
3
  * @lightSyntaxTransform
4
4
  * @nogrep
5
5
  */
@@ -9,6 +9,7 @@ export type UpdateCustomerInput = {
9
9
  email?: string | null;
10
10
  name?: string | null;
11
11
  newsletterOptIn?: boolean | null;
12
+ recaptchaToken?: string | null;
12
13
  };
13
14
  export type TestFontsFormUpdateCustomerMutation$variables = {
14
15
  input: UpdateCustomerInput;
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  /**
8
- * @generated SignedSource<<955383975fd3e6c3d1aead410f172705>>
8
+ * @generated SignedSource<<410f5861dc700f9d280fe855abbc8c0a>>
9
9
  * @lightSyntaxTransform
10
10
  * @nogrep
11
11
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @generated SignedSource<<2b508e476eab7361bd3e3adc71e33eec>>
2
+ * @generated SignedSource<<ee5a584fb3013fee58346747b616f67d>>
3
3
  * @lightSyntaxTransform
4
4
  * @nogrep
5
5
  */
@@ -10,6 +10,8 @@ export type TestFontsForm_Query$data = {
10
10
  readonly settings: {
11
11
  readonly eulaAgreementLabel: string | null;
12
12
  readonly newsletterOptInLabel: string | null;
13
+ readonly recaptchaEnabled: boolean;
14
+ readonly recaptchaSiteKey: string | null;
13
15
  } | null;
14
16
  readonly testFonts: {
15
17
  readonly archiveUrl: string | null;
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  /**
8
- * @generated SignedSource<<2b508e476eab7361bd3e3adc71e33eec>>
8
+ * @generated SignedSource<<ee5a584fb3013fee58346747b616f67d>>
9
9
  * @lightSyntaxTransform
10
10
  * @nogrep
11
11
  */
@@ -39,6 +39,18 @@ const node = function () {
39
39
  "kind": "ScalarField",
40
40
  "name": "newsletterOptInLabel",
41
41
  "storageKey": "newsletterOptInLabel(format:\"HTML\")"
42
+ }, {
43
+ "alias": null,
44
+ "args": null,
45
+ "kind": "ScalarField",
46
+ "name": "recaptchaEnabled",
47
+ "storageKey": null
48
+ }, {
49
+ "alias": null,
50
+ "args": null,
51
+ "kind": "ScalarField",
52
+ "name": "recaptchaSiteKey",
53
+ "storageKey": null
42
54
  }],
43
55
  "storageKey": null
44
56
  },
@@ -100,15 +112,15 @@ const node = function () {
100
112
  }]
101
113
  },
102
114
  "params": {
103
- "cacheID": "d7935ae01719384130b5923be45bb47a",
115
+ "cacheID": "328cab794a68cbe666374e901da4d26b",
104
116
  "id": null,
105
117
  "metadata": {},
106
118
  "name": "TestFontsForm_Query",
107
119
  "operationKind": "query",
108
- "text": "query TestFontsForm_Query {\n viewer {\n settings {\n eulaAgreementLabel(format: HTML)\n newsletterOptInLabel(format: HTML)\n }\n testFonts {\n archiveUrl\n }\n id\n }\n}\n"
120
+ "text": "query TestFontsForm_Query {\n viewer {\n settings {\n eulaAgreementLabel(format: HTML)\n newsletterOptInLabel(format: HTML)\n recaptchaEnabled\n recaptchaSiteKey\n }\n testFonts {\n archiveUrl\n }\n id\n }\n}\n"
109
121
  }
110
122
  };
111
123
  }();
112
- node.hash = "b8838ac445fa9addb19af5a2d653e979";
124
+ node.hash = "cd43f0cacc4dcf01cf94fb1ff97197ca";
113
125
  var _default = node;
114
126
  exports.default = _default;
@@ -159,6 +159,14 @@ function areCombiningChars(input) {
159
159
  }
160
160
  return true;
161
161
  }
162
+
163
+ // Arabic positional features that don't render correctly in Safari
164
+ // These require text shaping that Safari doesn't properly support with ZWJ
165
+ const UNSUPPORTED_POSITIONAL_FEATURES = ['init', 'medi', 'fina'];
166
+ function hasUnsupportedPositionalFeatures(features) {
167
+ if (!features) return false;
168
+ return features.some(f => UNSUPPORTED_POSITIONAL_FEATURES.includes(f));
169
+ }
162
170
  function CharacterViewerComponent(_ref3) {
163
171
  let {
164
172
  collection: collectionKey
@@ -277,15 +285,27 @@ function CharacterViewerComponent(_ref3) {
277
285
  const groups = (0, _react.useMemo)(() => {
278
286
  var _collection$glyphGrou;
279
287
  return (_collection$glyphGrou = collection.glyphGroups) === null || _collection$glyphGrou === void 0 ? void 0 : _collection$glyphGrou.map(_ref5 => {
288
+ var _characterSets$filter;
280
289
  let {
281
290
  name,
282
291
  characterSets
283
292
  } = _ref5;
284
293
  return {
285
294
  name,
286
- characterSets: characterSets === null || characterSets === void 0 ? void 0 : characterSets.map(charSet => flattenCharacterList(charSet, fontStyle.glyphNames)).filter(_utils.notEmpty)
295
+ characterSets: characterSets
296
+ // Filter out character sets with Arabic positional features (init, medi, fina)
297
+ // These don't render correctly in Safari due to text shaping limitations
298
+ === null || characterSets
299
+ // Filter out character sets with Arabic positional features (init, medi, fina)
300
+ // These don't render correctly in Safari due to text shaping limitations
301
+ === void 0 ? void 0 : (_characterSets$filter = characterSets
302
+ // Filter out character sets with Arabic positional features (init, medi, fina)
303
+ // These don't render correctly in Safari due to text shaping limitations
304
+ .filter(charSet => !hasUnsupportedPositionalFeatures(charSet.features))) === null || _characterSets$filter === void 0 ? void 0 : _characterSets$filter.map(charSet => flattenCharacterList(charSet, fontStyle.glyphNames)).filter(_utils.notEmpty)
287
305
  };
288
- });
306
+ })
307
+ // Filter out groups that have no character sets left after filtering
308
+ .filter(group => group.characterSets && group.characterSets.length > 0);
289
309
  }, [collection, fontStyle]);
290
310
  return /*#__PURE__*/_react.default.createElement("div", {
291
311
  className: "character-viewer",
@@ -8,8 +8,13 @@ interface FormConfig {
8
8
  interface StripeConfig {
9
9
  appearance?: Appearance;
10
10
  }
11
+ interface SegmentConfig {
12
+ writeKey?: string;
13
+ integrations?: Record<string, boolean>;
14
+ }
11
15
  interface TrackingConfig {
12
16
  enabled?: boolean;
17
+ segment?: SegmentConfig;
13
18
  }
14
19
  export interface Config {
15
20
  form?: FormConfig;
@@ -38,10 +43,10 @@ export declare const makeConfig: (config?: Config) => {
38
43
  buttonLabel: string;
39
44
  interactionStyle: "select" | "panel";
40
45
  columns: {
41
- features: (string | {
46
+ features: ({
42
47
  code: string;
43
48
  name: string;
44
- })[];
49
+ } | string)[];
45
50
  range: number;
46
51
  }[] | undefined;
47
52
  selectionStyle: "checkbox" | "bullet";
@@ -100,10 +105,10 @@ declare const _default: React.Context<{
100
105
  buttonLabel: string;
101
106
  interactionStyle: "select" | "panel";
102
107
  columns: {
103
- features: (string | {
108
+ features: ({
104
109
  code: string;
105
110
  name: string;
106
- })[];
111
+ } | string)[];
107
112
  range: number;
108
113
  }[] | undefined;
109
114
  selectionStyle: "checkbox" | "bullet";
@@ -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
  }
@@ -8,5 +8,5 @@ interface Select_props {
8
8
  }[];
9
9
  }
10
10
  type SelectHTMLProps = React.DetailedHTMLProps<React.SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
11
- declare const Select: ({ options, value, nullText, ...rest }: Select_props & Omit<SelectHTMLProps, 'value'>) => React.JSX.Element;
11
+ declare const Select: ({ options, value, nullText, ...rest }: Select_props & Omit<SelectHTMLProps, "value">) => React.JSX.Element;
12
12
  export default Select;
@@ -35,7 +35,7 @@ const StoreModalCart = _ref => {
35
35
  body: order && orderHasItems ? /*#__PURE__*/_react.default.createElement(_CartState.default, {
36
36
  order: order
37
37
  }, _ref2 => {
38
- var _data$viewer, _data$viewer$currentO, _data$viewer$currentO2, _order$orderItems2, _order$total, _data$viewer2, _data$viewer2$setting;
38
+ var _data$viewer, _data$viewer$currentO, _data$viewer$currentO2, _order$orderItems2, _order$total, _data$viewer2, _data$viewer2$setting, _data$viewer3, _data$viewer3$setting;
39
39
  let {
40
40
  onUpdateOrderVariableSelections,
41
41
  onRemoveDiscount,
@@ -71,7 +71,8 @@ const StoreModalCart = _ref => {
71
71
  }),
72
72
  checkout: /*#__PURE__*/_react.default.createElement(_StripeProvider.default, {
73
73
  viewer: data.viewer,
74
- providerType: "elements"
74
+ providerType: "elements",
75
+ requireClientSecret: ((_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.taxSystem) === 'fontdue'
75
76
  }, /*#__PURE__*/_react.default.createElement(_StoreModalUnifiedCheckout.default, {
76
77
  viewer: data.viewer,
77
78
  order: order,
@@ -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 !== "d95602902697c0bb7ca4be2089649198" && 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));
@@ -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,
@@ -7,11 +7,11 @@ declare const useFeaturesData: ({ fontStyle }: useFeatures_props) => {
7
7
  [feature: string]: string;
8
8
  };
9
9
  fontFeatures: {
10
- readonly stylisticSetNames: readonly {
10
+ readonly stylisticSetNames: ReadonlyArray<{
11
11
  readonly featureName: string;
12
12
  readonly humanName: string;
13
- }[];
14
- readonly supportedFeatures: readonly string[];
13
+ }>;
14
+ readonly supportedFeatures: ReadonlyArray<string>;
15
15
  };
16
16
  " $fragmentType": "useFeaturesData_fontStyle";
17
17
  };
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { EditorState } from 'draft-js';
3
2
  import { Alignment } from './types';
4
3
  import { VariableSettings } from '../../utils';