@lime-bundles/widget 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/index.cjs +672 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +654 -0
- package/dist/index.js.map +1 -0
- package/dist/lime-bundle.global.js +193 -0
- package/dist/lime-bundle.global.js.map +1 -0
- package/dist/lime-thankyou.cjs +2 -0
- package/dist/lime-thankyou.cjs.map +1 -0
- package/dist/lime-thankyou.global.js +2 -0
- package/dist/lime-thankyou.global.js.map +1 -0
- package/dist/lime-thankyou.js +2 -0
- package/dist/lime-thankyou.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/styles/widget-styles.ts","../src/thankyou/index.ts"],"sourcesContent":["/**\n * @lime-bundles/widget — Vanilla JS Web Component for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-bundle.js\"></script>\n * <lime-bundle shop-domain=\"...\" storefront-token=\"...\" bundle-gid=\"...\"></lime-bundle>\n */\nimport { LimeBundleElement } from \"./lime-bundle\";\n\n// Register custom element\nif (typeof customElements !== \"undefined\" && !customElements.get(\"lime-bundle\")) {\n customElements.define(\"lime-bundle\", LimeBundleElement);\n}\n\nexport { LimeBundleElement };\nexport { trackPurchase } from \"./thankyou/index\";\n","/**\n * <lime-bundle> Web Component — renders Lime Bundles on headless storefronts.\n *\n * Usage:\n * <lime-bundle\n * shop-domain=\"my-store.myshopify.com\"\n * storefront-token=\"abc123\"\n * bundle-gid=\"gid://shopify/Metaobject/12345\"\n * ></lime-bundle>\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundle,\n detectCartApi,\n createAjaxCartApi,\n createStorefrontCartApi,\n observeImpression,\n reportImpression,\n reportAddToCart,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n type CartLineItem,\n} from \"@lime-bundles/core\";\nimport { renderFixedBundle } from \"./renderers/fixed\";\nimport { renderMixMatchBundle } from \"./renderers/mix-match\";\nimport { renderVolumeBundle } from \"./renderers/volume\";\nimport { WIDGET_STYLES } from \"./styles/widget-styles\";\n\nexport class LimeBundleElement extends HTMLElement {\n static observedAttributes = [\n \"shop-domain\",\n \"storefront-token\",\n \"bundle-gid\",\n \"cart-id\",\n \"app-url\",\n \"analytics\",\n \"locale\",\n ];\n\n private shadow: ShadowRoot;\n private bundle: ParsedBundle | null = null;\n private abortController: AbortController | null = null;\n private impressionCleanup: (() => void) | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n connectedCallback() {\n this.render();\n this.fetchBundle();\n }\n\n disconnectedCallback() {\n this.abortController?.abort();\n this.teardownImpression();\n }\n\n /** Clean up stale bundle state and active impression observer. */\n private teardownImpression() {\n this.impressionCleanup?.();\n this.impressionCleanup = null;\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n // Ignore no-op writes (same value re-set) and pre-connect attribute parsing\n if (oldValue === newValue || !this.isConnected) return;\n\n if (name === \"bundle-gid\" || name === \"shop-domain\" || name === \"storefront-token\") {\n // Only refetch when all required attributes are present — prevents\n // partial-attribute races during element initialization or programmatic updates\n if (this.shopDomain && this.storefrontToken && this.bundleGid) {\n this.fetchBundle();\n }\n }\n }\n\n private get shopDomain(): string {\n return this.getAttribute(\"shop-domain\") ?? \"\";\n }\n\n private get storefrontToken(): string {\n return this.getAttribute(\"storefront-token\") ?? \"\";\n }\n\n private get bundleGid(): string {\n return this.getAttribute(\"bundle-gid\") ?? \"\";\n }\n\n private get cartId(): string | undefined {\n return this.getAttribute(\"cart-id\") ?? undefined;\n }\n\n private get appUrl(): string {\n return this.getAttribute(\"app-url\") ?? \"\";\n }\n\n private get analyticsEnabled(): boolean {\n return this.getAttribute(\"analytics\") !== \"false\";\n }\n\n private async fetchBundle() {\n if (!this.shopDomain || !this.storefrontToken || !this.bundleGid) {\n this.renderError(\"Missing required attributes: shop-domain, storefront-token, bundle-gid\");\n return;\n }\n\n // Abort any previous fetch and create a fresh controller.\n // Capture in a local const so post-await checks reference the controller\n // for THIS invocation, not a later one created by a concurrent attributeChangedCallback.\n this.abortController?.abort();\n const controller = new AbortController();\n this.abortController = controller;\n\n this.renderLoading();\n\n try {\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: this.bundleGid },\n );\n\n // Check the LOCAL controller — if a newer fetch replaced this.abortController,\n // this invocation's controller will be aborted.\n if (controller.signal.aborted) return;\n\n if (!data.metaobject) {\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\"Bundle not found\");\n return;\n }\n\n this.bundle = parseMetaobjectBundle(\n data.metaobject.id,\n data.metaobject.fields,\n );\n\n if (!this.bundle) {\n this.teardownImpression();\n this.renderError(\"Bundle is not active or has expired\");\n return;\n }\n\n this.renderBundle();\n this.setupImpression();\n } catch (err) {\n if (controller.signal.aborted) return;\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\n err instanceof Error ? err.message : \"Failed to load bundle\",\n );\n }\n }\n\n private renderBundle() {\n if (!this.bundle) return;\n\n const container = document.createElement(\"div\");\n container.className = \"lb-bundle\";\n container.setAttribute(\"role\", \"region\");\n container.setAttribute(\"aria-label\", this.bundle.title);\n\n const addToCart = async (items: CartLineItem[]) => {\n const apiType = detectCartApi();\n const cart =\n apiType === \"ajax\"\n ? createAjaxCartApi(this.bundleGid, this.bundle!.bundleType)\n : createStorefrontCartApi(\n createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n }),\n this.bundleGid,\n this.bundle!.bundleType,\n this.cartId,\n );\n\n let result: Awaited<ReturnType<typeof cart.addLines>>;\n try {\n result = await cart.addLines(items);\n } catch (err) {\n result = {\n success: false,\n error: err instanceof Error ? err.message : \"Cart add failed\",\n };\n }\n\n if (result.success) {\n // Always dispatch the CustomEvent so consumers can react to add-to-cart\n // regardless of analytics configuration\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:add-to-cart\", {\n detail: { items, cartId: result.cartId },\n bubbles: true,\n composed: true,\n }),\n );\n\n // Analytics reporting is separate — only fires when enabled and appUrl is set\n if (this.analyticsEnabled && this.appUrl) {\n const quantity = items.reduce((sum, i) => sum + i.quantity, 0);\n const totalPrice = items.reduce((sum, item) => {\n const product = this.bundle!.products.find((p) =>\n p.variants.nodes.some((v) => v.id === item.variantId),\n );\n const variant = product?.variants.nodes.find((v) => v.id === item.variantId);\n const price = variant ? parseFloat(variant.price.amount) : 0;\n return sum + price * item.quantity;\n }, 0);\n\n reportAddToCart(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle!.bundleType,\n productId: this.bundle!.products[0]?.id ?? \"\",\n quantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n },\n );\n }\n }\n\n return result;\n };\n\n switch (this.bundle.bundleType) {\n case \"fixed\":\n renderFixedBundle(container, this.bundle, addToCart);\n break;\n case \"mix_match\":\n renderMixMatchBundle(container, this.bundle, addToCart);\n break;\n case \"volume\":\n renderVolumeBundle(container, this.bundle, addToCart);\n break;\n }\n\n this.shadow.innerHTML = \"\";\n const style = document.createElement(\"style\");\n style.textContent = WIDGET_STYLES;\n this.shadow.appendChild(style);\n this.shadow.appendChild(container);\n\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:loaded\", {\n detail: {\n bundleType: this.bundle.bundleType,\n title: this.bundle.title,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private setupImpression() {\n if (!this.analyticsEnabled || !this.bundle || !this.appUrl) return;\n\n this.impressionCleanup?.();\n this.impressionCleanup = observeImpression(this, () => {\n reportImpression(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle!.bundleType,\n },\n );\n });\n }\n\n private renderLoading() {\n this.shadow.innerHTML = `\n <style>${WIDGET_STYLES}</style>\n <div class=\"lb-bundle lb-bundle--loading\">\n <div class=\"lb-skeleton lb-skeleton--title\"></div>\n <div class=\"lb-skeleton lb-skeleton--products\"></div>\n </div>\n `;\n }\n\n private renderError(message: string) {\n this.shadow.innerHTML = \"\";\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message, code: \"LOAD_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private render() {\n // Initial empty state\n this.shadow.innerHTML = `<style>${WIDGET_STYLES}</style>`;\n }\n}\n","/**\n * DOM renderer for fixed bundles.\n */\nimport { formatMoney, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderFixedBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Discount badge\n if (bundle.discountLabel) {\n const badge = document.createElement(\"span\");\n badge.className = \"lb-bundle__discount-badge\";\n badge.textContent = bundle.discountLabel;\n container.appendChild(badge);\n }\n\n // Products\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products\";\n\n for (const product of bundle.products) {\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product\";\n productEl.setAttribute(\"part\", \"product\");\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(product.priceRange.minVariantPrice.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n // CTA button\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.textContent = bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\";\n button.setAttribute(\"part\", \"button\");\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n button.textContent = \"Adding...\";\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 const result = await addToCart(items);\n\n button.disabled = false;\n button.textContent = bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\";\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n\n container.appendChild(button);\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for mix-and-match bundles.\n */\nimport { formatMoney, validateQuantity, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderMixMatchBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const selections = new Map<string, { variantId: string; quantity: number }>();\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Instructions\n const instructions = document.createElement(\"p\");\n instructions.className = \"lb-bundle__instructions\";\n instructions.textContent = bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : \"Select your items\";\n container.appendChild(instructions);\n\n // Products\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products lb-bundle__products--selectable\";\n\n for (const product of bundle.products) {\n const variant = product.variants.nodes.find((v) => v.availableForSale) ?? product.variants.nodes[0];\n if (!variant) continue;\n\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--selectable\";\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(variant.price.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n\n const selectBtn = document.createElement(\"button\");\n selectBtn.className = \"lb-bundle__select-btn\";\n selectBtn.textContent = variant.availableForSale ? \"Select\" : \"Sold out\";\n selectBtn.disabled = !variant.availableForSale;\n\n selectBtn.addEventListener(\"click\", () => {\n const key = product.id;\n if (selections.has(key)) {\n selections.delete(key);\n productEl.classList.remove(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Select\";\n } else {\n selections.set(key, { variantId: variant.id, quantity: 1 });\n productEl.classList.add(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Selected\";\n }\n updateCta();\n });\n\n productEl.appendChild(selectBtn);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n // Validation message\n const validationEl = document.createElement(\"p\");\n validationEl.className = \"lb-bundle__validation\";\n container.appendChild(validationEl);\n\n // CTA\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n button.disabled = true;\n container.appendChild(button);\n\n function updateCta() {\n const total = Array.from(selections.values()).reduce((s, v) => s + v.quantity, 0);\n const validation = validateQuantity(total, bundle.minQuantity, bundle.maxQuantity);\n button.disabled = !validation.valid;\n button.textContent = bundle.widgetConfig.ctaText ?? `Add ${total} Items to Cart`;\n validationEl.textContent = validation.message ?? \"\";\n }\n\n updateCta();\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n button.textContent = \"Adding...\";\n\n const items: CartLineItem[] = Array.from(selections.entries()).map(([, s]) => ({\n variantId: s.variantId,\n quantity: s.quantity,\n }));\n\n const result = await addToCart(items);\n updateCta();\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for volume bundles.\n */\nimport { formatMoney, calculateTierSavings, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderVolumeBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const product = bundle.products[0];\n if (!product) return;\n\n const basePrice = parseFloat(product.priceRange.minVariantPrice.amount);\n const currency = product.priceRange.minVariantPrice.currencyCode;\n let quantity = 1;\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Product\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--volume\";\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(basePrice, currency))} each</p>\n `;\n productEl.appendChild(info);\n container.appendChild(productEl);\n\n // Tier table\n const tiersDiv = document.createElement(\"div\");\n tiersDiv.className = \"lb-bundle__tiers\";\n tiersDiv.setAttribute(\"role\", \"table\");\n tiersDiv.setAttribute(\"aria-label\", \"Volume discounts\");\n container.appendChild(tiersDiv);\n\n // Quantity selector\n const qtyWrapper = document.createElement(\"div\");\n qtyWrapper.className = \"lb-bundle__quantity-selector\";\n const label = document.createElement(\"label\");\n label.textContent = \"Quantity\";\n qtyWrapper.appendChild(label);\n\n const qtyControl = document.createElement(\"div\");\n qtyControl.className = \"lb-bundle__quantity-control\";\n\n const minusBtn = document.createElement(\"button\");\n minusBtn.textContent = \"−\";\n minusBtn.setAttribute(\"aria-label\", \"Decrease quantity\");\n\n const qtyInput = document.createElement(\"input\");\n qtyInput.type = \"number\";\n qtyInput.min = \"1\";\n qtyInput.value = \"1\";\n qtyInput.className = \"lb-bundle__quantity-input\";\n\n const plusBtn = document.createElement(\"button\");\n plusBtn.textContent = \"+\";\n plusBtn.setAttribute(\"aria-label\", \"Increase quantity\");\n\n qtyControl.append(minusBtn, qtyInput, plusBtn);\n qtyWrapper.appendChild(qtyControl);\n container.appendChild(qtyWrapper);\n\n // CTA\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n container.appendChild(button);\n\n function updateTiers() {\n const savings = calculateTierSavings(bundle.volumeTiers, basePrice, quantity);\n tiersDiv.innerHTML = \"\";\n for (const ts of savings) {\n const row = document.createElement(\"div\");\n row.className = `lb-bundle__tier${ts.isActive ? \" lb-bundle__tier--active\" : \"\"}`;\n row.setAttribute(\"role\", \"row\");\n row.innerHTML = `\n <span class=\"lb-bundle__tier-quantity\" role=\"cell\">${ts.tier.minQuantity}+ items</span>\n <span class=\"lb-bundle__tier-price\" role=\"cell\">${escapeHtml(formatMoney(ts.unitPrice, currency))} each</span>\n <span class=\"lb-bundle__tier-savings\" role=\"cell\">Save ${ts.savingsPercent.toFixed(0)}%</span>\n ${ts.tier.label ? `<span class=\"lb-bundle__tier-label\" role=\"cell\">${escapeHtml(ts.tier.label)}</span>` : \"\"}\n `;\n tiersDiv.appendChild(row);\n }\n button.textContent = bundle.widgetConfig.ctaText ?? `Add ${quantity} to Cart`;\n }\n\n updateTiers();\n\n minusBtn.addEventListener(\"click\", () => {\n if (quantity > 1) { quantity--; qtyInput.value = String(quantity); updateTiers(); }\n });\n plusBtn.addEventListener(\"click\", () => {\n quantity++; qtyInput.value = String(quantity); updateTiers();\n });\n qtyInput.addEventListener(\"change\", () => {\n const val = parseInt(qtyInput.value, 10);\n if (!isNaN(val) && val > 0) { quantity = val; updateTiers(); }\n });\n\n button.addEventListener(\"click\", async () => {\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n button.disabled = true;\n button.textContent = \"Adding...\";\n\n const items: CartLineItem[] = [{ variantId: variant.id, quantity }];\n const result = await addToCart(items);\n\n button.disabled = false;\n updateTiers();\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * CSS styles inlined into Shadow DOM.\n * Uses CSS custom properties that pierce the shadow boundary for theming.\n */\nexport const WIDGET_STYLES = `\n:host {\n display: block;\n --lb-primary-color: #000;\n --lb-secondary-color: #666;\n --lb-accent-color: #2563eb;\n --lb-background: #fff;\n --lb-border-color: #e5e7eb;\n --lb-border-radius: 8px;\n --lb-font-family: inherit;\n --lb-font-size: 14px;\n --lb-spacing-sm: 8px;\n --lb-spacing-md: 16px;\n --lb-spacing-lg: 24px;\n --lb-button-bg: var(--lb-accent-color);\n --lb-button-text: #fff;\n --lb-button-radius: var(--lb-border-radius);\n --lb-savings-color: #16a34a;\n --lb-error-color: #dc2626;\n}\n\n.lb-bundle {\n font-family: var(--lb-font-family);\n font-size: var(--lb-font-size);\n color: var(--lb-primary-color);\n background: var(--lb-background);\n border: 1px solid var(--lb-border-color);\n border-radius: var(--lb-border-radius);\n padding: var(--lb-spacing-lg);\n}\n\n.lb-bundle__title { margin: 0 0 var(--lb-spacing-md); font-size: 1.25em; font-weight: 600; }\n.lb-bundle__discount-badge { display: inline-block; background: var(--lb-savings-color); color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 600; margin-bottom: var(--lb-spacing-md); }\n.lb-bundle__products { display: grid; gap: var(--lb-spacing-md); margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__product { display: flex; gap: var(--lb-spacing-md); align-items: center; padding: var(--lb-spacing-sm); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__product--selected { border-color: var(--lb-accent-color); }\n.lb-bundle__product-image { width: 64px; height: 64px; object-fit: cover; border-radius: calc(var(--lb-border-radius) - 2px); flex-shrink: 0; }\n.lb-bundle__product-info { flex: 1; min-width: 0; }\n.lb-bundle__product-title { margin: 0; font-weight: 500; }\n.lb-bundle__product-price { margin: 4px 0 0; color: var(--lb-secondary-color); }\n.lb-bundle__instructions { color: var(--lb-secondary-color); margin: 0 0 var(--lb-spacing-md); }\n.lb-bundle__tiers { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__tier { display: flex; align-items: center; gap: var(--lb-spacing-md); padding: var(--lb-spacing-sm) var(--lb-spacing-md); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); margin-bottom: var(--lb-spacing-sm); }\n.lb-bundle__tier--active { border-color: var(--lb-savings-color); }\n.lb-bundle__tier-savings { color: var(--lb-savings-color); font-weight: 600; }\n.lb-bundle__quantity-selector { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__quantity-selector label { display: block; margin-bottom: var(--lb-spacing-sm); font-weight: 500; }\n.lb-bundle__quantity-control { display: inline-flex; align-items: center; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__quantity-control button { width: 32px; height: 32px; border: none; background: transparent; cursor: pointer; font-size: 1.1em; display: flex; align-items: center; justify-content: center; }\n.lb-bundle__quantity-input { width: 40px; text-align: center; border: none; border-left: 1px solid var(--lb-border-color); border-right: 1px solid var(--lb-border-color); height: 32px; font-size: var(--lb-font-size); -moz-appearance: textfield; }\n.lb-bundle__quantity-input::-webkit-outer-spin-button, .lb-bundle__quantity-input::-webkit-inner-spin-button { -webkit-appearance: none; }\n.lb-bundle__select-btn { padding: 6px 12px; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); background: transparent; cursor: pointer; }\n.lb-bundle__select-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__cta { width: 100%; padding: 12px 24px; border: none; border-radius: var(--lb-button-radius); background: var(--lb-button-bg); color: var(--lb-button-text); font-size: 1em; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }\n.lb-bundle__cta:hover:not(:disabled) { opacity: 0.9; }\n.lb-bundle__cta:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__error { color: var(--lb-error-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n.lb-bundle__validation { color: var(--lb-secondary-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n\n.lb-skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: lb-shimmer 1.5s infinite; border-radius: var(--lb-border-radius); }\n.lb-skeleton--title { height: 24px; width: 60%; margin-bottom: var(--lb-spacing-md); }\n.lb-skeleton--products { height: 200px; }\n@keyframes lb-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n`;\n","/**\n * Thank-you page snippet — fires purchase events for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-thankyou.js\"></script>\n * <script>\n * LimeBundles.trackPurchase({\n * shopDomain: 'my-store.myshopify.com',\n * orderId: 'gid://shopify/Order/12345',\n * lineItems: [\n * { bundleGid: '...', bundleType: 'fixed', price: 29.99, quantity: 1 }\n * ]\n * });\n * </script>\n */\n\ninterface PurchaseLineItem {\n bundleGid: string;\n bundleType: string;\n price: number;\n quantity: number;\n}\n\ninterface TrackPurchaseInput {\n shopDomain: string;\n orderId: string;\n lineItems: PurchaseLineItem[];\n appUrl?: string;\n}\n\n// Deduplication: track order+bundle combos we've already reported\nconst reportedPurchases = new Set<string>();\n\nexport function trackPurchase(input: TrackPurchaseInput): void {\n const appUrl = input.appUrl ?? `https://${input.shopDomain}`;\n\n // Group line items by bundleGid (merge quantities and prices)\n const bundleMap = new Map<string, { bundleType: string; revenue: number; lineItemCount: number }>();\n\n for (const item of input.lineItems) {\n if (!item.bundleGid) continue;\n\n const existing = bundleMap.get(item.bundleGid);\n if (existing) {\n existing.revenue += item.price * item.quantity;\n existing.lineItemCount += 1;\n } else {\n bundleMap.set(item.bundleGid, {\n bundleType: item.bundleType,\n revenue: item.price * item.quantity,\n lineItemCount: 1,\n });\n }\n }\n\n // Fire purchase events for each unique bundle\n for (const [bundleGid, data] of bundleMap) {\n const dedupKey = `${input.orderId}:${bundleGid}`;\n if (reportedPurchases.has(dedupKey)) continue;\n reportedPurchases.add(dedupKey);\n\n const payload = {\n shopDomain: input.shopDomain,\n eventType: \"bundle_purchased\",\n bundleGid,\n bundleType: data.bundleType,\n orderId: input.orderId,\n revenue: Math.round(data.revenue * 100) / 100,\n lineItemCount: data.lineItemCount,\n occurredAt: new Date().toISOString(),\n };\n\n // Fire-and-forget with sendBeacon fallback\n const url = `${appUrl}/api/analytics`;\n const body = JSON.stringify(payload);\n\n try {\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n }).catch(() => {\n // Fallback to sendBeacon\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon) {\n navigator.sendBeacon(url, body);\n }\n });\n } catch {\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon) {\n navigator.sendBeacon(url, body);\n }\n }\n }\n}\n"],"mappings":"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","names":["import_core","import_core","escapeHtml","import_core","escapeHtml"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
declare class LimeBundleElement extends HTMLElement {
|
|
2
|
+
static observedAttributes: string[];
|
|
3
|
+
private shadow;
|
|
4
|
+
private bundle;
|
|
5
|
+
private abortController;
|
|
6
|
+
private impressionCleanup;
|
|
7
|
+
constructor();
|
|
8
|
+
connectedCallback(): void;
|
|
9
|
+
disconnectedCallback(): void;
|
|
10
|
+
/** Clean up stale bundle state and active impression observer. */
|
|
11
|
+
private teardownImpression;
|
|
12
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
13
|
+
private get shopDomain();
|
|
14
|
+
private get storefrontToken();
|
|
15
|
+
private get bundleGid();
|
|
16
|
+
private get cartId();
|
|
17
|
+
private get appUrl();
|
|
18
|
+
private get analyticsEnabled();
|
|
19
|
+
private fetchBundle;
|
|
20
|
+
private renderBundle;
|
|
21
|
+
private setupImpression;
|
|
22
|
+
private renderLoading;
|
|
23
|
+
private renderError;
|
|
24
|
+
private render;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Thank-you page snippet — fires purchase events for headless storefronts.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* <script src="https://unpkg.com/@lime-bundles/widget/dist/lime-thankyou.js"></script>
|
|
32
|
+
* <script>
|
|
33
|
+
* LimeBundles.trackPurchase({
|
|
34
|
+
* shopDomain: 'my-store.myshopify.com',
|
|
35
|
+
* orderId: 'gid://shopify/Order/12345',
|
|
36
|
+
* lineItems: [
|
|
37
|
+
* { bundleGid: '...', bundleType: 'fixed', price: 29.99, quantity: 1 }
|
|
38
|
+
* ]
|
|
39
|
+
* });
|
|
40
|
+
* </script>
|
|
41
|
+
*/
|
|
42
|
+
interface PurchaseLineItem {
|
|
43
|
+
bundleGid: string;
|
|
44
|
+
bundleType: string;
|
|
45
|
+
price: number;
|
|
46
|
+
quantity: number;
|
|
47
|
+
}
|
|
48
|
+
interface TrackPurchaseInput {
|
|
49
|
+
shopDomain: string;
|
|
50
|
+
orderId: string;
|
|
51
|
+
lineItems: PurchaseLineItem[];
|
|
52
|
+
appUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function trackPurchase(input: TrackPurchaseInput): void;
|
|
55
|
+
|
|
56
|
+
export { LimeBundleElement, trackPurchase };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
declare class LimeBundleElement extends HTMLElement {
|
|
2
|
+
static observedAttributes: string[];
|
|
3
|
+
private shadow;
|
|
4
|
+
private bundle;
|
|
5
|
+
private abortController;
|
|
6
|
+
private impressionCleanup;
|
|
7
|
+
constructor();
|
|
8
|
+
connectedCallback(): void;
|
|
9
|
+
disconnectedCallback(): void;
|
|
10
|
+
/** Clean up stale bundle state and active impression observer. */
|
|
11
|
+
private teardownImpression;
|
|
12
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
13
|
+
private get shopDomain();
|
|
14
|
+
private get storefrontToken();
|
|
15
|
+
private get bundleGid();
|
|
16
|
+
private get cartId();
|
|
17
|
+
private get appUrl();
|
|
18
|
+
private get analyticsEnabled();
|
|
19
|
+
private fetchBundle;
|
|
20
|
+
private renderBundle;
|
|
21
|
+
private setupImpression;
|
|
22
|
+
private renderLoading;
|
|
23
|
+
private renderError;
|
|
24
|
+
private render;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Thank-you page snippet — fires purchase events for headless storefronts.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* <script src="https://unpkg.com/@lime-bundles/widget/dist/lime-thankyou.js"></script>
|
|
32
|
+
* <script>
|
|
33
|
+
* LimeBundles.trackPurchase({
|
|
34
|
+
* shopDomain: 'my-store.myshopify.com',
|
|
35
|
+
* orderId: 'gid://shopify/Order/12345',
|
|
36
|
+
* lineItems: [
|
|
37
|
+
* { bundleGid: '...', bundleType: 'fixed', price: 29.99, quantity: 1 }
|
|
38
|
+
* ]
|
|
39
|
+
* });
|
|
40
|
+
* </script>
|
|
41
|
+
*/
|
|
42
|
+
interface PurchaseLineItem {
|
|
43
|
+
bundleGid: string;
|
|
44
|
+
bundleType: string;
|
|
45
|
+
price: number;
|
|
46
|
+
quantity: number;
|
|
47
|
+
}
|
|
48
|
+
interface TrackPurchaseInput {
|
|
49
|
+
shopDomain: string;
|
|
50
|
+
orderId: string;
|
|
51
|
+
lineItems: PurchaseLineItem[];
|
|
52
|
+
appUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function trackPurchase(input: TrackPurchaseInput): void;
|
|
55
|
+
|
|
56
|
+
export { LimeBundleElement, trackPurchase };
|