fontdue-js 3.5.1 → 3.7.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 (151) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -0
  3. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +3 -1
  4. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +1 -1
  5. package/dist/__generated__/CustomerSignInRequestLoginCodeMutation.graphql.d.ts +24 -0
  6. package/dist/__generated__/CustomerSignInRequestLoginCodeMutation.graphql.js +71 -0
  7. package/dist/__generated__/CustomerSignInVerifyLoginCodeMutation.graphql.d.ts +30 -0
  8. package/dist/__generated__/CustomerSignInVerifyLoginCodeMutation.graphql.js +98 -0
  9. package/dist/__generated__/FontFamiliesQuery.graphql.d.ts +4 -1
  10. package/dist/__generated__/FontFamiliesQuery.graphql.js +46 -39
  11. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.d.ts +3 -1
  12. package/dist/__generated__/NewsletterSignupUpdateCustomerMutation.graphql.js +1 -1
  13. package/dist/__generated__/PriceBar_node.graphql.d.ts +4 -1
  14. package/dist/__generated__/PriceBar_node.graphql.js +23 -16
  15. package/dist/__generated__/StoreModalFamily_collection.graphql.d.ts +3 -2
  16. package/dist/__generated__/StoreModalFamily_collection.graphql.js +3 -3
  17. package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.d.ts +23 -0
  18. package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.js +303 -0
  19. package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +4 -9
  20. package/dist/__generated__/StoreModalIndexQuery.graphql.js +90 -53
  21. package/dist/__generated__/StoreModalIndex_viewer.graphql.d.ts +24 -0
  22. package/dist/__generated__/StoreModalIndex_viewer.graphql.js +134 -0
  23. package/dist/__generated__/StoreModalProductContent_collection.graphql.d.ts +4 -1
  24. package/dist/__generated__/StoreModalProductContent_collection.graphql.js +12 -5
  25. package/dist/__generated__/StoreModalProductLicenseSelection_collection.graphql.d.ts +2 -1
  26. package/dist/__generated__/StoreModalProductLicenseSelection_collection.graphql.js +27 -24
  27. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +2 -19
  28. package/dist/__generated__/StoreModalProductQuery.graphql.js +143 -174
  29. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.d.ts +1 -1
  30. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.js +54 -53
  31. package/dist/__generated__/StoreModalProductSlugQuery.graphql.d.ts +38 -0
  32. package/dist/__generated__/StoreModalProductSlugQuery.graphql.js +750 -0
  33. package/dist/__generated__/StoreModalProductViewer.graphql.d.ts +34 -0
  34. package/dist/__generated__/StoreModalProductViewer.graphql.js +149 -0
  35. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.d.ts +3 -1
  36. package/dist/__generated__/StoreModalUnifiedCheckoutUpdateCustomerMutation.graphql.js +1 -1
  37. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +3 -1
  38. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +1 -1
  39. package/dist/__generated__/TypeTestersIDQuery.graphql.d.ts +1 -1
  40. package/dist/__generated__/TypeTestersIDQuery.graphql.js +17 -10
  41. package/dist/__generated__/TypeTestersRefetchQuery.graphql.d.ts +1 -1
  42. package/dist/__generated__/TypeTestersRefetchQuery.graphql.js +17 -10
  43. package/dist/__generated__/TypeTestersSlugQuery.graphql.d.ts +1 -1
  44. package/dist/__generated__/TypeTestersSlugQuery.graphql.js +17 -10
  45. package/dist/__generated__/storefrontEventsTrackStorefrontEventMutation.graphql.d.ts +41 -0
  46. package/dist/__generated__/storefrontEventsTrackStorefrontEventMutation.graphql.js +65 -0
  47. package/dist/__generated__/useCurrentCustomerQuery.graphql.d.ts +23 -0
  48. package/dist/__generated__/{ServerConfigProviderQuery.graphql.js → useCurrentCustomerQuery.graphql.js} +24 -39
  49. package/dist/__tests__/bundleSelectionOrder.test.js +92 -0
  50. package/dist/__tests__/collectionBundleSelection.test.js +441 -1446
  51. package/dist/__tests__/customerSession.test.js +456 -0
  52. package/dist/__tests__/infiniteScroll.test.js +180 -0
  53. package/dist/__tests__/licenseExclusions.test.js +2 -1
  54. package/dist/__tests__/storeModalRoutes.test.js +118 -0
  55. package/dist/__tests__/storefrontEvents.test.js +167 -0
  56. package/dist/__tests__/stripeElements.test.js +173 -0
  57. package/dist/__tests__/typeTesterAutofitFeatures.test.js +138 -0
  58. package/dist/__tests__/typeTesterInitialSize.test.js +82 -0
  59. package/dist/__tests__/typeTesterTruncate.test.js +218 -0
  60. package/dist/auth/session.d.ts +42 -0
  61. package/dist/auth/session.js +139 -0
  62. package/dist/auth/tokenStore.d.ts +61 -0
  63. package/dist/auth/tokenStore.js +162 -0
  64. package/dist/components/Cart/Checkout.js +19 -9
  65. package/dist/components/Cart/storefrontEvents.d.ts +34 -0
  66. package/dist/components/Cart/storefrontEvents.js +138 -0
  67. package/dist/components/ConfigContext.d.ts +2 -2
  68. package/dist/components/CustomerSignIn/index.d.ts +33 -0
  69. package/dist/components/CustomerSignIn/index.js +170 -0
  70. package/dist/components/FontFamilies/index.js +4 -3
  71. package/dist/components/NewsletterSignup/index.js +5 -1
  72. package/dist/components/Precart/index.js +6 -0
  73. package/dist/components/PriceBar/index.js +5 -3
  74. package/dist/components/Root/index.js +4 -34
  75. package/dist/components/Root/productState.d.ts +2 -8
  76. package/dist/components/Root/productState.js +46 -57
  77. package/dist/components/SKUPrice/index.js +7 -8
  78. package/dist/components/StoreModal/StoreModalFamily.js +4 -2
  79. package/dist/components/StoreModal/StoreModalIndex.js +48 -7
  80. package/dist/components/StoreModal/StoreModalProduct.d.ts +5 -1
  81. package/dist/components/StoreModal/StoreModalProduct.js +69 -16
  82. package/dist/components/StoreModal/index.js +14 -14
  83. package/dist/components/StoreModal/routes.js +9 -1
  84. package/dist/components/StoreModalProductLicenseSelection/StoreModalProductLicense.d.ts +2 -1
  85. package/dist/components/StoreModalProductLicenseSelection/StoreModalProductLicense.js +8 -3
  86. package/dist/components/StoreModalProductLicenseSelection/index.js +3 -2
  87. package/dist/components/TestFontsForm/index.js +5 -1
  88. package/dist/components/TypeTester/TypeTesterContent.js +9 -2
  89. package/dist/components/TypeTester/TypeTesterContext.js +17 -4
  90. package/dist/components/TypeTester/TypeTesterFitter.js +7 -2
  91. package/dist/components/TypeTester/TypeTesterStandalone.d.ts +2 -0
  92. package/dist/components/TypeTester/TypeTesterStandalone.js +2 -0
  93. package/dist/components/TypeTester/TypeTesterStandaloneElement.d.ts +1 -0
  94. package/dist/components/TypeTester/TypeTesterStandaloneElement.js +4 -1
  95. package/dist/components/TypeTester/TypeTesterVariableAxes.js +16 -10
  96. package/dist/components/TypeTester/index.d.ts +3 -1
  97. package/dist/components/TypeTester/index.js +2 -1
  98. package/dist/components/TypeTester/truncate.d.ts +12 -0
  99. package/dist/components/TypeTester/truncate.js +95 -0
  100. package/dist/components/TypeTester/useTypeTesterStyler.d.ts +3 -1
  101. package/dist/components/TypeTester/useTypeTesterStyler.js +36 -7
  102. package/dist/components/TypeTesters/TypeTestersElement.d.ts +2 -1
  103. package/dist/components/TypeTesters/TypeTestersElement.js +4 -1
  104. package/dist/components/TypeTesters/index.d.ts +2 -0
  105. package/dist/components/elements/StoreModalContainer/StoreModalContainerContext.d.ts +1 -0
  106. package/dist/components/elements/StoreModalContainer/index.js +9 -4
  107. package/dist/components/elements/StoreModalIndexLoadMore/index.d.ts +8 -0
  108. package/dist/components/elements/StoreModalIndexLoadMore/index.js +21 -0
  109. package/dist/components/elements/StoreModalUnifiedCheckout.d.ts +1 -1
  110. package/dist/components/elements/StoreModalUnifiedCheckout.js +20 -17
  111. package/dist/components/useInfiniteScroll.d.ts +28 -0
  112. package/dist/components/useInfiniteScroll.js +50 -0
  113. package/dist/fontdue.css +63 -0
  114. package/dist/hooks/useAutofit.d.ts +2 -1
  115. package/dist/hooks/useAutofit.js +10 -4
  116. package/dist/hooks/useCurrentCustomer.d.ts +34 -0
  117. package/dist/hooks/useCurrentCustomer.js +93 -0
  118. package/dist/reducer.d.ts +5 -5
  119. package/dist/reducer.js +9 -87
  120. package/dist/relay/environment.js +91 -50
  121. package/dist/selection.d.ts +40 -0
  122. package/dist/selection.js +494 -0
  123. package/dist/storeModalRoutes.d.ts +38 -0
  124. package/dist/storeModalRoutes.js +110 -0
  125. package/dist/stripeElements.d.ts +18 -0
  126. package/dist/stripeElements.js +49 -0
  127. package/dist/utils.d.ts +1 -12
  128. package/dist/utils.js +1 -36
  129. package/dist/version.js +1 -1
  130. package/package.json +1 -1
  131. package/dist/__generated__/NodePasswordFormIDQuery.graphql.d.ts +0 -21
  132. package/dist/__generated__/NodePasswordFormIDQuery.graphql.js +0 -86
  133. package/dist/__generated__/NodePasswordFormSlugQuery.graphql.d.ts +0 -25
  134. package/dist/__generated__/NodePasswordFormSlugQuery.graphql.js +0 -108
  135. package/dist/__generated__/ServerConfigProviderQuery.graphql.d.ts +0 -24
  136. package/dist/__generated__/TestModeBannerQuery.graphql.d.ts +0 -20
  137. package/dist/__generated__/TestModeBannerQuery.graphql.js +0 -87
  138. package/dist/__generated__/ThemeConfigQuery.graphql.d.ts +0 -20
  139. package/dist/__generated__/ThemeConfigQuery.graphql.js +0 -87
  140. package/dist/components/CartButton/index.server.d.ts +0 -3
  141. package/dist/components/CartButton/index.server.js +0 -18
  142. package/dist/components/CorsErrorModal.d.ts +0 -1
  143. package/dist/components/CorsErrorModal.js +0 -92
  144. package/dist/components/FontdueProvider/FontdueProviderClientComponent.d.ts +0 -14
  145. package/dist/components/FontdueProvider/FontdueProviderClientComponent.js +0 -61
  146. package/dist/components/ThemeConfig/index.server.d.ts +0 -2
  147. package/dist/components/ThemeConfig/index.server.js +0 -17
  148. package/dist/components/TypeTester/features.d.ts +0 -5
  149. package/dist/components/TypeTester/features.js +0 -143
  150. package/dist/data/unicodeData.d.ts +0 -4
  151. package/dist/data/unicodeData.js +0 -1
