@shopify/hydrogen-react
Version:
React components, hooks, and utilities for creating custom Shopify storefronts
486 lines (484 loc) • 15.7 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const require$$0 = require("react");
const useCartAPIStateMachine = require("./useCartAPIStateMachine.js");
const cartConstants = require("./cart-constants.js");
const jsxRuntime = require("react/jsx-runtime");
const CartContext = require$$0.createContext(null);
function useCart() {
const context = require$$0.useContext(CartContext);
if (!context) {
throw new Error("Expected a Cart Context, but no Cart Context was found");
}
return context;
}
function CartProvider({
children,
numCartLines,
onCreate,
onLineAdd,
onLineRemove,
onLineUpdate,
onNoteUpdate,
onBuyerIdentityUpdate,
onAttributesUpdate,
onDiscountCodesUpdate,
onCreateComplete,
onLineAddComplete,
onLineRemoveComplete,
onLineUpdateComplete,
onNoteUpdateComplete,
onBuyerIdentityUpdateComplete,
onAttributesUpdateComplete,
onDiscountCodesUpdateComplete,
data: cart,
cartFragment = defaultCartFragment,
customerAccessToken,
countryCode = "US"
}) {
var _a, _b, _c, _d, _e, _f, _g;
if (countryCode)
countryCode = countryCode.toUpperCase();
const [prevCountryCode, setPrevCountryCode] = require$$0.useState(countryCode);
const [prevCustomerAccessToken, setPrevCustomerAccessToken] = require$$0.useState(customerAccessToken);
const customerOverridesCountryCode = require$$0.useRef(false);
if (prevCountryCode !== countryCode || prevCustomerAccessToken !== customerAccessToken) {
setPrevCountryCode(countryCode);
setPrevCustomerAccessToken(customerAccessToken);
customerOverridesCountryCode.current = false;
}
const [cartState, cartSend] = useCartAPIStateMachine.useCartAPIStateMachine({
numCartLines,
data: cart,
cartFragment,
countryCode,
onCartActionEntry(context, event) {
try {
switch (event.type) {
case "CART_CREATE":
return onCreate == null ? void 0 : onCreate();
case "CARTLINE_ADD":
return onLineAdd == null ? void 0 : onLineAdd();
case "CARTLINE_REMOVE":
return onLineRemove == null ? void 0 : onLineRemove();
case "CARTLINE_UPDATE":
return onLineUpdate == null ? void 0 : onLineUpdate();
case "NOTE_UPDATE":
return onNoteUpdate == null ? void 0 : onNoteUpdate();
case "BUYER_IDENTITY_UPDATE":
return onBuyerIdentityUpdate == null ? void 0 : onBuyerIdentityUpdate();
case "CART_ATTRIBUTES_UPDATE":
return onAttributesUpdate == null ? void 0 : onAttributesUpdate();
case "DISCOUNT_CODES_UPDATE":
return onDiscountCodesUpdate == null ? void 0 : onDiscountCodesUpdate();
}
} catch (error) {
console.error("Cart entry action failed", error);
}
},
onCartActionOptimisticUI(context, event) {
var _a2, _b2, _c2, _d2;
if (!(context == null ? void 0 : context.cart))
return {
cart: void 0
};
switch (event.type) {
case "CARTLINE_REMOVE":
return {
...context,
lastValidCart: context.cart,
cart: {
...context.cart,
lines: (_b2 = (_a2 = context == null ? void 0 : context.cart) == null ? void 0 : _a2.lines) == null ? void 0 : _b2.filter((line) => (line == null ? void 0 : line.id) && !event.payload.lines.includes(line == null ? void 0 : line.id))
}
};
case "CARTLINE_UPDATE":
return {
...context,
lastValidCart: context.cart,
cart: {
...context.cart,
lines: (_d2 = (_c2 = context == null ? void 0 : context.cart) == null ? void 0 : _c2.lines) == null ? void 0 : _d2.map((line) => {
const updatedLine = event.payload.lines.find(({
id
}) => id === (line == null ? void 0 : line.id));
if (updatedLine && updatedLine.quantity) {
return {
...line,
quantity: updatedLine.quantity
};
}
return line;
})
}
};
}
return {
cart: context.cart ? {
...context.cart
} : void 0
};
},
onCartActionComplete(context, event) {
const cartActionEvent = event.payload.cartActionEvent;
try {
switch (event.type) {
case "RESOLVE":
switch (cartActionEvent.type) {
case "CART_CREATE":
return onCreateComplete == null ? void 0 : onCreateComplete();
case "CARTLINE_ADD":
return onLineAddComplete == null ? void 0 : onLineAddComplete();
case "CARTLINE_REMOVE":
return onLineRemoveComplete == null ? void 0 : onLineRemoveComplete();
case "CARTLINE_UPDATE":
return onLineUpdateComplete == null ? void 0 : onLineUpdateComplete();
case "NOTE_UPDATE":
return onNoteUpdateComplete == null ? void 0 : onNoteUpdateComplete();
case "BUYER_IDENTITY_UPDATE":
if (countryCodeNotUpdated(context, cartActionEvent)) {
customerOverridesCountryCode.current = true;
}
return onBuyerIdentityUpdateComplete == null ? void 0 : onBuyerIdentityUpdateComplete();
case "CART_ATTRIBUTES_UPDATE":
return onAttributesUpdateComplete == null ? void 0 : onAttributesUpdateComplete();
case "DISCOUNT_CODES_UPDATE":
return onDiscountCodesUpdateComplete == null ? void 0 : onDiscountCodesUpdateComplete();
}
}
} catch (error) {
console.error("onCartActionComplete failed", error);
}
}
});
const cartReady = require$$0.useRef(false);
const cartCompleted = cartState.matches("cartCompleted");
const countryChanged = (cartState.value === "idle" || cartState.value === "error" || cartState.value === "cartCompleted") && countryCode !== ((_c = (_b = (_a = cartState == null ? void 0 : cartState.context) == null ? void 0 : _a.cart) == null ? void 0 : _b.buyerIdentity) == null ? void 0 : _c.countryCode) && !cartState.context.errors;
const fetchingFromStorage = require$$0.useRef(false);
require$$0.useEffect(() => {
if (!cartReady.current && !fetchingFromStorage.current) {
if (!cart && storageAvailable("localStorage")) {
fetchingFromStorage.current = true;
try {
const cartId = window.localStorage.getItem(cartConstants.CART_ID_STORAGE_KEY);
if (cartId) {
cartSend({
type: "CART_FETCH",
payload: {
cartId
}
});
}
} catch (error) {
console.warn("error fetching cartId");
console.warn(error);
}
}
cartReady.current = true;
}
}, [cart, cartReady, cartSend]);
require$$0.useEffect(() => {
if (!countryChanged || customerOverridesCountryCode.current)
return;
cartSend({
type: "BUYER_IDENTITY_UPDATE",
payload: {
buyerIdentity: {
countryCode,
customerAccessToken
}
}
});
}, [countryCode, customerAccessToken, countryChanged, customerOverridesCountryCode, cartSend]);
const onCartReadySend = require$$0.useCallback((cartEvent) => {
if (!cartReady.current) {
return console.warn("Cart isn't ready yet");
}
cartSend(cartEvent);
}, [cartSend]);
require$$0.useEffect(() => {
var _a2, _b2, _c2;
if (((_b2 = (_a2 = cartState == null ? void 0 : cartState.context) == null ? void 0 : _a2.cart) == null ? void 0 : _b2.id) && storageAvailable("localStorage")) {
try {
window.localStorage.setItem(cartConstants.CART_ID_STORAGE_KEY, (_c2 = cartState.context.cart) == null ? void 0 : _c2.id);
} catch (error) {
console.warn("Failed to save cartId to localStorage", error);
}
}
}, [(_e = (_d = cartState == null ? void 0 : cartState.context) == null ? void 0 : _d.cart) == null ? void 0 : _e.id]);
require$$0.useEffect(() => {
if (cartCompleted && storageAvailable("localStorage")) {
try {
window.localStorage.removeItem(cartConstants.CART_ID_STORAGE_KEY);
} catch (error) {
console.warn("Failed to delete cartId from localStorage", error);
}
}
}, [cartCompleted]);
const cartCreate = require$$0.useCallback((cartInput) => {
var _a2, _b2;
if (countryCode && !((_a2 = cartInput.buyerIdentity) == null ? void 0 : _a2.countryCode)) {
if (cartInput.buyerIdentity == null) {
cartInput.buyerIdentity = {};
}
cartInput.buyerIdentity.countryCode = countryCode;
}
if (customerAccessToken && !((_b2 = cartInput.buyerIdentity) == null ? void 0 : _b2.customerAccessToken)) {
if (cartInput.buyerIdentity == null) {
cartInput.buyerIdentity = {};
}
cartInput.buyerIdentity.customerAccessToken = customerAccessToken;
}
onCartReadySend({
type: "CART_CREATE",
payload: cartInput
});
}, [countryCode, customerAccessToken, onCartReadySend]);
const cartDisplayState = useDelayedStateUntilHydration(cartState);
const cartContextValue = require$$0.useMemo(() => {
var _a2, _b2, _c2, _d2, _e2, _f2;
return {
...(_b2 = (_a2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _a2.cart) != null ? _b2 : {
lines: [],
attributes: []
},
status: transposeStatus(cartDisplayState.value),
error: (_c2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _c2.errors,
totalQuantity: (_f2 = (_e2 = (_d2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _d2.cart) == null ? void 0 : _e2.totalQuantity) != null ? _f2 : 0,
cartCreate,
linesAdd(lines) {
var _a3, _b3;
if ((_b3 = (_a3 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _a3.cart) == null ? void 0 : _b3.id) {
onCartReadySend({
type: "CARTLINE_ADD",
payload: {
lines
}
});
} else {
cartCreate({
lines
});
}
},
linesRemove(lines) {
onCartReadySend({
type: "CARTLINE_REMOVE",
payload: {
lines
}
});
},
linesUpdate(lines) {
onCartReadySend({
type: "CARTLINE_UPDATE",
payload: {
lines
}
});
},
noteUpdate(note) {
onCartReadySend({
type: "NOTE_UPDATE",
payload: {
note
}
});
},
buyerIdentityUpdate(buyerIdentity) {
onCartReadySend({
type: "BUYER_IDENTITY_UPDATE",
payload: {
buyerIdentity
}
});
},
cartAttributesUpdate(attributes) {
onCartReadySend({
type: "CART_ATTRIBUTES_UPDATE",
payload: {
attributes
}
});
},
discountCodesUpdate(discountCodes) {
onCartReadySend({
type: "DISCOUNT_CODES_UPDATE",
payload: {
discountCodes
}
});
},
cartFragment
};
}, [cartCreate, (_f = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _f.cart, (_g = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _g.errors, cartDisplayState.value, cartFragment, onCartReadySend]);
return /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, {
value: cartContextValue,
children
});
}
function transposeStatus(status) {
switch (status) {
case "uninitialized":
case "initializationError":
return "uninitialized";
case "idle":
case "cartCompleted":
case "error":
return "idle";
case "cartFetching":
return "fetching";
case "cartCreating":
return "creating";
case "cartLineAdding":
case "cartLineRemoving":
case "cartLineUpdating":
case "noteUpdating":
case "buyerIdentityUpdating":
case "cartAttributesUpdating":
case "discountCodesUpdating":
return "updating";
}
}
function useDelayedStateUntilHydration(state) {
const [isPending, startTransition] = require$$0.useTransition();
const [delayedState, setDelayedState] = require$$0.useState(state);
const firstTimePending = require$$0.useRef(false);
if (isPending) {
firstTimePending.current = true;
}
const firstTimePendingFinished = require$$0.useRef(false);
if (!isPending && firstTimePending.current) {
firstTimePendingFinished.current = true;
}
require$$0.useEffect(() => {
startTransition(() => {
if (!firstTimePendingFinished.current) {
setDelayedState(state);
}
});
}, [state]);
const displayState = firstTimePendingFinished.current ? state : delayedState;
return displayState;
}
function storageAvailable(type) {
let storage;
try {
storage = window[type];
const x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return e instanceof DOMException && (e.code === 22 || e.code === 1014 || e.name === "QuotaExceededError" || e.name === "NS_ERROR_DOM_QUOTA_REACHED") && storage && storage.length !== 0;
}
}
function countryCodeNotUpdated(context, event) {
var _a, _b;
return event.payload.buyerIdentity.countryCode && ((_b = (_a = context.cart) == null ? void 0 : _a.buyerIdentity) == null ? void 0 : _b.countryCode) !== event.payload.buyerIdentity.countryCode;
}
const defaultCartFragment = `
fragment CartFragment on Cart {
id
checkoutUrl
totalQuantity
buyerIdentity {
countryCode
customer {
id
email
firstName
lastName
displayName
}
email
phone
}
lines(first: $numCartLines) {
edges {
node {
id
quantity
attributes {
key
value
}
cost {
totalAmount {
amount
currencyCode
}
compareAtAmountPerQuantity {
amount
currencyCode
}
}
merchandise {
... on ProductVariant {
id
availableForSale
compareAtPriceV2 {
...MoneyFragment
}
priceV2 {
...MoneyFragment
}
requiresShipping
title
image {
...ImageFragment
}
product {
handle
title
}
selectedOptions {
name
value
}
}
}
}
}
}
cost {
subtotalAmount {
...MoneyFragment
}
totalAmount {
...MoneyFragment
}
totalDutyAmount {
...MoneyFragment
}
totalTaxAmount {
...MoneyFragment
}
}
note
attributes {
key
value
}
discountCodes {
code
}
}
fragment MoneyFragment on MoneyV2 {
currencyCode
amount
}
fragment ImageFragment on Image {
id
url
altText
width
height
}
`;
exports.CartContext = CartContext;
exports.CartProvider = CartProvider;
exports.defaultCartFragment = defaultCartFragment;
exports.storageAvailable = storageAvailable;
exports.useCart = useCart;
//# sourceMappingURL=CartProvider.js.map