fontdue-js 3.12.0 → 3.13.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.
- package/CHANGELOG.md +10 -0
- package/dist/__generated__/CustomerSignInElementQuery.graphql.d.ts +22 -0
- package/dist/__generated__/CustomerSignInElementQuery.graphql.js +87 -0
- package/dist/__generated__/StoreModalFamily_collection.graphql.d.ts +1 -1
- package/dist/__generated__/StoreModalFamily_collection.graphql.js +8 -4
- package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
- package/dist/__generated__/StoreModalProductQuery.graphql.js +9 -5
- package/dist/__generated__/StoreModalProductRefetchQuery.graphql.d.ts +1 -1
- package/dist/__generated__/StoreModalProductRefetchQuery.graphql.js +9 -5
- package/dist/__generated__/StoreModalProductSlugQuery.graphql.d.ts +1 -1
- package/dist/__generated__/StoreModalProductSlugQuery.graphql.js +9 -5
- package/dist/__generated__/productState_Query.graphql.d.ts +1 -29
- package/dist/__generated__/productState_Query.graphql.js +15 -30
- package/dist/__generated__/productState_collection.graphql.d.ts +1 -1
- package/dist/__generated__/productState_collection.graphql.js +8 -4
- package/dist/__generated__/productState_fontPack.graphql.d.ts +1 -1
- package/dist/__generated__/productState_fontPack.graphql.js +7 -3
- package/dist/__generated__/productState_node.graphql.d.ts +1 -29
- package/dist/__generated__/productState_node.graphql.js +8 -22
- package/dist/__tests__/bundleSelectionOrder.test.js +0 -1
- package/dist/__tests__/collectionBundleSelection.test.js +31 -6
- package/dist/__tests__/customerSignInElement.test.js +155 -0
- package/dist/__tests__/customerSignInRequest.test.js +85 -0
- package/dist/__tests__/stripeCheckoutError.test.js +110 -0
- package/dist/__tests__/typeTesterPrefetchStyles.test.js +108 -0
- package/dist/components/CustomerSignIn/CustomerSignInElement.d.ts +22 -0
- package/dist/components/CustomerSignIn/CustomerSignInElement.js +132 -0
- package/dist/components/CustomerSignIn/index.js +4 -2
- package/dist/components/Root/index.js +2 -0
- package/dist/components/Root/productState.js +7 -33
- package/dist/components/StoreModal/StoreModalFamily.js +9 -5
- package/dist/components/StripeProvider/index.js +19 -2
- package/dist/components/TypeTester/TypeTesterStyleSelect.js +6 -1
- package/dist/components/TypeTester/usePrefetchWhenNear.d.ts +14 -0
- package/dist/components/TypeTester/usePrefetchWhenNear.js +69 -0
- package/dist/fontdue.css +10 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/__generated__/productState_collectionBundle.graphql.d.ts +0 -41
- package/dist/__generated__/productState_collectionBundle.graphql.js +0 -81
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
4
|
+
import _CustomerSignInElementQuery from "../../__generated__/CustomerSignInElementQuery.graphql.js";
|
|
5
|
+
import React, { Suspense, useCallback, useState, useSyncExternalStore } from 'react';
|
|
6
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
7
|
+
import { graphql, useLazyLoadQuery } from 'react-relay';
|
|
8
|
+
import CustomerSignIn from './index.js';
|
|
9
|
+
import { clearTokens, getTokens, subscribeToTokens } from '../../auth/tokenStore.js';
|
|
10
|
+
import { useFontdueUrl } from '../UrlContext.js';
|
|
11
|
+
const query = (_CustomerSignInElementQuery.hash && _CustomerSignInElementQuery.hash !== "e7d4a8fb8d19581d6bc36f29090230af" && console.error("The definition of 'CustomerSignInElementQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _CustomerSignInElementQuery);
|
|
12
|
+
|
|
13
|
+
/** Dispatched on `window` once the customer is signed in; `detail.customer`. */
|
|
14
|
+
export const SIGNED_IN_EVENT = 'fontdue:customer-signed-in';
|
|
15
|
+
/** Dispatched on `window` when the customer clicks the element's sign-out button. */
|
|
16
|
+
export const SIGNED_OUT_EVENT = 'fontdue:customer-signed-out';
|
|
17
|
+
/**
|
|
18
|
+
* `<fontdue-customer-sign-in>`: the script-tag wrapper around `CustomerSignIn`.
|
|
19
|
+
*
|
|
20
|
+
* A page has no React of its own to read `useCurrentCustomer()` with, so the
|
|
21
|
+
* element shows the signed-in state itself – who is signed in and a way out –
|
|
22
|
+
* and tells the page about both changes with window events, as `setConsent`
|
|
23
|
+
* does with `fontdue:consent`.
|
|
24
|
+
*
|
|
25
|
+
* Not in the npm package's `exports`: sign-in is script-tag only for now.
|
|
26
|
+
*/
|
|
27
|
+
export default function CustomerSignInElement(_ref) {
|
|
28
|
+
let {
|
|
29
|
+
signOutLabel = 'Sign out',
|
|
30
|
+
...props
|
|
31
|
+
} = _ref;
|
|
32
|
+
const url = useFontdueUrl();
|
|
33
|
+
// The script tag never renders on a server, so reading the store during
|
|
34
|
+
// render can't cause a hydration mismatch – and it means a returning
|
|
35
|
+
// customer never sees the form flash before their session is read.
|
|
36
|
+
const accessToken = useSyncExternalStore(subscribeToTokens, () => {
|
|
37
|
+
var _getTokens;
|
|
38
|
+
return ((_getTokens = getTokens(url)) === null || _getTokens === void 0 ? void 0 : _getTokens.accessToken) ?? null;
|
|
39
|
+
}, () => null);
|
|
40
|
+
// The customer `verifyLoginCode` just returned, tied to the token it came
|
|
41
|
+
// with, so the signed-in state shows at once instead of after a lookup. Any
|
|
42
|
+
// other session – a page load, a sign-in in another element, a renewed
|
|
43
|
+
// token – is looked up.
|
|
44
|
+
const [justSignedIn, setJustSignedIn] = useState(null);
|
|
45
|
+
const handleSignedIn = useCallback(customer => {
|
|
46
|
+
if (customer) {
|
|
47
|
+
var _getTokens2;
|
|
48
|
+
setJustSignedIn({
|
|
49
|
+
accessToken: (_getTokens2 = getTokens(url)) === null || _getTokens2 === void 0 ? void 0 : _getTokens2.accessToken,
|
|
50
|
+
customer
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
window.dispatchEvent(new CustomEvent(SIGNED_IN_EVENT, {
|
|
54
|
+
detail: {
|
|
55
|
+
customer
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
}, [url]);
|
|
59
|
+
const signOut = useCallback(() => {
|
|
60
|
+
clearTokens(url);
|
|
61
|
+
setJustSignedIn(null);
|
|
62
|
+
window.dispatchEvent(new CustomEvent(SIGNED_OUT_EVENT));
|
|
63
|
+
}, [url]);
|
|
64
|
+
const form = /*#__PURE__*/React.createElement(CustomerSignIn, _extends({}, props, {
|
|
65
|
+
onSignedIn: handleSignedIn
|
|
66
|
+
}));
|
|
67
|
+
if (accessToken == null) return form;
|
|
68
|
+
if (justSignedIn && justSignedIn.accessToken === accessToken) {
|
|
69
|
+
return /*#__PURE__*/React.createElement(SignedIn, {
|
|
70
|
+
customer: justSignedIn.customer,
|
|
71
|
+
signOut: signOut,
|
|
72
|
+
signOutLabel: signOutLabel
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// A lookup that fails leaves the visitor able to sign in again rather than
|
|
77
|
+
// facing an empty element.
|
|
78
|
+
return /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
79
|
+
fallback: form,
|
|
80
|
+
resetKeys: [accessToken]
|
|
81
|
+
}, /*#__PURE__*/React.createElement(Suspense, {
|
|
82
|
+
fallback: null
|
|
83
|
+
}, /*#__PURE__*/React.createElement(SignedInLookup, {
|
|
84
|
+
key: accessToken,
|
|
85
|
+
form: form,
|
|
86
|
+
signOut: signOut,
|
|
87
|
+
signOutLabel: signOutLabel
|
|
88
|
+
})));
|
|
89
|
+
}
|
|
90
|
+
function SignedInLookup(_ref2) {
|
|
91
|
+
var _data$viewer;
|
|
92
|
+
let {
|
|
93
|
+
form,
|
|
94
|
+
signOut,
|
|
95
|
+
signOutLabel
|
|
96
|
+
} = _ref2;
|
|
97
|
+
// network-only: who is signed in *now*, never a store hit left over from the
|
|
98
|
+
// previous customer.
|
|
99
|
+
const data = useLazyLoadQuery(query, {}, {
|
|
100
|
+
fetchPolicy: 'network-only'
|
|
101
|
+
});
|
|
102
|
+
const customer = (_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : _data$viewer.currentCustomer;
|
|
103
|
+
|
|
104
|
+
// A token the server no longer honours is cleared by the network layer,
|
|
105
|
+
// which re-renders the element signed out. A null here without that is a
|
|
106
|
+
// session that names nobody; offer the form.
|
|
107
|
+
if (!customer) return form;
|
|
108
|
+
return /*#__PURE__*/React.createElement(SignedIn, {
|
|
109
|
+
customer: customer,
|
|
110
|
+
signOut: signOut,
|
|
111
|
+
signOutLabel: signOutLabel
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function SignedIn(_ref3) {
|
|
115
|
+
let {
|
|
116
|
+
customer,
|
|
117
|
+
signOut,
|
|
118
|
+
signOutLabel
|
|
119
|
+
} = _ref3;
|
|
120
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
121
|
+
className: "customer-sign-in",
|
|
122
|
+
"data-step": "signed-in"
|
|
123
|
+
}, /*#__PURE__*/React.createElement("p", {
|
|
124
|
+
className: "customer-sign-in__intro"
|
|
125
|
+
}, "Signed in as ", customer.email ?? customer.name, "."), /*#__PURE__*/React.createElement("div", {
|
|
126
|
+
className: "customer-sign-in__actions"
|
|
127
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
128
|
+
className: "customer-sign-in__back",
|
|
129
|
+
type: "button",
|
|
130
|
+
onClick: signOut
|
|
131
|
+
}, signOutLabel)));
|
|
132
|
+
}
|
|
@@ -54,9 +54,11 @@ function CustomerSignInFields(_ref) {
|
|
|
54
54
|
email
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
onCompleted: res => {
|
|
57
|
+
onCompleted: (res, errors) => {
|
|
58
58
|
var _res$requestLoginCode;
|
|
59
|
-
if (
|
|
59
|
+
if (errors && errors.length > 0) {
|
|
60
|
+
setError(errors[0].message);
|
|
61
|
+
} else if ((_res$requestLoginCode = res.requestLoginCode) !== null && _res$requestLoginCode !== void 0 && _res$requestLoginCode.success) {
|
|
60
62
|
setStep('code');
|
|
61
63
|
} else {
|
|
62
64
|
// The only unsuccessful answer is the rate limit, and it is about the
|
|
@@ -25,6 +25,8 @@ const customElementMap = {
|
|
|
25
25
|
'fontdue-cookie-notification': /*#__PURE__*/lazy(() => retryImport(() => import('../CookieNotification/index.js'))),
|
|
26
26
|
'fontdue-font-families': /*#__PURE__*/lazy(() => retryImport(() => import('../FontFamilies/index.js'))),
|
|
27
27
|
'fontdue-customer-login-form': /*#__PURE__*/lazy(() => retryImport(() => import('../CustomerLoginForm/index.js'))),
|
|
28
|
+
// Script tag only: CustomerSignIn isn't in the npm package's `exports` yet.
|
|
29
|
+
'fontdue-customer-sign-in': /*#__PURE__*/lazy(() => retryImport(() => import('../CustomerSignIn/CustomerSignInElement.js'))),
|
|
28
30
|
'fontdue-newsletter-signup': /*#__PURE__*/lazy(() => retryImport(() => import('../NewsletterSignup/NewsletterSignupElement.js'))),
|
|
29
31
|
'fontdue-node-password-form': /*#__PURE__*/lazy(() => retryImport(() => import('../NodePasswordForm/NodePasswordFormElement.js'))),
|
|
30
32
|
// @ts-ignore
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import _productState_Query from "../../__generated__/productState_Query.graphql.js";
|
|
2
2
|
import _productState_fontPack from "../../__generated__/productState_fontPack.graphql.js";
|
|
3
3
|
import _productState_node from "../../__generated__/productState_node.graphql.js";
|
|
4
|
-
import _productState_collectionBundle from "../../__generated__/productState_collectionBundle.graphql.js";
|
|
5
4
|
import _productState_bundle from "../../__generated__/productState_bundle.graphql.js";
|
|
6
5
|
import _productState_collection from "../../__generated__/productState_collection.graphql.js";
|
|
7
6
|
import _productState_sku from "../../__generated__/productState_sku.graphql.js";
|
|
8
7
|
import { fetchQuery, graphql } from 'react-relay';
|
|
9
8
|
import { notEmpty } from '../../utils.js';
|
|
10
9
|
_productState_sku.hash && _productState_sku.hash !== "2cee6ddddc85699f0c90e2607c0634ef" && console.error("The definition of 'productState_sku' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_sku;
|
|
11
|
-
_productState_collection.hash && _productState_collection.hash !== "
|
|
10
|
+
_productState_collection.hash && _productState_collection.hash !== "5ad23bbf68581f27e769ebb0cda70f8e" && console.error("The definition of 'productState_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_collection;
|
|
12
11
|
_productState_bundle.hash && _productState_bundle.hash !== "fa38bbc9f2062a727da4f5062b362054" && console.error("The definition of 'productState_bundle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_bundle;
|
|
13
|
-
|
|
14
|
-
_productState_node.hash && _productState_node.hash !== "a1e172db45ca550a254819d877497d12" && console.error("The definition of 'productState_node' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_node;
|
|
12
|
+
_productState_node.hash && _productState_node.hash !== "aab1ef07e0192a0d22a4c300084d9d05" && console.error("The definition of 'productState_node' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _productState_node;
|
|
15
13
|
|
|
16
14
|
// A pack is one SKU that grants every style in `fontStyles`. Its collection
|
|
17
15
|
// members (a family or a superfamily with its families) are fetched in the
|
|
@@ -41,37 +39,21 @@ const allSkus = function (collection) {
|
|
|
41
39
|
var _collection$bundles;
|
|
42
40
|
bundleSkus = (collection === null || collection === void 0 ? void 0 : (_collection$bundles = collection.bundles) === null || _collection$bundles === void 0 ? void 0 : _collection$bundles.flatMap(bundle => allSkus(bundle, includeCollections, includeFontStyles))) ?? [];
|
|
43
41
|
}
|
|
44
|
-
let collectionBundleSkus = [];
|
|
45
|
-
if ('collectionBundles' in collection && collection.collectionBundles) {
|
|
46
|
-
collectionBundleSkus = collection.collectionBundles.flatMap(collBundle => allSkus(collBundle, includeCollections, includeFontStyles));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// For collection bundles, get SKUs from member collections
|
|
50
|
-
let memberCollectionSkus = [];
|
|
51
|
-
if ('memberCollections' in collection && collection.memberCollections) {
|
|
52
|
-
memberCollectionSkus = collection.memberCollections.flatMap(member => {
|
|
53
|
-
var _member$fontStyles;
|
|
54
|
-
return [includeCollections ? member.sku : null, ...(includeFontStyles ? ((_member$fontStyles = member.fontStyles) === null || _member$fontStyles === void 0 ? void 0 : _member$fontStyles.map(style => style.sku)) ?? [] : [])];
|
|
55
|
-
}).filter(notEmpty);
|
|
56
|
-
}
|
|
57
42
|
let childrenSkus = [];
|
|
58
43
|
if ('children' in collection) {
|
|
59
44
|
var _collection$children;
|
|
60
45
|
childrenSkus = ((_collection$children = collection.children) === null || _collection$children === void 0 ? void 0 : _collection$children.flatMap(child => allSkus(child, includeCollections, includeFontStyles))) ?? [];
|
|
61
46
|
}
|
|
62
|
-
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, ...
|
|
47
|
+
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, ...childrenSkus].filter(notEmpty);
|
|
63
48
|
};
|
|
64
49
|
|
|
65
50
|
// The styles each SKU grants, by SKU id: a style's SKU grants that style, a
|
|
66
|
-
// bundle its styles
|
|
67
|
-
// and a
|
|
51
|
+
// bundle its styles (a whole-family member expanded to the family's styles), a
|
|
52
|
+
// family its styles and a superfamily its families' styles. This is all the
|
|
68
53
|
// selection needs to price any combination of them (see selection.ts).
|
|
69
54
|
|
|
70
55
|
const styleIdsOf = collection => {
|
|
71
56
|
var _collection$fontStyle2, _collection$children2;
|
|
72
|
-
if ('memberCollections' in collection && collection.memberCollections) {
|
|
73
|
-
return collection.memberCollections.flatMap(member => member.fontStyles.map(s => s.id));
|
|
74
|
-
}
|
|
75
57
|
const own = 'fontStyles' in collection ? ((_collection$fontStyle2 = collection.fontStyles) === null || _collection$fontStyle2 === void 0 ? void 0 : _collection$fontStyle2.map(s => s.id)) ?? [] : [];
|
|
76
58
|
const children = 'children' in collection ? ((_collection$children2 = collection.children) === null || _collection$children2 === void 0 ? void 0 : _collection$children2.flatMap(child => {
|
|
77
59
|
var _child$fontStyles;
|
|
@@ -83,8 +65,7 @@ const flattenSkuStyleIds = collection => {
|
|
|
83
65
|
var _collection$sku;
|
|
84
66
|
const result = {};
|
|
85
67
|
const add = (skuId, styleIds) => {
|
|
86
|
-
// A
|
|
87
|
-
// a collection bundle member) says nothing about what it grants.
|
|
68
|
+
// A SKU that lists no styles says nothing about what it grants.
|
|
88
69
|
if (skuId && styleIds.length > 0) result[skuId] = styleIds;
|
|
89
70
|
};
|
|
90
71
|
const addStyles = fontStyles => {
|
|
@@ -95,14 +76,7 @@ const flattenSkuStyleIds = collection => {
|
|
|
95
76
|
};
|
|
96
77
|
add((_collection$sku = collection.sku) === null || _collection$sku === void 0 ? void 0 : _collection$sku.id, styleIdsOf(collection));
|
|
97
78
|
if ('fontStyles' in collection) addStyles(collection.fontStyles);
|
|
98
|
-
|
|
99
|
-
for (const member of collection.memberCollections ?? []) {
|
|
100
|
-
var _member$sku;
|
|
101
|
-
add((_member$sku = member.sku) === null || _member$sku === void 0 ? void 0 : _member$sku.id, member.fontStyles.map(s => s.id));
|
|
102
|
-
addStyles(member.fontStyles);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const nested = [...('bundles' in collection ? collection.bundles ?? [] : []), ...('collectionBundles' in collection ? collection.collectionBundles ?? [] : []), ...('children' in collection ? collection.children ?? [] : [])];
|
|
79
|
+
const nested = [...('bundles' in collection ? collection.bundles ?? [] : []), ...('children' in collection ? collection.children ?? [] : [])];
|
|
106
80
|
for (const child of nested) Object.assign(result, flattenSkuStyleIds(child));
|
|
107
81
|
return result;
|
|
108
82
|
};
|
|
@@ -40,18 +40,22 @@ export default function StoreModalFamily(_ref) {
|
|
|
40
40
|
onSelect,
|
|
41
41
|
title
|
|
42
42
|
} = _ref;
|
|
43
|
-
const collection = useFragment((_StoreModalFamily_collection.hash && _StoreModalFamily_collection.hash !== "
|
|
43
|
+
const collection = useFragment((_StoreModalFamily_collection.hash && _StoreModalFamily_collection.hash !== "9f2a8cd937ebe2c06f2bf771d38de985" && 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);
|
|
44
44
|
const fontStyles = useMemo(() => stylesGrouped(collection.fontStyles), [collection]);
|
|
45
|
-
|
|
45
|
+
// An unlisted bundle gets no button, but its price still applies as an
|
|
46
|
+
// upgrade: the product state reads every bundle.
|
|
47
|
+
const listedBundles = (_collection$bundles = collection.bundles) === null || _collection$bundles === void 0 ? void 0 : _collection$bundles.filter(bundle => bundle.visibility !== 'UNLISTED');
|
|
48
|
+
|
|
49
|
+
// A superfamily sold only through its families has nothing of its own to
|
|
50
|
+
// show – unless one of its bundles, across its families, has a button.
|
|
51
|
+
if ((fontStyles === null || fontStyles === void 0 ? void 0 : fontStyles.length) === 0 && collection.collectionType === 'superfamily' && !collection.sku && !(listedBundles !== null && listedBundles !== void 0 && listedBundles.length)) return null;
|
|
46
52
|
return /*#__PURE__*/React.createElement(FamilyElement, null, {
|
|
47
53
|
title,
|
|
48
54
|
familyButton: /*#__PURE__*/React.createElement(FamilyButton, {
|
|
49
55
|
collection: collection,
|
|
50
56
|
onSelectSku: onSelect
|
|
51
57
|
}),
|
|
52
|
-
|
|
53
|
-
// upgrade: the product state reads every bundle.
|
|
54
|
-
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, {
|
|
58
|
+
bundles: listedBundles === null || listedBundles === void 0 ? void 0 : listedBundles.map(bundle => /*#__PURE__*/React.createElement(StoreModalBundleButton, {
|
|
55
59
|
key: bundle.id,
|
|
56
60
|
bundle: bundle,
|
|
57
61
|
onSelectSku: onSelect
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import _StripeProviderCreateCheckoutSessionMutation from "../../__generated__/StripeProviderCreateCheckoutSessionMutation.graphql.js";
|
|
2
2
|
import _StripeProvider_viewer from "../../__generated__/StripeProvider_viewer.graphql.js";
|
|
3
|
-
import React, { useCallback, useContext, useRef } from 'react';
|
|
3
|
+
import React, { useCallback, useContext, useRef, useState } from 'react';
|
|
4
4
|
import { commitMutation, graphql, useFragment, useRelayEnvironment } from 'react-relay';
|
|
5
5
|
import { Elements, EmbeddedCheckoutProvider } from '@stripe/react-stripe-js';
|
|
6
6
|
import { loadStripe } from '@stripe/stripe-js';
|
|
7
7
|
import { parseCustomProperties } from '../../utils.js';
|
|
8
8
|
import ConfigContext from '../ConfigContext.js';
|
|
9
|
+
import StoreModalPageContainer from '../elements/StoreModalPageContainer/index.js';
|
|
10
|
+
const CHECKOUT_ERROR = 'We couldn’t start the checkout – please try again';
|
|
9
11
|
const StripeProvider = _ref => {
|
|
10
12
|
var _data$currentOrder, _data$settings, _data$settings$uiFont, _data$settings$uiFont2, _data$settings$uiFont3, _data$settings2, _data$currentOrder2, _data$currentOrder2$s, _data$themeConfig;
|
|
11
13
|
let {
|
|
@@ -26,6 +28,7 @@ const StripeProvider = _ref => {
|
|
|
26
28
|
const {
|
|
27
29
|
stripe: config
|
|
28
30
|
} = useContext(ConfigContext);
|
|
31
|
+
const [checkoutError, setCheckoutError] = useState(null);
|
|
29
32
|
let customFontSource;
|
|
30
33
|
const woffSrc = (_data$settings = data.settings) === null || _data$settings === void 0 ? void 0 : (_data$settings$uiFont = _data$settings.uiFontStyle) === null || _data$settings$uiFont === void 0 ? void 0 : (_data$settings$uiFont2 = _data$settings$uiFont.webfontSources) === null || _data$settings$uiFont2 === void 0 ? void 0 : (_data$settings$uiFont3 = _data$settings$uiFont2.find(source => (source === null || source === void 0 ? void 0 : source.format) === 'woff2')) === null || _data$settings$uiFont3 === void 0 ? void 0 : _data$settings$uiFont3.url;
|
|
31
34
|
let fontFamily = (_data$settings2 = data.settings) !== null && _data$settings2 !== void 0 && _data$settings2.uiFontStyle ? `${data.settings.uiFontStyle.cssFamily} ${data.settings.uiFontStyle.name}` : undefined;
|
|
@@ -50,16 +53,22 @@ const StripeProvider = _ref => {
|
|
|
50
53
|
commitMutation(environment, {
|
|
51
54
|
mutation: (_StripeProviderCreateCheckoutSessionMutation.hash && _StripeProviderCreateCheckoutSessionMutation.hash !== "2ca9a7fcfed1292117e3cd6d0d95cca6" && console.error("The definition of 'StripeProviderCreateCheckoutSessionMutation' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StripeProviderCreateCheckoutSessionMutation),
|
|
52
55
|
variables: {},
|
|
53
|
-
onCompleted: response => {
|
|
56
|
+
onCompleted: (response, errors) => {
|
|
54
57
|
var _response$createOrder, _response$createOrder2, _response$createOrder3;
|
|
55
58
|
const clientSecret = (_response$createOrder = response.createOrderCheckoutSession) === null || _response$createOrder === void 0 ? void 0 : (_response$createOrder2 = _response$createOrder.order) === null || _response$createOrder2 === void 0 ? void 0 : (_response$createOrder3 = _response$createOrder2.stripeCheckoutSession) === null || _response$createOrder3 === void 0 ? void 0 : _response$createOrder3.clientSecret;
|
|
56
59
|
if (clientSecret) {
|
|
57
60
|
resolve(clientSecret);
|
|
58
61
|
} else {
|
|
62
|
+
var _errors$;
|
|
63
|
+
// Stripe refused the session (e.g. a total under its minimum
|
|
64
|
+
// charge): show its reason, which the embedded checkout would
|
|
65
|
+
// otherwise swallow into a generic error.
|
|
66
|
+
setCheckoutError((errors === null || errors === void 0 ? void 0 : (_errors$ = errors[0]) === null || _errors$ === void 0 ? void 0 : _errors$.message) ?? CHECKOUT_ERROR);
|
|
59
67
|
reject("Didn't find client secret");
|
|
60
68
|
}
|
|
61
69
|
},
|
|
62
70
|
onError: error => {
|
|
71
|
+
setCheckoutError(CHECKOUT_ERROR);
|
|
63
72
|
reject(`Error creating checkout session: ${error.message}`);
|
|
64
73
|
}
|
|
65
74
|
});
|
|
@@ -147,6 +156,14 @@ const StripeProvider = _ref => {
|
|
|
147
156
|
syncAddressCheckbox: 'none'
|
|
148
157
|
};
|
|
149
158
|
if (providerType === 'checkout') {
|
|
159
|
+
if (checkoutError) {
|
|
160
|
+
return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
|
|
161
|
+
body: /*#__PURE__*/React.createElement("div", {
|
|
162
|
+
className: "store-modal__cart__error",
|
|
163
|
+
role: "alert"
|
|
164
|
+
}, checkoutError)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
150
167
|
return /*#__PURE__*/React.createElement(EmbeddedCheckoutProvider, {
|
|
151
168
|
stripe: stripePromiseRef.current,
|
|
152
169
|
options: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
2
|
import { compareVariableSettings, variableInstanceSettings } from '../../utils.js';
|
|
3
3
|
import Select from '../Select/index.js';
|
|
4
|
+
import usePrefetchWhenNear from './usePrefetchWhenNear.js';
|
|
4
5
|
const VARIABLE_AXES = 'VARIABLE_AXES';
|
|
5
6
|
const LOADING = '__loading__';
|
|
6
7
|
const TypeTesterStyleSelect = _ref => {
|
|
@@ -19,6 +20,8 @@ const TypeTesterStyleSelect = _ref => {
|
|
|
19
20
|
onSelectFamilyId,
|
|
20
21
|
includeVariableAxesOption
|
|
21
22
|
} = _ref;
|
|
23
|
+
const containerRef = useRef(null);
|
|
24
|
+
usePrefetchWhenNear(containerRef, onOpen);
|
|
22
25
|
const handleSelectFontStyle = selection => {
|
|
23
26
|
if (selection === VARIABLE_AXES) {
|
|
24
27
|
onSelectVariableAxes();
|
|
@@ -90,7 +93,9 @@ const TypeTesterStyleSelect = _ref => {
|
|
|
90
93
|
cb(e.target.value);
|
|
91
94
|
};
|
|
92
95
|
return /*#__PURE__*/React.createElement("div", {
|
|
96
|
+
ref: containerRef,
|
|
93
97
|
className: "type-tester-select",
|
|
98
|
+
onPointerEnter: () => onOpen === null || onOpen === void 0 ? void 0 : onOpen(),
|
|
94
99
|
"data-loading": loading || undefined,
|
|
95
100
|
"aria-busy": loading || undefined,
|
|
96
101
|
style: {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const PREFETCH_ROOT_MARGIN = "300px";
|
|
2
|
+
export declare const PREFETCH_IDLE_TIMEOUT = 2000;
|
|
3
|
+
/**
|
|
4
|
+
* Calls `prefetch` once, at idle time, after the element first comes near the
|
|
5
|
+
* viewport. Native `<select>` menus snapshot their options when they open (in
|
|
6
|
+
* Safari the open menu never updates), so data a dropdown needs has to be
|
|
7
|
+
* there before the first click – fetching on mousedown is too late. Waiting
|
|
8
|
+
* for idle keeps the request off the initial render, and waiting for the
|
|
9
|
+
* viewport keeps a long page of testers from fetching for every one at once.
|
|
10
|
+
*
|
|
11
|
+
* const ref = useRef<HTMLDivElement>(null);
|
|
12
|
+
* usePrefetchWhenNear(ref, loadOptions);
|
|
13
|
+
*/
|
|
14
|
+
export default function usePrefetchWhenNear(ref: React.RefObject<Element | null>, prefetch: (() => void) | undefined): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
// How far outside the viewport a tester counts as "near".
|
|
4
|
+
export const PREFETCH_ROOT_MARGIN = '300px';
|
|
5
|
+
// Upper bound on waiting for the browser to go idle.
|
|
6
|
+
export const PREFETCH_IDLE_TIMEOUT = 2000;
|
|
7
|
+
function onIdle(cb) {
|
|
8
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
9
|
+
const id = window.requestIdleCallback(cb, {
|
|
10
|
+
timeout: PREFETCH_IDLE_TIMEOUT
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
cancel: () => window.cancelIdleCallback(id)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Safari has no requestIdleCallback.
|
|
17
|
+
const id = window.setTimeout(cb, 200);
|
|
18
|
+
return {
|
|
19
|
+
cancel: () => window.clearTimeout(id)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Calls `prefetch` once, at idle time, after the element first comes near the
|
|
25
|
+
* viewport. Native `<select>` menus snapshot their options when they open (in
|
|
26
|
+
* Safari the open menu never updates), so data a dropdown needs has to be
|
|
27
|
+
* there before the first click – fetching on mousedown is too late. Waiting
|
|
28
|
+
* for idle keeps the request off the initial render, and waiting for the
|
|
29
|
+
* viewport keeps a long page of testers from fetching for every one at once.
|
|
30
|
+
*
|
|
31
|
+
* const ref = useRef<HTMLDivElement>(null);
|
|
32
|
+
* usePrefetchWhenNear(ref, loadOptions);
|
|
33
|
+
*/
|
|
34
|
+
export default function usePrefetchWhenNear(ref, prefetch) {
|
|
35
|
+
const prefetchRef = useRef(prefetch);
|
|
36
|
+
prefetchRef.current = prefetch;
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const el = ref.current;
|
|
39
|
+
if (!el) return;
|
|
40
|
+
let idle = null;
|
|
41
|
+
const schedule = () => {
|
|
42
|
+
idle ??= onIdle(() => {
|
|
43
|
+
var _prefetchRef$current;
|
|
44
|
+
return (_prefetchRef$current = prefetchRef.current) === null || _prefetchRef$current === void 0 ? void 0 : _prefetchRef$current.call(prefetchRef);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
if (typeof IntersectionObserver !== 'function') {
|
|
48
|
+
schedule();
|
|
49
|
+
return () => {
|
|
50
|
+
var _idle;
|
|
51
|
+
return (_idle = idle) === null || _idle === void 0 ? void 0 : _idle.cancel();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const observer = new IntersectionObserver(entries => {
|
|
55
|
+
if (entries.some(entry => entry.isIntersecting)) {
|
|
56
|
+
observer.disconnect();
|
|
57
|
+
schedule();
|
|
58
|
+
}
|
|
59
|
+
}, {
|
|
60
|
+
rootMargin: PREFETCH_ROOT_MARGIN
|
|
61
|
+
});
|
|
62
|
+
observer.observe(el);
|
|
63
|
+
return () => {
|
|
64
|
+
var _idle2;
|
|
65
|
+
observer.disconnect();
|
|
66
|
+
(_idle2 = idle) === null || _idle2 === void 0 ? void 0 : _idle2.cancel();
|
|
67
|
+
};
|
|
68
|
+
}, [ref]);
|
|
69
|
+
}
|
package/dist/fontdue.css
CHANGED
|
@@ -2136,6 +2136,14 @@ input:checked + .checkbox__icon {
|
|
|
2136
2136
|
font-size: 60px;
|
|
2137
2137
|
}
|
|
2138
2138
|
|
|
2139
|
+
.store-modal__index-item__description:first-child {
|
|
2140
|
+
grid-column: 1/-2;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
.store-modal__index-item__buy {
|
|
2144
|
+
grid-column: -2/-1;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2139
2147
|
.store-modal__index-item__name {
|
|
2140
2148
|
font-size: 20px;
|
|
2141
2149
|
line-height: 30px;
|
|
@@ -4007,6 +4015,7 @@ input:disabled + .fontdue-admin-toolbar__switch {
|
|
|
4007
4015
|
|
|
4008
4016
|
fontdue-test-fonts-form,
|
|
4009
4017
|
fontdue-newsletter-signup,
|
|
4010
|
-
fontdue-customer-login-form
|
|
4018
|
+
fontdue-customer-login-form,
|
|
4019
|
+
fontdue-customer-sign-in {
|
|
4011
4020
|
font-size: 1rem;
|
|
4012
4021
|
}
|
package/dist/version.js
CHANGED
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
// (Fontage.ClientVersions) to spot sites that need to upgrade before a
|
|
9
9
|
// feature that depends on a newer client can work for them.
|
|
10
10
|
export const CLIENT_VERSION_HEADER = 'fontdue-client-version';
|
|
11
|
-
export const version = "3.
|
|
11
|
+
export const version = "3.13.0";
|
package/package.json
CHANGED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @generated SignedSource<<c1a8446af3677d58e3851338c54cf16e>>
|
|
3
|
-
* @lightSyntaxTransform
|
|
4
|
-
* @nogrep
|
|
5
|
-
*/
|
|
6
|
-
import { ReaderFragment } from 'relay-runtime';
|
|
7
|
-
import { FragmentRefs } from "relay-runtime";
|
|
8
|
-
export type productState_collectionBundle$data = {
|
|
9
|
-
readonly id: string;
|
|
10
|
-
readonly memberCollections: ReadonlyArray<{
|
|
11
|
-
readonly fontStyles: ReadonlyArray<{
|
|
12
|
-
readonly id: string;
|
|
13
|
-
readonly sku: {
|
|
14
|
-
readonly id: string;
|
|
15
|
-
readonly price: {
|
|
16
|
-
readonly amount: number;
|
|
17
|
-
};
|
|
18
|
-
} | null;
|
|
19
|
-
}>;
|
|
20
|
-
readonly id: string;
|
|
21
|
-
readonly sku: {
|
|
22
|
-
readonly id: string;
|
|
23
|
-
readonly price: {
|
|
24
|
-
readonly amount: number;
|
|
25
|
-
};
|
|
26
|
-
} | null;
|
|
27
|
-
}> | null;
|
|
28
|
-
readonly name: string;
|
|
29
|
-
readonly sku: {
|
|
30
|
-
readonly id: string;
|
|
31
|
-
readonly price: {
|
|
32
|
-
readonly amount: number;
|
|
33
|
-
};
|
|
34
|
-
} | null;
|
|
35
|
-
};
|
|
36
|
-
export type productState_collectionBundle$key = {
|
|
37
|
-
readonly " $data"?: productState_collectionBundle$data;
|
|
38
|
-
readonly " $fragmentSpreads": FragmentRefs<"productState_collectionBundle">;
|
|
39
|
-
};
|
|
40
|
-
declare const node: ReaderFragment;
|
|
41
|
-
export default node;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @generated SignedSource<<c1a8446af3677d58e3851338c54cf16e>>
|
|
3
|
-
* @lightSyntaxTransform
|
|
4
|
-
* @nogrep
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/* tslint:disable */
|
|
8
|
-
/* eslint-disable */
|
|
9
|
-
// @ts-nocheck
|
|
10
|
-
|
|
11
|
-
const node = function () {
|
|
12
|
-
var v0 = {
|
|
13
|
-
"alias": null,
|
|
14
|
-
"args": null,
|
|
15
|
-
"kind": "ScalarField",
|
|
16
|
-
"name": "id",
|
|
17
|
-
"storageKey": null
|
|
18
|
-
},
|
|
19
|
-
v1 = {
|
|
20
|
-
"alias": null,
|
|
21
|
-
"args": null,
|
|
22
|
-
"concreteType": "Sku",
|
|
23
|
-
"kind": "LinkedField",
|
|
24
|
-
"name": "sku",
|
|
25
|
-
"plural": false,
|
|
26
|
-
"selections": [v0 /*: any*/, {
|
|
27
|
-
"alias": null,
|
|
28
|
-
"args": null,
|
|
29
|
-
"concreteType": "Money",
|
|
30
|
-
"kind": "LinkedField",
|
|
31
|
-
"name": "price",
|
|
32
|
-
"plural": false,
|
|
33
|
-
"selections": [{
|
|
34
|
-
"alias": null,
|
|
35
|
-
"args": null,
|
|
36
|
-
"kind": "ScalarField",
|
|
37
|
-
"name": "amount",
|
|
38
|
-
"storageKey": null
|
|
39
|
-
}],
|
|
40
|
-
"storageKey": null
|
|
41
|
-
}],
|
|
42
|
-
"storageKey": null
|
|
43
|
-
};
|
|
44
|
-
return {
|
|
45
|
-
"argumentDefinitions": [],
|
|
46
|
-
"kind": "Fragment",
|
|
47
|
-
"metadata": {
|
|
48
|
-
"mask": false
|
|
49
|
-
},
|
|
50
|
-
"name": "productState_collectionBundle",
|
|
51
|
-
"selections": [v0 /*: any*/, {
|
|
52
|
-
"alias": null,
|
|
53
|
-
"args": null,
|
|
54
|
-
"kind": "ScalarField",
|
|
55
|
-
"name": "name",
|
|
56
|
-
"storageKey": null
|
|
57
|
-
}, v1 /*: any*/, {
|
|
58
|
-
"alias": null,
|
|
59
|
-
"args": null,
|
|
60
|
-
"concreteType": "FontCollection",
|
|
61
|
-
"kind": "LinkedField",
|
|
62
|
-
"name": "memberCollections",
|
|
63
|
-
"plural": true,
|
|
64
|
-
"selections": [v0 /*: any*/, v1 /*: any*/, {
|
|
65
|
-
"alias": null,
|
|
66
|
-
"args": null,
|
|
67
|
-
"concreteType": "FontStyle",
|
|
68
|
-
"kind": "LinkedField",
|
|
69
|
-
"name": "fontStyles",
|
|
70
|
-
"plural": true,
|
|
71
|
-
"selections": [v0 /*: any*/, v1 /*: any*/],
|
|
72
|
-
"storageKey": null
|
|
73
|
-
}],
|
|
74
|
-
"storageKey": null
|
|
75
|
-
}],
|
|
76
|
-
"type": "CollectionBundle",
|
|
77
|
-
"abstractKey": null
|
|
78
|
-
};
|
|
79
|
-
}();
|
|
80
|
-
node.hash = "6744aa8f57d14b0111a0c49f488d7b03";
|
|
81
|
-
export default node;
|