@@ -53,58 +53,50 @@ const allSkus = function (collection) {
53
53
  }
54
54
  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(notEmpty);
55
55
  };
56
- const flattenSkuData = collection => {
57
- let bundles = {};
58
- if ('bundles' in collection && collection.bundles) {
59
- bundles = collection.bundles.reduce((res, bundle) => ({
60
- ...res,
61
- ...flattenSkuData(bundle)
62
- }), {});
63
- }
64
- let collectionBundles = {};
65
- if ('collectionBundles' in collection && collection.collectionBundles) {
66
- collectionBundles = collection.collectionBundles.reduce((res, collBundle) => ({
67
- ...res,
68
- ...flattenSkuData(collBundle)
69
- }), {});
70
- }
71
- let children = {};
72
- if ('children' in collection && collection.children) {
73
- children = collection.children.reduce((res, child) => ({
74
- ...res,
75
- ...flattenSkuData(child)
76
- }), {});
77
- }
78
- let collectionData = {};
79
- if (collection.sku) {
80
- var _collection$fontStyle2, _collection$children2;
81
- // For collection bundles, get font style IDs from member collections
82
- const fontStyleIds = 'memberCollections' in collection && collection.memberCollections ? collection.memberCollections.flatMap(member => {
83
- var _member$fontStyles2;
84
- return ((_member$fontStyles2 = member.fontStyles) === null || _member$fontStyles2 === void 0 ? void 0 : _member$fontStyles2.map(style => style.id)) ?? [];
85
- }) : '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 => {
86
- var _child$fontStyles;
87
- return ((_child$fontStyles = child.fontStyles) === null || _child$fontStyles === void 0 ? void 0 : _child$fontStyles.map(style => style.id)) ?? [];
88
- })) ?? [] : []) : [];
89
- collectionData = {
90
- [collection.sku.id]: {
91
- fontStyleIds,
92
- fontStyleSkuIds: allSkus(collection, false, true).map(sku => sku.id),
93
- childrenSkuIds: allSkus(collection, true, false).map(sku => sku.id).filter(skuId => {
94
- var _collection$sku;
95
- return skuId !== ((_collection$sku = collection.sku) === null || _collection$sku === void 0 ? void 0 : _collection$sku.id);
96
- }),
97
- // exclude self
98
- name: collection.name
99
- }
100
- };
56
+
57
+ // The styles each SKU grants, by SKU id: a style's SKU grants that style, a
58
+ // bundle its styles, a family its styles, a superfamily its families' styles
59
+ // and a collection bundle its member collections' styles. This is all the
60
+ // selection needs to price any combination of them (see selection.ts).
61
+
62
+ const styleIdsOf = collection => {
63
+ var _collection$fontStyle2, _collection$children2;
64
+ if ('memberCollections' in collection && collection.memberCollections) {
65
+ return collection.memberCollections.flatMap(member => member.fontStyles.map(s => s.id));
101
66
  }
102
- return {
103
- ...collectionData,
104
- ...children,
105
- ...bundles,
106
- ...collectionBundles
67
+ const own = 'fontStyles' in collection ? ((_collection$fontStyle2 = collection.fontStyles) === null || _collection$fontStyle2 === void 0 ? void 0 : _collection$fontStyle2.map(s => s.id)) ?? [] : [];
68
+ const children = 'children' in collection ? ((_collection$children2 = collection.children) === null || _collection$children2 === void 0 ? void 0 : _collection$children2.flatMap(child => {
69
+ var _child$fontStyles;
70
+ return ((_child$fontStyles = child.fontStyles) === null || _child$fontStyles === void 0 ? void 0 : _child$fontStyles.map(s => s.id)) ?? [];
71
+ })) ?? [] : [];
72
+ return [...new Set([...own, ...children])];
73
+ };
74
+ const flattenSkuStyleIds = collection => {
75
+ var _collection$sku;
76
+ const result = {};
77
+ const add = (skuId, styleIds) => {
78
+ // A collection that returns no styles of its own (a superfamily listed as
79
+ // a collection bundle member) says nothing about what it grants.
80
+ if (skuId && styleIds.length > 0) result[skuId] = styleIds;
107
81
  };
82
+ const addStyles = fontStyles => {
83
+ for (const style of fontStyles ?? []) {
84
+ var _style$sku;
85
+ add((_style$sku = style.sku) === null || _style$sku === void 0 ? void 0 : _style$sku.id, [style.id]);
86
+ }
87
+ };
88
+ add((_collection$sku = collection.sku) === null || _collection$sku === void 0 ? void 0 : _collection$sku.id, styleIdsOf(collection));
89
+ if ('fontStyles' in collection) addStyles(collection.fontStyles);
90
+ if ('memberCollections' in collection) {
91
+ for (const member of collection.memberCollections ?? []) {
92
+ var _member$sku;
93
+ add((_member$sku = member.sku) === null || _member$sku === void 0 ? void 0 : _member$sku.id, member.fontStyles.map(s => s.id));
94
+ addStyles(member.fontStyles);
95
+ }
96
+ }
97
+ const nested = [...('bundles' in collection ? collection.bundles ?? [] : []), ...('collectionBundles' in collection ? collection.collectionBundles ?? [] : []), ...('children' in collection ? collection.children ?? [] : [])];
98
+ for (const child of nested) Object.assign(result, flattenSkuStyleIds(child));
99
+ return result;
108
100
  };
109
101
  const flattenSkuPrices = collection => {
110
102
  return allSkus(collection).reduce((acc, sku) => {
@@ -122,14 +114,11 @@ export const populateProductStateReducer = (state, action) => {
122
114
  return {
123
115
  ...state,
124
116
  // {
125
- // [collectionSkuId]: {
126
- // childrenSkuIds: [skuId...],
127
- // fontStyleSkuIds: [skuId...],
128
- // }
117
+ // [skuId]: [styleId...]
129
118
  // }
130
- collectionStyleSkus: {
131
- ...state.collectionStyleSkus,
132
- ...flattenSkuData(data.node)
119
+ skuStyleIds: {
120
+ ...state.skuStyleIds,
121
+ ...flattenSkuStyleIds(data.node)
133
122
  },
134
123
  // {
135
124
  // [skuId]: amount,
@@ -3,20 +3,19 @@ import _SKUPrice_sku from "../../__generated__/SKUPrice_sku.graphql.js";
3
3
  import React from 'react';
4
4
  import { graphql, useFragment } from 'react-relay';
5
5
  import Price from '../Price/index.js';
6
- import { collectionSkuIdWithDiscount, isSelected } from '../../reducer.js';
6
+ import { isSelected } from '../../reducer.js';
7
+ import { additionalCost } from '../../selection.js';
7
8
  import { useSelector } from 'react-redux';
8
9
  const getSavingsMultiplier = (state, options) => {
9
10
  if (options.showCollectionSavings === false) return 1;
10
11
  if (!options.sku) return 1;
11
12
  const baseSkuPrice = state.skuPrices[options.sku.id];
12
- const [collectionWithSavings, savingsAmount] = collectionSkuIdWithDiscount(state, options.sku.id);
13
+ const cost = additionalCost(state, options.sku.id);
13
14
 
14
- // we make it a multiplier since the `collectionSkuIdsDifference`
15
- // helper doesnt take license multiplication into account
16
- if (collectionWithSavings && baseSkuPrice > 0) {
17
- return savingsAmount / baseSkuPrice;
18
- }
19
- return 1;
15
+ // A multiplier, since `additionalCost` works in unlicensed prices and the
16
+ // price shown here carries the license's multipliers
17
+ if (cost === null || !(baseSkuPrice > 0)) return 1;
18
+ return cost / baseSkuPrice;
20
19
  };
21
20
  function SKUPrice(_ref) {
22
21
  let {
@@ -38,7 +38,7 @@ export default function StoreModalFamily(_ref) {
38
38
  onSelect,
39
39
  title
40
40
  } = _ref;
41
- const collection = useFragment((_StoreModalFamily_collection.hash && _StoreModalFamily_collection.hash !== "d2d429ebada7593dd5debcce7a69317d" && console.error("The definition of 'StoreModalFamily_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalFamily_collection), collectionKey);
41
+ const collection = useFragment((_StoreModalFamily_collection.hash && _StoreModalFamily_collection.hash !== "f136cd0050f0f75f561221fac75feb85" && console.error("The definition of 'StoreModalFamily_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalFamily_collection), collectionKey);
42
42
  const fontStyles = useMemo(() => stylesGrouped(collection), [collection]);
43
43
  if ((fontStyles === null || fontStyles === void 0 ? void 0 : fontStyles.length) === 0 && collection.collectionType === 'superfamily' && !collection.sku) return null;
44
44
  return /*#__PURE__*/React.createElement(FamilyElement, null, {
@@ -47,7 +47,9 @@ export default function StoreModalFamily(_ref) {
47
47
  collection: collection,
48
48
  onSelectSku: onSelect
49
49
  }),
50
- bundles: collection === null || collection === void 0 ? void 0 : (_collection$bundles = collection.bundles) === null || _collection$bundles === void 0 ? void 0 : _collection$bundles.filter(bundle => !bundle.hidden).map(bundle => /*#__PURE__*/React.createElement(StoreModalBundleButton, {
50
+ // An unlisted bundle gets no button, but its price still applies as an
51
+ // upgrade: the product state reads every bundle.
52
+ bundles: collection === null || collection === void 0 ? void 0 : (_collection$bundles = collection.bundles) === null || _collection$bundles === void 0 ? void 0 : _collection$bundles.filter(bundle => bundle.visibility !== 'UNLISTED').map(bundle => /*#__PURE__*/React.createElement(StoreModalBundleButton, {
51
53
  key: bundle.id,
52
54
  bundle: bundle,
53
55
  onSelectSku: onSelect
@@ -1,20 +1,61 @@
1
+ import _StoreModalIndex_viewer from "../../__generated__/StoreModalIndex_viewer.graphql.js";
1
2
  import _StoreModalIndexQuery from "../../__generated__/StoreModalIndexQuery.graphql.js";
2
- import React from 'react';
3
- import { graphql, usePreloadedQuery } from 'react-relay';
3
+ import React, { useCallback, useContext } from 'react';
4
+ import { graphql, usePaginationFragment, usePreloadedQuery } from 'react-relay';
4
5
  import StoreModalPageContainer from '../elements/StoreModalPageContainer/index.js';
6
+ import StoreModalIndexLoadMore from '../elements/StoreModalIndexLoadMore/index.js';
7
+ import StoreModalContainerContext from '../elements/StoreModalContainer/StoreModalContainerContext.js';
5
8
  import StoreModalIndexItem from './StoreModalIndexItem.js';
9
+ import useInfiniteScroll from '../useInfiniteScroll.js';
10
+
11
+ // How many collections arrive at a time. Enough that a typical catalogue is one
12
+ // request, few enough that a large one doesn't make the visitor wait for every
13
+ // collection's feature style before anything renders. The GraphQL below can't
14
+ // interpolate, so the same number is written into the query's `$count` default
15
+ // and the fragment's argument definitions — keep the three in step.
16
+ const PAGE_SIZE = 25;
17
+
18
+ // Start the next page while the sentinel is still this far below the modal's
19
+ // visible area, so the pages join up without the visitor reaching a dead end.
20
+ const LOAD_MORE_MARGIN = '0px 0px 600px 0px';
6
21
  const StoreModalIndex = _ref => {
7
- var _viewer$rootCollectio;
22
+ var _data$rootCollections;
8
23
  let {
9
24
  prepared
10
25
  } = _ref;
11
- const data = usePreloadedQuery((_StoreModalIndexQuery.hash && _StoreModalIndexQuery.hash !== "099c77808b41054885365ebe3eda03c8" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery), prepared.indexQuery);
12
- const viewer = data.viewer;
13
- const edges = (viewer === null || viewer === void 0 ? void 0 : (_viewer$rootCollectio = viewer.rootCollections) === null || _viewer$rootCollectio === void 0 ? void 0 : _viewer$rootCollectio.edges) ?? [];
26
+ const queryData = usePreloadedQuery((_StoreModalIndexQuery.hash && _StoreModalIndexQuery.hash !== "125a10155b690929f0eae6e9e6c7188c" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery), prepared.indexQuery);
27
+
28
+ // The query type only carries the fragment spread, not its data type, so
29
+ // both sides are named explicitly for `data` to be typed.
30
+ const {
31
+ data,
32
+ loadNext,
33
+ hasNext,
34
+ isLoadingNext
35
+ } = usePaginationFragment((_StoreModalIndex_viewer.hash && _StoreModalIndex_viewer.hash !== "f4bd120deb8eea66de4257529cbb022e" && console.error("The definition of 'StoreModalIndex_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndex_viewer), queryData.viewer);
36
+ const {
37
+ scrollContainer
38
+ } = useContext(StoreModalContainerContext);
39
+ const handleLoadMore = useCallback(() => {
40
+ // Relay ignores this while a page is already in flight, so the observer
41
+ // firing repeatedly on the way down costs nothing.
42
+ loadNext(PAGE_SIZE);
43
+ }, [loadNext]);
44
+ const sentinelRef = useInfiniteScroll({
45
+ enabled: hasNext && !isLoadingNext,
46
+ onLoadMore: handleLoadMore,
47
+ rootRef: scrollContainer,
48
+ rootMargin: LOAD_MORE_MARGIN
49
+ });
50
+ const edges = (data === null || data === void 0 ? void 0 : (_data$rootCollections = data.rootCollections) === null || _data$rootCollections === void 0 ? void 0 : _data$rootCollections.edges) ?? [];
14
51
  return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
15
- body: edges.map(edge => /*#__PURE__*/React.createElement(StoreModalIndexItem, {
52
+ body: /*#__PURE__*/React.createElement(React.Fragment, null, edges.map(edge => /*#__PURE__*/React.createElement(StoreModalIndexItem, {
16
53
  collection: edge.node,
17
54
  key: edge.node.id
55
+ })), hasNext && /*#__PURE__*/React.createElement(StoreModalIndexLoadMore, {
56
+ sentinelRef: sentinelRef,
57
+ onClick: handleLoadMore,
58
+ loading: isLoadingNext
18
59
  }))
19
60
  });
20
61
  };
@@ -1,9 +1,13 @@
1
1
  import React from 'react';
2
2
  import { PreloadedQuery } from 'react-relay';
3
3
  import { StoreModalProductQuery } from '__generated__/StoreModalProductQuery.graphql.js';
4
+ import { StoreModalProductSlugQuery } from '../../__generated__/StoreModalProductSlugQuery.graphql.js';
5
+ export declare const productIdQuery: import("react-relay").GraphQLTaggedNode;
6
+ export declare const productSlugQuery: import("react-relay").GraphQLTaggedNode;
7
+ export type StoreModalProductPreparedQuery = PreloadedQuery<StoreModalProductQuery> | PreloadedQuery<StoreModalProductSlugQuery>;
4
8
  interface StoreModalProductProps {
5
9
  prepared: {
6
- productQuery: PreloadedQuery<StoreModalProductQuery>;
10
+ productQuery: StoreModalProductPreparedQuery;
7
11
  };
8
12
  }
9
13
  declare const StoreModalProduct: React.FunctionComponent<StoreModalProductProps>;
@@ -1,7 +1,9 @@
1
1
  import _StoreModalProductContent_collection from "../../__generated__/StoreModalProductContent_collection.graphql.js";
2
+ import _StoreModalProductViewer from "../../__generated__/StoreModalProductViewer.graphql.js";
3
+ import _StoreModalProductSlugQuery from "../../__generated__/StoreModalProductSlugQuery.graphql.js";
2
4
  import _StoreModalProductQuery from "../../__generated__/StoreModalProductQuery.graphql.js";
3
5
  import React, { Fragment, useCallback, useContext, useEffect, useMemo } from 'react';
4
- import { graphql, useRelayEnvironment, usePreloadedQuery, useRefetchableFragment } from 'react-relay';
6
+ import { graphql, useRelayEnvironment, useFragment, usePreloadedQuery, useRefetchableFragment } from 'react-relay';
5
7
  import { shallowEqual, useDispatch, useSelector, useStore } from 'react-redux';
6
8
  import StoreModalFamily from './StoreModalFamily.js';
7
9
  import StoreModalProductSummary from '../StoreModalProductSummary/index.js';
@@ -14,19 +16,30 @@ import ConfigContext from '../ConfigContext.js';
14
16
  import { useRefetchOnLicenseChanges } from '../../hooks/useRefetchOnLicenseChanges.js';
15
17
  import StoreModalLicenseeIsBillingSelection from './StoreModalLicenseeIsBillingSelection.js';
16
18
  import { useLicenseAndOrderVariables } from '../../hooks/useLicenseAndOrderVariables.js';
17
- const StoreModalProduct = _ref => {
18
- var _config$storeModal, _viewer$orderVariable, _viewer$settings, _viewer$settings2, _viewer$settings3, _viewer$settings4, _viewer$settings5, _viewer$settings6, _viewer$settings7, _viewer$settings8, _collection$children;
19
+ import { trackProductViewed } from '../Cart/storefrontEvents.js';
20
+
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`,
23
+ // so each carries only the selection it needs; both hand the same collection
24
+ // and viewer to `StoreModalProductContent` below.
25
+ 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
+ // 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);
31
+ 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
+ const StoreModalProductContent = _ref => {
33
+ var _config$storeModal, _viewer$orderVariable, _viewer$settings, _viewer$settings2, _viewer$settings3, _viewer$settings4, _viewer$settings5, _viewer$settings6, _viewer$settings7, _viewer$settings8;
19
34
  let {
20
- prepared
35
+ collection: collectionRef,
36
+ viewer: viewerRef
21
37
  } = _ref;
22
38
  const config = useContext(ConfigContext);
23
39
  const environment = useRelayEnvironment();
24
- const initialData = usePreloadedQuery((_StoreModalProductQuery.hash && _StoreModalProductQuery.hash !== "051d45b088e87330de54058b441ad280" && console.error("The definition of 'StoreModalProductQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductQuery), prepared.productQuery);
25
- const [data, refetch] = useRefetchableFragment((_StoreModalProductContent_collection.hash && _StoreModalProductContent_collection.hash !== "0684ec2633ccc5d4d0866e7780df28cc" && 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), initialData.collection);
40
+ 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);
26
42
  const collection = data;
27
- const {
28
- viewer
29
- } = initialData;
30
43
  const refetchVariables = useMemo(() => collection !== null && collection !== void 0 && collection.id ? {
31
44
  id: collection.id
32
45
  } : undefined, [collection === null || collection === void 0 ? void 0 : collection.id]);
@@ -48,6 +61,9 @@ const StoreModalProduct = _ref => {
48
61
  useEffect(() => {
49
62
  if (collection !== null && collection !== void 0 && collection.id) populateProductState(store, environment, collection.id);
50
63
  }, [collection === null || collection === void 0 ? void 0 : collection.id]);
64
+ useEffect(() => {
65
+ if (collection !== null && collection !== void 0 && collection.id) trackProductViewed(environment, collection.id);
66
+ }, [collection === null || collection === void 0 ? void 0 : collection.id]);
51
67
  useEffect(() => {
52
68
  return () => {
53
69
  // unselect the skus when this component unmounts,
@@ -114,6 +130,12 @@ const StoreModalProduct = _ref => {
114
130
  __html: ((_viewer$settings8 = viewer.settings) === null || _viewer$settings8 === void 0 ? void 0 : _viewer$settings8.storeModalSelectFontsLabel) ?? ''
115
131
  }
116
132
  });
133
+
134
+ // An unlisted collection gets no block – no family button, bundles or
135
+ // styles – but stays in the product state, so selecting its siblings can
136
+ // still upgrade to it (an unlisted superfamily) or to a collection bundle
137
+ // containing it (an unlisted variable family).
138
+ const selectableFamilies = [collection, ...(collection.children ?? [])].filter(family => family.visibility !== 'UNLISTED');
117
139
  return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
118
140
  body: /*#__PURE__*/React.createElement(Fragment, null, licensesPosition === 'top' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StoreModalLicenseeIsBillingSelection, {
119
141
  disabled: false
@@ -136,13 +158,10 @@ const StoreModalProduct = _ref => {
136
158
  })), /*#__PURE__*/React.createElement("div", {
137
159
  className: "store-modal__families",
138
160
  "data-disabled": licensesPosition === 'top' ? !anyLicenseOptionsSelected || !hasSelectionForLicenseeIsBillingIdentity || !!requiredOrderVariablesNotYetSelected : false
139
- }, /*#__PURE__*/React.createElement(StoreModalFamily, {
140
- title: selectFontsLabel,
141
- collection: collection,
142
- onSelect: handleSelectProduct
143
- }), (_collection$children = collection.children) === null || _collection$children === void 0 ? void 0 : _collection$children.map(child => /*#__PURE__*/React.createElement(StoreModalFamily, {
144
- key: child.id,
145
- collection: child,
161
+ }, selectableFamilies.map((family, index) => /*#__PURE__*/React.createElement(StoreModalFamily, {
162
+ key: family.id,
163
+ title: index === 0 ? selectFontsLabel : undefined,
164
+ collection: family,
146
165
  onSelect: handleSelectProduct
147
166
  }))), licensesPosition === 'bottom' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StoreModalLicenseeIsBillingSelection, {
148
167
  disabled: !anySkusSelected
@@ -171,4 +190,38 @@ const StoreModalProduct = _ref => {
171
190
  }, "connect your Stripe account"), ' ', "to enable the cart.")
172
191
  });
173
192
  };
193
+ function StoreModalProductByIdRenderer(_ref2) {
194
+ let {
195
+ prepared
196
+ } = _ref2;
197
+ const data = usePreloadedQuery(productIdQuery, prepared.productQuery);
198
+ return /*#__PURE__*/React.createElement(StoreModalProductContent, {
199
+ collection: data.collection,
200
+ viewer: data.viewer
201
+ });
202
+ }
203
+ function StoreModalProductBySlugRenderer(_ref3) {
204
+ var _data$viewer, _data$viewer$slug;
205
+ let {
206
+ prepared
207
+ } = _ref3;
208
+ const data = usePreloadedQuery(productSlugQuery, prepared.productQuery);
209
+ 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,
211
+ viewer: data.viewer
212
+ });
213
+ }
214
+
215
+ // Which query the route prepared decides which hook runs — a hook can't be
216
+ // called conditionally, so each lives in its own renderer.
217
+ const StoreModalProduct = _ref4 => {
218
+ let {
219
+ prepared
220
+ } = _ref4;
221
+ return prepared.productQuery.name === 'StoreModalProductSlugQuery' ? /*#__PURE__*/React.createElement(StoreModalProductBySlugRenderer, {
222
+ prepared: prepared
223
+ }) : /*#__PURE__*/React.createElement(StoreModalProductByIdRenderer, {
224
+ prepared: prepared
225
+ });
226
+ };
174
227
  export default StoreModalProduct;
@@ -1,28 +1,28 @@
1
1
  'use client';
2
2
 
3
- import React, { Suspense, useEffect } from 'react';
3
+ import React, { Suspense, useContext, useEffect, useRef } from 'react';
4
4
  import StoreModalLoader from './StoreModalLoader.js';
5
5
  import StoreModalContainer from './StoreModalContainer.js';
6
6
  import StoreModalRouter from './StoreModalRouter.js';
7
7
  import { useDispatch } from 'react-redux';
8
8
  import { EnsureFontdueContext } from '../FontdueContextProvider/index.js';
9
+ import ConfigContext from '../ConfigContext.js';
10
+ import { storeModalUrlActions } from '../../storeModalRoutes.js';
9
11
  function StoreModalInner() {
10
12
  const dispatch = useDispatch();
13
+ // Read through a ref: `ConfigOverride` rebuilds the context value on every
14
+ // render, so depending on it would re-run the effect and re-open the modal
15
+ // after the visitor had closed it.
16
+ const configValue = useContext(ConfigContext);
17
+ const config = useRef(configValue);
18
+ config.current = configValue;
11
19
 
12
- // Check for store-modal=open URL parameter and open cart automatically
20
+ // `?store=open`, `?store=product/<id>` and the older `?cart=open` open the
21
+ // modal from a link. This lives here rather than in `Root` so it works for
22
+ // the npm integration too, where there is no `Root`.
13
23
  useEffect(() => {
14
- if (typeof window !== 'undefined') {
15
- const urlParams = new URLSearchParams(window.location.search);
16
- if (urlParams.get('cart') === 'open') {
17
- dispatch({
18
- type: 'STORE_MODAL_REPLACE',
19
- route: 'cart'
20
- });
21
- dispatch({
22
- type: 'OPEN_CART'
23
- });
24
- }
25
- }
24
+ if (typeof window === 'undefined') return;
25
+ storeModalUrlActions(window.location.search, config.current).forEach(action => dispatch(action));
26
26
  }, [dispatch]);
27
27
  return /*#__PURE__*/React.createElement(StoreModalLoader, null, /*#__PURE__*/React.createElement(Suspense, {
28
28
  fallback: null
@@ -5,6 +5,7 @@ import StoreModalCart from './StoreModalCart.js';
5
5
  import StoreModalCheckout from './StoreModalCheckout.js';
6
6
  import IndexQuery from '../../__generated__/StoreModalIndexQuery.graphql.js';
7
7
  import ProductQuery from '../../__generated__/StoreModalProductQuery.graphql.js';
8
+ import ProductSlugQuery from '../../__generated__/StoreModalProductSlugQuery.graphql.js';
8
9
  import CartQuery from '../../__generated__/StoreModalCartQuery.graphql.js';
9
10
  import CheckoutQuery from '../../__generated__/StoreModalCheckoutQuery.graphql.js';
10
11
  const route = routeConfig => routeConfig;
@@ -15,10 +16,17 @@ const routes = {
15
16
  indexQuery: loadQuery(environment, IndexQuery, variables)
16
17
  })
17
18
  }),
19
+ // A product arrives addressed either way — `?store=product/<id>` or
20
+ // `?store=product/<slug>` — and each identifier needs its own query, since
21
+ // a slug resolves through `viewer.slug` rather than `node`.
18
22
  product: route({
19
23
  Component: StoreModalProduct,
20
24
  prepare: (variables, environment) => ({
21
- productQuery: loadQuery(environment, ProductQuery, {
25
+ productQuery: variables.productSlug ? loadQuery(environment, ProductSlugQuery, {
26
+ ...variables,
27
+ licenseOptions: [],
28
+ orderVariables: []
29
+ }) : loadQuery(environment, ProductQuery, {
22
30
  ...variables,
23
31
  licenseOptions: [],
24
32
  orderVariables: []
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { StoreModalProductLicense_license$key } from '../../__generated__/StoreModalProductLicense_license.graphql.js';
3
3
  interface StoreModalProductLicenseProps {
4
4
  license: StoreModalProductLicense_license$key;
5
+ collectionId?: string;
5
6
  }
6
- export default function StoreModalProduceLicense({ license: licenseKey, }: StoreModalProductLicenseProps): React.JSX.Element;
7
+ export default function StoreModalProduceLicense({ license: licenseKey, collectionId, }: StoreModalProductLicenseProps): React.JSX.Element;
7
8
  export {};
@@ -6,6 +6,7 @@ import LicenseElement from './LicenseElement.js';
6
6
  import LicenseVariableElement from './LicenseVariableElement.js';
7
7
  import VariableTableAmounts from '../VariableTableAmounts/index.js';
8
8
  import LicenseVariableRadioElement from './LicenseVariableRadioElement.js';
9
+ import { useTrackProductCustomized } from '../Cart/storefrontEvents.js';
9
10
  const specFromLicense = license => {
10
11
  let licenseOptionSpec = {
11
12
  licenseId: license.id
@@ -31,7 +32,8 @@ const specFromLicense = license => {
31
32
  export default function StoreModalProduceLicense(_ref) {
32
33
  var _license$variables;
33
34
  let {
34
- license: licenseKey
35
+ license: licenseKey,
36
+ collectionId
35
37
  } = _ref;
36
38
  const license = useFragment((_StoreModalProductLicense_license.hash && _StoreModalProductLicense_license.hash !== "87cd41e76dcd8952d7096940a383b9f3" && console.error("The definition of 'StoreModalProductLicense_license' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductLicense_license), licenseKey);
37
39
  const licenseOptions = useSelector(state => state.licenseOptions);
@@ -43,6 +45,7 @@ export default function StoreModalProduceLicense(_ref) {
43
45
  });
44
46
  const isSelected = Boolean(selectedOptions);
45
47
  const dispatch = useDispatch();
48
+ const trackCustomized = useTrackProductCustomized(collectionId);
46
49
  const handleSelectLicense = useCallback(() => {
47
50
  var _license$excludedLice;
48
51
  dispatch({
@@ -52,7 +55,8 @@ export default function StoreModalProduceLicense(_ref) {
52
55
  licenseOptionSpec: specFromLicense(license),
53
56
  excludedLicenseIds: (_license$excludedLice = license.excludedLicenses) === null || _license$excludedLice === void 0 ? void 0 : _license$excludedLice.map(l => l.id)
54
57
  });
55
- }, [dispatch, license, isSelected]);
58
+ trackCustomized();
59
+ }, [dispatch, license, isSelected, trackCustomized]);
56
60
  const handleLicenseOptionChange = useCallback(_ref3 => {
57
61
  let {
58
62
  variableId,
@@ -72,7 +76,8 @@ export default function StoreModalProduceLicense(_ref) {
72
76
  variableId,
73
77
  optionId
74
78
  });
75
- }, [dispatch, license, isSelected]);
79
+ trackCustomized();
80
+ }, [dispatch, license, isSelected, trackCustomized]);
76
81
  useEffect(() => {
77
82
  if (license.defaultSelected) {
78
83
  var _license$excludedLice3;
@@ -11,7 +11,7 @@ export default function StoreModalProductLicenseSelection(_ref) {
11
11
  label
12
12
  }
13
13
  } = _ref;
14
- const collection = useFragment((_StoreModalProductLicenseSelection_collection.hash && _StoreModalProductLicenseSelection_collection.hash !== "2767ff3f8ddb4354051074bc3d0312e6" && 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 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);
15
15
  return /*#__PURE__*/React.createElement("div", {
16
16
  className: "store-modal__license-selection__container",
17
17
  "data-disabled": disabled
@@ -21,6 +21,7 @@ export default function StoreModalProductLicenseSelection(_ref) {
21
21
  className: "store-modal__license-selection__licenses"
22
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, {
23
23
  key: license.id,
24
- license: license
24
+ license: license,
25
+ collectionId: collection.id
25
26
  }))));
26
27
  }
@@ -67,7 +67,11 @@ const TestFontsFormComponent = _ref2 => {
67
67
  name,
68
68
  email,
69
69
  newsletterOptIn,
70
- recaptchaToken: token ?? undefined
70
+ recaptchaToken: token ?? undefined,
71
+ // Tells the server which form this was, so it can emit the
72
+ // matching analytics event: updateCustomer is shared with the
73
+ // checkout contact step and is otherwise indistinguishable.
74
+ form: 'TEST_FONTS'
71
75
  }
72
76
  },
73
77
  onCompleted: (res, errors) => {
@@ -19,7 +19,13 @@ const TypeTesterContent = /*#__PURE__*/React.forwardRef((_ref, ref) => {
19
19
  (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.focus();
20
20
  }
21
21
  }));
22
- const style = useTypeTesterStyler({
22
+
23
+ // `truncated` is pulled out of the spread: it drives a data attribute, not
24
+ // a DOM style prop.
25
+ const {
26
+ truncated,
27
+ ...style
28
+ } = useTypeTesterStyler({
23
29
  ...props,
24
30
  content,
25
31
  alignment
@@ -35,7 +41,8 @@ const TypeTesterContent = /*#__PURE__*/React.forwardRef((_ref, ref) => {
35
41
  useEffect(() => setMounted(true), []);
36
42
  const plainText = content.getCurrentContent().getPlainText();
37
43
  return /*#__PURE__*/React.createElement("div", _extends({}, style, {
38
- className: "type-tester__text__container"
44
+ className: "type-tester__text__container",
45
+ "data-truncated": truncated || undefined
39
46
  }), mounted ? /*#__PURE__*/React.createElement(Editor, {
40
47
  ref: editorRef,
41
48
  editorState: content,
@@ -2,7 +2,8 @@
2
2
 
3
3
  import "../../global-shim.js";
4
4
  import { ContentState, EditorState } from 'draft-js';
5
- import React, { createContext, useReducer } from 'react';
5
+ import React, { createContext, useContext, useReducer } from 'react';
6
+ import ConfigContext from '../ConfigContext.js';
6
7
  export const TypeTesterContext = /*#__PURE__*/createContext({});
7
8
  const updateTester = (state, id, newState, propagate) => {
8
9
  const testers = state.testers;
@@ -120,19 +121,31 @@ const groupReducer = (state, action) => {
120
121
  };
121
122
  }
122
123
  };
123
- const TypeTesterContextComponent = _ref => {
124
+ const clampSize = (size, _ref) => {
125
+ let {
126
+ min,
127
+ max
128
+ } = _ref;
129
+ return Math.min(Math.max(size, min), max);
130
+ };
131
+ const TypeTesterContextComponent = _ref2 => {
124
132
  let {
125
133
  children,
126
134
  testers: initialTestersState,
127
135
  defaultMode = 'group'
128
- } = _ref;
136
+ } = _ref2;
137
+ const {
138
+ typeTester: config
139
+ } = useContext(ConfigContext);
129
140
  const testers = Object.keys(initialTestersState).reduce((acc, id) => {
130
141
  const initialState = initialTestersState[id];
131
142
  const content = EditorState.createWithContent(ContentState.createFromText(initialState.content ?? ''));
132
143
  acc[id] = {
133
144
  content,
134
145
  contentEdited: false,
135
- size: initialState.size ?? 72,
146
+ // The size slider only spans the configured range, so a stored size
147
+ // outside it would render with the handle off the end of its track.
148
+ size: clampSize(initialState.size ?? 72, config.size),
136
149
  letterSpacing: initialState.letterSpacing ?? 0,
137
150
  lineHeight: initialState.lineHeight ?? 1,
138
151
  autofit: initialState.autofit,
@@ -6,9 +6,14 @@ const TypeTesterFitter = _ref => {
6
6
  children,
7
7
  ...props
8
8
  } = _ref;
9
- const style = useTypeTesterStyler(props);
9
+ const {
10
+ truncated,
11
+ ...style
12
+ } = useTypeTesterStyler(props);
10
13
  return /*#__PURE__*/React.createElement("div", _extends({
11
14
  className: "type-tester__text__container"
12
- }, style), children);
15
+ }, style, {
16
+ "data-truncated": truncated || undefined
17
+ }), children);
13
18
  };
14
19
  export default TypeTesterFitter;