fontdue-js 3.7.0 → 3.9.0

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 (135) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +8 -5
  3. package/dist/__generated__/AddToCartBannerQuery.graphql.d.ts +1 -1
  4. package/dist/__generated__/AddToCartBannerQuery.graphql.js +16 -10
  5. package/dist/__generated__/AddToCartBanner_item.graphql.d.ts +4 -1
  6. package/dist/__generated__/AddToCartBanner_item.graphql.js +7 -2
  7. package/dist/__generated__/AddToCartBanner_order.graphql.d.ts +4 -1
  8. package/dist/__generated__/AddToCartBanner_order.graphql.js +6 -1
  9. package/dist/__generated__/BuyButtonIDQuery.graphql.d.ts +3 -3
  10. package/dist/__generated__/BuyButtonIDQuery.graphql.js +31 -16
  11. package/dist/__generated__/BuyButtonPackIDQuery.graphql.d.ts +21 -0
  12. package/dist/__generated__/BuyButtonPackIDQuery.graphql.js +129 -0
  13. package/dist/__generated__/BuyButtonPackSlugQuery.graphql.d.ts +25 -0
  14. package/dist/__generated__/BuyButtonPackSlugQuery.graphql.js +167 -0
  15. package/dist/__generated__/BuyButtonSlugQuery.graphql.d.ts +3 -3
  16. package/dist/__generated__/BuyButtonSlugQuery.graphql.js +45 -17
  17. package/dist/__generated__/BuyButton_product.graphql.d.ts +27 -0
  18. package/dist/__generated__/BuyButton_product.graphql.js +72 -0
  19. package/dist/__generated__/CartItemProduct_product.graphql.d.ts +1 -1
  20. package/dist/__generated__/CartItemProduct_product.graphql.js +7 -2
  21. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.d.ts +1 -1
  22. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.js +26 -20
  23. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.d.ts +1 -1
  24. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.js +26 -20
  25. package/dist/__generated__/CartOrderUpdateMutation.graphql.d.ts +1 -1
  26. package/dist/__generated__/CartOrderUpdateMutation.graphql.js +26 -20
  27. package/dist/__generated__/CartQuery.graphql.d.ts +1 -1
  28. package/dist/__generated__/CartQuery.graphql.js +26 -20
  29. package/dist/__generated__/CartStateUpdateMutation.graphql.d.ts +1 -1
  30. package/dist/__generated__/CartStateUpdateMutation.graphql.js +26 -20
  31. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +1 -1
  32. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +26 -20
  33. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.d.ts +1 -1
  34. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.js +26 -20
  35. package/dist/__generated__/CollectionAa_Query.graphql.d.ts +1 -1
  36. package/dist/__generated__/CollectionAa_Query.graphql.js +20 -14
  37. package/dist/__generated__/CollectionAa_product.graphql.d.ts +7 -1
  38. package/dist/__generated__/CollectionAa_product.graphql.js +22 -16
  39. package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.d.ts +3 -3
  40. package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.js +9 -9
  41. package/dist/__generated__/NodePasswordFormElementPackSlugQuery.graphql.d.ts +25 -0
  42. package/dist/__generated__/NodePasswordFormElementPackSlugQuery.graphql.js +108 -0
  43. package/dist/__generated__/PrecartAddToCartMutation.graphql.d.ts +1 -1
  44. package/dist/__generated__/PrecartAddToCartMutation.graphql.js +32 -26
  45. package/dist/__generated__/StoreModalCartQuery.graphql.d.ts +1 -1
  46. package/dist/__generated__/StoreModalCartQuery.graphql.js +37 -31
  47. package/dist/__generated__/StoreModalIndexPackItem_fontPack.graphql.d.ts +23 -0
  48. package/dist/__generated__/StoreModalIndexPackItem_fontPack.graphql.js +58 -0
  49. package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +9 -1
  50. package/dist/__generated__/StoreModalIndexQuery.graphql.js +174 -110
  51. package/dist/__generated__/StoreModalPackButton_fontPack.graphql.d.ts +33 -0
  52. package/dist/__generated__/StoreModalPackButton_fontPack.graphql.js +119 -0
  53. package/dist/__generated__/StoreModalPack_fontPack.graphql.d.ts +45 -0
  54. package/dist/__generated__/StoreModalPack_fontPack.graphql.js +150 -0
  55. package/dist/__generated__/StoreModalProductContent_collection.graphql.d.ts +3 -2
  56. package/dist/__generated__/StoreModalProductContent_collection.graphql.js +36 -19
  57. package/dist/__generated__/{StoreModalProductLicenseSelection_collection.graphql.d.ts → StoreModalProductLicenseSelection_licenseable.graphql.d.ts} +6 -6
  58. package/dist/__generated__/{StoreModalProductLicenseSelection_collection.graphql.js → StoreModalProductLicenseSelection_licenseable.graphql.js} +5 -5
  59. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
  60. package/dist/__generated__/StoreModalProductQuery.graphql.js +159 -103
  61. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.d.ts +1 -1
  62. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.js +184 -128
  63. package/dist/__generated__/StoreModalProductSlugQuery.graphql.d.ts +5 -1
  64. package/dist/__generated__/StoreModalProductSlugQuery.graphql.js +248 -171
  65. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.d.ts +1 -1
  66. package/dist/__generated__/StoreModalProductSummaryRefetchQuery.graphql.js +9 -4
  67. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.d.ts +7 -1
  68. package/dist/__generated__/StoreModalProductSummary_viewer.graphql.js +7 -2
  69. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +1 -1
  70. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +26 -20
  71. package/dist/__generated__/TestFontsForm_Query.graphql.d.ts +14 -2
  72. package/dist/__generated__/TestFontsForm_Query.graphql.js +76 -18
  73. package/dist/__generated__/productState_Query.graphql.d.ts +99 -1
  74. package/dist/__generated__/productState_Query.graphql.js +43 -4
  75. package/dist/__generated__/productState_fontPack.graphql.d.ts +111 -0
  76. package/dist/__generated__/productState_fontPack.graphql.js +117 -0
  77. package/dist/__tests__/bundleSelectionOrder.test.js +2 -0
  78. package/dist/__tests__/collectionBundleSelection.test.js +2 -0
  79. package/dist/__tests__/fontPackSelection.test.js +435 -0
  80. package/dist/__tests__/licenseExclusions.test.js +2 -0
  81. package/dist/__tests__/storeModalPackButton.test.js +102 -0
  82. package/dist/__tests__/storeModalPackUnlisted.test.js +112 -0
  83. package/dist/__tests__/storeModalRoutes.test.js +9 -0
  84. package/dist/__tests__/testFontsFamilyChoice.test.js +150 -0
  85. package/dist/components/AddToCartBanner/index.js +1 -1
  86. package/dist/components/BuyButton/index.d.ts +29 -9
  87. package/dist/components/BuyButton/index.js +123 -26
  88. package/dist/components/BuyButton/index.server.d.ts +4 -2
  89. package/dist/components/BuyButton/index.server.js +24 -3
  90. package/dist/components/Cart/CartItem/CartItemProduct.js +3 -3
  91. package/dist/components/CollectionAa/index.js +2 -2
  92. package/dist/components/ConfigContext.d.ts +11 -0
  93. package/dist/components/ConfigContext.js +7 -1
  94. package/dist/components/NodePasswordForm/NodePasswordFormElement.d.ts +1 -1
  95. package/dist/components/NodePasswordForm/NodePasswordFormElement.js +45 -21
  96. package/dist/components/NodePasswordForm/index.d.ts +7 -4
  97. package/dist/components/NodePasswordForm/index.js +6 -6
  98. package/dist/components/Root/productState.js +57 -7
  99. package/dist/components/StoreModal/StoreModalFamily.d.ts +7 -0
  100. package/dist/components/StoreModal/StoreModalFamily.js +7 -5
  101. package/dist/components/StoreModal/StoreModalIndex.js +13 -3
  102. package/dist/components/StoreModal/StoreModalIndexEntry.d.ts +13 -0
  103. package/dist/components/StoreModal/StoreModalIndexEntry.js +43 -0
  104. package/dist/components/StoreModal/StoreModalIndexItem.js +7 -30
  105. package/dist/components/StoreModal/StoreModalIndexPackItem.d.ts +7 -0
  106. package/dist/components/StoreModal/StoreModalIndexPackItem.js +21 -0
  107. package/dist/components/StoreModal/StoreModalPack.d.ts +9 -0
  108. package/dist/components/StoreModal/StoreModalPack.js +79 -0
  109. package/dist/components/StoreModal/StoreModalPackButton.d.ts +8 -0
  110. package/dist/components/StoreModal/StoreModalPackButton.js +53 -0
  111. package/dist/components/StoreModal/StoreModalProduct.js +38 -15
  112. package/dist/components/StoreModalProductLicenseSelection/index.d.ts +3 -3
  113. package/dist/components/StoreModalProductLicenseSelection/index.js +6 -6
  114. package/dist/components/StoreModalProductSummary/index.js +1 -1
  115. package/dist/components/TestFontsForm/TestFontsFormElement.d.ts +4 -1
  116. package/dist/components/TestFontsForm/TestFontsFormElement.js +11 -3
  117. package/dist/components/TestFontsForm/index.d.ts +6 -3
  118. package/dist/components/TestFontsForm/index.js +82 -13
  119. package/dist/components/TestFontsForm/index.server.js +2 -1
  120. package/dist/components/TestFontsForm/variables.d.ts +8 -0
  121. package/dist/components/TestFontsForm/variables.js +22 -0
  122. package/dist/components/elements/StoreModalFamily/index.d.ts +1 -1
  123. package/dist/components/elements/StoreModalFamily/index.js +1 -1
  124. package/dist/fontdue.css +13 -0
  125. package/dist/reducer.d.ts +7 -0
  126. package/dist/reducer.js +9 -0
  127. package/dist/selection.d.ts +5 -1
  128. package/dist/selection.js +61 -16
  129. package/dist/storeModalRoutes.js +5 -4
  130. package/dist/version.js +1 -1
  131. package/package.json +1 -1
  132. package/dist/__generated__/BuyButton_collection.graphql.d.ts +0 -25
  133. package/dist/__generated__/BuyButton_collection.graphql.js +0 -49
  134. package/fontdue.css +0 -2733
  135. package/fontdue.css.map +0 -1
