@lime-bundles/react 2.3.0 → 2.4.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/dist/index.cjs +680 -237
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -2
- package/dist/index.d.ts +50 -2
- package/dist/index.js +635 -187
- package/dist/index.js.map +1 -1
- package/docs/css-variables.md +3 -0
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/FixedBundle.tsx","../src/hooks/useBundleData.ts","../src/fetchBundleData.ts","../src/fetchShopCustomCss.ts","../src/hooks/useAnalytics.ts","../src/hooks/useWidgetConfigVars.ts","../src/components/MixMatchBundle.tsx","../src/components/VolumeBundle.tsx","../src/hooks/useBundlesForProduct.ts"],"sourcesContent":["// Bundle components — BYO-cart via `onAddToCart`.\nexport { FixedBundle } from \"./components/FixedBundle\";\nexport { MixMatchBundle } from \"./components/MixMatchBundle\";\nexport { VolumeBundle } from \"./components/VolumeBundle\";\nexport type { BundleComponentProps } from \"./components/types\";\n\n// Data fetching\nexport {\n fetchBundleData,\n type FetchBundleDataOptions,\n} from \"./fetchBundleData\";\nexport {\n fetchShopCustomCss,\n type FetchShopCustomCssOptions,\n} from \"./fetchShopCustomCss\";\nexport {\n useBundleData,\n type UseBundleDataOptions,\n type UseBundleDataResult,\n} from \"./hooks/useBundleData\";\nexport {\n useBundlesForProduct,\n type UseBundlesForProductOptions,\n type UseBundlesForProductResult,\n} from \"./hooks/useBundlesForProduct\";\nexport { useAnalytics } from \"./hooks/useAnalytics\";\nexport { useWidgetConfigVars } from \"./hooks/useWidgetConfigVars\";\n\n// Re-export core types consumers commonly need.\nexport type {\n ParsedBundle,\n FixedBundleData,\n VolumeBundleData,\n MixMatchBundleData,\n BundleType,\n Product,\n ProductVariant,\n CartLineInput,\n WidgetConfig,\n HeaderConfig,\n LayoutConfig,\n ProductListConfig,\n ThumbnailRatio,\n PricingConfig,\n SavingsBarConfig,\n CtaConfig,\n CountdownConfig,\n PopularBadgeConfig,\n VolumeTier,\n DiscountConfig,\n ABVariantOverrides,\n PricingRow,\n FixedBundlePricing,\n ImageTransform,\n} from \"@lime-bundles/core\";\nexport { BundleParseError, StorefrontApiError } from \"@lime-bundles/core\";\n\n// DIY widget primitives — every helper a developer needs to build their\n// own bundle UI with the same data, math, and theming the first-party\n// widget uses. All live in `@lime-bundles/core`; importing from the\n// React package is shorthand for consumers who already pulled `react`.\nexport {\n // Bundle config + variant merging\n WIDGET_CONFIG_DEFAULTS,\n mergeWidgetConfig,\n applyWidgetConfigVars,\n thumbnailRatioVars,\n applyABVariantB,\n // Pricing (integer cents, matches Shopify Discount Function rounding)\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n // Volume-tier calculation\n calculateTierSavings,\n getActiveTier,\n // Mix-match quantity validation\n validateQuantity,\n // Image / countdown / money helpers\n transformImageUrl,\n formatCountdown,\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n // Analytics (optional — FixedBundle / VolumeBundle / MixMatchBundle\n // auto-fire these unless analyticsEnabled is set to false)\n reportImpression,\n reportAddToCart,\n observeImpression,\n // A/B testing\n getABTestAssignment,\n setConsent,\n hasConsent,\n // CSS injection + sanitizer\n injectCustomCss,\n sanitizeCustomCss,\n // Custom-storefront data fetching\n createStorefrontClient,\n fetchBundlesForProduct,\n parseMetaobjectBundle,\n parseMetaobjectBundleStrict,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n SHOP_CUSTOM_CSS_QUERY,\n} from \"@lime-bundles/core\";\n","/**\n * Fixed bundle component — pre-selected products with a shared discount.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function FixedBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"fixed\",\n enabled: analyticsEnabled !== false,\n });\n\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"fixed\"\n ? (result.bundle as FixedBundleData)\n : null;\n\n // Apply merchant-config CSS vars (including --lb-thumbnail-aspect-ratio)\n // to the component root via a callback ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n // Surface load/narrowing errors via onError — in a useEffect so we don't\n // fire side effects during render.\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"fixed\"\n ) {\n onError?.(\n new Error(\n `FixedBundle: expected bundleType=\"fixed\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle) return;\n\n // Qty resolution (variantQuantities → productQuantities → 1) lives in\n // `@lime-bundles/core` so the five-runtime fallback chain has a single\n // source of truth. See resolveBundleQty + PR #147 history.\n const bundleLines = bundle.products\n .filter((p) => p.variants.nodes.some((v) => v.availableForSale))\n .map((p) => {\n const variant = p.variants.nodes.find((v) => v.availableForSale)!;\n return {\n productId: p.id,\n variant,\n quantity: resolveBundleQty(bundle, p.id, variant.id),\n };\n })\n .filter((line) => line.quantity > 0);\n\n if (bundleLines.length === 0) return;\n\n const lines: CartLineInput[] = bundleLines.map((line) => ({\n merchandiseId: line.variant.id,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n const totalPrice = bundleLines.reduce((sum, line) => {\n const price = parseFloat(line.variant.price.amount);\n const discounted = calculateDiscount(\n price,\n bundle.discountConfig.discountType,\n bundle.discountConfig.discountValue,\n );\n return sum + discounted * line.quantity;\n }, 0);\n const totalQuantity = bundleLines.reduce(\n (sum, line) => sum + line.quantity,\n 0,\n );\n // productId is intentionally omitted — a fixed bundle spans multiple\n // products with no single \"page product\"; bundleGid on the event\n // already attributes to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [bundle, onAddToCart, onError, trackAddToCart]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n // Bundle loaded but wasn't a \"fixed\" variant — render nothing; the\n // useEffect above has already invoked onError with a descriptive message.\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--fixed ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <div className=\"lb-bundle__products\">\n {bundle.products.map((product) => {\n // Render the variant we'll actually add to cart so the price (and\n // unit price) match what the customer is buying — falling back to\n // the price range only when no variant is resolvable.\n const variant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n const priceText = variant\n ? formatMoney(variant.price.amount, currency)\n : formatMoney(\n product.priceRange.minVariantPrice.amount,\n currency,\n );\n const unitPriceText = variant\n ? formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n )\n : null;\n return (\n <div key={product.id} className=\"lb-bundle__product\" part=\"product\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">{priceText}</p>\n {unitPriceText && (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? \"Add Bundle to Cart\")}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundleData` with loading / error\n * state + abort-on-unmount.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundleData` directly and pass the result to a client\n * component as a prop — the hook's only value-add over the async\n * function is React state management.\n *\n * `UseBundleDataResult` is a discriminated union locked at v1.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport { injectCustomCss, type ParsedBundle } from \"@lime-bundles/core\";\nimport { fetchBundleData } from \"../fetchBundleData\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n}\n\nexport type UseBundleDataResult =\n | { status: \"loading\"; bundle: null; error: null }\n | { status: \"success\"; bundle: ParsedBundle; error: null }\n | { status: \"error\"; bundle: null; error: Error };\n\nconst INITIAL_STATE: UseBundleDataResult = {\n status: \"loading\",\n bundle: null,\n error: null,\n};\n\nexport function useBundleData(\n options: UseBundleDataOptions,\n): UseBundleDataResult {\n const [state, setState] = useState<UseBundleDataResult>(INITIAL_STATE);\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundleData({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n bundleGid: options.bundleGid,\n signal: controller.signal,\n })\n .then((bundle) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundle, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundle: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch — best-effort, failures never block\n // bundle rendering.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.bundleGid,\n ]);\n\n return state;\n}\n","/**\n * Framework-agnostic async fetcher for bundle data.\n *\n * Primary path for Next.js App Router / RSC and Hydrogen loaders — call\n * this in a server-side context and pass the result as a prop to a\n * `\"use client\"` component. `useBundleData` is NOT needed in that\n * component because the data is already in hand.\n *\n * Always forward the end-buyer's IP via `buyerIp` when calling from SSR:\n * Shopify may return 430 Security Rejection for server-originated traffic\n * without it.\n *\n * Throws:\n * - `StorefrontApiError` on network / HTTP / GraphQL errors\n * - `BundleParseError` when the metaobject is missing, invalid type,\n * inactive, not yet started, or expired\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundleStrict,\n BundleParseError,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR. */\n buyerIp?: string;\n /** Abort the in-flight request (used by `useBundleData` to cancel on unmount). */\n signal?: AbortSignal;\n /** Override the Storefront API version pinned in `createStorefrontClient`. */\n apiVersion?: string;\n}\n\nexport async function fetchBundleData(\n options: FetchBundleDataOptions,\n): Promise<ParsedBundle> {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n apiVersion: options.apiVersion,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: options.bundleGid },\n { signal: options.signal },\n );\n\n if (!data.metaobject) {\n throw new BundleParseError(\n `Bundle not found: ${options.bundleGid}`,\n \"not_found\",\n );\n }\n\n return parseMetaobjectBundleStrict(\n data.metaobject.id,\n data.metaobject.fields,\n );\n}\n","/**\n * Best-effort fetch for the shop's `$app:custom_css` metafield.\n *\n * Returns the raw CSS string (still needs sanitization before injection —\n * see `injectCustomCss` from core) or null if the metafield is unset or\n * the request fails.\n *\n * **Per-shop in-memory cache.** Multiple `<FixedBundle>` / `<VolumeBundle>`\n * / `<MixMatchBundle>` components on the same page share one network\n * request — the first caller fetches, concurrent callers await the\n * same promise, and resolved values are cached so subsequent mounts\n * return instantly. Failures are NOT cached so the next caller retries.\n *\n * The cache lives for the life of the JS module (typically one page\n * load). Merchants updating their CSS see changes on the next page\n * load. The cache is exposed as `__clearShopCssCache` for test use only.\n */\nimport {\n createStorefrontClient,\n SHOP_CUSTOM_CSS_QUERY,\n type ShopCustomCssResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchShopCustomCssOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n buyerIp?: string;\n signal?: AbortSignal;\n}\n\n// Cache map: key = shopDomain, value = pending or resolved promise.\n// Failures delete their own entry so retries get a fresh fetch.\nconst cache = new Map<string, Promise<string | null>>();\n\nexport async function fetchShopCustomCss(\n options: FetchShopCustomCssOptions,\n): Promise<string | null> {\n const cached = cache.get(options.shopDomain);\n if (cached) return cached;\n\n const promise = (async () => {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n });\n\n const data = await client.query<ShopCustomCssResponse>(\n SHOP_CUSTOM_CSS_QUERY,\n undefined,\n { signal: options.signal },\n );\n\n return data.shop?.metafield?.value ?? null;\n })();\n\n // Cache the promise synchronously so concurrent callers dedupe.\n cache.set(options.shopDomain, promise);\n\n // Evict on failure so the next caller can retry.\n promise.catch(() => {\n if (cache.get(options.shopDomain) === promise) {\n cache.delete(options.shopDomain);\n }\n });\n\n return promise;\n}\n\n/**\n * Test-only helper. Clears the per-shop CSS cache so each test starts\n * from a known state. Do not call from application code.\n */\nexport function __clearShopCssCache(): void {\n cache.clear();\n}\n","/**\n * Hook for analytics event reporting (impressions + add-to-cart).\n * Impressions fire once per mount using IntersectionObserver.\n */\nimport { useEffect, useMemo, useRef, useCallback, useState } from \"react\";\nimport {\n reportImpression,\n reportAddToCart,\n observeImpression,\n type AnalyticsConfig,\n} from \"@lime-bundles/core\";\n\nexport interface UseAnalyticsOptions {\n shopDomain: string;\n appUrl: string;\n bundleGid: string;\n bundleType: string;\n enabled?: boolean;\n abTestId?: string;\n abVariant?: string;\n}\n\nexport function useAnalytics(options: UseAnalyticsOptions) {\n const impressionFiredRef = useRef(false);\n // Callback ref + state so the observer effect re-runs when the host element\n // is attached (e.g. after a `loading` branch swaps in the real DOM node).\n const [element, setElement] = useState<HTMLElement | null>(null);\n const elementRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n const config: AnalyticsConfig = useMemo(\n () => ({ shopDomain: options.shopDomain, appUrl: options.appUrl }),\n [options.shopDomain, options.appUrl],\n );\n\n // Reset the fired flag when bundle identity changes so a reused component\n // (e.g. via navigation) can report a fresh impression.\n useEffect(() => {\n impressionFiredRef.current = false;\n }, [options.bundleGid, options.abVariant]);\n\n // Set up impression observer once the element is attached\n useEffect(() => {\n if (options.enabled === false) return;\n if (impressionFiredRef.current) return;\n if (!element) return;\n\n const cleanup = observeImpression(element, () => {\n if (impressionFiredRef.current) return;\n impressionFiredRef.current = true;\n\n reportImpression(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n });\n\n return cleanup;\n }, [\n element,\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ]);\n\n const trackAddToCart = useCallback(\n (event: {\n /** Optional — omit for multi-product bundles (fixed, mix-match). */\n productId?: string;\n quantity: number;\n totalPrice: number;\n }) => {\n if (options.enabled === false) return;\n\n reportAddToCart(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n productId: event.productId,\n quantity: event.quantity,\n totalPrice: event.totalPrice,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n },\n [\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ],\n );\n\n return { elementRef, trackAddToCart };\n}\n","/**\n * Apply a bundle's `widgetConfig` to the component root as CSS custom\n * properties. Returns a callback ref so it composes with other callback\n * refs (e.g. `useAnalytics().elementRef`) without needing a ref-merge\n * library.\n *\n * Mirrors what the web component does in `applyWidgetConfigVars` — the\n * React surface uses a smaller subset of `--lb-*` vars in its own\n * stylesheet (`@lime-bundles/react/styles.css`), but having every var set\n * means merchant choices like `productList.thumbnailRatio` flow through to\n * the React image rules without further plumbing. Vars the React stylesheet\n * doesn't read are harmless extras.\n */\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { applyWidgetConfigVars, type WidgetConfig } from \"@lime-bundles/core\";\n\nexport function useWidgetConfigVars(\n config: WidgetConfig | null | undefined,\n): (el: HTMLElement | null) => void {\n const elementRef = useRef<HTMLElement | null>(null);\n\n // Re-apply when config changes (e.g. async bundle load completes).\n useEffect(() => {\n if (elementRef.current && config) {\n applyWidgetConfigVars(elementRef.current, config);\n }\n }, [config]);\n\n // Apply on first attachment so the merchant's chosen ratio is in effect\n // before the browser does its first paint.\n return useCallback(\n (el: HTMLElement | null) => {\n elementRef.current = el;\n if (el && config) applyWidgetConfigVars(el, config);\n },\n [config],\n );\n}\n","/**\n * Mix-and-match bundle component — customer picks products from a pool.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n validateQuantity,\n resolveBundleQty,\n type CartLineInput,\n type MixMatchBundleData,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\ninterface SelectedItem {\n productId: string;\n variantId: string;\n quantity: number;\n}\n\nexport function MixMatchBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"mix_match\",\n enabled: analyticsEnabled !== false,\n });\n\n const [selections, setSelections] = useState<Map<string, SelectedItem>>(\n new Map(),\n );\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"mix_match\"\n ? (result.bundle as MixMatchBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"mix_match\"\n ) {\n onError?.(\n new Error(\n `MixMatchBundle: expected bundleType=\"mix_match\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const totalQuantity = Array.from(selections.values()).reduce(\n (sum, s) => sum + s.quantity,\n 0,\n );\n\n const validation = bundle\n ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity)\n : { valid: false, totalQuantity: 0, message: null };\n\n const toggleProduct = useCallback(\n (productId: string, variant: ProductVariant) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variant.id}`;\n if (next.has(key)) {\n next.delete(key);\n } else {\n // Pre-fill slot qty from the canonical resolver — variant-level\n // wins, product-level fallback, then 1. Customer can still adjust\n // via the stepper afterwards.\n const configuredQty = bundle\n ? resolveBundleQty(bundle, productId, variant.id)\n : 1;\n next.set(key, {\n productId,\n variantId: variant.id,\n quantity: configuredQty,\n });\n }\n return next;\n });\n },\n [bundle],\n );\n\n const updateQuantity = useCallback(\n (productId: string, variantId: string, quantity: number) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variantId}`;\n if (quantity <= 0) {\n next.delete(key);\n } else {\n next.set(key, { productId, variantId, quantity });\n }\n return next;\n });\n },\n [],\n );\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !validation.valid) return;\n\n const lines: CartLineInput[] = Array.from(selections.values()).map((s) => ({\n merchandiseId: s.variantId,\n quantity: s.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n\n const totalPrice = lines.reduce((sum, line) => {\n const product = bundle.products.find((p) =>\n p.variants.nodes.some((v) => v.id === line.merchandiseId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === line.merchandiseId,\n );\n return (\n sum + parseFloat(variant?.price.amount ?? \"0\") * line.quantity\n );\n }, 0);\n\n // productId is intentionally omitted — mix-match bundles span\n // multiple products; bundleGid attributes the event to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n setSelections(new Map());\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n selections,\n validation.valid,\n onAddToCart,\n onError,\n trackAddToCart,\n totalQuantity,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--mix-match ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <p className=\"lb-bundle__instructions\">\n {bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : bundle.maxQuantity\n ? `Select up to ${bundle.maxQuantity} items`\n : \"Select your items\"}\n </p>\n\n <div className=\"lb-bundle__products lb-bundle__products--selectable\">\n {bundle.products.map((product) => {\n const defaultVariant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n if (!defaultVariant) return null;\n\n const key = `${product.id}:${defaultVariant.id}`;\n const selected = selections.get(key);\n\n return (\n <div\n key={product.id}\n className={`lb-bundle__product lb-bundle__product--selectable ${selected ? \"lb-bundle__product--selected\" : \"\"}`}\n >\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(defaultVariant.price.amount, currency)}\n </p>\n {(() => {\n const unitPriceText = formatUnitPrice(\n defaultVariant.unitPrice,\n defaultVariant.unitPriceMeasurement,\n currency,\n );\n return unitPriceText ? (\n <p className=\"lb-bundle__product-unit-price\">\n {unitPriceText}\n </p>\n ) : null;\n })()}\n </div>\n <div className=\"lb-bundle__product-actions\">\n {selected ? (\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label={`Decrease ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity - 1,\n )\n }\n >\n −\n </button>\n <span>{selected.quantity}</span>\n <button\n aria-label={`Increase ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity + 1,\n )\n }\n >\n +\n </button>\n </div>\n ) : (\n <button\n className=\"lb-bundle__select-btn\"\n onClick={() => toggleProduct(product.id, defaultVariant)}\n disabled={!defaultVariant.availableForSale}\n >\n {defaultVariant.availableForSale ? \"Select\" : \"Sold out\"}\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {validation.message && (\n <p className=\"lb-bundle__validation\" role=\"status\">\n {validation.message}\n </p>\n )}\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart || !validation.valid}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${totalQuantity} Items to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Volume bundle component — quantity selector with tier calculator.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateTierSavings,\n getActiveTier,\n type CartLineInput,\n type VolumeBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function VolumeBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"volume\",\n enabled: analyticsEnabled !== false,\n });\n\n const [quantity, setQuantity] = useState(1);\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"volume\"\n ? (result.bundle as VolumeBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"volume\"\n ) {\n onError?.(\n new Error(\n `VolumeBundle: expected bundleType=\"volume\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const product = bundle?.products[0];\n const basePrice = product\n ? parseFloat(product.priceRange.minVariantPrice.amount)\n : 0;\n const currency = product?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const tierSavings = bundle\n ? calculateTierSavings(\n bundle.volumeTiers,\n basePrice,\n quantity,\n bundle.discountConfig.discountType,\n )\n : [];\n const activeTier = bundle\n ? getActiveTier(bundle.volumeTiers, quantity)\n : null;\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !product) return;\n\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n const lines: CartLineInput[] = [\n {\n merchandiseId: variant.id,\n quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n },\n ];\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n // Reuse the canonical computation from calculateTierSavings so the\n // analytics unit price matches what the component rendered.\n const unitPrice =\n tierSavings.find((ts) => ts.tier === activeTier)?.unitPrice ?? basePrice;\n trackAddToCart({\n productId: product.id,\n quantity,\n totalPrice: Math.round(unitPrice * quantity * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n product,\n quantity,\n activeTier,\n basePrice,\n onAddToCart,\n onError,\n trackAddToCart,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--tiers\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n if (!product) return null;\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--volume ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n <div className=\"lb-bundle__product lb-bundle__product--volume\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(basePrice, currency)} each\n </p>\n {(() => {\n // Volume bundles surface the first available variant — read unit\n // pricing off the same variant so the row matches what's bought.\n const variant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n const unitPriceText = variant\n ? formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n )\n : null;\n return unitPriceText ? (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n ) : null;\n })()}\n </div>\n </div>\n\n <div\n className=\"lb-bundle__tiers\"\n role=\"table\"\n aria-label=\"Volume discounts\"\n >\n {tierSavings.map((ts) => (\n <div\n key={ts.tier.minQuantity}\n className={`lb-bundle__tier ${ts.isActive ? \"lb-bundle__tier--active\" : \"\"}`}\n role=\"row\"\n >\n <span className=\"lb-bundle__tier-quantity\" role=\"cell\">\n {ts.tier.minQuantity}+ items\n </span>\n <span className=\"lb-bundle__tier-price\" role=\"cell\">\n {formatMoney(ts.unitPrice, currency)} each\n </span>\n <span className=\"lb-bundle__tier-savings\" role=\"cell\">\n Save {ts.savingsPercent.toFixed(0)}%\n </span>\n </div>\n ))}\n </div>\n\n <div className=\"lb-bundle__quantity-selector\">\n <label htmlFor={`lb-qty-${bundle.id}`}>Quantity</label>\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label=\"Decrease quantity\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n >\n −\n </button>\n <input\n id={`lb-qty-${bundle.id}`}\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => {\n const val = parseInt(e.target.value, 10);\n if (!isNaN(val) && val > 0) setQuantity(val);\n }}\n className=\"lb-bundle__quantity-input\"\n />\n <button\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((q) => q + 1)}\n >\n +\n </button>\n </div>\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundlesForProduct` — the\n * React-SDK analogue of the web component's auto-detect-by-current-product\n * mode. The web component reads the handle from `location.pathname`; a\n * React app already has it on the router, so the hook takes it as an\n * explicit `productHandle` option.\n *\n * Returns every active bundle configured against the product. Inactive,\n * draft, scheduled-for-later, and expired bundles are filtered out by\n * the core helper — consumers don't have to guard.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundlesForProduct` directly and pass the list to a client\n * component as a prop. This hook's only value-add over the async\n * function is React state management + abort-on-unmount.\n *\n * `UseBundlesForProductResult` is a discriminated union locked at v2.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport {\n fetchBundlesForProduct,\n injectCustomCss,\n type ParsedBundle,\n} from \"@lime-bundles/core\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundlesForProductOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n productHandle: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR paths. */\n buyerIp?: string;\n}\n\nexport type UseBundlesForProductResult =\n | { status: \"loading\"; bundles: null; error: null }\n | { status: \"success\"; bundles: ParsedBundle[]; error: null }\n | { status: \"error\"; bundles: null; error: Error };\n\nconst INITIAL_STATE: UseBundlesForProductResult = {\n status: \"loading\",\n bundles: null,\n error: null,\n};\n\nexport function useBundlesForProduct(\n options: UseBundlesForProductOptions,\n): UseBundlesForProductResult {\n const [state, setState] = useState<UseBundlesForProductResult>(\n INITIAL_STATE,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundlesForProduct({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n productHandle: options.productHandle,\n buyerIp: options.buyerIp,\n signal: controller.signal,\n })\n .then((bundles) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundles, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundles: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch so storefronts using this hook get\n // merchant styling automatically — same behaviour as `useBundleData`.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.productHandle,\n options.buyerIp,\n ]);\n\n return state;\n}\n"],"mappings":"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","names":["import_react","import_core","import_core","import_core","import_react","import_core","import_react","import_core","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_core","INITIAL_STATE","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/FixedBundle.tsx","../src/hooks/useBundleData.ts","../src/fetchBundleData.ts","../src/fetchShopCustomCss.ts","../src/hooks/useAnalytics.ts","../src/hooks/useWidgetConfigVars.ts","../src/hooks/useVariantSelection.ts","../src/components/VariantDropdown.tsx","../src/components/MixMatchBundle.tsx","../src/components/VolumeBundle.tsx","../src/hooks/useBundlesForProduct.ts"],"sourcesContent":["// Bundle components — BYO-cart via `onAddToCart`.\nexport { FixedBundle } from \"./components/FixedBundle\";\nexport { MixMatchBundle } from \"./components/MixMatchBundle\";\nexport { VolumeBundle } from \"./components/VolumeBundle\";\nexport type { BundleComponentProps } from \"./components/types\";\n\n// Variant picker primitives — composable for merchants who want to build\n// their own bundle UI on top of `useBundleData` while reusing the same\n// accessible custom dropdown.\nexport {\n VariantDropdown,\n type VariantDropdownProps,\n type VariantDropdownOption,\n} from \"./components/VariantDropdown\";\nexport {\n useVariantSelection,\n type UseVariantSelectionOptions,\n type UseVariantSelectionResult,\n} from \"./hooks/useVariantSelection\";\n\n// Data fetching\nexport {\n fetchBundleData,\n type FetchBundleDataOptions,\n} from \"./fetchBundleData\";\nexport {\n fetchShopCustomCss,\n type FetchShopCustomCssOptions,\n} from \"./fetchShopCustomCss\";\nexport {\n useBundleData,\n type UseBundleDataOptions,\n type UseBundleDataResult,\n} from \"./hooks/useBundleData\";\nexport {\n useBundlesForProduct,\n type UseBundlesForProductOptions,\n type UseBundlesForProductResult,\n} from \"./hooks/useBundlesForProduct\";\nexport { useAnalytics } from \"./hooks/useAnalytics\";\nexport { useWidgetConfigVars } from \"./hooks/useWidgetConfigVars\";\n\n// Re-export core types consumers commonly need.\nexport type {\n ParsedBundle,\n FixedBundleData,\n VolumeBundleData,\n MixMatchBundleData,\n BundleType,\n Product,\n ProductVariant,\n CartLineInput,\n WidgetConfig,\n HeaderConfig,\n LayoutConfig,\n ProductListConfig,\n ThumbnailRatio,\n PricingConfig,\n SavingsBarConfig,\n CtaConfig,\n CountdownConfig,\n PopularBadgeConfig,\n VolumeTier,\n DiscountConfig,\n ABVariantOverrides,\n PricingRow,\n FixedBundlePricing,\n ImageTransform,\n} from \"@lime-bundles/core\";\nexport { BundleParseError, StorefrontApiError } from \"@lime-bundles/core\";\n\n// DIY widget primitives — every helper a developer needs to build their\n// own bundle UI with the same data, math, and theming the first-party\n// widget uses. All live in `@lime-bundles/core`; importing from the\n// React package is shorthand for consumers who already pulled `react`.\nexport {\n // Bundle config + variant merging\n WIDGET_CONFIG_DEFAULTS,\n mergeWidgetConfig,\n applyWidgetConfigVars,\n thumbnailRatioVars,\n applyABVariantB,\n // Pricing (integer cents, matches Shopify Discount Function rounding)\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n // Volume-tier calculation\n calculateTierSavings,\n getActiveTier,\n // Mix-match quantity validation\n validateQuantity,\n // Image / countdown / money helpers\n transformImageUrl,\n formatCountdown,\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n // Analytics (optional — FixedBundle / VolumeBundle / MixMatchBundle\n // auto-fire these unless analyticsEnabled is set to false)\n reportImpression,\n reportAddToCart,\n observeImpression,\n // A/B testing\n getABTestAssignment,\n setConsent,\n hasConsent,\n // CSS injection + sanitizer\n injectCustomCss,\n sanitizeCustomCss,\n // Custom-storefront data fetching\n createStorefrontClient,\n fetchBundlesForProduct,\n parseMetaobjectBundle,\n parseMetaobjectBundleStrict,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n SHOP_CUSTOM_CSS_QUERY,\n} from \"@lime-bundles/core\";\n","/**\n * Fixed bundle component — pre-selected products with a shared discount.\n */\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n type Product,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport { useVariantSelection } from \"../hooks/useVariantSelection\";\nimport { VariantDropdown } from \"./VariantDropdown\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function FixedBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"fixed\",\n enabled: analyticsEnabled !== false,\n });\n\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n // Tracks the customer's selected variant per product. Seeded with first\n // available variant on first render; updated by <ProductRow> via callback.\n const [selectedVariants, setSelectedVariants] = useState<\n Record<string, ProductVariant | null>\n >({});\n\n const handleVariantChange = useCallback(\n (productId: string, variant: ProductVariant | null) => {\n setSelectedVariants((prev) => {\n if (prev[productId] === variant) return prev;\n return { ...prev, [productId]: variant };\n });\n },\n [],\n );\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"fixed\"\n ? (result.bundle as FixedBundleData)\n : null;\n\n // Apply merchant-config CSS vars (including --lb-thumbnail-aspect-ratio)\n // to the component root via a callback ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n // Surface load/narrowing errors via onError — in a useEffect so we don't\n // fire side effects during render.\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"fixed\"\n ) {\n onError?.(\n new Error(\n `FixedBundle: expected bundleType=\"fixed\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle) return;\n\n // Qty resolution (variantQuantities → productQuantities → 1) lives in\n // `@lime-bundles/core` so the five-runtime fallback chain has a single\n // source of truth. See resolveBundleQty + PR #147 history.\n const bundleLines = bundle.products\n .filter((p) => p.variants.nodes.some((v) => v.availableForSale))\n .map((p) => {\n const variant =\n selectedVariants[p.id] ??\n p.variants.nodes.find((v) => v.availableForSale)!;\n return {\n productId: p.id,\n variant,\n quantity: resolveBundleQty(bundle, p.id, variant.id),\n };\n })\n .filter((line) => line.quantity > 0);\n\n if (bundleLines.length === 0) return;\n\n const lines: CartLineInput[] = bundleLines.map((line) => ({\n merchandiseId: line.variant.id,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n const totalPrice = bundleLines.reduce((sum, line) => {\n const price = parseFloat(line.variant.price.amount);\n const discounted = calculateDiscount(\n price,\n bundle.discountConfig.discountType,\n bundle.discountConfig.discountValue,\n );\n return sum + discounted * line.quantity;\n }, 0);\n const totalQuantity = bundleLines.reduce(\n (sum, line) => sum + line.quantity,\n 0,\n );\n // productId is intentionally omitted — a fixed bundle spans multiple\n // products with no single \"page product\"; bundleGid on the event\n // already attributes to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [bundle, onAddToCart, onError, trackAddToCart, selectedVariants]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n // Bundle loaded but wasn't a \"fixed\" variant — render nothing; the\n // useEffect above has already invoked onError with a descriptive message.\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--fixed ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <div className=\"lb-bundle__products\">\n {bundle.products.map((product) => (\n <FixedProductRow\n key={product.id}\n product={product}\n currency={currency}\n onVariantChange={handleVariantChange}\n />\n ))}\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? \"Add Bundle to Cart\")}\n </button>\n </div>\n );\n}\n\ninterface FixedProductRowProps {\n product: Product;\n currency: string;\n onVariantChange: (productId: string, variant: ProductVariant | null) => void;\n}\n\nfunction FixedProductRow({ product, currency, onVariantChange }: FixedProductRowProps) {\n const variants = product.variants.nodes;\n const optionNames = useMemo<string[]>(() => {\n const first = variants[0];\n return first ? first.selectedOptions.map((o) => o.name) : [];\n }, [variants]);\n\n const showPicker = variants.length > 1 && optionNames.length > 0;\n\n const { selectedValues, selectedVariant, setOptionValue, optionsFor } =\n useVariantSelection({\n variants,\n optionNames,\n });\n\n // Surface the selection to the parent so handleAddToCart picks the right\n // variant. Includes initial seed via effect so cart payload reflects the\n // first-available default before any picker interaction.\n useEffect(() => {\n onVariantChange(product.id, selectedVariant ?? null);\n }, [selectedVariant, product.id, onVariantChange]);\n\n // selectedVariant resolves null only during a transient invalid combo\n // (e.g. Size=Medium+Color=Red where that pairing is sold out). Fall back\n // to the first available so the row keeps showing a sensible price.\n const displayVariant =\n selectedVariant ?? variants.find((v) => v.availableForSale) ?? variants[0];\n const priceText = displayVariant\n ? formatMoney(displayVariant.price.amount, currency)\n : formatMoney(product.priceRange.minVariantPrice.amount, currency);\n const unitPriceText = displayVariant\n ? formatUnitPrice(\n displayVariant.unitPrice,\n displayVariant.unitPriceMeasurement,\n currency,\n )\n : null;\n\n const thumbImage = displayVariant?.image ?? product.featuredImage;\n\n return (\n <div className=\"lb-bundle__product\" part=\"product\">\n {thumbImage && (\n <img\n src={thumbImage.url}\n alt={thumbImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">{priceText}</p>\n {unitPriceText && (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n )}\n {showPicker && (\n <div className=\"lb-bundle__product-variant-pickers\">\n {optionNames.map((optionName, optionIndex) => {\n const dropdownOptions = optionsFor(optionIndex).map((o) => ({\n value: o.value,\n label: o.value,\n disabled: o.disabled,\n }));\n return (\n <VariantDropdown\n key={optionName}\n options={dropdownOptions}\n value={selectedValues[optionIndex] ?? null}\n onChange={(v) => setOptionValue(optionIndex, v)}\n ariaLabel={optionName}\n />\n );\n })}\n </div>\n )}\n </div>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundleData` with loading / error\n * state + abort-on-unmount.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundleData` directly and pass the result to a client\n * component as a prop — the hook's only value-add over the async\n * function is React state management.\n *\n * `UseBundleDataResult` is a discriminated union locked at v1.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport { injectCustomCss, type ParsedBundle } from \"@lime-bundles/core\";\nimport { fetchBundleData } from \"../fetchBundleData\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n}\n\nexport type UseBundleDataResult =\n | { status: \"loading\"; bundle: null; error: null }\n | { status: \"success\"; bundle: ParsedBundle; error: null }\n | { status: \"error\"; bundle: null; error: Error };\n\nconst INITIAL_STATE: UseBundleDataResult = {\n status: \"loading\",\n bundle: null,\n error: null,\n};\n\nexport function useBundleData(\n options: UseBundleDataOptions,\n): UseBundleDataResult {\n const [state, setState] = useState<UseBundleDataResult>(INITIAL_STATE);\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundleData({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n bundleGid: options.bundleGid,\n signal: controller.signal,\n })\n .then((bundle) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundle, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundle: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch — best-effort, failures never block\n // bundle rendering.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.bundleGid,\n ]);\n\n return state;\n}\n","/**\n * Framework-agnostic async fetcher for bundle data.\n *\n * Primary path for Next.js App Router / RSC and Hydrogen loaders — call\n * this in a server-side context and pass the result as a prop to a\n * `\"use client\"` component. `useBundleData` is NOT needed in that\n * component because the data is already in hand.\n *\n * Always forward the end-buyer's IP via `buyerIp` when calling from SSR:\n * Shopify may return 430 Security Rejection for server-originated traffic\n * without it.\n *\n * Throws:\n * - `StorefrontApiError` on network / HTTP / GraphQL errors\n * - `BundleParseError` when the metaobject is missing, invalid type,\n * inactive, not yet started, or expired\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundleStrict,\n BundleParseError,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR. */\n buyerIp?: string;\n /** Abort the in-flight request (used by `useBundleData` to cancel on unmount). */\n signal?: AbortSignal;\n /** Override the Storefront API version pinned in `createStorefrontClient`. */\n apiVersion?: string;\n}\n\nexport async function fetchBundleData(\n options: FetchBundleDataOptions,\n): Promise<ParsedBundle> {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n apiVersion: options.apiVersion,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: options.bundleGid },\n { signal: options.signal },\n );\n\n if (!data.metaobject) {\n throw new BundleParseError(\n `Bundle not found: ${options.bundleGid}`,\n \"not_found\",\n );\n }\n\n return parseMetaobjectBundleStrict(\n data.metaobject.id,\n data.metaobject.fields,\n );\n}\n","/**\n * Best-effort fetch for the shop's `$app:custom_css` metafield.\n *\n * Returns the raw CSS string (still needs sanitization before injection —\n * see `injectCustomCss` from core) or null if the metafield is unset or\n * the request fails.\n *\n * **Per-shop in-memory cache.** Multiple `<FixedBundle>` / `<VolumeBundle>`\n * / `<MixMatchBundle>` components on the same page share one network\n * request — the first caller fetches, concurrent callers await the\n * same promise, and resolved values are cached so subsequent mounts\n * return instantly. Failures are NOT cached so the next caller retries.\n *\n * The cache lives for the life of the JS module (typically one page\n * load). Merchants updating their CSS see changes on the next page\n * load. The cache is exposed as `__clearShopCssCache` for test use only.\n */\nimport {\n createStorefrontClient,\n SHOP_CUSTOM_CSS_QUERY,\n type ShopCustomCssResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchShopCustomCssOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n buyerIp?: string;\n signal?: AbortSignal;\n}\n\n// Cache map: key = shopDomain, value = pending or resolved promise.\n// Failures delete their own entry so retries get a fresh fetch.\nconst cache = new Map<string, Promise<string | null>>();\n\nexport async function fetchShopCustomCss(\n options: FetchShopCustomCssOptions,\n): Promise<string | null> {\n const cached = cache.get(options.shopDomain);\n if (cached) return cached;\n\n const promise = (async () => {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n });\n\n const data = await client.query<ShopCustomCssResponse>(\n SHOP_CUSTOM_CSS_QUERY,\n undefined,\n { signal: options.signal },\n );\n\n return data.shop?.metafield?.value ?? null;\n })();\n\n // Cache the promise synchronously so concurrent callers dedupe.\n cache.set(options.shopDomain, promise);\n\n // Evict on failure so the next caller can retry.\n promise.catch(() => {\n if (cache.get(options.shopDomain) === promise) {\n cache.delete(options.shopDomain);\n }\n });\n\n return promise;\n}\n\n/**\n * Test-only helper. Clears the per-shop CSS cache so each test starts\n * from a known state. Do not call from application code.\n */\nexport function __clearShopCssCache(): void {\n cache.clear();\n}\n","/**\n * Hook for analytics event reporting (impressions + add-to-cart).\n * Impressions fire once per mount using IntersectionObserver.\n */\nimport { useEffect, useMemo, useRef, useCallback, useState } from \"react\";\nimport {\n reportImpression,\n reportAddToCart,\n observeImpression,\n type AnalyticsConfig,\n} from \"@lime-bundles/core\";\n\nexport interface UseAnalyticsOptions {\n shopDomain: string;\n appUrl: string;\n bundleGid: string;\n bundleType: string;\n enabled?: boolean;\n abTestId?: string;\n abVariant?: string;\n}\n\nexport function useAnalytics(options: UseAnalyticsOptions) {\n const impressionFiredRef = useRef(false);\n // Callback ref + state so the observer effect re-runs when the host element\n // is attached (e.g. after a `loading` branch swaps in the real DOM node).\n const [element, setElement] = useState<HTMLElement | null>(null);\n const elementRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n const config: AnalyticsConfig = useMemo(\n () => ({ shopDomain: options.shopDomain, appUrl: options.appUrl }),\n [options.shopDomain, options.appUrl],\n );\n\n // Reset the fired flag when bundle identity changes so a reused component\n // (e.g. via navigation) can report a fresh impression.\n useEffect(() => {\n impressionFiredRef.current = false;\n }, [options.bundleGid, options.abVariant]);\n\n // Set up impression observer once the element is attached\n useEffect(() => {\n if (options.enabled === false) return;\n if (impressionFiredRef.current) return;\n if (!element) return;\n\n const cleanup = observeImpression(element, () => {\n if (impressionFiredRef.current) return;\n impressionFiredRef.current = true;\n\n reportImpression(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n });\n\n return cleanup;\n }, [\n element,\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ]);\n\n const trackAddToCart = useCallback(\n (event: {\n /** Optional — omit for multi-product bundles (fixed, mix-match). */\n productId?: string;\n quantity: number;\n totalPrice: number;\n }) => {\n if (options.enabled === false) return;\n\n reportAddToCart(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n productId: event.productId,\n quantity: event.quantity,\n totalPrice: event.totalPrice,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n },\n [\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ],\n );\n\n return { elementRef, trackAddToCart };\n}\n","/**\n * Apply a bundle's `widgetConfig` to the component root as CSS custom\n * properties. Returns a callback ref so it composes with other callback\n * refs (e.g. `useAnalytics().elementRef`) without needing a ref-merge\n * library.\n *\n * Mirrors what the web component does in `applyWidgetConfigVars` — the\n * React surface uses a smaller subset of `--lb-*` vars in its own\n * stylesheet (`@lime-bundles/react/styles.css`), but having every var set\n * means merchant choices like `productList.thumbnailRatio` flow through to\n * the React image rules without further plumbing. Vars the React stylesheet\n * doesn't read are harmless extras.\n */\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { applyWidgetConfigVars, type WidgetConfig } from \"@lime-bundles/core\";\n\nexport function useWidgetConfigVars(\n config: WidgetConfig | null | undefined,\n): (el: HTMLElement | null) => void {\n const elementRef = useRef<HTMLElement | null>(null);\n\n // Re-apply when config changes (e.g. async bundle load completes).\n useEffect(() => {\n if (elementRef.current && config) {\n applyWidgetConfigVars(elementRef.current, config);\n }\n }, [config]);\n\n // Apply on first attachment so the merchant's chosen ratio is in effect\n // before the browser does its first paint.\n return useCallback(\n (el: HTMLElement | null) => {\n elementRef.current = el;\n if (el && config) applyWidgetConfigVars(el, config);\n },\n [config],\n );\n}\n","/**\n * Per-product variant selection state for the React bundle components.\n *\n * Owns positional option-value selection for one product; derives the\n * resolved `ProductVariant` and an availability map (which option values\n * are currently disabled given the other selections). Mirrors the\n * `findVariantByOptions` / `isOptionValueAvailable` logic that powers the\n * Liquid widget and the `<lime-bundle>` web component, so the cascade\n * behaviour matches across all surfaces.\n */\nimport { useEffect, useMemo, useState, useCallback } from \"react\";\nimport { variants as variantsNs, type ProductVariant } from \"@lime-bundles/core\";\n\nconst { findVariantByOptions, isOptionValueAvailable, toPickerVariant } = variantsNs;\n\nexport interface UseVariantSelectionOptions {\n /** Storefront-API variants for the product (eligible set already filtered). */\n variants: ReadonlyArray<ProductVariant>;\n /** Positional option names for the product, e.g. `['Size', 'Color']`. */\n optionNames: ReadonlyArray<string>;\n /**\n * Optional initial variant. Defaults to the first available variant, or\n * the first variant if none are available.\n */\n initialVariant?: ProductVariant | null;\n /**\n * Canonical option-value order per option position (e.g. from\n * `Product.options[].values` on the Storefront API). When supplied, the\n * dropdown UI renders values in this merchant-defined order. When omitted,\n * order is variant-discovery order (which may not match Shopify admin).\n */\n optionValuesByPosition?: ReadonlyArray<ReadonlyArray<string>>;\n}\n\nexport interface UseVariantSelectionResult {\n /** Positional option values matching `optionNames`. */\n selectedValues: ReadonlyArray<string>;\n /** The resolved variant for the current selection (null when no exact match). */\n selectedVariant: ProductVariant | null;\n /** Set the value at a single option position. */\n setOptionValue: (optionIndex: number, value: string) => void;\n /** All distinct values for an option position, paired with availability. */\n optionsFor: (\n optionIndex: number,\n ) => ReadonlyArray<{ value: string; disabled: boolean }>;\n}\n\nfunction defaultInitial(\n variants: ReadonlyArray<ProductVariant>,\n): ProductVariant | null {\n return variants.find((v) => v.availableForSale) ?? variants[0] ?? null;\n}\n\nexport function useVariantSelection({\n variants,\n optionNames,\n initialVariant,\n optionValuesByPosition,\n}: UseVariantSelectionOptions): UseVariantSelectionResult {\n // Resolve the seed variant once per render. Memoised so a single\n // defaultInitial(variants) walk per render cycle covers both the\n // initial useState seed and the reset effect below.\n const seed = useMemo(\n () => initialVariant ?? defaultInitial(variants),\n [initialVariant, variants],\n );\n const [selectedValues, setSelectedValues] = useState<string[]>(() =>\n seed ? seed.selectedOptions.map((o) => o.value) : optionNames.map(() => \"\"),\n );\n\n // Reset selection when the caller swaps the product or supplies a new\n // initial variant. Keying on seed.id keeps variants/optionNames\n // recomputes from firing this spuriously.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally keyed on seed?.id, not seed itself\n useEffect(() => {\n if (!seed) return;\n setSelectedValues(seed.selectedOptions.map((o) => o.value));\n }, [seed?.id]);\n\n const pickerVariants = useMemo(\n () => variants.map((v) => toPickerVariant(v)),\n [variants],\n );\n\n const selectedVariant = useMemo<ProductVariant | null>(() => {\n if (selectedValues.length !== optionNames.length) return null;\n const match = findVariantByOptions(pickerVariants, selectedValues);\n if (!match) return null;\n return variants.find((v) => v.id === match.id) ?? null;\n }, [pickerVariants, selectedValues, variants, optionNames.length]);\n\n const setOptionValue = useCallback(\n (optionIndex: number, value: string) => {\n setSelectedValues((prev) => {\n const next = prev.slice();\n next[optionIndex] = value;\n return next;\n });\n },\n [],\n );\n\n const optionsByPosition = useMemo(() => {\n if (optionValuesByPosition) {\n return optionValuesByPosition.map((values) => values.slice());\n }\n const out: string[][] = optionNames.map(() => []);\n for (const v of variants) {\n v.selectedOptions.forEach((o, i) => {\n if (i < out.length && !out[i].includes(o.value)) out[i].push(o.value);\n });\n }\n return out;\n }, [variants, optionNames, optionValuesByPosition]);\n\n const optionsFor = useCallback(\n (optionIndex: number) => {\n const values = optionsByPosition[optionIndex] ?? [];\n return values.map((value) => ({\n value,\n disabled: !isOptionValueAvailable(\n pickerVariants,\n optionIndex,\n value,\n selectedValues,\n ),\n }));\n },\n [optionsByPosition, pickerVariants, selectedValues],\n );\n\n return {\n selectedValues,\n selectedVariant,\n setOptionValue,\n optionsFor,\n };\n}\n","/**\n * Accessible custom variant-picker dropdown for React bundle components.\n *\n * Controlled component — caller owns `value` and `onChange`. Mirrors the\n * WAI-ARIA combobox pattern shipped by the Liquid theme dropdown and the\n * `<lime-bundle>` web component dropdown. Keyboard / type-ahead /\n * positioning logic comes from `@lime-bundles/core` so all three surfaces\n * stay in lockstep.\n *\n * Asymmetry with the Liquid + web component surfaces: those expose a hidden\n * native `<select>` and the dropdown UI resyncs via MutationObserver if an\n * external script mutates `select.value`. The React component has no such\n * escape hatch — to change selection externally, re-render with a new\n * `value` prop. This is intentional: a synthetic-event-driven hidden\n * select would fight React reconciliation.\n */\nimport {\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { dropdown } from \"@lime-bundles/core\";\n\nconst { computePosition, emptyTypeAheadState, handleKey, pushTypeAheadChar } =\n dropdown;\n\n// Layout-tuning constants for panel-height calculation. Inlined here\n// rather than imported from core so they stay tweakable without locking\n// numeric values into the public SDK contract.\nconst ITEM_HEIGHT_PX = 32;\nconst LIST_PAD_Y = 8;\nconst MAX_VISIBLE_ITEMS = 8;\ntype ComputePositionResult = dropdown.ComputePositionResult;\ntype TypeAheadState = dropdown.TypeAheadState;\n\nexport interface VariantDropdownOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface VariantDropdownProps {\n options: ReadonlyArray<VariantDropdownOption>;\n value: string | null;\n onChange: (value: string) => void;\n /** Accessible label, e.g. `\"Size\"`. */\n ariaLabel?: string;\n className?: string;\n}\n\nexport function VariantDropdown({\n options,\n value,\n onChange,\n ariaLabel,\n className,\n}: VariantDropdownProps) {\n const idBase = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listboxRef = useRef<HTMLUListElement>(null);\n const typeAheadRef = useRef<TypeAheadState>(emptyTypeAheadState());\n const [isOpen, setIsOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const [position, setPosition] = useState<ComputePositionResult | null>(null);\n\n const selectedIndex = useMemo(\n () => options.findIndex((o) => o.value === value),\n [options, value],\n );\n\n const optionsState = useMemo(\n () => options.map((o) => ({ disabled: !!o.disabled, label: o.label })),\n [options],\n );\n\n const selectedLabel =\n selectedIndex >= 0 ? options[selectedIndex].label : \"\";\n\n function open() {\n if (isOpen) return;\n setIsOpen(true);\n setActiveIndex(\n selectedIndex >= 0 && !optionsState[selectedIndex]?.disabled\n ? selectedIndex\n : optionsState.findIndex((o) => !o.disabled),\n );\n }\n\n function close(restoreFocus: boolean) {\n setIsOpen(false);\n setActiveIndex(-1);\n if (restoreFocus) triggerRef.current?.focus();\n }\n\n function commit(index: number) {\n const opt = options[index];\n if (!opt || opt.disabled) return;\n if (opt.value !== value) onChange(opt.value);\n close(true);\n }\n\n // position:fixed coords recomputed on open, then again on every scroll/resize\n // while open (those handlers close the panel — see effect below — so this\n // layout effect only runs once per open transition).\n useLayoutEffect(() => {\n if (!isOpen) return;\n const trigger = triggerRef.current;\n if (!trigger) return;\n const rect = trigger.getBoundingClientRect();\n if (rect.width === 0) return;\n const visibleCount = Math.min(options.length || 1, MAX_VISIBLE_ITEMS);\n const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;\n setPosition(\n computePosition({\n trigger: {\n top: rect.top,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.width,\n },\n viewportHeight: window.innerHeight,\n desiredHeight,\n }),\n );\n }, [isOpen, options.length]);\n\n useEffect(() => {\n if (!isOpen) return;\n // composedPath() handles shadow-DOM retargeting (for merchants who\n // embed this component inside a custom element with shadow root).\n const isInsideDropdown = (event: Event): boolean => {\n const path = event.composedPath();\n const trigger = triggerRef.current;\n const listbox = listboxRef.current;\n return (\n (trigger != null && path.includes(trigger)) ||\n (listbox != null && path.includes(listbox))\n );\n };\n const onPointerdown = (event: PointerEvent) => {\n if (isInsideDropdown(event)) return;\n close(false);\n };\n const onScroll = (event: Event) => {\n // Scrolling within the listbox itself must not close the dropdown.\n if (isInsideDropdown(event)) return;\n close(false);\n };\n const onResize = () => close(false);\n document.addEventListener(\"pointerdown\", onPointerdown, true);\n window.addEventListener(\"scroll\", onScroll, true);\n window.addEventListener(\"resize\", onResize);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerdown, true);\n window.removeEventListener(\"scroll\", onScroll, true);\n window.removeEventListener(\"resize\", onResize);\n };\n }, [isOpen]);\n\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return;\n const listbox = listboxRef.current;\n if (!listbox) return;\n const li = listbox.querySelector<HTMLElement>(\n `#${CSS.escape(`${idBase}-opt-${activeIndex}`)}`,\n );\n if (!li) return;\n // Scroll within the listbox only — Element.scrollIntoView falls through\n // to the document scroll when the listbox itself isn't overflowing,\n // which can yank the page.\n const liTop = li.offsetTop;\n const liBottom = liTop + li.offsetHeight;\n const visTop = listbox.scrollTop;\n const visBottom = visTop + listbox.clientHeight;\n if (liTop < visTop) {\n listbox.scrollTop = liTop;\n } else if (liBottom > visBottom) {\n listbox.scrollTop = liBottom - listbox.clientHeight;\n }\n }, [activeIndex, isOpen, idBase]);\n\n function onKeyDown(event: React.KeyboardEvent) {\n const action = handleKey(\n {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n altKey: event.altKey,\n shiftKey: event.shiftKey,\n },\n { isOpen, activeIndex, selectedIndex, options: optionsState },\n );\n if (action.preventDefault) event.preventDefault();\n switch (action.type) {\n case \"open\":\n open();\n if (action.activeIndex >= 0) setActiveIndex(action.activeIndex);\n return;\n case \"close\":\n close(action.restoreFocus);\n return;\n case \"move-active\":\n setActiveIndex(action.activeIndex);\n return;\n case \"commit\":\n commit(action.index);\n return;\n case \"type-ahead\": {\n const result = pushTypeAheadChar(\n typeAheadRef.current,\n action.char,\n Date.now(),\n optionsState,\n );\n typeAheadRef.current = result.newState;\n if (result.matchedIndex !== null) {\n if (!isOpen) open();\n setActiveIndex(result.matchedIndex);\n }\n return;\n }\n case \"passthrough\":\n return;\n }\n }\n\n function onShellBlur(event: React.FocusEvent) {\n const next = event.relatedTarget as Node | null;\n if (next && event.currentTarget.contains(next)) return;\n // Defer so focus has a chance to land elsewhere first.\n setTimeout(() => {\n if (!isOpen) return;\n const active = document.activeElement;\n if (\n !triggerRef.current?.contains(active) &&\n !listboxRef.current?.contains(active)\n ) {\n close(false);\n }\n }, 0);\n }\n\n const listboxId = `${idBase}-listbox`;\n const activeDescendantId =\n activeIndex >= 0 ? `${idBase}-opt-${activeIndex}` : undefined;\n\n // Only max-height comes from JS; CSS [data-placement] selectors\n // anchor the listbox against the position:relative .lb-dropdown shell\n // via top: 100% / bottom: 100%, so we never depend on inline coords\n // landing before paint.\n const panelStyle: React.CSSProperties | undefined = position\n ? { maxHeight: position.maxHeight }\n : undefined;\n\n return (\n <div\n className={`lb-dropdown${className ? ` ${className}` : \"\"}`}\n data-lb-dropdown=\"\"\n onBlur={onShellBlur}\n >\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"lb-dropdown-trigger\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-controls={listboxId}\n aria-label={ariaLabel}\n aria-activedescendant={activeDescendantId}\n onClick={(e) => {\n e.preventDefault();\n if (isOpen) close(false);\n else open();\n }}\n onKeyDown={onKeyDown}\n >\n <span className=\"lb-dropdown-trigger-value\">{selectedLabel}</span>\n <span className=\"lb-dropdown-chevron\" aria-hidden=\"true\" />\n </button>\n <ul\n ref={listboxRef}\n id={listboxId}\n className=\"lb-dropdown-listbox\"\n role=\"listbox\"\n aria-label={ariaLabel}\n hidden={!isOpen}\n data-placement={position?.placement}\n style={panelStyle}\n // Prevent mousedown on a non-focusable <li> from blurring the\n // trigger button. Without this, focusout fires on the shell,\n // queues onShellBlur's setTimeout(0), and the close runs before\n // the synthesized click — so commit never runs and the trigger\n // label never updates. Mobile is unaffected because touchstart\n // doesn't shift focus.\n onMouseDown={(e) => e.preventDefault()}\n >\n {options.map((opt, i) => (\n // Keyboard activation lives on the trigger combobox via\n // aria-activedescendant — the WAI-ARIA combobox pattern keeps\n // focus on the button and tracks the active option by id, so\n // the option <li> elements deliberately have no key handlers.\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions\n <li\n key={opt.value}\n id={`${idBase}-opt-${i}`}\n className={`lb-dropdown-option${i === activeIndex ? \" is-active\" : \"\"}`}\n role=\"option\"\n aria-selected={i === selectedIndex}\n aria-disabled={opt.disabled || undefined}\n data-value={opt.value}\n onClick={() => commit(i)}\n onMouseMove={() => {\n if (!opt.disabled && i !== activeIndex) setActiveIndex(i);\n }}\n >\n {opt.label}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n","/**\n * Mix-and-match bundle component — customer picks products from a pool.\n */\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n validateQuantity,\n resolveBundleQty,\n type CartLineInput,\n type MixMatchBundleData,\n type Product,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport { useVariantSelection } from \"../hooks/useVariantSelection\";\nimport { VariantDropdown } from \"./VariantDropdown\";\nimport type { BundleComponentProps } from \"./types\";\n\ninterface SelectedItem {\n productId: string;\n variantId: string;\n quantity: number;\n}\n\nexport function MixMatchBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"mix_match\",\n enabled: analyticsEnabled !== false,\n });\n\n const [selections, setSelections] = useState<Map<string, SelectedItem>>(\n new Map(),\n );\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"mix_match\"\n ? (result.bundle as MixMatchBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"mix_match\"\n ) {\n onError?.(\n new Error(\n `MixMatchBundle: expected bundleType=\"mix_match\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const totalQuantity = Array.from(selections.values()).reduce(\n (sum, s) => sum + s.quantity,\n 0,\n );\n\n const validation = bundle\n ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity)\n : { valid: false, totalQuantity: 0, message: null };\n\n const toggleProduct = useCallback(\n (productId: string, variant: ProductVariant) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variant.id}`;\n if (next.has(key)) {\n next.delete(key);\n } else {\n // Pre-fill slot qty from the canonical resolver — variant-level\n // wins, product-level fallback, then 1. Customer can still adjust\n // via the stepper afterwards.\n const configuredQty = bundle\n ? resolveBundleQty(bundle, productId, variant.id)\n : 1;\n next.set(key, {\n productId,\n variantId: variant.id,\n quantity: configuredQty,\n });\n }\n return next;\n });\n },\n [bundle],\n );\n\n const updateQuantity = useCallback(\n (productId: string, variantId: string, quantity: number) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variantId}`;\n if (quantity <= 0) {\n next.delete(key);\n } else {\n next.set(key, { productId, variantId, quantity });\n }\n return next;\n });\n },\n [],\n );\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !validation.valid) return;\n\n const lines: CartLineInput[] = Array.from(selections.values()).map((s) => ({\n merchandiseId: s.variantId,\n quantity: s.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n\n const totalPrice = lines.reduce((sum, line) => {\n const product = bundle.products.find((p) =>\n p.variants.nodes.some((v) => v.id === line.merchandiseId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === line.merchandiseId,\n );\n return sum + parseFloat(variant?.price.amount ?? \"0\") * line.quantity;\n }, 0);\n\n // productId is intentionally omitted — mix-match bundles span\n // multiple products; bundleGid attributes the event to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n setSelections(new Map());\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n selections,\n validation.valid,\n onAddToCart,\n onError,\n trackAddToCart,\n totalQuantity,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--mix-match ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">\n {bundle.discountLabel}\n </span>\n )}\n\n <p className=\"lb-bundle__instructions\">\n {bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : bundle.maxQuantity\n ? `Select up to ${bundle.maxQuantity} items`\n : \"Select your items\"}\n </p>\n\n <div className=\"lb-bundle__products lb-bundle__products--selectable\">\n {bundle.products.map((product) => (\n <MixMatchProductRow\n key={product.id}\n product={product}\n currency={currency}\n selections={selections}\n onToggle={toggleProduct}\n onUpdateQuantity={updateQuantity}\n />\n ))}\n </div>\n\n {validation.message && (\n <p className=\"lb-bundle__validation\" role=\"status\">\n {validation.message}\n </p>\n )}\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart || !validation.valid}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ??\n `Add ${totalQuantity} Items to Cart`)}\n </button>\n </div>\n );\n}\n\ninterface MixMatchProductRowProps {\n product: Product;\n currency: string;\n selections: Map<string, SelectedItem>;\n onToggle: (productId: string, variant: ProductVariant) => void;\n onUpdateQuantity: (\n productId: string,\n variantId: string,\n quantity: number,\n ) => void;\n}\n\nfunction MixMatchProductRow({\n product,\n currency,\n selections,\n onToggle,\n onUpdateQuantity,\n}: MixMatchProductRowProps) {\n const variants = product.variants.nodes;\n const optionNames = useMemo<string[]>(() => {\n const first = variants[0];\n return first ? first.selectedOptions.map((o) => o.name) : [];\n }, [variants]);\n const showPicker = variants.length > 1 && optionNames.length > 0;\n\n const { selectedValues, selectedVariant, setOptionValue, optionsFor } =\n useVariantSelection({ variants, optionNames });\n\n const displayVariant =\n selectedVariant ??\n variants.find((v) => v.availableForSale) ??\n variants[0] ??\n null;\n if (!displayVariant) return null;\n\n const key = `${product.id}:${displayVariant.id}`;\n const selected = selections.get(key);\n const thumbImage = displayVariant.image ?? product.featuredImage;\n const unitPriceText = formatUnitPrice(\n displayVariant.unitPrice,\n displayVariant.unitPriceMeasurement,\n currency,\n );\n\n return (\n <div\n className={`lb-bundle__product lb-bundle__product--selectable ${\n selected ? \"lb-bundle__product--selected\" : \"\"\n }`}\n >\n {thumbImage && (\n <img\n src={thumbImage.url}\n alt={thumbImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(displayVariant.price.amount, currency)}\n </p>\n {unitPriceText && (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n )}\n {showPicker && !selected && (\n <div className=\"lb-bundle__product-variant-pickers\">\n {optionNames.map((optionName, optionIndex) => {\n const dropdownOptions = optionsFor(optionIndex).map((o) => ({\n value: o.value,\n label: o.value,\n disabled: o.disabled,\n }));\n return (\n <VariantDropdown\n key={optionName}\n options={dropdownOptions}\n value={selectedValues[optionIndex] ?? null}\n onChange={(v) => setOptionValue(optionIndex, v)}\n ariaLabel={optionName}\n />\n );\n })}\n </div>\n )}\n </div>\n <div className=\"lb-bundle__product-actions\">\n {selected ? (\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label={`Decrease ${product.title}`}\n onClick={() =>\n onUpdateQuantity(\n product.id,\n displayVariant.id,\n selected.quantity - 1,\n )\n }\n >\n −\n </button>\n <span>{selected.quantity}</span>\n <button\n aria-label={`Increase ${product.title}`}\n onClick={() =>\n onUpdateQuantity(\n product.id,\n displayVariant.id,\n selected.quantity + 1,\n )\n }\n >\n +\n </button>\n </div>\n ) : (\n <button\n className=\"lb-bundle__select-btn\"\n onClick={() => onToggle(product.id, displayVariant)}\n disabled={!displayVariant.availableForSale}\n >\n {displayVariant.availableForSale ? \"Select\" : \"Sold out\"}\n </button>\n )}\n </div>\n </div>\n );\n}\n","/**\n * Volume bundle component — quantity selector with tier calculator.\n */\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateTierSavings,\n getActiveTier,\n type CartLineInput,\n type VolumeBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport { useVariantSelection } from \"../hooks/useVariantSelection\";\nimport { VariantDropdown } from \"./VariantDropdown\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function VolumeBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"volume\",\n enabled: analyticsEnabled !== false,\n });\n\n const [quantity, setQuantity] = useState(1);\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"volume\"\n ? (result.bundle as VolumeBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (result.status === \"success\" && result.bundle.bundleType !== \"volume\") {\n onError?.(\n new Error(\n `VolumeBundle: expected bundleType=\"volume\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const product = bundle?.products[0];\n const variants = useMemo(() => product?.variants.nodes ?? [], [product]);\n const optionNames = useMemo<string[]>(() => {\n const first = variants[0];\n return first ? first.selectedOptions.map((o) => o.name) : [];\n }, [variants]);\n const showPicker = variants.length > 1 && optionNames.length > 0;\n const { selectedValues, selectedVariant, setOptionValue, optionsFor } =\n useVariantSelection({ variants, optionNames });\n\n const displayVariant =\n selectedVariant ?? variants.find((v) => v.availableForSale) ?? variants[0];\n const basePrice = displayVariant\n ? parseFloat(displayVariant.price.amount)\n : product\n ? parseFloat(product.priceRange.minVariantPrice.amount)\n : 0;\n const currency = product?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const thumbImage = displayVariant?.image ?? product?.featuredImage ?? null;\n const unitPriceText = displayVariant\n ? formatUnitPrice(\n displayVariant.unitPrice,\n displayVariant.unitPriceMeasurement,\n currency,\n )\n : null;\n\n const tierSavings = useMemo(\n () =>\n bundle\n ? calculateTierSavings(\n bundle.volumeTiers,\n basePrice,\n quantity,\n bundle.discountConfig.discountType,\n )\n : [],\n [bundle, basePrice, quantity],\n );\n const activeTier = bundle\n ? getActiveTier(bundle.volumeTiers, quantity)\n : null;\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !product) return;\n\n const variant =\n selectedVariant ?? product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n const lines: CartLineInput[] = [\n {\n merchandiseId: variant.id,\n quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n },\n ];\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n // Reuse the canonical computation from calculateTierSavings so the\n // analytics unit price matches what the component rendered.\n const unitPrice =\n tierSavings.find((ts) => ts.tier === activeTier)?.unitPrice ??\n basePrice;\n trackAddToCart({\n productId: product.id,\n quantity,\n totalPrice: Math.round(unitPrice * quantity * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n product,\n quantity,\n activeTier,\n basePrice,\n onAddToCart,\n onError,\n trackAddToCart,\n selectedVariant,\n tierSavings,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--tiers\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n if (!product) return null;\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--volume ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n <div className=\"lb-bundle__product lb-bundle__product--volume\">\n {thumbImage && (\n <img\n src={thumbImage.url}\n alt={thumbImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(basePrice, currency)} each\n </p>\n {unitPriceText && (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n )}\n {showPicker && (\n <div className=\"lb-bundle__product-variant-pickers\">\n {optionNames.map((optionName, optionIndex) => {\n const dropdownOptions = optionsFor(optionIndex).map((o) => ({\n value: o.value,\n label: o.value,\n disabled: o.disabled,\n }));\n return (\n <VariantDropdown\n key={optionName}\n options={dropdownOptions}\n value={selectedValues[optionIndex] ?? null}\n onChange={(v) => setOptionValue(optionIndex, v)}\n ariaLabel={optionName}\n />\n );\n })}\n </div>\n )}\n </div>\n </div>\n\n <div\n className=\"lb-bundle__tiers\"\n role=\"table\"\n aria-label=\"Volume discounts\"\n >\n {tierSavings.map((ts) => (\n <div\n key={ts.tier.minQuantity}\n className={`lb-bundle__tier ${ts.isActive ? \"lb-bundle__tier--active\" : \"\"}`}\n role=\"row\"\n >\n <span className=\"lb-bundle__tier-quantity\" role=\"cell\">\n {ts.tier.minQuantity}+ items\n </span>\n <span className=\"lb-bundle__tier-price\" role=\"cell\">\n {formatMoney(ts.unitPrice, currency)} each\n </span>\n <span className=\"lb-bundle__tier-savings\" role=\"cell\">\n Save {ts.savingsPercent.toFixed(0)}%\n </span>\n </div>\n ))}\n </div>\n\n <div className=\"lb-bundle__quantity-selector\">\n <label htmlFor={`lb-qty-${bundle.id}`}>Quantity</label>\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label=\"Decrease quantity\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n >\n −\n </button>\n <input\n id={`lb-qty-${bundle.id}`}\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => {\n const val = parseInt(e.target.value, 10);\n if (!isNaN(val) && val > 0) setQuantity(val);\n }}\n className=\"lb-bundle__quantity-input\"\n />\n <button\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((q) => q + 1)}\n >\n +\n </button>\n </div>\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundlesForProduct` — the\n * React-SDK analogue of the web component's auto-detect-by-current-product\n * mode. The web component reads the handle from `location.pathname`; a\n * React app already has it on the router, so the hook takes it as an\n * explicit `productHandle` option.\n *\n * Returns every active bundle configured against the product. Inactive,\n * draft, scheduled-for-later, and expired bundles are filtered out by\n * the core helper — consumers don't have to guard.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundlesForProduct` directly and pass the list to a client\n * component as a prop. This hook's only value-add over the async\n * function is React state management + abort-on-unmount.\n *\n * `UseBundlesForProductResult` is a discriminated union locked at v2.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport {\n fetchBundlesForProduct,\n injectCustomCss,\n type ParsedBundle,\n} from \"@lime-bundles/core\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundlesForProductOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n productHandle: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR paths. */\n buyerIp?: string;\n}\n\nexport type UseBundlesForProductResult =\n | { status: \"loading\"; bundles: null; error: null }\n | { status: \"success\"; bundles: ParsedBundle[]; error: null }\n | { status: \"error\"; bundles: null; error: Error };\n\nconst INITIAL_STATE: UseBundlesForProductResult = {\n status: \"loading\",\n bundles: null,\n error: null,\n};\n\nexport function useBundlesForProduct(\n options: UseBundlesForProductOptions,\n): UseBundlesForProductResult {\n const [state, setState] = useState<UseBundlesForProductResult>(\n INITIAL_STATE,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundlesForProduct({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n productHandle: options.productHandle,\n buyerIp: options.buyerIp,\n signal: controller.signal,\n })\n .then((bundles) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundles, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundles: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch so storefronts using this hook get\n // merchant styling automatically — same behaviour as `useBundleData`.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.productHandle,\n options.buyerIp,\n ]);\n\n return state;\n}\n"],"mappings":"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","names":["import_react","import_core","import_core","import_core","import_react","import_core","import_react","import_core","import_react","import_core","variantsNs","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_core","INITIAL_STATE","import_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { CartLineInput, ParsedBundle, WidgetConfig } from '@lime-bundles/core';
|
|
2
|
+
import { CartLineInput, ProductVariant, ParsedBundle, WidgetConfig } from '@lime-bundles/core';
|
|
3
3
|
export { ABVariantOverrides, BUNDLES_FOR_PRODUCT_QUERY, BUNDLE_METAOBJECT_QUERY, BundleParseError, BundleType, CartLineInput, CountdownConfig, CtaConfig, DiscountConfig, FixedBundleData, FixedBundlePricing, HeaderConfig, ImageTransform, LayoutConfig, MixMatchBundleData, ParsedBundle, PopularBadgeConfig, PricingConfig, PricingRow, Product, ProductListConfig, ProductVariant, SHOP_CUSTOM_CSS_QUERY, SavingsBarConfig, StorefrontApiError, ThumbnailRatio, VolumeBundleData, VolumeTier, WIDGET_CONFIG_DEFAULTS, WidgetConfig, applyABVariantB, applyWidgetConfigVars, calculateDiscount, calculateTierSavings, computeBundleSaleCents, computeFixedPricing, createStorefrontClient, fetchBundlesForProduct, formatCents, formatCountdown, formatMoney, formatUnitPrice, getABTestAssignment, getActiveTier, hasConsent, injectCustomCss, mergeWidgetConfig, observeImpression, parseCents, parseMetaobjectBundle, parseMetaobjectBundleStrict, percentageDiscountUnit, reportAddToCart, reportImpression, sanitizeCustomCss, setConsent, thumbnailRatioVars, transformImageUrl, validateQuantity } from '@lime-bundles/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -40,6 +40,54 @@ declare function MixMatchBundle(props: BundleComponentProps): react_jsx_runtime.
|
|
|
40
40
|
|
|
41
41
|
declare function VolumeBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
42
42
|
|
|
43
|
+
interface VariantDropdownOption {
|
|
44
|
+
value: string;
|
|
45
|
+
label: string;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
}
|
|
48
|
+
interface VariantDropdownProps {
|
|
49
|
+
options: ReadonlyArray<VariantDropdownOption>;
|
|
50
|
+
value: string | null;
|
|
51
|
+
onChange: (value: string) => void;
|
|
52
|
+
/** Accessible label, e.g. `"Size"`. */
|
|
53
|
+
ariaLabel?: string;
|
|
54
|
+
className?: string;
|
|
55
|
+
}
|
|
56
|
+
declare function VariantDropdown({ options, value, onChange, ariaLabel, className, }: VariantDropdownProps): react_jsx_runtime.JSX.Element;
|
|
57
|
+
|
|
58
|
+
interface UseVariantSelectionOptions {
|
|
59
|
+
/** Storefront-API variants for the product (eligible set already filtered). */
|
|
60
|
+
variants: ReadonlyArray<ProductVariant>;
|
|
61
|
+
/** Positional option names for the product, e.g. `['Size', 'Color']`. */
|
|
62
|
+
optionNames: ReadonlyArray<string>;
|
|
63
|
+
/**
|
|
64
|
+
* Optional initial variant. Defaults to the first available variant, or
|
|
65
|
+
* the first variant if none are available.
|
|
66
|
+
*/
|
|
67
|
+
initialVariant?: ProductVariant | null;
|
|
68
|
+
/**
|
|
69
|
+
* Canonical option-value order per option position (e.g. from
|
|
70
|
+
* `Product.options[].values` on the Storefront API). When supplied, the
|
|
71
|
+
* dropdown UI renders values in this merchant-defined order. When omitted,
|
|
72
|
+
* order is variant-discovery order (which may not match Shopify admin).
|
|
73
|
+
*/
|
|
74
|
+
optionValuesByPosition?: ReadonlyArray<ReadonlyArray<string>>;
|
|
75
|
+
}
|
|
76
|
+
interface UseVariantSelectionResult {
|
|
77
|
+
/** Positional option values matching `optionNames`. */
|
|
78
|
+
selectedValues: ReadonlyArray<string>;
|
|
79
|
+
/** The resolved variant for the current selection (null when no exact match). */
|
|
80
|
+
selectedVariant: ProductVariant | null;
|
|
81
|
+
/** Set the value at a single option position. */
|
|
82
|
+
setOptionValue: (optionIndex: number, value: string) => void;
|
|
83
|
+
/** All distinct values for an option position, paired with availability. */
|
|
84
|
+
optionsFor: (optionIndex: number) => ReadonlyArray<{
|
|
85
|
+
value: string;
|
|
86
|
+
disabled: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
89
|
+
declare function useVariantSelection({ variants, optionNames, initialVariant, optionValuesByPosition, }: UseVariantSelectionOptions): UseVariantSelectionResult;
|
|
90
|
+
|
|
43
91
|
/**
|
|
44
92
|
* Framework-agnostic async fetcher for bundle data.
|
|
45
93
|
*
|
|
@@ -142,4 +190,4 @@ declare function useAnalytics(options: UseAnalyticsOptions): {
|
|
|
142
190
|
|
|
143
191
|
declare function useWidgetConfigVars(config: WidgetConfig | null | undefined): (el: HTMLElement | null) => void;
|
|
144
192
|
|
|
145
|
-
export { type BundleComponentProps, type FetchBundleDataOptions, type FetchShopCustomCssOptions, FixedBundle, MixMatchBundle, type UseBundleDataOptions, type UseBundleDataResult, type UseBundlesForProductOptions, type UseBundlesForProductResult, VolumeBundle, fetchBundleData, fetchShopCustomCss, useAnalytics, useBundleData, useBundlesForProduct, useWidgetConfigVars };
|
|
193
|
+
export { type BundleComponentProps, type FetchBundleDataOptions, type FetchShopCustomCssOptions, FixedBundle, MixMatchBundle, type UseBundleDataOptions, type UseBundleDataResult, type UseBundlesForProductOptions, type UseBundlesForProductResult, type UseVariantSelectionOptions, type UseVariantSelectionResult, VariantDropdown, type VariantDropdownOption, type VariantDropdownProps, VolumeBundle, fetchBundleData, fetchShopCustomCss, useAnalytics, useBundleData, useBundlesForProduct, useVariantSelection, useWidgetConfigVars };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { CartLineInput, ParsedBundle, WidgetConfig } from '@lime-bundles/core';
|
|
2
|
+
import { CartLineInput, ProductVariant, ParsedBundle, WidgetConfig } from '@lime-bundles/core';
|
|
3
3
|
export { ABVariantOverrides, BUNDLES_FOR_PRODUCT_QUERY, BUNDLE_METAOBJECT_QUERY, BundleParseError, BundleType, CartLineInput, CountdownConfig, CtaConfig, DiscountConfig, FixedBundleData, FixedBundlePricing, HeaderConfig, ImageTransform, LayoutConfig, MixMatchBundleData, ParsedBundle, PopularBadgeConfig, PricingConfig, PricingRow, Product, ProductListConfig, ProductVariant, SHOP_CUSTOM_CSS_QUERY, SavingsBarConfig, StorefrontApiError, ThumbnailRatio, VolumeBundleData, VolumeTier, WIDGET_CONFIG_DEFAULTS, WidgetConfig, applyABVariantB, applyWidgetConfigVars, calculateDiscount, calculateTierSavings, computeBundleSaleCents, computeFixedPricing, createStorefrontClient, fetchBundlesForProduct, formatCents, formatCountdown, formatMoney, formatUnitPrice, getABTestAssignment, getActiveTier, hasConsent, injectCustomCss, mergeWidgetConfig, observeImpression, parseCents, parseMetaobjectBundle, parseMetaobjectBundleStrict, percentageDiscountUnit, reportAddToCart, reportImpression, sanitizeCustomCss, setConsent, thumbnailRatioVars, transformImageUrl, validateQuantity } from '@lime-bundles/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -40,6 +40,54 @@ declare function MixMatchBundle(props: BundleComponentProps): react_jsx_runtime.
|
|
|
40
40
|
|
|
41
41
|
declare function VolumeBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
42
42
|
|
|
43
|
+
interface VariantDropdownOption {
|
|
44
|
+
value: string;
|
|
45
|
+
label: string;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
}
|
|
48
|
+
interface VariantDropdownProps {
|
|
49
|
+
options: ReadonlyArray<VariantDropdownOption>;
|
|
50
|
+
value: string | null;
|
|
51
|
+
onChange: (value: string) => void;
|
|
52
|
+
/** Accessible label, e.g. `"Size"`. */
|
|
53
|
+
ariaLabel?: string;
|
|
54
|
+
className?: string;
|
|
55
|
+
}
|
|
56
|
+
declare function VariantDropdown({ options, value, onChange, ariaLabel, className, }: VariantDropdownProps): react_jsx_runtime.JSX.Element;
|
|
57
|
+
|
|
58
|
+
interface UseVariantSelectionOptions {
|
|
59
|
+
/** Storefront-API variants for the product (eligible set already filtered). */
|
|
60
|
+
variants: ReadonlyArray<ProductVariant>;
|
|
61
|
+
/** Positional option names for the product, e.g. `['Size', 'Color']`. */
|
|
62
|
+
optionNames: ReadonlyArray<string>;
|
|
63
|
+
/**
|
|
64
|
+
* Optional initial variant. Defaults to the first available variant, or
|
|
65
|
+
* the first variant if none are available.
|
|
66
|
+
*/
|
|
67
|
+
initialVariant?: ProductVariant | null;
|
|
68
|
+
/**
|
|
69
|
+
* Canonical option-value order per option position (e.g. from
|
|
70
|
+
* `Product.options[].values` on the Storefront API). When supplied, the
|
|
71
|
+
* dropdown UI renders values in this merchant-defined order. When omitted,
|
|
72
|
+
* order is variant-discovery order (which may not match Shopify admin).
|
|
73
|
+
*/
|
|
74
|
+
optionValuesByPosition?: ReadonlyArray<ReadonlyArray<string>>;
|
|
75
|
+
}
|
|
76
|
+
interface UseVariantSelectionResult {
|
|
77
|
+
/** Positional option values matching `optionNames`. */
|
|
78
|
+
selectedValues: ReadonlyArray<string>;
|
|
79
|
+
/** The resolved variant for the current selection (null when no exact match). */
|
|
80
|
+
selectedVariant: ProductVariant | null;
|
|
81
|
+
/** Set the value at a single option position. */
|
|
82
|
+
setOptionValue: (optionIndex: number, value: string) => void;
|
|
83
|
+
/** All distinct values for an option position, paired with availability. */
|
|
84
|
+
optionsFor: (optionIndex: number) => ReadonlyArray<{
|
|
85
|
+
value: string;
|
|
86
|
+
disabled: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
89
|
+
declare function useVariantSelection({ variants, optionNames, initialVariant, optionValuesByPosition, }: UseVariantSelectionOptions): UseVariantSelectionResult;
|
|
90
|
+
|
|
43
91
|
/**
|
|
44
92
|
* Framework-agnostic async fetcher for bundle data.
|
|
45
93
|
*
|
|
@@ -142,4 +190,4 @@ declare function useAnalytics(options: UseAnalyticsOptions): {
|
|
|
142
190
|
|
|
143
191
|
declare function useWidgetConfigVars(config: WidgetConfig | null | undefined): (el: HTMLElement | null) => void;
|
|
144
192
|
|
|
145
|
-
export { type BundleComponentProps, type FetchBundleDataOptions, type FetchShopCustomCssOptions, FixedBundle, MixMatchBundle, type UseBundleDataOptions, type UseBundleDataResult, type UseBundlesForProductOptions, type UseBundlesForProductResult, VolumeBundle, fetchBundleData, fetchShopCustomCss, useAnalytics, useBundleData, useBundlesForProduct, useWidgetConfigVars };
|
|
193
|
+
export { type BundleComponentProps, type FetchBundleDataOptions, type FetchShopCustomCssOptions, FixedBundle, MixMatchBundle, type UseBundleDataOptions, type UseBundleDataResult, type UseBundlesForProductOptions, type UseBundlesForProductResult, type UseVariantSelectionOptions, type UseVariantSelectionResult, VariantDropdown, type VariantDropdownOption, type VariantDropdownProps, VolumeBundle, fetchBundleData, fetchShopCustomCss, useAnalytics, useBundleData, useBundlesForProduct, useVariantSelection, useWidgetConfigVars };
|