fontdue-js 2.19.2 → 2.20.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 (123) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +11 -10
  3. package/dist/__generated__/CartItemProduct_product.graphql.d.ts +1 -7
  4. package/dist/__generated__/CartItemProduct_product.graphql.js +11 -36
  5. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.d.ts +1 -1
  6. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.js +65 -23
  7. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.d.ts +1 -1
  8. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.js +65 -23
  9. package/dist/__generated__/CartOrderUpdateMutation.graphql.d.ts +1 -1
  10. package/dist/__generated__/CartOrderUpdateMutation.graphql.js +65 -23
  11. package/dist/__generated__/CartQuery.graphql.d.ts +1 -1
  12. package/dist/__generated__/CartQuery.graphql.js +119 -87
  13. package/dist/__generated__/CartStateUpdateMutation.graphql.d.ts +1 -1
  14. package/dist/__generated__/CartStateUpdateMutation.graphql.js +65 -23
  15. package/dist/__generated__/CharacterViewerIDQuery.graphql.d.ts +1 -1
  16. package/dist/__generated__/CharacterViewerIDQuery.graphql.js +40 -26
  17. package/dist/__generated__/CharacterViewerSlugQuery.graphql.d.ts +1 -1
  18. package/dist/__generated__/CharacterViewerSlugQuery.graphql.js +40 -26
  19. package/dist/__generated__/CharacterViewerStyleRefetchQuery.graphql.d.ts +1 -1
  20. package/dist/__generated__/CharacterViewerStyleRefetchQuery.graphql.js +62 -41
  21. package/dist/__generated__/CharacterViewer_collection.graphql.d.ts +1 -3
  22. package/dist/__generated__/CharacterViewer_collection.graphql.js +6 -13
  23. package/dist/__generated__/CharacterViewer_family.graphql.d.ts +1 -2
  24. package/dist/__generated__/CharacterViewer_family.graphql.js +2 -8
  25. package/dist/__generated__/CharacterViewer_style.graphql.d.ts +2 -1
  26. package/dist/__generated__/CharacterViewer_style.graphql.js +6 -2
  27. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +1 -1
  28. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +65 -23
  29. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.d.ts +1 -1
  30. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.js +65 -23
  31. package/dist/__generated__/CollectionAa_Query.graphql.d.ts +1 -1
  32. package/dist/__generated__/CollectionAa_Query.graphql.js +57 -3
  33. package/dist/__generated__/Family_node.graphql.d.ts +1 -2
  34. package/dist/__generated__/Family_node.graphql.js +2 -8
  35. package/dist/__generated__/FontFamiliesQuery.graphql.d.ts +1 -1
  36. package/dist/__generated__/FontFamiliesQuery.graphql.js +80 -31
  37. package/dist/__generated__/FontStyle_fontStyle.graphql.d.ts +2 -3
  38. package/dist/__generated__/FontStyle_fontStyle.graphql.js +4 -12
  39. package/dist/__generated__/PrecartAddToCartMutation.graphql.d.ts +1 -1
  40. package/dist/__generated__/PrecartAddToCartMutation.graphql.js +71 -29
  41. package/dist/__generated__/ServerConfigProviderQuery.graphql.d.ts +24 -0
  42. package/dist/__generated__/ServerConfigProviderQuery.graphql.js +108 -0
  43. package/dist/__generated__/StoreModalCartQuery.graphql.d.ts +1 -1
  44. package/dist/__generated__/StoreModalCartQuery.graphql.js +108 -76
  45. package/dist/__generated__/StoreModalContainerQuery.graphql.d.ts +4 -7
  46. package/dist/__generated__/StoreModalContainerQuery.graphql.js +58 -25
  47. package/dist/__generated__/StoreModalFamily_collection.graphql.d.ts +1 -2
  48. package/dist/__generated__/StoreModalFamily_collection.graphql.js +2 -8
  49. package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.d.ts +1 -4
  50. package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.js +2 -17
  51. package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +1 -1
  52. package/dist/__generated__/StoreModalIndexQuery.graphql.js +48 -9
  53. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
  54. package/dist/__generated__/StoreModalProductQuery.graphql.js +85 -41
  55. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.d.ts +1 -1
  56. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.js +78 -34
  57. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +1 -1
  58. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +65 -23
  59. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.d.ts +1 -1
  60. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.js +55 -7
  61. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.d.ts +1 -1
  62. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.js +56 -8
  63. package/dist/__generated__/TypeTester_fontStyle.graphql.d.ts +1 -4
  64. package/dist/__generated__/TypeTester_fontStyle.graphql.js +2 -17
  65. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.d.ts +1 -1
  66. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.js +55 -7
  67. package/dist/__generated__/TypeTestersIDQuery.graphql.d.ts +1 -1
  68. package/dist/__generated__/TypeTestersIDQuery.graphql.js +55 -7
  69. package/dist/__generated__/TypeTestersRefetchQuery.graphql.d.ts +1 -1
  70. package/dist/__generated__/TypeTestersRefetchQuery.graphql.js +55 -7
  71. package/dist/__generated__/TypeTestersSlugQuery.graphql.d.ts +1 -1
  72. package/dist/__generated__/TypeTestersSlugQuery.graphql.js +55 -7
  73. package/dist/__generated__/useFontStyle_fontStyle.graphql.d.ts +28 -0
  74. package/dist/__generated__/useFontStyle_fontStyle.graphql.js +94 -0
  75. package/dist/__tests__/collectionBundleSelection.test.js +0 -1
  76. package/dist/components/Cart/CartItem/CartItemProduct.js +3 -9
  77. package/dist/components/CharacterViewer/index.js +5 -11
  78. package/dist/components/ConfigContext.d.ts +13 -0
  79. package/dist/components/ConfigContext.js +6 -2
  80. package/dist/components/ConsentBanner/consent.d.ts +18 -0
  81. package/dist/components/ConsentBanner/consent.js +93 -0
  82. package/dist/components/ConsentBanner/index.d.ts +3 -0
  83. package/dist/components/ConsentBanner/index.js +84 -0
  84. package/dist/components/Family/index.js +2 -5
  85. package/dist/components/FontStyle/index.js +2 -8
  86. package/dist/components/FontdueProvider/FontdueProviderClientComponent.js +14 -1
  87. package/dist/components/FontdueProvider/index.js +1 -2
  88. package/dist/components/FontdueProvider/index.server.js +1 -2
  89. package/dist/components/ServerConfigProvider/index.d.ts +8 -0
  90. package/dist/components/ServerConfigProvider/index.js +41 -0
  91. package/dist/components/StoreModal/StoreModalContainer.js +15 -17
  92. package/dist/components/StoreModal/StoreModalFamily.js +1 -5
  93. package/dist/components/StoreModal/StoreModalIndexItem.js +6 -11
  94. package/dist/components/Tracking/index.d.ts +2 -0
  95. package/dist/components/Tracking/index.js +166 -0
  96. package/dist/components/TypeTester/TypeTesterSlider.js +10 -3
  97. package/dist/components/TypeTester/index.js +2 -5
  98. package/dist/components/TypeTester/useTypeTesterStyler.js +27 -85
  99. package/dist/components/UrlContext.d.ts +3 -0
  100. package/dist/components/UrlContext.js +15 -0
  101. package/dist/components/elements/StoreModalContainer/index.d.ts +2 -6
  102. package/dist/components/elements/StoreModalContainer/index.js +3 -8
  103. package/dist/components/elements/StoreModalFamily/index.d.ts +0 -1
  104. package/dist/components/elements/StoreModalFamily/index.js +1 -2
  105. package/dist/components/useConsent.d.ts +9 -0
  106. package/dist/components/useConsent.js +26 -0
  107. package/dist/components/useFont.d.ts +29 -0
  108. package/dist/components/useFont.js +77 -0
  109. package/dist/components/useFontLoaded.d.ts +24 -0
  110. package/dist/components/useFontLoaded.js +60 -0
  111. package/dist/components/useFontStyle.d.ts +3 -4
  112. package/dist/components/useFontStyle.js +24 -16
  113. package/dist/deepMerge.d.ts +4 -0
  114. package/dist/deepMerge.js +24 -0
  115. package/dist/fontLoader.d.ts +13 -0
  116. package/dist/fontLoader.js +55 -0
  117. package/dist/fontdue.css +65 -0
  118. package/dist/hooks/useAutofit.d.ts +20 -0
  119. package/dist/hooks/useAutofit.js +114 -0
  120. package/dist/react-ranger.js +5 -1
  121. package/dist/reducer.d.ts +0 -4
  122. package/dist/reducer.js +0 -11
  123. package/package.json +5 -2
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ interface UseFontLoaded_props {
3
+ fontFamily: string;
4
+ /** @deprecated No longer used — kept for backward compatibility */
5
+ fontWeight?: string;
6
+ /** @deprecated No longer used — kept for backward compatibility */
7
+ fontStyle?: string;
8
+ }
9
+ /**
10
+ * Hook for external consumers (npm clients) to detect when a font is loaded.
11
+ *
12
+ * Fonts can be loaded by CSS @font-face rules (e.g. via PreloadWebfonts) or
13
+ * by fontdue-js components via the FontFace API. Uses document.fonts.load()
14
+ * to force the browser to load matching fonts even when no DOM text uses
15
+ * the font-family yet.
16
+ *
17
+ * Usage:
18
+ * const { style, loaded } = useFontLoaded({ fontFamily: "Helvetica Now Regular" });
19
+ */
20
+ declare const useFontLoaded: ({ fontFamily, }: UseFontLoaded_props) => {
21
+ style: React.CSSProperties;
22
+ loaded: boolean;
23
+ };
24
+ export default useFontLoaded;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ const FALLBACK_FAMILY = 'Fallback';
9
+
10
+ /**
11
+ * Hook for external consumers (npm clients) to detect when a font is loaded.
12
+ *
13
+ * Fonts can be loaded by CSS @font-face rules (e.g. via PreloadWebfonts) or
14
+ * by fontdue-js components via the FontFace API. Uses document.fonts.load()
15
+ * to force the browser to load matching fonts even when no DOM text uses
16
+ * the font-family yet.
17
+ *
18
+ * Usage:
19
+ * const { style, loaded } = useFontLoaded({ fontFamily: "Helvetica Now Regular" });
20
+ */
21
+ const useFontLoaded = _ref => {
22
+ let {
23
+ fontFamily
24
+ } = _ref;
25
+ const [loaded, setLoaded] = (0, _react.useState)(false);
26
+ (0, _react.useEffect)(() => {
27
+ let cancelled = false;
28
+ const tryLoad = () => {
29
+ document.fonts.load(`400 16px "${fontFamily}"`).then(fontFaces => {
30
+ if (!cancelled && fontFaces.length > 0) setLoaded(true);
31
+ }).catch(() => {});
32
+ };
33
+
34
+ // Try immediately (works if @font-face exists or FontFace already added)
35
+ tryLoad();
36
+
37
+ // Also listen for future font loads (e.g., fontdue-js component adds
38
+ // FontFace via API after this hook mounts)
39
+ document.fonts.addEventListener('loadingdone', tryLoad);
40
+ return () => {
41
+ cancelled = true;
42
+ document.fonts.removeEventListener('loadingdone', tryLoad);
43
+ };
44
+ }, [fontFamily]);
45
+ const cssStyle = loaded ? {
46
+ fontFamily: `"${fontFamily}", ${FALLBACK_FAMILY}`,
47
+ fontWeight: 400,
48
+ fontStyle: 'normal'
49
+ } : {
50
+ fontFamily: FALLBACK_FAMILY,
51
+ fontWeight: 'normal',
52
+ fontStyle: 'normal'
53
+ };
54
+ return {
55
+ style: cssStyle,
56
+ loaded
57
+ };
58
+ };
59
+ var _default = useFontLoaded;
60
+ exports.default = _default;
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
+ import { useFontStyle_fontStyle$key } from '../__generated__/useFontStyle_fontStyle.graphql';
2
3
  interface UseFontStyle_props {
3
- fontFamily: string;
4
- fontWeight?: string;
5
- fontStyle?: string;
4
+ fontStyle: useFontStyle_fontStyle$key | null | undefined;
6
5
  }