@@ -6,6 +6,7 @@ import React, { Fragment, useCallback, useContext, useEffect, useMemo } from 're
6
6
  import { graphql, useRelayEnvironment, useFragment, usePreloadedQuery, useRefetchableFragment } from 'react-relay';
7
7
  import { shallowEqual, useDispatch, useSelector, useStore } from 'react-redux';
8
8
  import StoreModalFamily from './StoreModalFamily.js';
9
+ import StoreModalPack from './StoreModalPack.js';
9
10
  import StoreModalProductSummary from '../StoreModalProductSummary/index.js';
10
11
  import StoreModalPageContainer from '../elements/StoreModalPageContainer/index.js';
11
12
  import StoreModalProductLicenseSelection from '../StoreModalProductLicenseSelection/index.js';
@@ -18,16 +19,16 @@ import StoreModalLicenseeIsBillingSelection from './StoreModalLicenseeIsBillingS
18
19
  import { useLicenseAndOrderVariables } from '../../hooks/useLicenseAndOrderVariables.js';
19
20
  import { trackProductViewed } from '../Cart/storefrontEvents.js';
20
21
 
21
- // A product is addressed by collection id or by slug — the same two
22
- // identifiers `BuyButton` takes. Two queries rather than one with `@skip`,
22
+ // A product – a collection or a font pack – is addressed by id or by slug,
23
+ // the same two identifiers `BuyButton` takes. Two queries rather than one with `@skip`,
23
24
  // so each carries only the selection it needs; both hand the same collection
24
25
  // and viewer to `StoreModalProductContent` below.
