fontdue-js 2.26.1 → 2.28.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 (85) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/__generated__/TypeTesterFamiliesQuery.graphql.d.ts +29 -0
  3. package/dist/__generated__/TypeTesterFamiliesQuery.graphql.js +174 -0
  4. package/dist/__generated__/TypeTesterFamiliesStylesQuery.graphql.d.ts +35 -0
  5. package/dist/__generated__/TypeTesterFamiliesStylesQuery.graphql.js +170 -0
  6. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.d.ts +1 -1
  7. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.js +4 -4
  8. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.d.ts +1 -3
  9. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.js +78 -161
  10. package/dist/__generated__/TypeTesterStyleSelectData_fontStyle.graphql.d.ts +2 -1
  11. package/dist/__generated__/TypeTesterStyleSelectData_fontStyle.graphql.js +18 -17
  12. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.d.ts +1 -1
  13. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.js +4 -4
  14. package/dist/__generated__/TypeTestersIDQuery.graphql.d.ts +1 -5
  15. package/dist/__generated__/TypeTestersIDQuery.graphql.js +101 -195
  16. package/dist/__generated__/TypeTestersRefetchQuery.graphql.d.ts +1 -1
  17. package/dist/__generated__/TypeTestersRefetchQuery.graphql.js +4 -4
  18. package/dist/__generated__/TypeTestersSlugQuery.graphql.d.ts +1 -3
  19. package/dist/__generated__/TypeTestersSlugQuery.graphql.js +102 -178
  20. package/dist/__generated__/orderTrackingUpdateOrderTrackingMutation.graphql.d.ts +27 -0
  21. package/dist/__generated__/orderTrackingUpdateOrderTrackingMutation.graphql.js +72 -0
  22. package/dist/components/Cart/CartOrder.js +8 -0
  23. package/dist/components/Cart/orderTracking.d.ts +10 -0
  24. package/dist/components/Cart/orderTracking.js +43 -0
  25. package/dist/components/ConfigContext.d.ts +2 -2
  26. package/dist/components/FontStyle/index.d.ts +2 -0
  27. package/dist/components/FontStyle/index.js +4 -2
  28. package/dist/components/FontdueProvider/FontdueProviderClientComponent.js +2 -1
  29. package/dist/components/TypeTester/TypeTesterFamilies.d.ts +37 -0
  30. package/dist/components/TypeTester/TypeTesterFamilies.js +128 -0
  31. package/dist/components/TypeTester/TypeTesterStandalone.js +2 -7
  32. package/dist/components/TypeTester/TypeTesterStandalone.server.js +1 -2
  33. package/dist/components/TypeTester/TypeTesterStyleSelect.d.ts +15 -5
  34. package/dist/components/TypeTester/TypeTesterStyleSelect.js +57 -47
  35. package/dist/components/TypeTester/TypeTesterStyleSelectData.d.ts +1 -3
  36. package/dist/components/TypeTester/TypeTesterStyleSelectData.js +210 -52
  37. package/dist/components/TypeTester/index.d.ts +1 -3
  38. package/dist/components/TypeTester/index.js +6 -6
  39. package/dist/components/TypeTester/useTypeTesterStyler.d.ts +4 -2
  40. package/dist/components/TypeTester/useTypeTesterStyler.js +78 -11
  41. package/dist/components/TypeTesters/index.js +6 -20
  42. package/dist/components/TypeTesters/index.server.js +2 -4
  43. package/dist/components/elements/StoreModalUnifiedCheckout.js +8 -0
  44. package/dist/components/useFontStyle.d.ts +7 -0
  45. package/dist/components/useFontStyle.js +2 -1
  46. package/dist/fontdue.css +8 -16
  47. package/dist/hooks.d.ts +1 -1
  48. package/dist/hooks.js +14 -1
  49. package/package.json +1 -1
  50. package/dist/__generated__/FontdueProviderQuery.graphql.d.ts +0 -19
  51. package/dist/__generated__/FontdueProviderQuery.graphql.js +0 -140
  52. package/dist/__generated__/ServerConfigProvider_viewer.graphql.d.ts +0 -23
  53. package/dist/__generated__/ServerConfigProvider_viewer.graphql.js +0 -57
  54. package/dist/__generated__/TestModeBanner_viewer.graphql.d.ts +0 -19
  55. package/dist/__generated__/TestModeBanner_viewer.graphql.js +0 -36
  56. package/dist/__generated__/ThemeConfig_viewer.graphql.d.ts +0 -19
  57. package/dist/__generated__/ThemeConfig_viewer.graphql.js +0 -36
  58. package/dist/__generated__/TypeTesterStyleSelectData_viewer.graphql.d.ts +0 -42
  59. package/dist/__generated__/TypeTesterStyleSelectData_viewer.graphql.js +0 -166
  60. package/dist/__generated__/TypeTester_viewer.graphql.d.ts +0 -17
  61. package/dist/__generated__/TypeTester_viewer.graphql.js +0 -40
  62. package/dist/__generated__/TypeTesters_viewer.graphql.d.ts +0 -17
  63. package/dist/__generated__/TypeTesters_viewer.graphql.js +0 -40
  64. package/dist/components/BuyingOptions/index.d.ts +0 -9
  65. package/dist/components/CookieNotification/index.d.ts +0 -13
  66. package/dist/components/FontdueContextProvider/index.d.ts +0 -17
  67. package/dist/components/FontdueContextProvider/index.js +0 -108
  68. package/dist/components/FontdueContextProvider/index.server.d.ts +0 -4
  69. package/dist/components/FontdueContextProvider/index.server.js +0 -7
  70. package/dist/components/FontdueProvider/useAuxUIOwner.d.ts +0 -1
  71. package/dist/components/FontdueProvider/useAuxUIOwner.js +0 -28
  72. package/dist/components/TypeTester/TypeTesterStandalone.preload.d.ts +0 -14
  73. package/dist/components/TypeTester/TypeTesterStandalone.preload.js +0 -20
  74. package/dist/config.d.ts +0 -7
  75. package/dist/config.js +0 -31
  76. package/dist/global-shim.d.ts +0 -1
  77. package/dist/global-shim.js +0 -8
  78. package/dist/hooks/useResizeObserver.d.ts +0 -11
  79. package/dist/hooks/useResizeObserver.js +0 -23
  80. package/dist/index.d.ts +0 -2
  81. package/dist/index.js +0 -1
  82. package/dist/loadFontdueProviderQuery.d.ts +0 -3
  83. package/dist/loadFontdueProviderQuery.js +0 -10
  84. package/dist/vite.d.ts +0 -2
  85. package/dist/vite.js +0 -139