7
- declare const useFontStyle: ({ fontFamily, fontWeight, fontStyle, }: UseFontStyle_props) => {
6
+ declare const useFontStyle: ({ fontStyle: fontStyleKey, }: UseFontStyle_props) => {
8
7
  style: React.CSSProperties;
9
8
  loaded: boolean;
10
9
  };
@@ -4,36 +4,44 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _fontfaceobserver = _interopRequireDefault(require("fontfaceobserver"));
7
+ var _useFontStyle_fontStyle2 = _interopRequireDefault(require("../__generated__/useFontStyle_fontStyle.graphql"));
8
8
  var _react = require("react");
9
+ var _reactRelay = require("react-relay");
10
+ var _fontLoader = require("../fontLoader");
9
11
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
12
  const FALLBACK_FAMILY = 'Fallback';
11
13
  const useFontStyle = _ref => {
12
14
  let {
13
- fontFamily,
14
- fontWeight,
15
- fontStyle
15
+ fontStyle: fontStyleKey
16
16
  } = _ref;
17
+ const fontStyle = (0, _reactRelay.useFragment)((_useFontStyle_fontStyle2.default.hash && _useFontStyle_fontStyle2.default.hash !== "cc36ae8da5627792c27e9d3ad2b07258" && console.error("The definition of 'useFontStyle_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _useFontStyle_fontStyle2.default), fontStyleKey ?? null);
18
+ const fontFamily = fontStyle ? `${fontStyle.cssFamily} ${fontStyle.name}` : null;
19
+ const sources = fontStyle === null || fontStyle === void 0 ? void 0 : fontStyle.webfontSources;
20
+ const verticalMetrics = fontStyle === null || fontStyle === void 0 ? void 0 : fontStyle.verticalMetrics;
21
+
22
+ // Always initialize as false to avoid SSR/client hydration mismatch.
23
+ // The effect below will detect already-loaded fonts on mount.
17
24
  const [loaded, setLoaded] = (0, _react.useState)(false);
18
25
  const mounted = (0, _react.useRef)(false);
19
- (0, _react.useEffect)(() => {
20
- setLoaded(false);
21
- const observer = new _fontfaceobserver.default(fontFamily, {
22
- weight: fontWeight,
23
- style: fontStyle
24
- });
25
- observer.load(null, 90000).then(() => {
26
- // since there's no easy way to cancel the promise...
27
- if (mounted.current) setLoaded(true);
28
- }).catch(console.error);
29
- }, [fontFamily, fontWeight, fontStyle]);
30
26
  (0, _react.useEffect)(() => {
31
27
  mounted.current = true;
32
28
  return () => {
33
29
  mounted.current = false;
34
30
  };
35
31
  }, []);
36
- const cssStyle = loaded ? {
32
+ (0, _react.useEffect)(() => {
33
+ if (!fontFamily) return;
34
+ if ((0, _fontLoader.isFontLoaded)(fontFamily)) {
35
+ setLoaded(true);
36
+ return;
37
+ }
38
+ if (!sources || sources.length === 0) return;
39
+ setLoaded(false);
40
+ (0, _fontLoader.loadFont)(fontFamily, sources, verticalMetrics).then(() => {
41
+ if (mounted.current) setLoaded(true);
42
+ }).catch(console.error);
43
+ }, [fontFamily]);
44
+ const cssStyle = loaded && fontFamily ? {
37
45
  fontFamily: `"${fontFamily}", ${FALLBACK_FAMILY}`,
38
46
  fontWeight: 400,
39
47
  fontStyle: 'normal'
@@ -0,0 +1,4 @@
1
+ type Obj = Record<string, unknown>;
2
+ /** Deep merge two objects. Properties from `overlay` take precedence. */
3
+ export declare function deepMerge<T extends Obj>(base: Obj, overlay: Obj): T;
4
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.deepMerge = deepMerge;
7
+ function isPlainObject(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
9
+ }
10
+
11
+ /** Deep merge two objects. Properties from `overlay` take precedence. */
12
+ function deepMerge(base, overlay) {
13
+ const result = {
14
+ ...base
15
+ };
16
+ for (const key of Object.keys(overlay)) {
17
+ if (isPlainObject(result[key]) && isPlainObject(overlay[key])) {
18
+ result[key] = deepMerge(result[key], overlay[key]);
19
+ } else {
20
+ result[key] = overlay[key];
21
+ }
22
+ }
23
+ return result;
24
+ }
@@ -0,0 +1,13 @@
1
+ interface FontSource {
2
+ readonly url: string | null;
3
+ readonly format: string | null;
4
+ }
5
+ interface VerticalMetrics {
6
+ readonly unitsPerEm: number;
7
+ readonly ascender: number;
8
+ readonly descender: number;
9
+ readonly lineGap: number | null;
10
+ }
11
+ export declare function isFontLoaded(fontFamily: string): boolean;
12
+ export declare function loadFont(fontFamily: string, sources: ReadonlyArray<FontSource | null>, verticalMetrics?: VerticalMetrics | null): Promise<FontFace | null>;
13
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isFontLoaded = isFontLoaded;
7
+ exports.loadFont = loadFont;
8
+ const cache = new Map();
9
+ const loaded = new Set();
10
+ function isFontLoaded(fontFamily) {
11
+ return loaded.has(fontFamily);
12
+ }
13
+ async function loadFont(fontFamily, sources, verticalMetrics) {
14
+ // Return cached promise if loading is in progress or complete
15
+ const existing = cache.get(fontFamily);
16
+ if (existing) return existing;
17
+ const promise = doLoadFont(fontFamily, sources, verticalMetrics);
18
+ cache.set(fontFamily, promise);
19
+ promise.catch(() => {
20
+ cache.delete(fontFamily);
21
+ loaded.delete(fontFamily);
22
+ });
23
+ return promise;
24
+ }
25
+ async function doLoadFont(fontFamily, sources, verticalMetrics) {
26
+ const validSources = sources.filter(s => s != null && s.url != null);
27
+ const source = validSources.find(s => s.format === 'woff2') ?? validSources[0];
28
+ if (!source || !source.url) throw new Error(`No font sources for ${fontFamily}`);
29
+ const response = await fetch(source.url);
30
+ if (!response.ok) throw new Error(`Failed to fetch font: ${response.status}`);
31
+ const buffer = await response.arrayBuffer();
32
+ const descriptors = {
33
+ weight: '400',
34
+ style: 'normal',
35
+ display: 'block'
36
+ };
37
+ if (verticalMetrics && verticalMetrics.unitsPerEm > 0) {
38
+ const {
39
+ unitsPerEm,
40
+ ascender,
41
+ descender,
42
+ lineGap
43
+ } = verticalMetrics;
44
+ descriptors.ascentOverride = `${ascender / unitsPerEm * 100}%`;
45
+ descriptors.descentOverride = `${descender / unitsPerEm * -100}%`;
46
+ if (lineGap != null) {
47
+ descriptors.lineGapOverride = `${lineGap / unitsPerEm * 100}%`;
48
+ }
49
+ }
50
+ const fontFace = new FontFace(fontFamily, buffer, descriptors);
51
+ await fontFace.load();
52
+ document.fonts.add(fontFace);
53
+ loaded.add(fontFamily);
54
+ return fontFace;
55
+ }
package/dist/fontdue.css CHANGED
@@ -876,6 +876,71 @@ div[data-component=TypeTesters] {
876
876
  opacity: 0.5;
877
877
  }
878
878
 
879
+ .fontdue-consent-banner {
880
+ position: fixed;
881
+ bottom: 16px;
882
+ left: 16px;
883
+ z-index: 999999;
884
+ max-width: 420px;
885
+ background-color: var(--secondary_background_color, #fafafa);
886
+ color: var(--primary_text_color, #000);
887
+ font-family: inherit;
888
+ font-size: 14px;
889
+ line-height: 1.5;
890
+ border: 1px solid var(--horizontal_rule_color, #ccc);
891
+ }
892
+ @media (max-width: 480px) {
893
+ .fontdue-consent-banner {
894
+ left: 8px;
895
+ right: 8px;
896
+ max-width: none;
897
+ }
898
+ }
899
+ .fontdue-consent-banner__container {
900
+ padding: 20px 24px;
901
+ display: flex;
902
+ flex-direction: column;
903
+ gap: 16px;
904
+ }
905
+ .fontdue-consent-banner__message {
906
+ margin: 0;
907
+ }
908
+ .fontdue-consent-banner__actions {
909
+ display: flex;
910
+ gap: 8px;
911
+ }
912
+ .fontdue-consent-banner__button {
913
+ appearance: none;
914
+ cursor: pointer;
915
+ font-family: inherit;
916
+ font-size: 14px;
917
+ line-height: 1;
918
+ padding: 10px 20px;
919
+ border-radius: 0;
920
+ white-space: nowrap;
921
+ transition: background-color 150ms ease, color 150ms ease;
922
+ }
923
+ .fontdue-consent-banner__button--primary {
924
+ background-color: var(--button_background_color, #000);
925
+ color: var(--button_text_color, #fff);
926
+ border: 1px solid var(--button_border_color, transparent);
927
+ }
928
+ .fontdue-consent-banner__button--primary:hover {
929
+ background-color: var(--button_hover_background_color, #333);
930
+ color: var(--button_hover_text_color, #fff);
931
+ border-color: var(--button_hover_border_color, transparent);
932
+ }
933
+ .fontdue-consent-banner__button--secondary {
934
+ background-color: transparent;
935
+ color: var(--primary_text_color, #000);
936
+ border: 1px solid var(--horizontal_rule_color, #ccc);
937
+ }
938
+ .fontdue-consent-banner__button--secondary:hover {
939
+ background-color: var(--button_hover_background_color, #333);
940
+ color: var(--button_hover_text_color, #fff);
941
+ border-color: var(--button_hover_border_color, transparent);
942
+ }
943
+
879
944
  :root {
880
945
  --section_spacing: 60px;
881
946
  }
@@ -0,0 +1,20 @@
1
+ export interface UseAutofitProps {
2
+ text: string;
3
+ fontFamily: string;
4
+ fontSize: number;
5
+ min?: number;
6
+ max?: number;
7
+ letterSpacing?: number;
8
+ fontWeight?: React.CSSProperties['fontWeight'];
9
+ fontStyle?: string;
10
+ fontVariationSettings?: string;
11
+ enabled?: boolean;
12
+ padding?: number;
13
+ }
14
+ export interface UseAutofitResult {
15
+ ref: React.RefObject<HTMLDivElement | null>;
16
+ fontSize: number;
17
+ ready: boolean;
18
+ }
19
+ declare const useAutofit: ({ text, fontFamily, fontSize: externalFontSize, min, max, letterSpacing, fontWeight, fontStyle, fontVariationSettings, enabled, padding, }: UseAutofitProps) => UseAutofitResult;
20
+ export default useAutofit;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ var _resizeObserver = _interopRequireDefault(require("@react-hook/resize-observer"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const REF_SIZE = 100;
11
+
12
+ // Canvas measurement (fast path)
13
+ let sharedCanvas = null;
14
+ function getCanvasContext() {
15
+ if (!sharedCanvas) sharedCanvas = document.createElement('canvas');
16
+ return sharedCanvas.getContext('2d');
17
+ }
18
+
19
+ // DOM measurement (fallback for variable fonts)
20
+ let measureEl = null;
21
+ function getMeasureElement() {
22
+ if (!measureEl) {
23
+ measureEl = document.createElement('span');
24
+ measureEl.style.position = 'absolute';
25
+ measureEl.style.top = '-9999px';
26
+ measureEl.style.left = '-9999px';
27
+ measureEl.style.whiteSpace = 'pre';
28
+ measureEl.style.visibility = 'hidden';
29
+ document.body.appendChild(measureEl);
30
+ }
31
+ return measureEl;
32
+ }
33
+ function measureWithCanvas(text, fontFamily, fontWeight, fontStyleValue, letterSpacing) {
34
+ const ctx = getCanvasContext();
35
+ ctx.font = `${fontStyleValue} ${fontWeight} ${REF_SIZE}px ${fontFamily}`;
36
+ ctx.letterSpacing = `${letterSpacing}em`;
37
+ const width = ctx.measureText(text).width;
38
+ ctx.letterSpacing = '0px';
39
+ return width;
40
+ }
41
+ function measureWithDOM(text, fontFamily, fontWeight, fontStyleValue, letterSpacing, fontVariationSettings) {
42
+ const el = getMeasureElement();
43
+ el.style.fontFamily = fontFamily;
44
+ el.style.fontWeight = `${fontWeight}`;
45
+ el.style.fontStyle = fontStyleValue;
46
+ el.style.fontSize = `${REF_SIZE}px`;
47
+ el.style.letterSpacing = `${letterSpacing}em`;
48
+ el.style.fontVariationSettings = fontVariationSettings;
49
+ el.textContent = text;
50
+ return el.getBoundingClientRect().width;
51
+ }
52
+ const useAutofit = _ref => {
53
+ let {
54
+ text,
55
+ fontFamily,
56
+ fontSize: externalFontSize,
57
+ min = 8,
58
+ max = 999,
59
+ letterSpacing = 0,
60
+ fontWeight = 400,
61
+ fontStyle = 'normal',
62
+ fontVariationSettings,
63
+ enabled = true,
64
+ padding = 0
65
+ } = _ref;
66
+ const ref = (0, _react.useRef)(null);
67
+ const [fontSize, setFontSize] = (0, _react.useState)(externalFontSize);
68
+ const [containerWidth, setContainerWidth] = (0, _react.useState)(0);
69
+ const [fontVersion, setFontVersion] = (0, _react.useState)(0);
70
+ const [measured, setMeasured] = (0, _react.useState)(false);
71
+ (0, _resizeObserver.default)(ref, entry => {
72
+ setContainerWidth(entry.contentRect.width);
73
+ });
74
+
75
+ // Trigger re-measurement when the font becomes available.
76
+ // If already loaded (e.g. parent waited for font), the promise
77
+ // resolves as a microtask and the re-measurement is near-instant.
78
+ (0, _react.useLayoutEffect)(() => {
79
+ if (!enabled || !fontFamily) return;
80
+ const fontString = `${fontStyle} ${fontWeight} ${REF_SIZE}px ${fontFamily}`;
81
+ document.fonts.load(fontString).then(() => {
82
+ setFontVersion(v => v + 1);
83
+ });
84
+ }, [fontFamily, fontWeight, fontStyle, enabled]);
85
+ (0, _react.useLayoutEffect)(() => {
86
+ if (!enabled) return;
87
+ const availableWidth = containerWidth - padding;
88
+ if (availableWidth <= 0 || !text) return;
89
+
90
+ // Use DOM measurement when variable font settings are active
91
+ // (canvas doesn't support fontVariationSettings in most browsers).
92
+ // Otherwise use canvas for better performance (no layout reflow).
93
+ const refWidth = fontVariationSettings ? measureWithDOM(text, fontFamily, fontWeight, fontStyle, letterSpacing, fontVariationSettings) : measureWithCanvas(text, fontFamily, fontWeight, fontStyle, letterSpacing);
94
+ if (refWidth <= 0) return;
95
+
96
+ // Text width scales linearly with font size.
97
+ // Floor to avoid sub-pixel rounding that causes text to wrap.
98
+ const newSize = Math.min(Math.max(min, Math.floor(availableWidth / (refWidth / REF_SIZE))), max);
99
+ setFontSize(newSize);
100
+ if (!measured) setMeasured(true);
101
+ }, [text, fontFamily, fontWeight, fontStyle, fontVariationSettings, letterSpacing, containerWidth, enabled, min, max, padding, fontVersion]);
102
+ if (!enabled) return {
103
+ ref,
104
+ fontSize: externalFontSize,
105
+ ready: true
106
+ };
107
+ return {
108
+ ref,
109
+ fontSize,
110
+ ready: measured
111
+ };
112
+ };
113
+ var _default = useAutofit;
114
+ exports.default = _default;
@@ -165,7 +165,11 @@ function useRanger(_ref) {
165
165
  document.addEventListener('mouseup', handleRelease);
166
166
  document.addEventListener('touchend', handleRelease);
167
167
  }, [getLatest, handleDrag]);
168
- const getPercentageForValue = _react.default.useCallback(val => interpolator.getPercentageForValue(val, min, max), [interpolator, max, min]);
168
+ const getPercentageForValue = _react.default.useCallback(val => {
169
+ const pct = interpolator.getPercentageForValue(val, min, max);
170
+ // Round to 4 decimal places for consistent SSR/client hydration
171
+ return Math.round(pct * 10000) / 10000;
172
+ }, [interpolator, max, min]);
169
173
 
170
174
  // Build the ticks
171
175
  const ticks = _react.default.useMemo(() => {
package/dist/reducer.d.ts CHANGED
@@ -17,7 +17,6 @@ export interface OrderVariableSelectionState {
17
17
  countryCode?: string | null;
18
18
  }
19
19
  export interface FontdueState {
20
- stylesheets: string[];
21
20
  cartOpen: boolean;
22
21
  precartOpen: boolean;
23
22
  selectedSkuIds: {
@@ -73,9 +72,6 @@ export type FontdueAction = {
73
72
  selected: boolean;
74
73
  } | {
75
74
  type: 'UNSELECT_SKUS';
76
- } | {
77
- type: 'REGISTER_STYLESHEET';
78
- href: string;
79
75
  } | {
80
76
  type: 'POPULATE_PRODUCT_STATE';
81
77
  data: productState_Query$data;
package/dist/reducer.js CHANGED
@@ -248,16 +248,6 @@ const reducer = (state, action) => {
248
248
  ...state,
249
249
  cartOpen: false
250
250
  };
251
- case 'REGISTER_STYLESHEET':
252
- const existing = document.querySelector(`link[href="${action.href}"]`);
253
- if (existing) return state;
254
- return {
255
- ...state,
256
- stylesheets: state.stylesheets.filter(href => href !== action.href).concat(action.href).sort()
257
-
258
- // sort is important here since moving stylesheets around causes them to
259
- // get re-evaluated
260
- };
261
251
  case 'STORE_MODAL_NAVIGATE':
262
252
  return storeModalNavigate(state, action);
263
253
  case 'STORE_MODAL_REPLACE':
@@ -286,7 +276,6 @@ function createDefaultStore(config) {
286
276
  collectionStyleSkus: {},
287
277
  skuPrices: {},
288
278
  collectionSkus: {},
289
- stylesheets: [],
290
279
  fetchedCollectionIds: [],
291
280
  storeModalRoute: {
292
281
  name: 'index',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "2.19.2",
3
+ "version": "2.20.0",
4
4
  "scripts": {
5
5
  "build": "npm run relay && run-p build-js build-css build-ts",
6
6
  "build-js": "babel src --out-dir dist --extensions .ts,.tsx,.js,.jsx",
@@ -115,6 +115,9 @@
115
115
  "react-server": "./dist/components/TypeTesters/index.server.js",
116
116
  "default": "./dist/components/TypeTesters/index.js"
117
117
  },
118
- "./useFontStyle": "./dist/components/useFontStyle.js"
118
+ "./useConsent": "./dist/components/useConsent.js",
119
+ "./useFont": "./dist/components/useFont.js",
120
+ "./useFontStyle": "./dist/components/useFont.js",
121
+ "./useAutofit": "./dist/hooks/useAutofit.js"
119
122
  }
120
123
  }