@lime-bundles/react 0.1.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/hooks/useCart.cjs +84 -0
- package/dist/hooks/useCart.cjs.map +1 -0
- package/dist/hooks/useCart.d.cts +19 -0
- package/dist/hooks/useCart.d.ts +19 -0
- package/dist/hooks/useCart.js +64 -0
- package/dist/hooks/useCart.js.map +1 -0
- package/dist/index.cjs +689 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +677 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/FixedBundle.tsx","../src/hooks/useBundleData.ts","../src/hooks/useCart.ts","../src/hooks/useAnalytics.ts","../src/components/MixMatchBundle.tsx","../src/components/VolumeBundle.tsx"],"sourcesContent":["// Bundle Components\nexport { FixedBundle } from \"./components/FixedBundle\";\nexport { MixMatchBundle } from \"./components/MixMatchBundle\";\nexport { VolumeBundle } from \"./components/VolumeBundle\";\nexport type { BundleComponentProps } from \"./components/types\";\n\n// Hooks (for advanced usage)\nexport { useBundleData, type UseBundleDataResult } from \"./hooks/useBundleData\";\nexport { useCart, type UseCartResult } from \"./hooks/useCart\";\nexport { useAnalytics } from \"./hooks/useAnalytics\";\n\n// Re-export core types consumers commonly need\nexport type {\n ParsedBundle,\n BundleType,\n Product,\n ProductVariant,\n CartLineItem,\n AddToCartResult,\n} from \"@lime-bundles/core\";\n","/**\n * Fixed bundle component — displays pre-selected products with discount.\n */\nimport { useCallback } from \"react\";\nimport { formatMoney, calculateDiscount, type CartLineItem } from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useCart } from \"../hooks/useCart\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function FixedBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n cartId,\n cartApi,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const { bundle, loading, error: loadError } = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { addToCart, loading: cartLoading, error: cartError } = useCart({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n bundleType: \"fixed\",\n cartId,\n cartApi,\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 handleAddToCart = useCallback(async () => {\n if (!bundle) return;\n\n const items: CartLineItem[] = 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 { variantId: variant.id, quantity: 1 };\n });\n\n if (items.length === 0) return;\n\n const result = await addToCart(items);\n\n if (result.success) {\n const totalPrice = bundle.products.reduce((sum, p) => {\n const price = parseFloat(p.priceRange.minVariantPrice.amount);\n return sum + calculateDiscount(price, bundle.discountConfig.discountType, bundle.discountConfig.discountValue);\n }, 0);\n\n trackAddToCart({\n productId: bundle.products[0]?.id ?? \"\",\n quantity: 1,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n onAddToCart?.(items);\n }\n }, [bundle, addToCart, trackAddToCart, onAddToCart]);\n\n if (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 (loadError || !bundle) {\n if (onError) onError(loadError ?? \"Bundle not found\");\n return null;\n }\n\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={elementRef}\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\">\n {bundle.discountLabel}\n </span>\n )}\n\n <div className=\"lb-bundle__products\">\n {bundle.products.map((product) => (\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\">\n {formatMoney(product.priceRange.minVariantPrice.amount, currency)}\n </p>\n </div>\n </div>\n ))}\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">{cartError}</p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={cartLoading}\n aria-busy={cartLoading}\n >\n {cartLoading\n ? \"Adding...\"\n : bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\"}\n </button>\n </div>\n );\n}\n","/**\n * Hook to fetch and parse bundle data from the Storefront API.\n */\nimport { useState, useEffect, useRef } from \"react\";\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundle,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n} from \"@lime-bundles/core\";\n\nexport interface UseBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n}\n\nexport interface UseBundleDataResult {\n bundle: ParsedBundle | null;\n loading: boolean;\n error: string | null;\n}\n\nexport function useBundleData(\n options: UseBundleDataOptions,\n): UseBundleDataResult {\n const [bundle, setBundle] = useState<ParsedBundle | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => {\n // Cancel previous request\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setLoading(true);\n setError(null);\n\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n });\n\n client\n .query<BundleMetaobjectResponse>(BUNDLE_METAOBJECT_QUERY, {\n id: options.bundleGid,\n })\n .then((data) => {\n if (controller.signal.aborted) return;\n\n if (!data.metaobject) {\n setError(\"Bundle not found\");\n setBundle(null);\n } else {\n const parsed = parseMetaobjectBundle(\n data.metaobject.id,\n data.metaobject.fields,\n );\n if (parsed) {\n setBundle(parsed);\n } else {\n setError(\"Bundle is not active or has expired\");\n setBundle(null);\n }\n }\n setLoading(false);\n })\n .catch((err) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err.message : \"Failed to load bundle\");\n setLoading(false);\n });\n\n return () => {\n controller.abort();\n };\n }, [options.shopDomain, options.storefrontAccessToken, options.bundleGid]);\n\n return { bundle, loading, error };\n}\n","/**\n * Hook for cart API operations.\n * Auto-detects AJAX vs Storefront Cart API.\n */\nimport { useState, useCallback, useMemo } from \"react\";\nimport {\n detectCartApi,\n createAjaxCartApi,\n createStorefrontCartApi,\n createStorefrontClient,\n type CartApi,\n type CartLineItem,\n type AddToCartResult,\n} from \"@lime-bundles/core\";\n\nexport interface UseCartOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n bundleType: string;\n cartId?: string;\n cartApi?: \"ajax\" | \"storefront\"; // Manual override\n}\n\nexport interface UseCartResult {\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>;\n loading: boolean;\n error: string | null;\n lastResult: AddToCartResult | null;\n}\n\nexport function useCart(options: UseCartOptions): UseCartResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [lastResult, setLastResult] = useState<AddToCartResult | null>(null);\n\n const cart: CartApi = useMemo(() => {\n const apiType = options.cartApi ?? detectCartApi();\n\n if (apiType === \"ajax\") {\n return createAjaxCartApi(options.bundleGid, options.bundleType);\n }\n\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n });\n return createStorefrontCartApi(\n client,\n options.bundleGid,\n options.bundleType,\n options.cartId,\n );\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.bundleGid,\n options.bundleType,\n options.cartId,\n options.cartApi,\n ]);\n\n const addToCart = useCallback(\n async (items: CartLineItem[]): Promise<AddToCartResult> => {\n setLoading(true);\n setError(null);\n try {\n const result = await cart.addLines(items);\n setLastResult(result);\n if (!result.success) {\n setError(result.error ?? \"Failed to add to cart\");\n }\n return result;\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Failed to add to cart\";\n setError(message);\n const result: AddToCartResult = { success: false, error: message };\n setLastResult(result);\n return result;\n } finally {\n setLoading(false);\n }\n },\n [cart],\n );\n\n return { addToCart, loading, error, lastResult };\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 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 * Mix-and-match bundle component — customer picks products from a pool.\n */\nimport { useState, useCallback } from \"react\";\nimport {\n formatMoney,\n validateQuantity,\n type CartLineItem,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useCart } from \"../hooks/useCart\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\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 cartId,\n cartApi,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const { bundle, loading, error: loadError } = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { addToCart, loading: cartLoading, error: cartError } = useCart({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n bundleType: \"mix_match\",\n cartId,\n cartApi,\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>>(new Map());\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 next.set(key, { productId, variantId: variant.id, quantity: 1 });\n }\n return next;\n });\n },\n [],\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 items: CartLineItem[] = Array.from(selections.values()).map((s) => ({\n variantId: s.variantId,\n quantity: s.quantity,\n }));\n\n const result = await addToCart(items);\n\n if (result.success) {\n const totalPrice = items.reduce((sum, item) => {\n const product = bundle.products.find((p) =>\n p.variants.nodes.some((v) => v.id === item.variantId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === item.variantId,\n );\n return sum + (parseFloat(variant?.price.amount ?? \"0\") * item.quantity);\n }, 0);\n\n trackAddToCart({\n productId: bundle.products[0]?.id ?? \"\",\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n onAddToCart?.(items);\n setSelections(new Map());\n }\n }, [bundle, selections, validation.valid, addToCart, trackAddToCart, totalQuantity, onAddToCart]);\n\n if (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 (loadError || !bundle) {\n if (onError) onError(loadError ?? \"Bundle not found\");\n return null;\n }\n\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={elementRef}\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 = product.variants.nodes.find((v) => v.availableForSale) ?? 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 </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={() => updateQuantity(product.id, defaultVariant.id, selected.quantity - 1)}\n >\n −\n </button>\n <span>{selected.quantity}</span>\n <button\n aria-label={`Increase ${product.title}`}\n onClick={() => updateQuantity(product.id, defaultVariant.id, selected.quantity + 1)}\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\">{validation.message}</p>\n )}\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">{cartError}</p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={cartLoading || !validation.valid}\n aria-busy={cartLoading}\n >\n {cartLoading\n ? \"Adding...\"\n : bundle.widgetConfig.ctaText ?? `Add ${totalQuantity} Items to Cart`}\n </button>\n </div>\n );\n}\n","/**\n * Volume bundle component — quantity selector with tier calculator.\n */\nimport { useState, useCallback } from \"react\";\nimport {\n formatMoney,\n calculateTierSavings,\n getActiveTier,\n type CartLineItem,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useCart } from \"../hooks/useCart\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function VolumeBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n cartId,\n cartApi,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const { bundle, loading, error: loadError } = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { addToCart, loading: cartLoading, error: cartError } = useCart({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n bundleType: \"volume\",\n cartId,\n cartApi,\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\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(bundle.volumeTiers, 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 = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n const items: CartLineItem[] = [\n { variantId: variant.id, quantity },\n ];\n\n const result = await addToCart(items);\n\n if (result.success) {\n const unitPrice = activeTier\n ? basePrice * (1 - activeTier.discountValue / 100)\n : basePrice;\n\n trackAddToCart({\n productId: product.id,\n quantity,\n totalPrice: Math.round(unitPrice * quantity * 100) / 100,\n });\n\n onAddToCart?.(items);\n }\n }, [bundle, product, quantity, activeTier, basePrice, addToCart, trackAddToCart, onAddToCart]);\n\n if (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 (loadError || !bundle || !product) {\n if (onError) onError(loadError ?? \"Bundle not found\");\n return null;\n }\n\n return (\n <div\n ref={elementRef}\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 {/* Product display */}\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 </div>\n </div>\n\n {/* Tier calculator */}\n <div className=\"lb-bundle__tiers\" role=\"table\" aria-label=\"Volume discounts\">\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 {ts.tier.label && (\n <span className=\"lb-bundle__tier-label\" role=\"cell\">\n {ts.tier.label}\n </span>\n )}\n </div>\n ))}\n </div>\n\n {/* Quantity selector */}\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\">{cartError}</p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={cartLoading}\n aria-busy={cartLoading}\n >\n {cartLoading\n ? \"Adding...\"\n : bundle.widgetConfig.ctaText ?? `Add ${quantity} to Cart`}\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_core","import_react","import_core","import_react","import_core","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CartLineItem, ParsedBundle } from '@lime-bundles/core';
|
|
3
|
+
export { AddToCartResult, BundleType, CartLineItem, ParsedBundle, Product, ProductVariant } from '@lime-bundles/core';
|
|
4
|
+
export { UseCartResult, useCart } from './hooks/useCart.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared props for all bundle components.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface BundleComponentProps {
|
|
11
|
+
shopDomain: string;
|
|
12
|
+
storefrontAccessToken: string;
|
|
13
|
+
bundleGid: string;
|
|
14
|
+
cartId?: string;
|
|
15
|
+
cartApi?: "ajax" | "storefront";
|
|
16
|
+
appUrl?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
locale?: string;
|
|
19
|
+
analyticsEnabled?: boolean;
|
|
20
|
+
onAddToCart?: (items: CartLineItem[]) => void;
|
|
21
|
+
onError?: (error: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare function FixedBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
25
|
+
|
|
26
|
+
declare function MixMatchBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
27
|
+
|
|
28
|
+
declare function VolumeBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
29
|
+
|
|
30
|
+
interface UseBundleDataOptions {
|
|
31
|
+
shopDomain: string;
|
|
32
|
+
storefrontAccessToken: string;
|
|
33
|
+
bundleGid: string;
|
|
34
|
+
}
|
|
35
|
+
interface UseBundleDataResult {
|
|
36
|
+
bundle: ParsedBundle | null;
|
|
37
|
+
loading: boolean;
|
|
38
|
+
error: string | null;
|
|
39
|
+
}
|
|
40
|
+
declare function useBundleData(options: UseBundleDataOptions): UseBundleDataResult;
|
|
41
|
+
|
|
42
|
+
interface UseAnalyticsOptions {
|
|
43
|
+
shopDomain: string;
|
|
44
|
+
appUrl: string;
|
|
45
|
+
bundleGid: string;
|
|
46
|
+
bundleType: string;
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
abTestId?: string;
|
|
49
|
+
abVariant?: string;
|
|
50
|
+
}
|
|
51
|
+
declare function useAnalytics(options: UseAnalyticsOptions): {
|
|
52
|
+
elementRef: (el: HTMLElement | null) => void;
|
|
53
|
+
trackAddToCart: (event: {
|
|
54
|
+
productId: string;
|
|
55
|
+
quantity: number;
|
|
56
|
+
totalPrice: number;
|
|
57
|
+
}) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { type BundleComponentProps, FixedBundle, MixMatchBundle, type UseBundleDataResult, VolumeBundle, useAnalytics, useBundleData };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CartLineItem, ParsedBundle } from '@lime-bundles/core';
|
|
3
|
+
export { AddToCartResult, BundleType, CartLineItem, ParsedBundle, Product, ProductVariant } from '@lime-bundles/core';
|
|
4
|
+
export { UseCartResult, useCart } from './hooks/useCart.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared props for all bundle components.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface BundleComponentProps {
|
|
11
|
+
shopDomain: string;
|
|
12
|
+
storefrontAccessToken: string;
|
|
13
|
+
bundleGid: string;
|
|
14
|
+
cartId?: string;
|
|
15
|
+
cartApi?: "ajax" | "storefront";
|
|
16
|
+
appUrl?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
locale?: string;
|
|
19
|
+
analyticsEnabled?: boolean;
|
|
20
|
+
onAddToCart?: (items: CartLineItem[]) => void;
|
|
21
|
+
onError?: (error: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare function FixedBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
25
|
+
|
|
26
|
+
declare function MixMatchBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
27
|
+
|
|
28
|
+
declare function VolumeBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
|
|
29
|
+
|
|
30
|
+
interface UseBundleDataOptions {
|
|
31
|
+
shopDomain: string;
|
|
32
|
+
storefrontAccessToken: string;
|
|
33
|
+
bundleGid: string;
|
|
34
|
+
}
|
|
35
|
+
interface UseBundleDataResult {
|
|
36
|
+
bundle: ParsedBundle | null;
|
|
37
|
+
loading: boolean;
|
|
38
|
+
error: string | null;
|
|
39
|
+
}
|
|
40
|
+
declare function useBundleData(options: UseBundleDataOptions): UseBundleDataResult;
|
|
41
|
+
|
|
42
|
+
interface UseAnalyticsOptions {
|
|
43
|
+
shopDomain: string;
|
|
44
|
+
appUrl: string;
|
|
45
|
+
bundleGid: string;
|
|
46
|
+
bundleType: string;
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
abTestId?: string;
|
|
49
|
+
abVariant?: string;
|
|
50
|
+
}
|
|
51
|
+
declare function useAnalytics(options: UseAnalyticsOptions): {
|
|
52
|
+
elementRef: (el: HTMLElement | null) => void;
|
|
53
|
+
trackAddToCart: (event: {
|
|
54
|
+
productId: string;
|
|
55
|
+
quantity: number;
|
|
56
|
+
totalPrice: number;
|
|
57
|
+
}) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { type BundleComponentProps, FixedBundle, MixMatchBundle, type UseBundleDataResult, VolumeBundle, useAnalytics, useBundleData };
|