);
}
function Chrome({ children }) {
const nav = window.useLemonaNav();
const [menuOpen, setMenuOpen] = React.useState(false);
const [cartOpen, setCartOpen] = React.useState(false);
const [cart, setCart] = React.useState([]);
const [counts, setCounts] = React.useState({ cart: 0, wish: 0 });
const bag = useBagQuote(cart, cartOpen);
// Header badges read the shared cart and the customer's saved wishlist, so they are
// correct on every page rather than hardcoded to zero.
//
// The Store object is looked up at CALL time, not captured once when Chrome mounts, and
// every method is feature-checked before it is called. A stale or partially loaded
// store.js therefore degrades to a zero badge instead of throwing inside the effect and
// taking the whole page down with it. As soon as the real API is present the badges
// populate normally on the next sync.
React.useEffect(() => {
const sync = () => {
const S = window.LemonaStore;
const cart = S && typeof S.getCartCount === "function" ? Number(S.getCartCount()) || 0 : 0;
let wish = 0;
if (S && typeof S.getWishlist === "function") {
const list = S.getWishlist();
wish = Array.isArray(list) ? list.length : 0;
}
setCounts({ cart: cart, wish: wish });
// Keep the drawer's own line list in step with the shared cart.
if (S && typeof S.getCart === "function") {
try { setCart(S.getCart() || []); } catch (e) { /* leave the last known list */ }
}
};
sync();
const S = window.LemonaStore;
if (S && typeof S.loadWishlist === "function") {
// Wrapped in try/catch as well as the promise catch: a synchronous throw from an
// older implementation must not stop the listeners below from being attached.
try { Promise.resolve(S.loadWishlist()).then(sync).catch(function () {}); }
catch (e) { /* wishlist stays at its current value */ }
}
window.addEventListener("lemona:cart-changed", sync);
window.addEventListener("lemona:wishlist-changed", sync);
return () => {
window.removeEventListener("lemona:cart-changed", sync);
window.removeEventListener("lemona:wishlist-changed", sync);
};
}, []);
return (
{window.StoreNotice ? : null}
{ location.href = "account.html"; }}
onWishlistClick={() => { location.href = "account.html#wishlist"; }}
onCartClick={() => setCartOpen(true)}
onMenuClick={() => setMenuOpen(true)} />
{children} setMenuOpen(false)}>
{/* The bag drawer for every page that renders through Chrome (account, category,
checkout, CMS pages, blog). index.html and product.html render their own Header
and their own drawer, so they are unaffected. This owns no cart state of its
own: every read and write goes through the shared LemonaStore cart, so the
badge, this drawer and checkout can never disagree. */}
setCartOpen(false)}
footer={cart.length ? : null}>
{cart.length ? cart.map((l) => (
{
const S = window.LemonaStore;
if (S && typeof S.updateCartQuantity === "function") setCart(S.updateCartQuantity(l.id, q) || []);
}}
onRemove={() => {
const S = window.LemonaStore;
if (S && typeof S.removeFromCart === "function") setCart(S.removeFromCart(l.id) || []);
}} />
)) : (
)}
);
}
/** Bag subtotal from the shared cart lines. Mirrors the drawer maths already used on the
* homepage and product page; the server remains the authority at checkout. */
function cartSubtotal(lines) {
return (lines || []).reduce(function (n, l) {
return n + (Number(l.price) || 0) * (Number(l.quantity) || 0);
}, 0);
}
/** Slug from /product/, /category/ or ?slug= when rewrites are off. */
function slugFromUrl() {
const q = new URLSearchParams(location.search).get("slug");
if (q) return q;
const parts = location.pathname.split("/").filter(Boolean);
const last = parts[parts.length - 1] || "";
return last.endsWith(".html") ? "" : last;
}
Object.assign(window, { Chrome, slugFromUrl });
})();