@@ -4,19 +4,18 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = TypeTesterStyleSelectData;
7
- var _TypeTesterStyleSelectData_viewer2 = _interopRequireDefault(require("../../__generated__/TypeTesterStyleSelectData_viewer.graphql"));
8
7
  var _TypeTesterStyleSelectData_fontStyle2 = _interopRequireDefault(require("../../__generated__/TypeTesterStyleSelectData_fontStyle.graphql"));
9
8
  var _TypeTesterStyleSelectData_fontStyleData2 = _interopRequireDefault(require("../../__generated__/TypeTesterStyleSelectData_fontStyleData.graphql"));
10
9
  var _react = _interopRequireWildcard(require("react"));
11
10
  var _reactRelay = require("react-relay");
11
+ var _relayRuntime = require("relay-runtime");
12
12
  var _utils = require("../../utils");
13
13
  var _TypeTesterStyleSelect = _interopRequireDefault(require("./TypeTesterStyleSelect"));
14
- var _relayRuntime = require("relay-runtime");
14
+ var _TypeTesterFamilies = require("./TypeTesterFamilies");
15
15
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
16
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
18
  _TypeTesterStyleSelectData_fontStyleData2.default.hash && _TypeTesterStyleSelectData_fontStyleData2.default.hash !== "fb09207ccab08c7e73677486469a3c95" && console.error("The definition of 'TypeTesterStyleSelectData_fontStyleData' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTesterStyleSelectData_fontStyleData2.default;
19
-
20
19
  // get a score for similarity between two styles
21
20
  // 0 < n <= 1
22
21
  // where 1 = exact match
@@ -37,77 +36,236 @@ const computeSimilarity = (left, right) => {
37
36
  const stretchSimilarity = left.cssStretch === right.cssStretch ? 1 : 0.9;
38
37
  return weightSimilarity * styleSimilarity * stretchSimilarity;
39
38
  };
40
- function TypeTesterStyleSelectData(_ref) {
41
- var _viewer$families, _viewer$families$edge, _fontStyle$variableIn;
39
+
40
+ // Build the `selectedFamily` shape the dropdown expects. Uses the lazily
41
+ // fetched styles when available, otherwise stubs the current family with just
42
+ // the current style so the collapsed selector still shows something meaningful.
43
+ function buildSelectedFamily(selectedFamilyId, entry, fontStyle, families) {
44
+ var _fontStyle$family, _families$find, _fontStyle$family2;
45
+ const currentFamilyId = ((_fontStyle$family = fontStyle.family) === null || _fontStyle$family === void 0 ? void 0 : _fontStyle$family.id) ?? '';
46
+ const name = (families === null || families === void 0 ? void 0 : (_families$find = families.find(f => f.id === selectedFamilyId)) === null || _families$find === void 0 ? void 0 : _families$find.name) ?? (selectedFamilyId === currentFamilyId ? (_fontStyle$family2 = fontStyle.family) === null || _fontStyle$family2 === void 0 ? void 0 : _fontStyle$family2.name : undefined) ?? '';
47
+ if (entry !== null && entry !== void 0 && entry.styles) {
48
+ return {
49
+ id: selectedFamilyId,
50
+ name,
51
+ isVariableFont: entry.isVariableFont,
52
+ fontStyles: entry.styles.map(style => ({
53
+ id: style.id,
54
+ name: style.name,
55
+ variableInstances: style.variableInstances
56
+ }))
57
+ };
58
+ }
59
+ if (selectedFamilyId === currentFamilyId) {
60
+ var _fontStyle$variableIn;
61
+ return {
62
+ id: selectedFamilyId,
63
+ name,
64
+ isVariableFont: (((_fontStyle$variableIn = fontStyle.variableInstances) === null || _fontStyle$variableIn === void 0 ? void 0 : _fontStyle$variableIn.length) ?? 0) > 0,
65
+ fontStyles: [{
66
+ id: fontStyle.id,
67
+ name: fontStyle.name,
68
+ variableInstances: fontStyle.variableInstances ?? null
69
+ }]
70
+ };
71
+ }
72
+
73
+ // Switched to a family whose styles haven't arrived yet.
74
+ return {
75
+ id: selectedFamilyId,
76
+ name,
77
+ isVariableFont: false,
78
+ fontStyles: null
79
+ };
80
+ }
81
+ function TypeTesterStyleSelectSelectable(_ref) {
82
+ var _fontStyle$family3;
42
83
  let {
43
- fontStyle: fontStyleKey,
44
- viewer: viewerKey,
84
+ fontStyle,
85
+ variableSettings,
45
86
  onSelectFontStyleValue,
46
87
  onSelectVariableAxes,
47
- config,
48
- variableSettings,
49
- includeVariableAxesOption
88
+ includeVariableAxesOption,
89
+ fallbackName
50
90
  } = _ref;
51
- const fontStyle = (0, _reactRelay.useFragment)((_TypeTesterStyleSelectData_fontStyle2.default.hash && _TypeTesterStyleSelectData_fontStyle2.default.hash !== "187778816a910be9ab709431d1d818b3" && console.error("The definition of 'TypeTesterStyleSelectData_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTesterStyleSelectData_fontStyle2.default), fontStyleKey);
52
- const viewer = (0, _reactRelay.useFragment)((_TypeTesterStyleSelectData_viewer2.default.hash && _TypeTesterStyleSelectData_viewer2.default.hash !== "5e5834d9202f90bdde5a0a7db49e8639" && console.error("The definition of 'TypeTesterStyleSelectData_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTesterStyleSelectData_viewer2.default), viewerKey);
53
- const familyData = viewer === null || viewer === void 0 ? void 0 : (_viewer$families = viewer.families) === null || _viewer$families === void 0 ? void 0 : (_viewer$families$edge = _viewer$families.edges) === null || _viewer$families$edge === void 0 ? void 0 : _viewer$families$edge.map(edge => edge.node).filter(_utils.notEmpty).filter(family => {
54
- var _family$featureStyle, _family$featureStyle$;
55
- return (// make sure the family options' supportedLanguages intersect with the current selection's supportedLanuages.
56
- // e.g. if the current font style only supports 'ar', only show families that include 'ar'
57
- (_family$featureStyle = family.featureStyle) === null || _family$featureStyle === void 0 ? void 0 : (_family$featureStyle$ = _family$featureStyle.supportedLanguages) === null || _family$featureStyle$ === void 0 ? void 0 : _family$featureStyle$.some(lang => {
58
- var _fontStyle$supportedL;
59
- return (_fontStyle$supportedL = fontStyle.supportedLanguages) === null || _fontStyle$supportedL === void 0 ? void 0 : _fontStyle$supportedL.includes(lang);
60
- })
61
- );
62
- });
91
+ const {
92
+ families,
93
+ loadingFamilies,
94
+ loadFamilies,
95
+ getFamilyStyles,
96
+ loadFamilyStyles
97
+ } = (0, _TypeTesterFamilies.useTypeTesterFamilies)();
98
+ const currentFamilyId = ((_fontStyle$family3 = fontStyle.family) === null || _fontStyle$family3 === void 0 ? void 0 : _fontStyle$family3.id) ?? '';
99
+
100
+ // The family the user has switched to but whose styles are still loading.
101
+ // We keep the style selector + font on the *current* family until this
102
+ // family's data is ready, then commit family + style + font together — so the
103
+ // toolbar never flashes a blank style selector mid-switch.
104
+ const [pendingFamilyId, setPendingFamilyId] = (0, _react.useState)(null);
105
+
106
+ // Tracks the family we've already submitted a style pick for, so the pick
107
+ // effect fires exactly once per switch (it stays mounted until the font
108
+ // commits, after which `fontStyle` would otherwise retrigger it).
109
+ const submittedFamilyRef = (0, _react.useRef)(null);
110
+
111
+ // While a switch is in flight the pick sets the live variableSettings ahead
112
+ // of the font committing; freezing to the last settled value keeps the style
113
+ // selector showing the active font's instance until they realign.
114
+ const frozenVariableSettingsRef = (0, _react.useRef)(variableSettings);
115
+ const handleOpen = (0, _react.useCallback)(() => {
116
+ loadFamilies();
117
+ loadFamilyStyles(currentFamilyId);
118
+ }, [loadFamilies, loadFamilyStyles, currentFamilyId]);
63
119
  const handleSelectFamilyId = (0, _react.useCallback)(id => {
64
- const family = familyData && familyData.find(family => family.id === id);
65
- if (!family) return;
120
+ if (id === currentFamilyId) {
121
+ // Re-selecting the active family cancels any in-flight switch.
122
+ setPendingFamilyId(null);
123
+ submittedFamilyRef.current = null;
124
+ return;
125
+ }
126
+ loadFamilyStyles(id);
127
+ setPendingFamilyId(id);
128
+ }, [loadFamilyStyles, currentFamilyId]);
129
+ const pendingEntry = pendingFamilyId ? getFamilyStyles(pendingFamilyId) : undefined;
130
+
131
+ // A switch is visibly in flight from the moment the user picks a new family
132
+ // until the font actually commits to it (currentFamilyId catches up).
133
+ const switchingToPending = !!pendingFamilyId && currentFamilyId !== pendingFamilyId;
134
+
135
+ // Once the picked family's styles arrive, choose a matching style and report
136
+ // it upward. We deliberately keep `pendingFamilyId` set (and the busy cue on)
137
+ // until the font actually switches — clearing it here would briefly revert the
138
+ // family dropdown during the changed-styles round-trip.
139
+ (0, _react.useEffect)(() => {
140
+ if (!pendingFamilyId || !pendingEntry || pendingEntry.loading || !pendingEntry.styles) {
141
+ return;
142
+ }
143
+ if (submittedFamilyRef.current === pendingFamilyId) return;
144
+ submittedFamilyRef.current = pendingFamilyId;
145
+ const styles = pendingEntry.styles;
66
146
  let fontStyleId = fontStyle.id;
67
- let variableSettings;
68
- if (family.isVariableFont) {
69
- var _family$fontStyles;
70
- const newFontStyle = (_family$fontStyles = family.fontStyles) === null || _family$fontStyles === void 0 ? void 0 : _family$fontStyles[0];
147
+ let newVariableSettings;
148
+ if (pendingEntry.isVariableFont) {
149
+ const newFontStyle = styles[0];
71
150
  if (newFontStyle) {
72
151
  var _newFontStyle$variabl;
73
152
  fontStyleId = newFontStyle.id;
74
153
  const instance = (_newFontStyle$variabl = newFontStyle.variableInstances) === null || _newFontStyle$variabl === void 0 ? void 0 : _newFontStyle$variabl[0];
75
- if (instance) {
76
- variableSettings = (0, _utils.variableInstanceSettings)(instance);
77
- }
154
+ if (instance) newVariableSettings = (0, _utils.variableInstanceSettings)(instance);
78
155
  }
79
- } else {
80
- var _family$fontStyles2;
81
- const styleWeightDifferences = ((_family$fontStyles2 = family.fontStyles) === null || _family$fontStyles2 === void 0 ? void 0 : _family$fontStyles2.map(right => {
82
- return {
83
- id: right.id,
84
- similarity: computeSimilarity(fontStyle, right)
85
- };
86
- }).filter(_utils.notEmpty)) || [];
87
- fontStyleId = styleWeightDifferences.reduce((closest, current) => {
88
- if (current.similarity > closest.similarity) return current;
89
- return closest;
90
- }).id;
156
+ } else if (styles.length > 0) {
157
+ fontStyleId = styles.map(right => ({
158
+ id: right.id,
159
+ similarity: computeSimilarity(fontStyle, right)
160
+ })).reduce((closest, current) => current.similarity > closest.similarity ? current : closest).id;
91
161
  }
92
162
  onSelectFontStyleValue({
93
163
  fontStyleId,
94
- variableSettings
164
+ variableSettings: newVariableSettings
95
165
  });
96
- }, [familyData, fontStyle]);
97
- const instance = variableSettings ? (_fontStyle$variableIn = fontStyle.variableInstances) === null || _fontStyle$variableIn === void 0 ? void 0 : _fontStyle$variableIn.find(instance => (0, _utils.compareVariableSettings)(instance, variableSettings)) : null;
98
- return config.selectable && familyData !== null && familyData !== void 0 && familyData.length ? /*#__PURE__*/_react.default.createElement(_TypeTesterStyleSelect.default, {
99
- families: familyData,
166
+ }, [pendingFamilyId, pendingEntry, fontStyle, onSelectFontStyleValue]);
167
+
168
+ // The font has actually switched to the picked family drop the pending
169
+ // state, which lifts the busy cue and settles the family dropdown on it.
170
+ (0, _react.useEffect)(() => {
171
+ if (pendingFamilyId && currentFamilyId === pendingFamilyId) {
172
+ setPendingFamilyId(null);
173
+ submittedFamilyRef.current = null;
174
+ }
175
+ }, [currentFamilyId, pendingFamilyId]);
176
+
177
+ // Capture the variableSettings while settled so it can be held frozen for the
178
+ // duration of a switch (see `frozenVariableSettingsRef`).
179
+ (0, _react.useEffect)(() => {
180
+ if (!switchingToPending) frozenVariableSettingsRef.current = variableSettings;
181
+ }, [switchingToPending, variableSettings]);
182
+
183
+ // Family options, filtered so they intersect the current style's languages
184
+ // (e.g. an Arabic-only style only offers families that also support Arabic).
185
+ const familyOptions = (0, _react.useMemo)(() => {
186
+ if (!families) {
187
+ return fontStyle.family ? [{
188
+ id: fontStyle.family.id,
189
+ name: fontStyle.family.name
190
+ }] : [];
191
+ }
192
+ return families.filter(family => {
193
+ var _family$featureStyle, _family$featureStyle$;
194
+ return (_family$featureStyle = family.featureStyle) === null || _family$featureStyle === void 0 ? void 0 : (_family$featureStyle$ = _family$featureStyle.supportedLanguages) === null || _family$featureStyle$ === void 0 ? void 0 : _family$featureStyle$.some(lang => {
195
+ var _fontStyle$supportedL;
196
+ return (_fontStyle$supportedL = fontStyle.supportedLanguages) === null || _fontStyle$supportedL === void 0 ? void 0 : _fontStyle$supportedL.includes(lang);
197
+ });
198
+ }).map(family => ({
199
+ id: family.id,
200
+ name: family.name
201
+ }));
202
+ }, [families, fontStyle.family, fontStyle.supportedLanguages]);
203
+ if (!fontStyle.family) {
204
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, fallbackName);
205
+ }
206
+
207
+ // Once the list has loaded, a zero-language font (no family shares the current
208
+ // style's languages) falls back to the plain name, matching the
209
+ // non-selectable rendering. See FD-628.
210
+ if (families && familyOptions.length === 0) {
211
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, fallbackName);
212
+ }
213
+
214
+ // Style options always reflect the *active* font's family. When a pending
215
+ // switch commits, `fontStyle` updates and this moves to the new family — so
216
+ // the style selector + font transition together with no blank intermediate.
217
+ const selectedFamily = buildSelectedFamily(currentFamilyId, getFamilyStyles(currentFamilyId), fontStyle, families);
218
+
219
+ // Hold the displayed instance on the active font's value during a switch; once
220
+ // it commits, use the live (now-synced) value so axis drags etc. flow through.
221
+ const effectiveVariableSettings = switchingToPending ? frozenVariableSettingsRef.current : variableSettings;
222
+ return /*#__PURE__*/_react.default.createElement(_TypeTesterStyleSelect.default, {
223
+ families: familyOptions,
224
+ selectedFamily: selectedFamily
225
+ // Hold the displayed family on the active font's family until the switch
226
+ // commits, so both selectors + the font flip together. The busy cue is
227
+ // the only immediate feedback while the picked family's styles load.
228
+ ,
229
+ familyValue: currentFamilyId,
100
230
  selectedFontStyleId: fontStyle.id,
231
+ selectedVariableSettings: effectiveVariableSettings,
232
+ loadingFamilies: loadingFamilies && !families,
233
+ loading: switchingToPending,
234
+ onOpen: handleOpen,
235
+ onSelectFamilyId: handleSelectFamilyId,
101
236
  onSelectFontStyleValue: onSelectFontStyleValue,
102
237
  onSelectVariableAxes: onSelectVariableAxes,
103
- onSelectFamilyId: handleSelectFamilyId,
104
- selectedVariableSettings: variableSettings,
105
238
  includeVariableAxesOption: includeVariableAxesOption
106
- }) : fontStyle.family && /*#__PURE__*/_react.default.createElement("div", {
239
+ });
240
+ }
241
+ function TypeTesterStyleSelectData(_ref2) {
242
+ var _fontStyle$variableIn2;
243
+ let {
244
+ fontStyle: fontStyleKey,
245
+ onSelectFontStyleValue,
246
+ onSelectVariableAxes,
247
+ config,
248
+ variableSettings,
249
+ includeVariableAxesOption
250
+ } = _ref2;
251
+ const fontStyle = (0, _reactRelay.useFragment)((_TypeTesterStyleSelectData_fontStyle2.default.hash && _TypeTesterStyleSelectData_fontStyle2.default.hash !== "368fd433966e05fc6ebecd2b60c71e3f" && console.error("The definition of 'TypeTesterStyleSelectData_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTesterStyleSelectData_fontStyle2.default), fontStyleKey);
252
+ const instance = variableSettings ? (_fontStyle$variableIn2 = fontStyle.variableInstances) === null || _fontStyle$variableIn2 === void 0 ? void 0 : _fontStyle$variableIn2.find(inst => (0, _utils.compareVariableSettings)(inst, variableSettings)) : null;
253
+ const name = fontStyle.family ? /*#__PURE__*/_react.default.createElement("div", {
107
254
  className: "type-tester__name"
108
255
  }, /*#__PURE__*/_react.default.createElement("span", {
109
256
  className: "type-tester__name__family"
110
257
  }, fontStyle.family.name), ' ', /*#__PURE__*/_react.default.createElement("span", {
111
258
  className: "type-tester__name__style"
112
- }, variableSettings ? (instance === null || instance === void 0 ? void 0 : instance.name) ?? 'Custom' : fontStyle.name.replace(/ /g, '\xa0')));
259
+ }, variableSettings ? (instance === null || instance === void 0 ? void 0 : instance.name) ?? 'Custom' : fontStyle.name.replace(/ /g, '\xa0'))) : null;
260
+ if (!config.selectable) {
261
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, name);
262
+ }
263
+ return /*#__PURE__*/_react.default.createElement(TypeTesterStyleSelectSelectable, {
264
+ fontStyle: fontStyle,
265
+ variableSettings: variableSettings,
266
+ onSelectFontStyleValue: onSelectFontStyleValue,
267
+ onSelectVariableAxes: onSelectVariableAxes,
268
+ includeVariableAxesOption: includeVariableAxesOption,
269
+ fallbackName: name
270
+ });
113
271
  }
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import { TypeTester_viewer$key } from '../../__generated__/TypeTester_viewer.graphql';
3
2
  import { TypeTester_fontStyle$key } from '../../__generated__/TypeTester_fontStyle.graphql';
4
3
  import { Alignment, TypeTesterAxesPosition } from './types';
5
4
  export interface TypeTesterConfig {
@@ -16,7 +15,7 @@ export interface TypeTesterConfig {
16
15
  toolsPosition?: 'inline' | 'floating';
17
16
  selectButtonStyle?: 'inline' | 'outlined';
18
17
  selectButtonLabel?: string;
19
- truncate?: boolean;
18
+ truncate?: boolean | number;
20
19
  bulletStyle?: 'square' | 'round';
21
20
  priceText?: boolean;
22
21
  autofitOnChange?: boolean;
@@ -67,7 +66,6 @@ export interface TypeTesterBaseProps {
67
66
  }
68
67
  interface TypeTesterProps extends TypeTesterBaseProps {
69
68
  fontStyle: TypeTester_fontStyle$key | null;
70
- viewer: TypeTester_viewer$key;
71
69
  onStyleSelect: (fontStyleId: string) => void;
72
70
  tags?: readonly string[] | null;
73
71
  isLoading?: boolean;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _TypeTester_viewer2 = _interopRequireDefault(require("../../__generated__/TypeTester_viewer.graphql"));
8
7
  var _TypeTester_fontStyle2 = _interopRequireDefault(require("../../__generated__/TypeTester_fontStyle.graphql"));
9
8
  var _react = _interopRequireWildcard(require("react"));
10
9
  var _reactRelay = require("react-relay");
@@ -30,7 +29,6 @@ const TypeTester = _ref => {
30
29
  id,
31
30
  productId,
32
31
  fontStyle: fontStyleKey,
33
- viewer: viewerKey,
34
32
  tags,
35
33
  onFocus,
36
34
  onBlur,
@@ -41,7 +39,6 @@ const TypeTester = _ref => {
41
39
  isLoading
42
40
  } = _ref;
43
41
  const fontStyle = (0, _reactRelay.useFragment)((_TypeTester_fontStyle2.default.hash && _TypeTester_fontStyle2.default.hash !== "bf89a32c8a0661f2102bbcf8c6bd1aa7" && console.error("The definition of 'TypeTester_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTester_fontStyle2.default), fontStyleKey);
44
- const viewer = (0, _reactRelay.useFragment)((_TypeTester_viewer2.default.hash && _TypeTester_viewer2.default.hash !== "26c2b3145462407675ab9b202b0d1b4c" && console.error("The definition of 'TypeTester_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTester_viewer2.default), viewerKey);
45
42
  const contentRef = (0, _react.useRef)(null);
46
43
  const ref = (0, _react.useRef)(null);
47
44
  const [buyButtonIsHovered, setBuyButtonIsHovered] = (0, _react.useState)(false);
@@ -57,7 +54,9 @@ const TypeTester = _ref => {
57
54
  // inline axis slider, so updating it mid-drag changes its width and shifts
58
55
  // the slider. Debounce the value used purely for the name display — the font
59
56
  // preview and sliders keep reading the live `props.variableSettings`.
60
- const nameVariableSettings = (0, _hooks.useDebouncedValue)(props.variableSettings, 200);
57
+ // Keyed on the font style id so switching fonts snaps the instance name
58
+ // immediately (in sync with the new style) rather than lagging the switch.
59
+ const nameVariableSettings = (0, _hooks.useDebouncedValue)(props.variableSettings, 200, fontStyle === null || fontStyle === void 0 ? void 0 : fontStyle.id);
61
60
  (0, _react.useEffect)(() => {
62
61
  if (onFocus && props.focused) onFocus();
63
62
  if (onBlur && !props.focused) onBlur();
@@ -94,7 +93,6 @@ const TypeTester = _ref => {
94
93
  }, /*#__PURE__*/_react.default.createElement(_TypeTesterStyleSelectData.default, {
95
94
  fontStyle: fontStyle,
96
95
  variableSettings: nameVariableSettings,
97
- viewer: viewer,
98
96
  onSelectFontStyleValue: _ref2 => {
99
97
  let {
100
98
  fontStyleId,
@@ -153,12 +151,14 @@ const TypeTester = _ref => {
153
151
  }, _ref3 => {
154
152
  let {
155
153
  loaded,
156
- style
154
+ style,
155
+ verticalMetrics
157
156
  } = _ref3;
158
157
  if (loaded) {
159
158
  return /*#__PURE__*/_react.default.createElement(_TypeTesterContent.default, _extends({}, props, {
160
159
  ref: contentRef,
161
160
  fontStyles: style,
161
+ verticalMetrics: verticalMetrics,
162
162
  truncate: config.truncate,
163
163
  direction: props.direction,
164
164
  min: config.size.min,
@@ -1,6 +1,7 @@
1
1
  import { EditorState } from 'draft-js';
2
2
  import { Alignment } from './types';
3
3
  import { VariableSettings } from '../../utils';
4
+ import { VerticalMetrics } from '../useFontStyle';
4
5
  export type ColumnsConfig = false | {
5
6
  count: 'auto' | number;
6
7
  width: string;
@@ -19,15 +20,16 @@ export interface UseTypeTesterStylerProps {
19
20
  letterSpacing: number;
20
21
  features: string[];
21
22
  focused: boolean;
22
- truncate: boolean;
23
+ truncate: boolean | number;
23
24
  fontStyles: React.CSSProperties;
25
+ verticalMetrics?: VerticalMetrics | null;
24
26
  content: EditorState;
25
27
  contentEdited: boolean;
26
28
  alignment: Alignment;
27
29
  variableSettings: VariableSettings | null;
28
30
  columns: ColumnsConfig;
29
31
  }
30
- declare const useTypeTesterStyler: ({ size, autofit, autofitOnChange, features, setSize, min, max, truncate, focused, lineHeight, letterSpacing, fontStyles, content, contentEdited, alignment, variableSettings, columns, }: UseTypeTesterStylerProps) => {
32
+ declare const useTypeTesterStyler: ({ size, autofit, autofitOnChange, features, setSize, min, max, truncate, focused, lineHeight, letterSpacing, fontStyles, verticalMetrics, content, contentEdited, alignment, variableSettings, columns, }: UseTypeTesterStylerProps) => {
31
33
  ref: import("react").RefObject<HTMLDivElement | null>;
32
34
  style: import("react").CSSProperties;
33
35
  };
@@ -21,6 +21,7 @@ const useTypeTesterStyler = _ref => {
21
21
  lineHeight,
22
22
  letterSpacing,
23
23
  fontStyles,
24
+ verticalMetrics,
24
25
  content,
25
26
  contentEdited,
26
27
  alignment,
@@ -57,23 +58,89 @@ const useTypeTesterStyler = _ref => {
57
58
  });
58
59
  }
59
60
  }, [autofitSize]);
60
- const shouldTruncate = truncate && !focused;
61
+
62
+ // `truncate` is `true` (1 line), a positive number (that many lines), or
63
+ // falsy (off). Editing always shows the full paragraph, so only truncate
64
+ // when the tester is not focused.
65
+ const truncateLines = truncate === true ? 1 : typeof truncate === 'number' ? Math.max(0, Math.floor(truncate)) : 0;
66
+ const shouldTruncate = truncateLines > 0 && !focused;
67
+
68
+ // How far a line's ink spills past its line-box, top and bottom, as a fraction
69
+ // of the font-size (em). `fontLoader` feeds these exact metrics into the
70
+ // @font-face as ascent/descent-override, so this matches the browser's line
71
+ // geometry precisely. It is 0 once the line-height is at least the font's
72
+ // natural content height ((ascender − descender) / unitsPerEm) — i.e. for any
73
+ // normal reading line-height — and only grows for tight, overlapping setting.
74
+ // Kept in em (not px) so it scales with the *rendered* size, including the
75
+ // `--type-tester--adjustment` that shrinks the preview on mobile.
76
+ const lineOverflowEm = shouldTruncate && verticalMetrics && verticalMetrics.unitsPerEm > 0 ? Math.max(0, ((verticalMetrics.ascender - verticalMetrics.descender) / verticalMetrics.unitsPerEm - lineHeight) / 2) : 0;
77
+
78
+ // Columns are what make truncation overflow flow sideways — into clipped,
79
+ // off-screen columns — instead of stacking below the last visible line. That
80
+ // is what lets the metric padding reveal descenders/ascenders cleanly, and it
81
+ // means a multi-column tester (e.g. a 3-column specimen) truncates to
82
+ // `truncateLines` lines *per column*. When truncating with columns disabled we
83
+ // still establish a single column so the clipped overflow behaves the same way.
84
+ const columnStyle = columns ? {
85
+ columnCount: columns.count,
86
+ columnWidth: columns.width,
87
+ columnGap: columns.gap
88
+ } : undefined;
89
+
90
+ // A glyph's side bearings — ink that sits outside its advance width, like a
91
+ // leading lowercase 'j' or an italic overhang — would be shorn by the inline
92
+ // clip. Per-glyph bearings aren't in the font metrics, so reserve a flat 0.1em
93
+ // on each inline edge and cancel it with a negative margin so
94
+ // wrapping and text position stay put.
95
+ const sideBearing = shouldTruncate ? '0.1em' : '0px';
61
96
  const style = {
62
97
  fontSize: autofit ? `${size}px` : `calc(var(--type-tester--adjustment, 1) * ${size}px)`,
63
98
  lineHeight: `${lineHeight}`,
64
- height: shouldTruncate ? `${size * lineHeight}px` : 'auto',
65
- overflow: shouldTruncate ? 'hidden' : 'visible',
99
+ ...(shouldTruncate ? {
100
+ // Cap each column at `truncateLines` lines. `max-height` (not `height`)
101
+ // lets shorter paragraphs collapse to their content instead of padding
102
+ // out to N lines of whitespace. Columns send the clipped overflow
103
+ // sideways (into off-screen columns) rather than below, so the last
104
+ // visible line of every column is never partially covered by the next
105
+ // one. That lets the symmetric `lineOverflowEm` padding fully reveal the
106
+ // first line's ascenders/accents and the last line's descenders at any
107
+ // line-height; the negative margins cancel that padding in layout so a
108
+ // collapsed paragraph still occupies exactly its line count.
109
+ //
110
+ // Everything is in `em` so it tracks the rendered font-size (including
111
+ // the mobile `--type-tester--adjustment`); the `+ 2px` is slack that
112
+ // keeps sub-pixel rounding from dropping the Nth line (the extra never
113
+ // reveals line N+1 because that line is clipped out to the side).
114
+ boxSizing: 'border-box',
115
+ maxHeight: `calc(${truncateLines * lineHeight + 2 * lineOverflowEm}em + 2px)`,
116
+ paddingTop: `${lineOverflowEm}em`,
117
+ paddingBottom: `${lineOverflowEm}em`,
118
+ paddingLeft: sideBearing,
119
+ paddingRight: sideBearing,
120
+ marginTop: `${-lineOverflowEm}em`,
121
+ marginBottom: `${-lineOverflowEm}em`,
122
+ overflow: 'hidden',
123
+ // Fill each column to its full N lines before spilling to the next, and
124
+ // allow a final column with a single line — otherwise the default
125
+ // `widows: 2` / `column-fill: balance` rebalance a 4-line paragraph into
126
+ // two columns of 2, showing N−1 lines instead of N.
127
+ columnFill: 'auto',
128
+ widows: 1,
129
+ orphans: 1,
130
+ ...(columnStyle ?? {
131
+ columnCount: 1
132
+ })
133
+ } : {
134
+ height: 'auto',
135
+ overflow: 'visible',
136
+ ...columnStyle
137
+ }),
66
138
  fontFeatureSettings,
67
- marginRight: alignment !== 'right' ? -sideBuffer : 0,
68
- marginLeft: alignment === 'right' ? -sideBuffer : 0,
139
+ marginRight: `calc(${alignment !== 'right' ? -sideBuffer : 0}px - ${sideBearing})`,
140
+ marginLeft: `calc(${alignment === 'right' ? -sideBuffer : 0}px - ${sideBearing})`,
69
141
  letterSpacing: `${letterSpacing}em`,
70
142
  fontVariationSettings,
71
- textAlign: alignment,
72
- ...(columns && {
73
- columnCount: columns.count,
74
- columnWidth: columns.width,
75
- columnGap: columns.gap
76
- })
143
+ textAlign: alignment
77
144
  };
78
145
  return {
79
146
  ref,
@@ -9,7 +9,6 @@ exports.TypeTestersPreloadedSlugQueryRenderer = TypeTestersPreloadedSlugQueryRen
9
9
  exports.default = TypeTesters;
10
10
  var _TypeTestersSlugQuery2 = _interopRequireDefault(require("../../__generated__/TypeTestersSlugQuery.graphql"));
11
11
  var _TypeTestersIDQuery2 = _interopRequireDefault(require("../../__generated__/TypeTestersIDQuery.graphql"));
12
- var _TypeTesters_viewer2 = _interopRequireDefault(require("../../__generated__/TypeTesters_viewer.graphql"));
13
12
  var _TypeTesters_collection2 = _interopRequireDefault(require("../../__generated__/TypeTesters_collection.graphql"));
14
13
  var _TypeTestersChangedStylesQuery2 = _interopRequireDefault(require("../../__generated__/TypeTestersChangedStylesQuery.graphql"));
15
14
  var _react = _interopRequireWildcard(require("react"));
@@ -61,7 +60,6 @@ function TypeTestersComponent(_ref) {
61
60
  var _collection$typeTeste2, _collection$typeTeste3, _collection$typeTeste4, _collection$typeTeste5;
62
61
  let {
63
62
  collection: collectionKey,
64
- viewer: viewerKey,
65
63
  defaultMode,
66
64
  autofit,
67
65
  features: featuresOverride,
@@ -99,7 +97,6 @@ function TypeTestersComponent(_ref) {
99
97
  }
100
98
  });
101
99
  }, [environment, changedStyles, licenseOptionsSpecs, orderVariableSelections]);
102
- const viewer = (0, _reactRelay.useFragment)((_TypeTesters_viewer2.default.hash && _TypeTesters_viewer2.default.hash !== "8fbadb67d6506fee0f3ccc09624517c6" && console.error("The definition of 'TypeTesters_viewer' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTesters_viewer2.default), viewerKey);
103
100
  const precartOpen = (0, _reactRedux.useSelector)(state => state.precartOpen);
104
101
  const refetchVariables = (0, _react.useMemo)(() => collection !== null && collection !== void 0 && collection.id ? {
105
102
  id: collection.id,
@@ -184,7 +181,6 @@ function TypeTestersComponent(_ref) {
184
181
  productId: productId,
185
182
  features: features,
186
183
  axes: collection.typeTesterAxes,
187
- viewer: viewer,
188
184
  tags: node.tags
189
185
  }));
190
186
  }));
@@ -194,7 +190,7 @@ function TypeTestersComponent(_ref) {
194
190
  // TODO: add a new component that will be used to query the changed styles, then the base component can call usePreloadedQuery
195
191
  // https://relay.dev/docs/api-reference/use-query-loader/
196
192
 
197
- const idQuery = (_TypeTestersIDQuery2.default.hash && _TypeTestersIDQuery2.default.hash !== "918c212b46e9aaaceeebe47935bb65d7" && console.error("The definition of 'TypeTestersIDQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTestersIDQuery2.default);
193
+ const idQuery = (_TypeTestersIDQuery2.default.hash && _TypeTestersIDQuery2.default.hash !== "d3bbb8623a258269114d6e16169cab6b" && console.error("The definition of 'TypeTestersIDQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTestersIDQuery2.default);
198
194
  function TypeTestersPreloadedIDQueryRenderer(_ref3) {
199
195
  let {
200
196
  preloadedQuery,
@@ -211,21 +207,17 @@ function TypeTestersIDQueryRenderer(_ref4) {
211
207
  excludeTags,
212
208
  ...rest
213
209
  } = _ref4;
214
- const {
215
- typeTester: config
216
- } = (0, _react.useContext)(_ConfigContext.default);
217
210
  const data = (0, _reactRelay.useLazyLoadQuery)(idQuery, {
218
211
  collectionId,
219
212
  tags,
220
- excludeTags,
221
- selectable: config.selectable
213
+ excludeTags
222
214
  });
223
215
  return /*#__PURE__*/_react.default.createElement(TypeTestersComponent, _extends({}, data, rest, {
224
216
  tags: tags,
225
217
  excludeTags: excludeTags
226
218
  }));
227
219
  }
228
- const slugQuery = (_TypeTestersSlugQuery2.default.hash && _TypeTestersSlugQuery2.default.hash !== "6182a027996d13aaa03264bae680252c" && console.error("The definition of 'TypeTestersSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTestersSlugQuery2.default);
220
+ const slugQuery = (_TypeTestersSlugQuery2.default.hash && _TypeTestersSlugQuery2.default.hash !== "a7965166991a9ccda985c694ac45a093" && console.error("The definition of 'TypeTestersSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TypeTestersSlugQuery2.default);
229
221
  function TypeTestersPreloadedSlugQueryRenderer(_ref5) {
230
222
  var _data$viewer$slug;
231
223
  let {
@@ -235,8 +227,7 @@ function TypeTestersPreloadedSlugQueryRenderer(_ref5) {
235
227
  const queryRef = (0, _useSerializablePreloadedQuery.default)(preloadedQuery);
236
228
  const data = (0, _reactRelay.usePreloadedQuery)(slugQuery, queryRef);
237
229
  return /*#__PURE__*/_react.default.createElement(TypeTestersComponent, _extends({
238
- collection: ((_data$viewer$slug = data.viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection) ?? null,
239
- viewer: data.viewer
230
+ collection: ((_data$viewer$slug = data.viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection) ?? null
240
231
  }, rest));
241
232
  }
242
233
  function TypeTestersSlugQueryRenderer(_ref6) {
@@ -247,18 +238,13 @@ function TypeTestersSlugQueryRenderer(_ref6) {
247
238
  excludeTags,
248
239
  ...rest
249
240
  } = _ref6;
250
- const {
251
- typeTester: config
252
- } = (0, _react.useContext)(_ConfigContext.default);
253
241
  const data = (0, _reactRelay.useLazyLoadQuery)(slugQuery, {
254
242
  collectionSlug,
255
243
  tags,
256
- excludeTags,
257
- selectable: config.selectable
244
+ excludeTags
258
245
  });
259
246
  return /*#__PURE__*/_react.default.createElement(TypeTestersComponent, _extends({
260
- collection: ((_data$viewer$slug2 = data.viewer.slug) === null || _data$viewer$slug2 === void 0 ? void 0 : _data$viewer$slug2.collection) ?? null,
261
- viewer: data.viewer
247
+ collection: ((_data$viewer$slug2 = data.viewer.slug) === null || _data$viewer$slug2 === void 0 ? void 0 : _data$viewer$slug2.collection) ?? null
262
248
  }, rest, {
263
249
  tags: tags,
264
250
  excludeTags: excludeTags
@@ -23,8 +23,7 @@ async function TypeTesters(_ref) {
23
23
  const preloadedQuery = await (0, _loadSerializableQuery.default)(_TypeTestersIDQuery.default, {
24
24
  collectionId,
25
25
  tags,
26
- excludeTags,
27
- selectable: true
26
+ excludeTags
28
27
  });
29
28
  return /*#__PURE__*/_react.default.createElement(_index.TypeTestersPreloadedIDQueryRenderer, _extends({
30
29
  preloadedQuery: preloadedQuery
@@ -34,8 +33,7 @@ async function TypeTesters(_ref) {
34
33
  const preloadedQuery = await (0, _loadSerializableQuery.default)(_TypeTestersSlugQuery.default, {
35
34
  collectionSlug,
36
35
  tags,
37
- excludeTags,
38
- selectable: true
36
+ excludeTags
39
37
  });
40
38
  return /*#__PURE__*/_react.default.createElement(_index.TypeTestersPreloadedSlugQueryRenderer, _extends({
41
39
  preloadedQuery: preloadedQuery