25
26
  export const productIdQuery = (_StoreModalProductQuery.hash && _StoreModalProductQuery.hash !== "e3ca6ec04f2df35aa68baf439c70cf42" && console.error("The definition of 'StoreModalProductQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductQuery);
26
27
 
27
- // No `@required` on `fontCollection` here, unlike BuyButton: a slug naming
28
- // nothing — or naming an article rather than a collection — should land on
29
- // StoreModalProductContent's "Collection not found", not throw.
30
- export const productSlugQuery = (_StoreModalProductSlugQuery.hash && _StoreModalProductSlugQuery.hash !== "f0bb35ed9f88ad214b40fd1f3b92fed0" && console.error("The definition of 'StoreModalProductSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductSlugQuery);
28
+ // No `@required` on `fontCollection` / `fontPack` here, unlike BuyButton: a
29
+ // slug naming nothing — or naming an article rather than a product — should
30
+ // land on StoreModalProductContent's "Product not found", not throw.
31
+ export const productSlugQuery = (_StoreModalProductSlugQuery.hash && _StoreModalProductSlugQuery.hash !== "bd2c1bf7b7b154501ea47213af58725e" && console.error("The definition of 'StoreModalProductSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductSlugQuery);
31
32
  const productViewerFragment = (_StoreModalProductViewer.hash && _StoreModalProductViewer.hash !== "3d2fc7da461822a44bb26e9b3f1715fb" && console.error("The definition of 'StoreModalProductViewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductViewer);
32
33
  const StoreModalProductContent = _ref => {
33
34
  var _config$storeModal, _viewer$orderVariable, _viewer$settings, _viewer$settings2, _viewer$settings3, _viewer$settings4, _viewer$settings5, _viewer$settings6, _viewer$settings7, _viewer$settings8;
@@ -38,8 +39,11 @@ const StoreModalProductContent = _ref => {
38
39
  const config = useContext(ConfigContext);
39
40
  const environment = useRelayEnvironment();
40
41
  const viewer = useFragment(productViewerFragment, viewerRef);
41
- const [data, refetch] = useRefetchableFragment((_StoreModalProductContent_collection.hash && _StoreModalProductContent_collection.hash !== "f7a9e591b5e1596f4d518fc5d9d46e91" && console.error("The definition of 'StoreModalProductContent_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductContent_collection), collectionRef ?? null);
42
- const collection = data;
42
+ const [data, refetch] = useRefetchableFragment((_StoreModalProductContent_collection.hash && _StoreModalProductContent_collection.hash !== "c8ce87bca2032637d922f535fc168f56" && console.error("The definition of 'StoreModalProductContent_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductContent_collection), collectionRef ?? null);
43
+
44
+ // The product page sells a collection or a font pack; anything else the id
45
+ // resolves to is not a product.
46
+ const collection = (data === null || data === void 0 ? void 0 : data.__typename) === 'FontCollection' || (data === null || data === void 0 ? void 0 : data.__typename) === 'FontPack' ? data : null;
43
47
  const refetchVariables = useMemo(() => collection !== null && collection !== void 0 && collection.id ? {
44
48
  id: collection.id
45
49
  } : undefined, [collection === null || collection === void 0 ? void 0 : collection.id]);
@@ -61,6 +65,21 @@ const StoreModalProductContent = _ref => {
61
65
  useEffect(() => {
62
66
  if (collection !== null && collection !== void 0 && collection.id) populateProductState(store, environment, collection.id);
63
67
  }, [collection === null || collection === void 0 ? void 0 : collection.id]);
68
+
69
+ // The page reports the product it shows, whether the route named it by id or
70
+ // by slug, so a selection made on it stays within what it shows
71
+ // (selection.ts).
72
+ useEffect(() => {
73
+ if (!(collection !== null && collection !== void 0 && collection.id)) return;
74
+ dispatch({
75
+ type: 'STORE_MODAL_VIEW_PRODUCT',
76
+ productId: collection.id
77
+ });
78
+ return () => dispatch({
79
+ type: 'STORE_MODAL_VIEW_PRODUCT',
80
+ productId: null
81
+ });
82
+ }, [collection === null || collection === void 0 ? void 0 : collection.id]);
64
83
  useEffect(() => {
65
84
  if (collection !== null && collection !== void 0 && collection.id) trackProductViewed(environment, collection.id);
66
85
  }, [collection === null || collection === void 0 ? void 0 : collection.id]);
@@ -87,7 +106,7 @@ const StoreModalProductContent = _ref => {
87
106
  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));
88
107
  if (!collection) {
89
108
  return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
90
- body: 'Collection not found'
109
+ body: 'Product not found'
91
110
  });
92
111
  }
93
112
  const selectLicenseeLabel = /*#__PURE__*/React.createElement("div", {
@@ -135,7 +154,7 @@ const StoreModalProductContent = _ref => {
135
154
  // styles – but stays in the product state, so selecting its siblings can
136
155
  // still upgrade to it (an unlisted superfamily) or to a collection bundle
137
156
  // containing it (an unlisted variable family).
138
- const selectableFamilies = [collection, ...(collection.children ?? [])].filter(family => family.visibility !== 'UNLISTED');
157
+ const selectableFamilies = collection.__typename === 'FontCollection' ? [collection, ...(collection.children ?? [])].filter(family => family.visibility !== 'UNLISTED') : [];
139
158
  return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
140
159
  body: /*#__PURE__*/React.createElement(Fragment, null, licensesPosition === 'top' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StoreModalLicenseeIsBillingSelection, {
141
160
  disabled: false
@@ -151,14 +170,18 @@ const StoreModalProductContent = _ref => {
151
170
  }, {
152
171
  label: selectOrderVariablesLabel
153
172
  }), /*#__PURE__*/React.createElement(StoreModalProductLicenseSelection, {
154
- collection: collection,
173
+ licenseable: collection,
155
174
  disabled: !hasSelectionForLicenseeIsBillingIdentity || Boolean(requiredOrderVariablesNotYetSelected)
156
175
  }, {
157
176
  label: selectLicensesLabel
158
177
  })), /*#__PURE__*/React.createElement("div", {
159
178
  className: "store-modal__families",
160
179
  "data-disabled": licensesPosition === 'top' ? !anyLicenseOptionsSelected || !hasSelectionForLicenseeIsBillingIdentity || !!requiredOrderVariablesNotYetSelected : false
161
- }, selectableFamilies.map((family, index) => /*#__PURE__*/React.createElement(StoreModalFamily, {
180
+ }, collection.__typename === 'FontPack' ? /*#__PURE__*/React.createElement(StoreModalPack, {
181
+ title: selectFontsLabel,
182
+ pack: collection,
183
+ onSelect: handleSelectProduct
184
+ }) : selectableFamilies.map((family, index) => /*#__PURE__*/React.createElement(StoreModalFamily, {
162
185
  key: family.id,
163
186
  title: index === 0 ? selectFontsLabel : undefined,
164
187
  collection: family,
@@ -177,7 +200,7 @@ const StoreModalProductContent = _ref => {
177
200
  }, {
178
201
  label: selectOrderVariablesLabel
179
202
  }), /*#__PURE__*/React.createElement(StoreModalProductLicenseSelection, {
180
- collection: collection,
203
+ licenseable: collection,
181
204
  disabled: !anySkusSelected || !hasSelectionForLicenseeIsBillingIdentity || Boolean(requiredOrderVariablesNotYetSelected)
182
205
  }, {
183
206
  label: selectLicensesLabel
@@ -201,13 +224,13 @@ function StoreModalProductByIdRenderer(_ref2) {
201
224
  });
202
225
  }
203
226
  function StoreModalProductBySlugRenderer(_ref3) {
204
- var _data$viewer, _data$viewer$slug;
227
+ var _data$viewer, _data$viewer$slug, _data$viewer2, _data$viewer2$slug;
205
228
  let {
206
229
  prepared
207
230
  } = _ref3;
208
231
  const data = usePreloadedQuery(productSlugQuery, prepared.productQuery);
209
232
  return /*#__PURE__*/React.createElement(StoreModalProductContent, {
210
- collection: (_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : (_data$viewer$slug = _data$viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection,
233
+ collection: ((_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : (_data$viewer$slug = _data$viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection) ?? ((_data$viewer2 = data.viewer) === null || _data$viewer2 === void 0 ? void 0 : (_data$viewer2$slug = _data$viewer2.slug) === null || _data$viewer2$slug === void 0 ? void 0 : _data$viewer2$slug.pack),
211
234
  viewer: data.viewer
212
235
  });
213
236
  }
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
- import { StoreModalProductLicenseSelection_collection$key } from '__generated__/StoreModalProductLicenseSelection_collection.graphql.js';
2
+ import { StoreModalProductLicenseSelection_licenseable$key } from '__generated__/StoreModalProductLicenseSelection_licenseable.graphql.js';
3
3
  interface StoreModalProductLicenseSelectionProps {
4
- collection: StoreModalProductLicenseSelection_collection$key;
4
+ licenseable: StoreModalProductLicenseSelection_licenseable$key;
5
5
  disabled: boolean;
6
6
  children: {
7
7
  label: React.ReactNode;
8
8
  };
9
9
  }
10
- export default function StoreModalProductLicenseSelection({ collection: collectionKey, disabled, children: { label }, }: StoreModalProductLicenseSelectionProps): React.JSX.Element;
10
+ export default function StoreModalProductLicenseSelection({ licenseable: licenseableKey, disabled, children: { label }, }: StoreModalProductLicenseSelectionProps): React.JSX.Element;
11
11
  export {};
@@ -1,17 +1,17 @@
1
- import _StoreModalProductLicenseSelection_collection from "../../__generated__/StoreModalProductLicenseSelection_collection.graphql.js";
1
+ import _StoreModalProductLicenseSelection_licenseable from "../../__generated__/StoreModalProductLicenseSelection_licenseable.graphql.js";
2
2
  import React from 'react';
3
3
  import { graphql, useFragment } from 'react-relay';
4
4
  import StoreModalProductLicense from './StoreModalProductLicense.js';
5
5
  export default function StoreModalProductLicenseSelection(_ref) {
6
- var _collection$licenses;
6
+ var _licenseable$licenses;
7
7
  let {
8
- collection: collectionKey,
8
+ licenseable: licenseableKey,
9
9
  disabled,
10
10
  children: {
11
11
  label
12
12
  }
13
13
  } = _ref;
14
- const collection = useFragment((_StoreModalProductLicenseSelection_collection.hash && _StoreModalProductLicenseSelection_collection.hash !== "c3609e849ae8057e71542d32716f3ddc" && console.error("The definition of 'StoreModalProductLicenseSelection_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductLicenseSelection_collection), collectionKey);
14
+ const licenseable = useFragment((_StoreModalProductLicenseSelection_licenseable.hash && _StoreModalProductLicenseSelection_licenseable.hash !== "4016af73fd5c8c03fa7bcdfba6ce0b9b" && console.error("The definition of 'StoreModalProductLicenseSelection_licenseable' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductLicenseSelection_licenseable), licenseableKey);
15
15
  return /*#__PURE__*/React.createElement("div", {
16
16
  className: "store-modal__license-selection__container",
17
17
  "data-disabled": disabled
@@ -19,9 +19,9 @@ export default function StoreModalProductLicenseSelection(_ref) {
19
19
  className: "store-modal__license-selection__title"
20
20
  }, label), /*#__PURE__*/React.createElement("div", {
21
21
  className: "store-modal__license-selection__licenses"
22
- }, collection === null || collection === void 0 ? void 0 : (_collection$licenses = collection.licenses) === null || _collection$licenses === void 0 ? void 0 : _collection$licenses.map(license => license && /*#__PURE__*/React.createElement(StoreModalProductLicense, {
22
+ }, licenseable === null || licenseable === void 0 ? void 0 : (_licenseable$licenses = licenseable.licenses) === null || _licenseable$licenses === void 0 ? void 0 : _licenseable$licenses.map(license => license && /*#__PURE__*/React.createElement(StoreModalProductLicense, {
23
23
  key: license.id,
24
24
  license: license,
25
- collectionId: collection.id
25
+ collectionId: licenseable.id
26
26
  }))));
27
27
  }
@@ -57,7 +57,7 @@ const StoreModalProductSummary = _ref => {
57
57
  const refetchVariables = useMemo(() => ({
58
58
  selectedSkuIds: selectedSkuIdsArray
59
59
  }), [selectedSkuIdsArray]);
60
- const [viewer, refetch] = useRefetchableFragment((_StoreModalProductSummary_viewer.hash && _StoreModalProductSummary_viewer.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_viewer), viewerKey);
60
+ const [viewer, refetch] = useRefetchableFragment((_StoreModalProductSummary_viewer.hash && _StoreModalProductSummary_viewer.hash !== "afd13ac5f41519eea722d5138b244b25" && 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_viewer), viewerKey);
61
61
  useRefetchOnLicenseChanges({
62
62
  environment,
63
63
  refetch,
@@ -3,6 +3,9 @@ interface TestFontsFormElement_props {
3
3
  agreementLabel?: string;
4
4
  downloadLabel?: string;
5
5
  newsletterCheckboxChecked?: string;
6
+ collectionId?: string;
7
+ collectionSlug?: string;
8
+ familySelection?: string;
6
9
  }
7
- declare const TestFontsFormElement: (props: TestFontsFormElement_props) => React.JSX.Element;
10
+ declare const TestFontsFormElement: ({ familySelection, ...props }: TestFontsFormElement_props) => React.JSX.Element;
8
11
  export default TestFontsFormElement;
@@ -1,7 +1,15 @@
1
1
  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); }
2
2
  import React from 'react';
3
3
  import TestFontsFormFetcher from './index.js';
4
- const TestFontsFormElement = props => /*#__PURE__*/React.createElement(TestFontsFormFetcher, _extends({}, props, {
5
- newsletterCheckboxChecked: props.newsletterCheckboxChecked === 'true' ? true : false
6
- }));
4
+ const FAMILY_SELECTIONS = ['all', 'choice'];
5
+ const TestFontsFormElement = _ref => {
6
+ let {
7
+ familySelection,
8
+ ...props
9
+ } = _ref;
10
+ return /*#__PURE__*/React.createElement(TestFontsFormFetcher, _extends({}, props, {
11
+ familySelection: familySelection && FAMILY_SELECTIONS.includes(familySelection) ? familySelection : undefined,
12
+ newsletterCheckboxChecked: props.newsletterCheckboxChecked === 'true' ? true : false
13
+ }));
14
+ };
7
15
  export default TestFontsFormElement;
@@ -1,14 +1,17 @@
1
1
  import React from 'react';
2
2
  import { TestFontsForm_Query } from '../../__generated__/TestFontsForm_Query.graphql.js';
3
3
  import { SerializablePreloadedQuery, type LoadQueryOptions } from '../../relay/loadSerializableQuery.js';
4
- import { Config } from '../ConfigContext.js';
5
- export interface TestFontsForm_props {
4
+ import { Config, TestFontsFamilySelection } from '../ConfigContext.js';
5
+ import { TestFontsFormCollectionProps } from './variables.js';
6
+ export interface TestFontsForm_props extends TestFontsFormCollectionProps {
6
7
  agreementLabel?: string;
7
8
  downloadLabel?: string;
8
9
  newsletterCheckboxChecked?: boolean;
10
+ familySelection?: TestFontsFamilySelection;
9
11
  }
10
12
  export type TestFontsFormPreloadedQuery = SerializablePreloadedQuery<TestFontsForm_Query>;
11
- export declare function loadTestFontsFormQuery(options?: LoadQueryOptions): Promise<TestFontsFormPreloadedQuery>;
13
+ export type LoadTestFontsFormQueryVariables = TestFontsFormCollectionProps;
14
+ export declare function loadTestFontsFormQuery(variables?: LoadTestFontsFormQueryVariables | LoadQueryOptions, options?: LoadQueryOptions): Promise<TestFontsFormPreloadedQuery>;
12
15
  export declare function TestFontsFormPreloadedQueryRenderer({ preloadedQuery, ...rest }: TestFontsForm_props & {
13
16
  preloadedQuery: TestFontsFormPreloadedQuery;
14
17
  }): React.JSX.Element;
@@ -3,7 +3,7 @@
3
3
  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); }
4
4
  import _TestFontsForm_Query from "../../__generated__/TestFontsForm_Query.graphql.js";
5
5
  import _TestFontsFormUpdateCustomerMutation from "../../__generated__/TestFontsFormUpdateCustomerMutation.graphql.js";
6
- import React, { useState, useRef, useCallback } from 'react';
6
+ import React, { useState, useRef, useCallback, useContext, useId } from 'react';
7
7
  import { commitMutation, graphql, useRelayEnvironment, useLazyLoadQuery, usePreloadedQuery } from 'react-relay';
8
8
  // The script-injecting reCAPTCHA wrapper (loads Google's api.js). Importing the
9
9
  // package's barebone named export instead was the FD-917 root cause; see
@@ -16,8 +16,18 @@ import Checkbox from '../Checkbox/index.js';
16
16
  import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
17
17
  import useSerializablePreloadedQuery from '../../relay/useSerializablePreloadedQuery.js';
18
18
  import { EnsureFontdueContext, LazyQueryBoundary } from '../FontdueContextProvider/index.js';
19
+ import ConfigContext from '../ConfigContext.js';
20
+ import { testFontsFormQueryVariables } from './variables.js';
19
21
  import { useRecaptchaTimeout, isRecaptchaScriptLoaded, RECAPTCHA_UNAVAILABLE_MESSAGE } from '../../hooks/useRecaptchaTimeout.js';
20
22
  const updateCustomerMutation = (_TestFontsFormUpdateCustomerMutation.hash && _TestFontsFormUpdateCustomerMutation.hash !== "ba56958399f0893bd667ff02c33a6975" && console.error("The definition of 'TestFontsFormUpdateCustomerMutation' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TestFontsFormUpdateCustomerMutation);
23
+ // The archive endpoint's family filter: `collection_ids[]` narrows the ZIP to
24
+ // those families; none means every family.
25
+ const COLLECTION_IDS_PARAM = 'collection_ids[]';
26
+ const withCollectionIds = (archiveUrl, collectionIds) => {
27
+ if (collectionIds.length === 0) return archiveUrl;
28
+ const query = new URLSearchParams(collectionIds.map(id => [COLLECTION_IDS_PARAM, id]));
29
+ return `${archiveUrl}${archiveUrl.includes('?') ? '&' : '?'}${query}`;
30
+ };
21
31
  const TestFontsDownloading = _ref => {
22
32
  let {
23
33
  archiveUrl
@@ -31,12 +41,15 @@ const TestFontsDownloading = _ref => {
31
41
  }, "here"), ".")));
32
42
  };
33
43
  const TestFontsFormComponent = _ref2 => {
34
- var _data$viewer$settings, _data$viewer$settings2, _data$viewer$testFont;
44
+ var _data$viewer$testFont, _data$viewer$fontdueC, _data$viewer$settings, _data$viewer$settings2, _data$viewer$testFont2;
35
45
  let {
36
46
  data,
37
47
  agreementLabel,
38
48
  downloadLabel = 'Download test fonts',
39
- newsletterCheckboxChecked = false
49
+ newsletterCheckboxChecked = false,
50
+ collectionId,
51
+ collectionSlug,
52
+ familySelection: familySelectionProp
40
53
  } = _ref2;
41
54
  const [email, setEmail] = useState('');
42
55
  const [name, setName] = useState('');
@@ -52,7 +65,28 @@ const TestFontsFormComponent = _ref2 => {
52
65
  const recaptchaRef = useRef(null);
53
66
  const recaptchaTimeout = useRecaptchaTimeout();
54
67
  const environment = useRelayEnvironment();
68
+ const config = useContext(ConfigContext);
69
+ const idPrefix = useId();
70
+ const [chosenIds, setChosenIds] = useState([]);
55
71
  if (!data.viewer) return null;
72
+ const families = ((_data$viewer$testFont = data.viewer.testFonts) === null || _data$viewer$testFont === void 0 ? void 0 : _data$viewer$testFont.families) ?? [];
73
+ const {
74
+ collectionIds,
75
+ collectionSlugs
76
+ } = testFontsFormQueryVariables({
77
+ collectionId,
78
+ collectionSlug
79
+ });
80
+ // A form for particular collections downloads exactly those families.
81
+ const limited = collectionIds != null || collectionSlugs != null;
82
+ // This form's prop, then code config (which, on the script-tag path, already
83
+ // has the dashboard setting merged beneath it), then the dashboard setting.
84
+ const familySelection = familySelectionProp ?? config.testFonts.familySelection ?? ((_data$viewer$fontdueC = data.viewer.fontdueConfig) === null || _data$viewer$fontdueC === void 0 ? void 0 : _data$viewer$fontdueC.testFonts.familySelection) ?? 'all';
85
+ // One family leaves nothing to choose.
86
+ const choosing = !limited && familySelection === 'choice' && families.length > 1;
87
+ const downloadIds = limited ? families.map(family => family.id) : choosing ? families.map(family => family.id).filter(id => chosenIds.includes(id)) : [];
88
+ const toggleFamily = (id, checked) => setChosenIds(ids => checked ? [...ids, id] : ids.filter(other => other !== id));
89
+ const allChosen = families.every(family => chosenIds.includes(family.id));
56
90
  const recaptchaEnabled = ((_data$viewer$settings = data.viewer.settings) === null || _data$viewer$settings === void 0 ? void 0 : _data$viewer$settings.recaptchaEnabled) ?? false;
57
91
  const recaptchaSiteKey = (_data$viewer$settings2 = data.viewer.settings) === null || _data$viewer$settings2 === void 0 ? void 0 : _data$viewer$settings2.recaptchaSiteKey;
58
92
  const submitForm = useCallback(token => {
@@ -151,23 +185,48 @@ const TestFontsFormComponent = _ref2 => {
151
185
  }
152
186
  submitForm(recaptchaToken);
153
187
  };
154
-
155
- // Button is disabled only while actively submitting or waiting for reCAPTCHA
156
- const disabled = !eulaAgreed || !email || !name;
188
+ const disabled = !eulaAgreed || !email || !name || choosing && downloadIds.length === 0;
157
189
  const settings = data.viewer.settings;
158
190
  const newsletterOptInLabel = settings === null || settings === void 0 ? void 0 : settings.newsletterOptInLabel;
159
191
  const eulaAgreementLabel = settings === null || settings === void 0 ? void 0 : settings.eulaAgreementLabel;
160
- const archiveUrl = (_data$viewer$testFont = data.viewer.testFonts) === null || _data$viewer$testFont === void 0 ? void 0 : _data$viewer$testFont.archiveUrl;
192
+ const archiveUrl = (_data$viewer$testFont2 = data.viewer.testFonts) === null || _data$viewer$testFont2 === void 0 ? void 0 : _data$viewer$testFont2.archiveUrl;
193
+ if (limited && families.length === 0) {
194
+ return /*#__PURE__*/React.createElement("div", {
195
+ className: "test-fonts test-fonts--unavailable"
196
+ }, /*#__PURE__*/React.createElement("p", null, "Test fonts aren\u2019t available for this family."));
197
+ }
161
198
  return /*#__PURE__*/React.createElement("div", {
162
199
  className: "test-fonts"
163
200
  }, error && /*#__PURE__*/React.createElement("div", {
164
201
  className: "test-fonts__errors"
165
202
  }, error), submitted ? /*#__PURE__*/React.createElement(TestFontsDownloading, {
166
- archiveUrl: archiveUrl
203
+ archiveUrl: withCollectionIds(archiveUrl, downloadIds)
167
204
  }) : /*#__PURE__*/React.createElement("form", {
168
205
  className: "test-fonts__form",
169
206
  onSubmit: handleSubmit
207
+ }, choosing && /*#__PURE__*/React.createElement("div", {
208
+ className: "test-fonts__section test-fonts__families",
209
+ role: "group",
210
+ "aria-label": "Families"
170
211
  }, /*#__PURE__*/React.createElement("div", {
212
+ className: "test-fonts__checkbox-field test-fonts__families__all"
213
+ }, /*#__PURE__*/React.createElement(Checkbox, {
214
+ id: `${idPrefix}-families-all`,
215
+ checked: allChosen,
216
+ onChange: event => setChosenIds(event.target.checked ? families.map(family => family.id) : [])
217
+ }), /*#__PURE__*/React.createElement("label", {
218
+ htmlFor: `${idPrefix}-families-all`
219
+ }, "Select all")), families.map((family, index) => /*#__PURE__*/React.createElement("div", {
220
+ className: "test-fonts__checkbox-field test-fonts__family",
221
+ key: family.id
222
+ }, /*#__PURE__*/React.createElement(Checkbox, {
223
+ id: `${idPrefix}-family-${index}`,
224
+ value: family.id,
225
+ checked: chosenIds.includes(family.id),
226
+ onChange: event => toggleFamily(family.id, event.target.checked)
227
+ }), /*#__PURE__*/React.createElement("label", {
228
+ htmlFor: `${idPrefix}-family-${index}`
229
+ }, family.name)))), /*#__PURE__*/React.createElement("div", {
171
230
  className: "test-fonts__section"
172
231
  }, /*#__PURE__*/React.createElement(TextField, {
173
232
  label: "Name *",
@@ -223,11 +282,21 @@ const TestFontsFormComponent = _ref2 => {
223
282
  }, " \u2192")))), /*#__PURE__*/React.createElement("form", {
224
283
  action: archiveUrl ?? undefined,
225
284
  ref: downloadForm
226
- }));
285
+ }, downloadIds.map(id => /*#__PURE__*/React.createElement("input", {
286
+ key: id,
287
+ type: "hidden",
288
+ name: COLLECTION_IDS_PARAM,
289
+ value: id
290
+ }))));
227
291
  };
228
- const query = (_TestFontsForm_Query.hash && _TestFontsForm_Query.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_Query);
229
- export async function loadTestFontsFormQuery(options) {
230
- return loadSerializableQuery(TestFontsFormQueryNode, {}, options);
292
+ const query = (_TestFontsForm_Query.hash && _TestFontsForm_Query.hash !== "da75e312a602ac0ee2dfb79271299c77" && 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_Query);
293
+ // Pass the same `collectionId` / `collectionSlug` as the form's props.
294
+ export async function loadTestFontsFormQuery(variables, options) {
295
+ // Before the collection variables existed this took `options` alone.
296
+ if (variables && ('url' in variables || 'headers' in variables)) {
297
+ return loadTestFontsFormQuery({}, variables);
298
+ }
299
+ return loadSerializableQuery(TestFontsFormQueryNode, testFontsFormQueryVariables(variables ?? {}), options);
231
300
  }
232
301
  export function TestFontsFormPreloadedQueryRenderer(_ref4) {
233
302
  let {
@@ -244,7 +313,7 @@ export function TestFontsFormPreloadedQueryRenderer(_ref4) {
244
313
  }, rest));
245
314
  }
246
315
  function TestFontsFormLazyQueryRenderer(props) {
247
- const data = useLazyLoadQuery(query, {});
316
+ const data = useLazyLoadQuery(query, testFontsFormQueryVariables(props));
248
317
  return /*#__PURE__*/React.createElement(TestFontsFormComponent, _extends({
249
318
  data: data
250
319
  }, props));
@@ -2,6 +2,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
2
2
  import React from 'react';
3
3
  import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
4
4
  import { TestFontsFormPreloadedQueryRenderer } from './index.js';
5
+ import { testFontsFormQueryVariables } from './variables.js';
5
6
  import TestFontsFormQueryNode from '../../__generated__/TestFontsForm_Query.graphql.js';
6
7
  // Stub for the RSC export condition. See FontdueProvider/index.server.tsx
7
8
  // for the rationale.
@@ -14,7 +15,7 @@ export function loadTestFontsFormQuery() {
14
15
  // `<TestFontsForm />` in an RSC without manually invoking
15
16
  // `loadTestFontsFormQuery` + threading `preloadedQuery` through props.
16
17
  export default async function TestFontsForm(props) {
17
- const preloadedQuery = await loadSerializableQuery(TestFontsFormQueryNode, {});
18
+ const preloadedQuery = await loadSerializableQuery(TestFontsFormQueryNode, testFontsFormQueryVariables(props));
18
19
  return /*#__PURE__*/React.createElement(TestFontsFormPreloadedQueryRenderer, _extends({
19
20
  preloadedQuery: preloadedQuery
20
21
  }, props));
@@ -0,0 +1,8 @@
1
+ export interface TestFontsFormCollectionProps {
2
+ collectionId?: string | readonly string[];
3
+ collectionSlug?: string | readonly string[];
4
+ }
5
+ export declare const testFontsFormQueryVariables: ({ collectionId, collectionSlug, }: TestFontsFormCollectionProps) => {
6
+ collectionIds: string[] | null;
7
+ collectionSlugs: string[] | null;
8
+ };
@@ -0,0 +1,22 @@
1
+ // Shared by the client component and the RSC entry (index.server.tsx), so it
2
+ // lives outside the 'use client' module: a server component can't call a
3
+ // function imported from one.
4
+
5
+ const toList = value => {
6
+ if (value == null) return null;
7
+ const list = (typeof value === 'string' ? value.split(',') : [...value]).map(item => item.trim()).filter(Boolean);
8
+ return list.length > 0 ? list : null;
9
+ };
10
+
11
+ // The query's variables for a form's collection props: null (no narrowing)
12
+ // unless the form is limited to particular collections.
13
+ export const testFontsFormQueryVariables = _ref => {
14
+ let {
15
+ collectionId,
16
+ collectionSlug
17
+ } = _ref;
18
+ return {
19
+ collectionIds: toList(collectionId),
20
+ collectionSlugs: toList(collectionSlug)
21
+ };
22
+ };
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  interface StoreModalFamilyProps {
3
3
  children: {
4
4
  title: React.ReactNode;
5
- familyButton: React.ReactElement<any>;
5
+ familyButton?: React.ReactElement<any> | null;
6
6
  bundles?: React.ReactElement<any>[] | null;
7
7
  styleGroups?: React.ReactElement<any>[][] | null;
8
8
  };
@@ -12,7 +12,7 @@ const StoreModalFamily = _ref => {
12
12
  className: "store-modal__family__container"
13
13
  }, title && /*#__PURE__*/React.createElement("h3", {
14
14
  className: "store-modal__family__title"
15
- }, title), /*#__PURE__*/React.cloneElement(familyButton, {
15
+ }, title), familyButton && /*#__PURE__*/React.cloneElement(familyButton, {
16
16
  className: 'store-modal__family__family-button'
17
17
  }), bundles === null || bundles === void 0 ? void 0 : bundles.map(bundle => /*#__PURE__*/React.cloneElement(bundle, {
18
18
  className: 'store-modal__family__bundle-button'
package/dist/fontdue.css CHANGED
@@ -182,6 +182,13 @@
182
182
  .test-fonts__checkbox-field label p {
183
183
  margin: 0;
184
184
  }
185
+ .test-fonts__families {
186
+ --checkbox-size: 20px;
187
+ --checkbox-margin: 10px;
188
+ }
189
+ .test-fonts__families .test-fonts__checkbox-field {
190
+ margin-bottom: 10px;
191
+ }
185
192
  .test-fonts__agreement[data-highlighted=true] {
186
193
  color: red;
187
194
  }
@@ -2017,6 +2024,12 @@ input:checked + .checkbox__icon {
2017
2024
  display: none;
2018
2025
  }
2019
2026
 
2027
+ .store-modal__family-button__styles-price {
2028
+ color: var(--secondary_text_color);
2029
+ text-decoration: line-through;
2030
+ margin-right: 0.5em;
2031
+ }
2032
+
2020
2033
  .store-modal__family-button__name {
2021
2034
  font-size: 32px;
2022
2035
  line-height: normal;
package/dist/reducer.d.ts CHANGED
@@ -28,6 +28,10 @@ export interface FontdueState {
28
28
  collectionSkus: {
29
29
  [collectionSkuId: string]: string | null;
30
30
  };
31
+ productSkuIds: {
32
+ [productId: string]: string[];
33
+ };
34
+ viewedProductId: string | null;
31
35
  skuPrices: {
32
36
  [collectionSkuId: string]: number;
33
37
  };
@@ -88,6 +92,9 @@ export type FontdueAction = {
88
92
  variables?: object;
89
93
  } | {
90
94
  type: 'STORE_MODAL_BACK';
95
+ } | {
96
+ type: 'STORE_MODAL_VIEW_PRODUCT';
97
+ productId: string | null;
91
98
  } | {
92
99
  type: 'UPDATE_ORDER_VARIABLE_SELECTIONS';
93
100
  selections: OrderVariableSelectionState[];
package/dist/reducer.js CHANGED
@@ -157,6 +157,13 @@ const reducer = (state, action) => {
157
157
  return storeModalReplace(state, action);
158
158
  case 'STORE_MODAL_BACK':
159
159
  return storeModalBack(state);
160
+ // The product page being viewed bounds what a pick on it may sit inside
161
+ // (selection.ts), so a change of page re-prices the selection.
162
+ case 'STORE_MODAL_VIEW_PRODUCT':
163
+ return state.viewedProductId === action.productId ? state : repriceSelection({
164
+ ...state,
165
+ viewedProductId: action.productId
166
+ });
160
167
  case 'UPDATE_ORDER_VARIABLE_SELECTIONS':
161
168
  return updateOrderVariableSelections(state, action);
162
169
  case 'UPDATE_LICENSEE_IS_BILLING_IDENTITY':
@@ -188,6 +195,8 @@ export function createDefaultStore(config) {
188
195
  skuStyleIds: {},
189
196
  skuPrices: {},
190
197
  collectionSkus: {},
198
+ productSkuIds: {},
199
+ viewedProductId: null,
191
200
  fetchedCollectionIds: [],
192
201
  storeModalRoute: initialRoute,
193
202
  storeModalHistory: [],
@@ -1,5 +1,9 @@
1
1
  import type { FontdueState } from './reducer.js';
2
2
  type Catalog = Pick<FontdueState, 'skuStyleIds' | 'skuPrices'>;
3
+ interface ViewedProduct {
4
+ id: string;
5
+ skuIds: ReadonlySet<string>;
6
+ }
3
7
  /**
4
8
  * The cheapest set of SKUs in which each of `picks` sits inside one SKU
5
9
  * granting all of its styles, sorted. Picks whose styles or price aren't known
@@ -9,7 +13,7 @@ type Catalog = Pick<FontdueState, 'skuStyleIds' | 'skuPrices'>;
9
13
  * cheapestSkuIds(['sku:bold', 'sku:regular', 'sku:light'], catalog)
10
14
  * // => ['sku:light+regular+bold'] rather than a pair plus a single
11
15
  */
12
- export declare const cheapestSkuIds: (picks: readonly string[], catalog: Catalog) => string[];
16
+ export declare const cheapestSkuIds: (picks: readonly string[], catalog: Catalog, viewed?: ViewedProduct | null) => string[];
13
17
  /**
14
18
  * Re-derives `selectedSkuIds` from the picks. A pick whose styles or price
15
19
  * aren't known yet – selected from the host page before its product data