@lime-bundles/widget 4.1.0 → 4.1.1

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 CHANGED
@@ -26,7 +26,7 @@ __export(index_exports, {
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
28
  // src/lime-bundle.ts
29
- var import_core5 = require("@lime-bundles/core");
29
+ var import_core6 = require("@lime-bundles/core");
30
30
 
31
31
  // ../render/src/adapt.ts
32
32
  var import_core = require("@lime-bundles/core");
@@ -511,7 +511,7 @@ function buildWidgetShell(typeClass, opts) {
511
511
  "data-countdown": ""
512
512
  });
513
513
  const label2 = box("span", "lb-bundle-countdown-label");
514
- label2.textContent = opts.countdownLabel ?? "Ends in";
514
+ label2.textContent = opts.countdownLabel ?? "Limited time offer";
515
515
  countdown.appendChild(label2);
516
516
  countdown.appendChild(
517
517
  box("span", "lb-bundle-countdown-timer", {
@@ -523,10 +523,12 @@ function buildWidgetShell(typeClass, opts) {
523
523
  const products = box("div", typeClass + "__products lb-edge-fade");
524
524
  root.appendChild(products);
525
525
  root.appendChild(box("div", "lb-bundle-divider"));
526
- const summary = box("div", "lb-bundle-summary");
526
+ const summary = box("div", "lb-bundle-summary", {
527
+ "data-pricing-section": ""
528
+ });
527
529
  const text = box("div", "lb-bundle-summary__text");
528
530
  const label = box("span", "lb-bundle-summary__label");
529
- label.textContent = opts.summaryLabel ?? "Bundle price";
531
+ label.textContent = opts.summaryLabel ?? "Bundle total";
530
532
  text.appendChild(label);
531
533
  if (opts.withItemCount) {
532
534
  label.appendChild(box("span", "", { "data-item-count": "" }));
@@ -557,6 +559,7 @@ function buildWidgetShell(typeClass, opts) {
557
559
  summary.appendChild(prices);
558
560
  root.appendChild(summary);
559
561
  const cta = box("button", "lb-bundle-cta", {
562
+ "data-cta-text": opts.ctaText,
560
563
  type: "button",
561
564
  "data-add-bundle": ""
562
565
  });
@@ -575,7 +578,9 @@ function buildWidgetShell(typeClass, opts) {
575
578
  );
576
579
  return { root, products, cta };
577
580
  }
578
- function buildVolumeTiers(tiers, groupLabel) {
581
+ function buildVolumeTiers(tiers, groupLabel, opts = {}) {
582
+ const showCompare = opts.showCompare !== false;
583
+ const showPriceEach = opts.showPriceEach !== false;
579
584
  const group = box("div", "lb-volume__tiers lb-edge-fade", {
580
585
  role: "radiogroup",
581
586
  "aria-label": groupLabel,
@@ -600,8 +605,17 @@ function buildVolumeTiers(tiers, groupLabel) {
600
605
  label.textContent = tier.label;
601
606
  info.appendChild(label);
602
607
  const price = box("span", "lb-volume__tier-price");
603
- price.appendChild(box("span", "lb-volume__tier-compare"));
604
- price.appendChild(box("span", "", { "data-tier-price-each": "" }));
608
+ if (showCompare && (tier.percent > 0 || tier.amountCents > 0)) {
609
+ price.appendChild(box("span", "lb-volume__tier-compare"));
610
+ }
611
+ if (showPriceEach) {
612
+ price.appendChild(box("span", "", { "data-tier-price-each": "" }));
613
+ if (opts.unitLabel) {
614
+ const unit = box("span", "lb-volume__tier-unit");
615
+ unit.textContent = opts.unitLabel;
616
+ price.appendChild(unit);
617
+ }
618
+ }
605
619
  info.appendChild(price);
606
620
  el2.appendChild(info);
607
621
  if (tier.badgeLabel || tier.savingsLabel) {
@@ -710,7 +724,10 @@ var DEFAULT_STRINGS = {
710
724
  required: "Required",
711
725
  soldOut: "Sold out",
712
726
  outOfStock: "Out of stock",
713
- itemsOutOfStock: "__COUNT__ items out of stock",
727
+ itemsOutOfStock: {
728
+ one: "__COUNT__ item out of stock",
729
+ other: "__COUNT__ items out of stock"
730
+ },
714
731
  quantity: "Quantity",
715
732
  increaseQuantity: "Increase quantity",
716
733
  decreaseQuantity: "Decrease quantity",
@@ -730,11 +747,26 @@ var DEFAULT_STRINGS = {
730
747
  stepsCompleted: "__COUNT__ of __TOTAL__ steps completed",
731
748
  each: " each",
732
749
  buyQty: "Buy __COUNT__",
750
+ itemCount: {
751
+ one: "__COUNT__ item",
752
+ other: "__COUNT__ items"
753
+ },
733
754
  needsSpots: {
734
755
  one: "Needs __COUNT__ spot",
735
756
  other: "Needs __COUNT__ spots"
736
757
  }
737
758
  };
759
+ function formatItemsOutOfStock(t, count) {
760
+ const raw = t.itemsOutOfStock;
761
+ let tpl;
762
+ if (raw && typeof raw === "object") {
763
+ const form = new Intl.PluralRules(t.locale || "en").select(count);
764
+ tpl = raw[form] || raw.other;
765
+ } else {
766
+ tpl = raw;
767
+ }
768
+ return (tpl || "__COUNT__ items out of stock").split("__COUNT__").join(String(count));
769
+ }
738
770
 
739
771
  // ../render/src/dropdown/bind.ts
740
772
  var import_core2 = require("@lime-bundles/core");
@@ -1191,7 +1223,7 @@ function renderFixedBundle(container, bundle, outOfStockBehavior, onAddToCart, o
1191
1223
  shell.cta.disabled = true;
1192
1224
  const label = shell.cta.querySelector("[data-cta-label]");
1193
1225
  if (label) {
1194
- label.textContent = (t.itemsOutOfStock || "__COUNT__ items out of stock").split("__COUNT__").join(String(oosProducts.length));
1226
+ label.textContent = formatItemsOutOfStock(t, oosProducts.length);
1195
1227
  }
1196
1228
  } else {
1197
1229
  shell.cta.addEventListener("click", () => {
@@ -2556,17 +2588,17 @@ function buildPickerModal(opts) {
2556
2588
  }
2557
2589
 
2558
2590
  // src/renderers/mix-match.ts
2559
- function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart, onCleanup) {
2591
+ function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart, onCleanup, currentProductHandle) {
2560
2592
  const wc = bundle.widgetConfig;
2561
2593
  const t = DEFAULT_STRINGS;
2562
2594
  const requiredQty = bundle.minQuantity || 1;
2563
2595
  const rulesMap = mergeRuleMaps(bundle.productRules, bundle.variantRules);
2564
2596
  const adapted = adaptProducts(bundle);
2565
- const eligibleProducts = adapted.map((p) => ({
2597
+ const eligibleProducts = adapted.map((p, i) => ({
2566
2598
  id: Number(p.productId),
2567
2599
  title: p.title ?? "",
2568
2600
  url: p.url ?? null,
2569
- type: "",
2601
+ type: bundle.products[i]?.productType ?? "",
2570
2602
  featuredImage: p.featuredImage ?? null,
2571
2603
  available: p.variants.some((v) => v.available),
2572
2604
  optionNames: p.optionNames,
@@ -2611,16 +2643,21 @@ function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart
2611
2643
  const overlay = buildPickerModal({
2612
2644
  bundleGid: bundle.id,
2613
2645
  domId: bundle.id.replace(/\D/g, ""),
2614
- showSearch: true,
2615
- showTypeFilters: false,
2646
+ showSearch: wc.showSearch !== false,
2647
+ showTypeFilters: wc.mixMatchShowTypeFilters !== false,
2616
2648
  segmentCount: requiredQty,
2617
2649
  wizard: false
2618
2650
  });
2619
2651
  shell.root.appendChild(overlay);
2652
+ const currentIdx = currentProductHandle ? bundle.products.findIndex((p) => p.handle === currentProductHandle) : -1;
2620
2653
  const selectedItems = seedSelection(
2621
2654
  eligibleProducts,
2622
2655
  rulesMap,
2623
- requiredQty
2656
+ requiredQty,
2657
+ {
2658
+ enabled: !!currentProductHandle,
2659
+ currentProductId: currentIdx !== -1 ? eligibleProducts[currentIdx]?.id ?? null : null
2660
+ }
2624
2661
  );
2625
2662
  const rowCapacity = {
2626
2663
  remainingSpots: () => remainingUnits(selectedItems, requiredQty),
@@ -2657,7 +2694,13 @@ function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart
2657
2694
  shell.root,
2658
2695
  selectedItems,
2659
2696
  requiredQty,
2660
- bundle,
2697
+ // The shared updater reads the theme data-block shape: discount fields
2698
+ // flat, not nested under discountConfig. Passing the bundle itself left
2699
+ // `discountType` undefined, so the summary never painted.
2700
+ {
2701
+ discountType: bundle.discountConfig.discountType,
2702
+ discountValue: bundle.discountConfig.discountValue
2703
+ },
2661
2704
  (total, type, value) => (0, import_core3.calculateDiscount)(total, type, value),
2662
2705
  updatePricing2(formatMoney)
2663
2706
  );
@@ -2670,7 +2713,7 @@ function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart
2670
2713
  rulesMap,
2671
2714
  eligibleProducts,
2672
2715
  requiredQty,
2673
- showQtySelector: true,
2716
+ showQtySelector: wc.mixMatchShowQuantitySelector !== false,
2674
2717
  items: selectedItems,
2675
2718
  formatMoney,
2676
2719
  rowCapacity,
@@ -2733,6 +2776,9 @@ function renderMixMatchBundle(container, bundle, outOfStockBehavior, onAddToCart
2733
2776
  onCleanup?.(bindDropdowns(shell.root));
2734
2777
  }
2735
2778
 
2779
+ // src/renderers/volume.ts
2780
+ var import_core4 = require("@lime-bundles/core");
2781
+
2736
2782
  // ../render/src/volume/pricing.ts
2737
2783
  function calcTierPrice(basePrice, discountType, tierEl) {
2738
2784
  if (discountType === "fixed_amount") {
@@ -2761,6 +2807,23 @@ function formatItemCount(qty, translations) {
2761
2807
  const tpl = translations.itemCount[form] || translations.itemCount.other || "__COUNT__";
2762
2808
  return " (" + tpl.split("__COUNT__").join(String(qty)) + ")";
2763
2809
  }
2810
+ function updateAllTierPrices(allTiers, basePrice, discountType, formatMoney) {
2811
+ for (let i = 0; i < allTiers.length; i++) {
2812
+ const tierEl = allTiers[i];
2813
+ const priceEachEl = tierEl.querySelector(
2814
+ "[data-tier-price-each]"
2815
+ );
2816
+ if (priceEachEl) {
2817
+ priceEachEl.textContent = formatMoney(
2818
+ calcTierPrice(basePrice, discountType, tierEl)
2819
+ );
2820
+ }
2821
+ const compareEl = tierEl.querySelector(
2822
+ ".lb-volume__tier-compare"
2823
+ );
2824
+ if (compareEl) compareEl.textContent = formatMoney(basePrice);
2825
+ }
2826
+ }
2764
2827
  function selectTier(container, allTiers, basePrice, discountType, index, translations, formatMoney) {
2765
2828
  for (let i = 0; i < allTiers.length; i++) {
2766
2829
  allTiers[i].setAttribute("aria-checked", i === index ? "true" : "false");
@@ -2818,8 +2881,17 @@ function renderVolumeBundle(container, bundle, outOfStockBehavior, onAddToCart)
2818
2881
  const discountType = bundle.discountConfig.discountType;
2819
2882
  const formatMoney = intlFormatMoney(product.priceRange.minVariantPrice.currencyCode);
2820
2883
  const t = DEFAULT_STRINGS;
2821
- const popularIdx = wc.popularBadge?.visible ? Math.floor((bundle.volumeTiers.length - 1) / 2) : -1;
2822
- const defaultIdx = 0;
2884
+ const bestIdx = (0, import_core4.bestValueTierIndex)(bundle.volumeTiers, discountType);
2885
+ const defaultIdx = (0, import_core4.resolveDefaultTierIndex)(
2886
+ wc.defaultTier,
2887
+ bundle.volumeTiers.length,
2888
+ bestIdx
2889
+ );
2890
+ const popularIdx = (0, import_core4.resolvePopularTierIndex)(
2891
+ wc.popularBadge?.tierIndex,
2892
+ wc.popularBadge?.visible !== false,
2893
+ bestIdx
2894
+ );
2823
2895
  const tiers = bundle.volumeTiers.map((tier, i) => {
2824
2896
  const percent = discountType === "fixed_amount" ? 0 : Math.round(tier.percentage ?? 0);
2825
2897
  const amountCents = discountType === "fixed_amount" ? Math.round((tier.amount ?? 0) * 100) : 0;
@@ -2840,16 +2912,21 @@ function renderVolumeBundle(container, bundle, outOfStockBehavior, onAddToCart)
2840
2912
  title: bundle.title,
2841
2913
  subtitle: bundle.description,
2842
2914
  summaryLabel: "Total",
2843
- withItemCount: true,
2915
+ withItemCount: wc.pricing?.showItemCount !== false,
2844
2916
  totalPriceSlot: true,
2845
2917
  showSavingsBar: wc.savingsBar?.visible !== false,
2846
2918
  showComparePrice: wc.pricing?.showCompareAtPrice !== false,
2847
2919
  ctaText: wc.cta?.ctaText || t.addToCart || "Add to cart",
2848
2920
  endsAt: wc.countdown?.showCountdown === false ? null : bundle.endsAt
2849
2921
  });
2850
- const group = buildVolumeTiers(tiers, "Select quantity");
2922
+ const group = buildVolumeTiers(tiers, "Select quantity", {
2923
+ showCompare: wc.pricing?.showComparePrice !== false,
2924
+ showPriceEach: wc.pricing?.showPerUnitPrice !== false,
2925
+ unitLabel: (t.each || "each").trim()
2926
+ });
2851
2927
  shell.products.replaceWith(group);
2852
2928
  const tierEls = group.querySelectorAll("[data-tier-index]");
2929
+ updateAllTierPrices(tierEls, basePrice, discountType, formatMoney);
2853
2930
  let selectedIndex = defaultIdx;
2854
2931
  const apply = () => selectTier(shell.root, tierEls, basePrice, discountType, selectedIndex, t, formatMoney);
2855
2932
  group.addEventListener("click", (e) => {
@@ -2999,6 +3076,13 @@ function buildBogoCartItems(sides, percent, buyQty, getQty) {
2999
3076
  }
3000
3077
 
3001
3078
  // src/renderers/bogo.ts
3079
+ function buildBogoPlus() {
3080
+ const plus = document.createElement("div");
3081
+ plus.className = "lb-bogo__plus";
3082
+ plus.setAttribute("aria-hidden", "true");
3083
+ plus.innerHTML = '<svg width="12" height="12" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><line x1="9" y1="3" x2="9" y2="15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="3" y1="9" x2="15" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>';
3084
+ return plus;
3085
+ }
3002
3086
  function renderBogoBundle(container, bundle, outOfStockBehavior, onAddToCart, onCleanup) {
3003
3087
  const wc = bundle.widgetConfig;
3004
3088
  const buyProduct = bundle.products.find((p) => p.id === bundle.buyProductId);
@@ -3047,6 +3131,7 @@ function renderBogoBundle(container, bundle, outOfStockBehavior, onAddToCart, on
3047
3131
  const badgeText = percent >= 100 ? "Free" : percent + "% off";
3048
3132
  sides.forEach((side, i) => {
3049
3133
  const isGet = i === 1;
3134
+ if (isGet) shell.products.appendChild(buildBogoPlus());
3050
3135
  const isOos = !side.variants.some((v) => v.available);
3051
3136
  const row = buildRowSkeleton(side, t, {
3052
3137
  isOos,
@@ -3075,7 +3160,7 @@ function renderBogoBundle(container, bundle, outOfStockBehavior, onAddToCart, on
3075
3160
  shell.cta.disabled = true;
3076
3161
  const label = shell.cta.querySelector("[data-cta-label]");
3077
3162
  if (label) {
3078
- label.textContent = (t.itemsOutOfStock || "__COUNT__ items out of stock").split("__COUNT__").join(String(oosCount));
3163
+ label.textContent = formatItemsOutOfStock(t, oosCount);
3079
3164
  }
3080
3165
  } else {
3081
3166
  shell.cta.addEventListener("click", () => {
@@ -3095,7 +3180,7 @@ function renderBogoBundle(container, bundle, outOfStockBehavior, onAddToCart, on
3095
3180
  }
3096
3181
 
3097
3182
  // src/renderers/multi-step.ts
3098
- var import_core4 = require("@lime-bundles/core");
3183
+ var import_core5 = require("@lime-bundles/core");
3099
3184
 
3100
3185
  // ../render/src/multi-step/i18n.ts
3101
3186
  function stepOfLabel(i, total, t) {
@@ -3724,7 +3809,7 @@ function renderMultiStepBundle(container, bundle, outOfStockBehavior, onAddToCar
3724
3809
  variantQuantities: bundle.variantQuantities
3725
3810
  };
3726
3811
  const pools = steps.map(
3727
- (_, i) => (0, import_core4.productsForStep)(bundle, i).map((product) => {
3812
+ (_, i) => (0, import_core5.productsForStep)(bundle, i).map((product) => {
3728
3813
  const p = adaptProduct(product, bundle.id, {
3729
3814
  quantities,
3730
3815
  allowedVariantIds: null
@@ -3798,8 +3883,8 @@ function renderMultiStepBundle(container, bundle, outOfStockBehavior, onAddToCar
3798
3883
  const overlay = buildPickerModal({
3799
3884
  bundleGid: bundle.id,
3800
3885
  domId: bundle.id.replace(/\D/g, ""),
3801
- showSearch: true,
3802
- showTypeFilters: true,
3886
+ showSearch: wc.showSearch !== false,
3887
+ showTypeFilters: wc.mixMatchShowTypeFilters !== false,
3803
3888
  segmentCount: null,
3804
3889
  wizard: true
3805
3890
  });
@@ -3834,7 +3919,7 @@ function renderMultiStepBundle(container, bundle, outOfStockBehavior, onAddToCar
3834
3919
  discountType: bundle.discountConfig.discountType,
3835
3920
  discountValue: bundle.discountConfig.discountValue
3836
3921
  },
3837
- (total, type, value) => (0, import_core4.calculateDiscount)(total, type, value),
3922
+ (total, type, value) => (0, import_core5.calculateDiscount)(total, type, value),
3838
3923
  updatePricing2(formatMoney)
3839
3924
  );
3840
3925
  updateProgress2(bodyDeps);
@@ -3846,8 +3931,8 @@ function renderMultiStepBundle(container, bundle, outOfStockBehavior, onAddToCar
3846
3931
  selections,
3847
3932
  t,
3848
3933
  rulesMap,
3849
- showQtySelector: true,
3850
- showTypeFilters: true,
3934
+ showQtySelector: wc.mixMatchShowQuantitySelector !== false,
3935
+ showTypeFilters: wc.mixMatchShowTypeFilters !== false,
3851
3936
  formatMoney,
3852
3937
  dropdown: void 0,
3853
3938
  portalTarget: null,
@@ -3897,7 +3982,7 @@ function renderMultiStepBundle(container, bundle, outOfStockBehavior, onAddToCar
3897
3982
  }
3898
3983
 
3899
3984
  // src/lime-bundle.ts
3900
- var import_core6 = require("@lime-bundles/core");
3985
+ var import_core7 = require("@lime-bundles/core");
3901
3986
 
3902
3987
  // src/utils/input-mode.ts
3903
3988
  var NAV_KEYS = /* @__PURE__ */ new Set([
@@ -6553,6 +6638,13 @@ var LimeBundleElement = class extends HTMLElement {
6553
6638
  buyer = void 0;
6554
6639
  shadow;
6555
6640
  bundles = [];
6641
+ /**
6642
+ * The product handle this embed is standing on, when one can be resolved
6643
+ * (attribute → meta tag → /products/<handle> path). The mix-and-match
6644
+ * courtesy seed uses it to pre-add the current product the way the theme
6645
+ * widget does; null on pages with no product context.
6646
+ */
6647
+ currentProductHandle = null;
6556
6648
  abortController = null;
6557
6649
  impressionCleanups = [];
6558
6650
  /**
@@ -6572,7 +6664,7 @@ var LimeBundleElement = class extends HTMLElement {
6572
6664
  * before the first render. Defaults to `show_greyed_out` so a failed or
6573
6665
  * missing fetch degrades to "bundles still render".
6574
6666
  */
6575
- outOfStockBehavior = import_core5.DEFAULT_OUT_OF_STOCK_BEHAVIOR;
6667
+ outOfStockBehavior = import_core6.DEFAULT_OUT_OF_STOCK_BEHAVIOR;
6576
6668
  constructor() {
6577
6669
  super();
6578
6670
  this.shadow = this.attachShadow({ mode: "open" });
@@ -6634,13 +6726,13 @@ var LimeBundleElement = class extends HTMLElement {
6634
6726
  * no buyer/country/language is set.
6635
6727
  */
6636
6728
  wrapQueryForContext(query) {
6637
- return (0, import_core5.hasInContext)({
6729
+ return (0, import_core6.hasInContext)({
6638
6730
  shopDomain: this.shopDomain,
6639
6731
  accessToken: this.storefrontToken,
6640
6732
  country: this.country,
6641
6733
  language: this.language,
6642
6734
  buyer: this.buyer
6643
- }) ? (0, import_core5.withInContext)(query) : query;
6735
+ }) ? (0, import_core6.withInContext)(query) : query;
6644
6736
  }
6645
6737
  /**
6646
6738
  * Returns true if this bundle should be hidden for the current market.
@@ -6649,7 +6741,7 @@ var LimeBundleElement = class extends HTMLElement {
6649
6741
  * marketIds doesn't include the configured market.
6650
6742
  */
6651
6743
  isMarketHidden(bundle) {
6652
- return !(0, import_core5.isVisibleInMarket)(bundle, this.marketId);
6744
+ return !(0, import_core6.isVisibleInMarket)(bundle, this.marketId);
6653
6745
  }
6654
6746
  async fetchBundle() {
6655
6747
  if (!this.shopDomain || !this.storefrontToken) {
@@ -6662,7 +6754,7 @@ var LimeBundleElement = class extends HTMLElement {
6662
6754
  const controller = new AbortController();
6663
6755
  this.abortController = controller;
6664
6756
  this.renderLoading();
6665
- const client = (0, import_core5.createStorefrontClient)({
6757
+ const client = (0, import_core6.createStorefrontClient)({
6666
6758
  shopDomain: this.shopDomain,
6667
6759
  accessToken: this.storefrontToken,
6668
6760
  country: this.country,
@@ -6672,11 +6764,12 @@ var LimeBundleElement = class extends HTMLElement {
6672
6764
  try {
6673
6765
  let bundlePromise;
6674
6766
  let singleBundleMode = false;
6767
+ this.currentProductHandle = resolveProductHandle(this.productHandleAttr);
6675
6768
  if (this.bundleGid) {
6676
6769
  singleBundleMode = true;
6677
6770
  bundlePromise = this.fetchSingleBundle(client, controller.signal);
6678
6771
  } else {
6679
- const handle = resolveProductHandle(this.productHandleAttr);
6772
+ const handle = this.currentProductHandle;
6680
6773
  if (!handle) {
6681
6774
  this.teardownImpressions();
6682
6775
  this.renderError(
@@ -6690,7 +6783,7 @@ var LimeBundleElement = class extends HTMLElement {
6690
6783
  handle
6691
6784
  );
6692
6785
  }
6693
- const shopSettingsPromise = client.query(import_core5.SHOP_SETTINGS_QUERY, void 0, {
6786
+ const shopSettingsPromise = client.query(import_core6.SHOP_SETTINGS_QUERY, void 0, {
6694
6787
  signal: controller.signal
6695
6788
  }).catch(() => null);
6696
6789
  await bundlePromise;
@@ -6703,11 +6796,11 @@ var LimeBundleElement = class extends HTMLElement {
6703
6796
  const shopSettings = await shopSettingsPromise;
6704
6797
  const customCss = shopSettings?.shop?.customCss?.value;
6705
6798
  if (customCss) {
6706
- (0, import_core5.injectCustomCss)(this.shopDomain, customCss);
6707
- const sanitized = (0, import_core5.sanitizeCustomCss)(customCss);
6799
+ (0, import_core6.injectCustomCss)(this.shopDomain, customCss);
6800
+ const sanitized = (0, import_core6.sanitizeCustomCss)(customCss);
6708
6801
  if (sanitized.ok) this.shopCustomCss = sanitized.css;
6709
6802
  }
6710
- this.outOfStockBehavior = (0, import_core5.parseOutOfStockBehavior)(
6803
+ this.outOfStockBehavior = (0, import_core6.parseOutOfStockBehavior)(
6711
6804
  shopSettings?.shop?.outOfStockBehavior?.value
6712
6805
  );
6713
6806
  this.renderBundles();
@@ -6722,7 +6815,7 @@ var LimeBundleElement = class extends HTMLElement {
6722
6815
  }
6723
6816
  async fetchSingleBundle(client, signal) {
6724
6817
  const data = await client.query(
6725
- this.wrapQueryForContext(import_core5.BUNDLE_METAOBJECT_QUERY),
6818
+ this.wrapQueryForContext(import_core6.BUNDLE_METAOBJECT_QUERY),
6726
6819
  { id: this.bundleGid },
6727
6820
  { signal }
6728
6821
  );
@@ -6730,7 +6823,7 @@ var LimeBundleElement = class extends HTMLElement {
6730
6823
  this.bundles = [];
6731
6824
  return;
6732
6825
  }
6733
- const parsed = (0, import_core5.parseMetaobjectBundle)(
6826
+ const parsed = (0, import_core6.parseMetaobjectBundle)(
6734
6827
  data.metaobject.id,
6735
6828
  data.metaobject.fields
6736
6829
  );
@@ -6738,7 +6831,7 @@ var LimeBundleElement = class extends HTMLElement {
6738
6831
  }
6739
6832
  async fetchProductBundles(client, signal, productHandle) {
6740
6833
  const data = await client.query(
6741
- this.wrapQueryForContext(import_core5.BUNDLES_FOR_PRODUCT_QUERY),
6834
+ this.wrapQueryForContext(import_core6.BUNDLES_FOR_PRODUCT_QUERY),
6742
6835
  { handle: productHandle },
6743
6836
  { signal }
6744
6837
  );
@@ -6749,10 +6842,10 @@ var LimeBundleElement = class extends HTMLElement {
6749
6842
  const refs = data.product.metafield?.references?.nodes ?? [];
6750
6843
  const bundles = [];
6751
6844
  for (const ref of refs) {
6752
- const parsed = (0, import_core5.parseMetaobjectBundle)(ref.id, ref.fields);
6845
+ const parsed = (0, import_core6.parseMetaobjectBundle)(ref.id, ref.fields);
6753
6846
  if (parsed && !this.isMarketHidden(parsed)) bundles.push(parsed);
6754
6847
  }
6755
- this.bundles = (0, import_core5.resolveAbTests)(bundles, (0, import_core5.getVisitorId)());
6848
+ this.bundles = (0, import_core6.resolveAbTests)(bundles, (0, import_core6.getVisitorId)());
6756
6849
  }
6757
6850
  /**
6758
6851
  * Dispatch add-to-cart with a cancelable event, then — unless a listener
@@ -6782,7 +6875,7 @@ var LimeBundleElement = class extends HTMLElement {
6782
6875
  */
6783
6876
  async defaultAddToCart(lines) {
6784
6877
  if (typeof window === "undefined") return;
6785
- const client = (0, import_core5.createStorefrontClient)({
6878
+ const client = (0, import_core6.createStorefrontClient)({
6786
6879
  shopDomain: this.shopDomain,
6787
6880
  accessToken: this.storefrontToken
6788
6881
  });
@@ -6793,7 +6886,7 @@ var LimeBundleElement = class extends HTMLElement {
6793
6886
  let checkoutUrl = null;
6794
6887
  if (existingCartId) {
6795
6888
  const res = await client.query(
6796
- import_core5.CART_LINES_ADD_MUTATION,
6889
+ import_core6.CART_LINES_ADD_MUTATION,
6797
6890
  { cartId: existingCartId, lines }
6798
6891
  );
6799
6892
  const payload = res.cartLinesAdd;
@@ -6805,7 +6898,7 @@ var LimeBundleElement = class extends HTMLElement {
6805
6898
  }
6806
6899
  if (!checkoutUrl) {
6807
6900
  const res = await client.query(
6808
- import_core5.CART_CREATE_MUTATION,
6901
+ import_core6.CART_CREATE_MUTATION,
6809
6902
  { input: { lines } }
6810
6903
  );
6811
6904
  const payload = res.cartCreate;
@@ -6851,7 +6944,7 @@ var LimeBundleElement = class extends HTMLElement {
6851
6944
  const price = variant ? parseFloat(variant.price.amount) : 0;
6852
6945
  return sum + price * line.quantity;
6853
6946
  }, 0);
6854
- (0, import_core5.reportAddToCart)(
6947
+ (0, import_core6.reportAddToCart)(
6855
6948
  { shopDomain: this.shopDomain, appUrl: this.appUrl },
6856
6949
  {
6857
6950
  bundleGid: bundle.id,
@@ -6890,7 +6983,8 @@ var LimeBundleElement = class extends HTMLElement {
6890
6983
  container.setAttribute("aria-label", bundle.title);
6891
6984
  container.setAttribute("data-bundle-type", bundle.bundleType);
6892
6985
  container.setAttribute("data-bundle-gid", bundle.id);
6893
- (0, import_core6.applyWidgetConfigVars)(container, bundle.widgetConfig);
6986
+ if (bundle.endsAt) container.setAttribute("data-ends-at", bundle.endsAt);
6987
+ (0, import_core7.applyWidgetConfigVars)(container, bundle.widgetConfig);
6894
6988
  this.renderCleanups.push(trackInputMode(container));
6895
6989
  const dispatch = (lines) => this.handleAddToCart(bundle, lines);
6896
6990
  const registerCleanup = (fn) => this.renderCleanups.push(fn);
@@ -6910,7 +7004,8 @@ var LimeBundleElement = class extends HTMLElement {
6910
7004
  bundle,
6911
7005
  this.outOfStockBehavior,
6912
7006
  dispatch,
6913
- registerCleanup
7007
+ registerCleanup,
7008
+ this.currentProductHandle
6914
7009
  );
6915
7010
  break;
6916
7011
  case "volume":
@@ -6956,8 +7051,8 @@ var LimeBundleElement = class extends HTMLElement {
6956
7051
  }
6957
7052
  setupImpressionFor(bundle, element) {
6958
7053
  if (!this.analyticsEnabled || !this.appUrl) return;
6959
- const cleanup = (0, import_core5.observeImpression)(element, () => {
6960
- (0, import_core5.reportImpression)(
7054
+ const cleanup = (0, import_core6.observeImpression)(element, () => {
7055
+ (0, import_core6.reportImpression)(
6961
7056
  { shopDomain: this.shopDomain, appUrl: this.appUrl },
6962
7057
  {
6963
7058
  bundleGid: bundle.id,
package/dist/index.d.cts CHANGED
@@ -61,6 +61,13 @@ declare class LimeBundleElement extends HTMLElement {
61
61
  buyer: BuyerResolver | undefined;
62
62
  private shadow;
63
63
  private bundles;
64
+ /**
65
+ * The product handle this embed is standing on, when one can be resolved
66
+ * (attribute → meta tag → /products/<handle> path). The mix-and-match
67
+ * courtesy seed uses it to pre-add the current product the way the theme
68
+ * widget does; null on pages with no product context.
69
+ */
70
+ private currentProductHandle;
64
71
  private abortController;
65
72
  private impressionCleanups;
66
73
  /**
package/dist/index.d.ts CHANGED
@@ -61,6 +61,13 @@ declare class LimeBundleElement extends HTMLElement {
61
61
  buyer: BuyerResolver | undefined;
62
62
  private shadow;
63
63
  private bundles;
64
+ /**
65
+ * The product handle this embed is standing on, when one can be resolved
66
+ * (attribute → meta tag → /products/<handle> path). The mix-and-match
67
+ * courtesy seed uses it to pre-add the current product the way the theme
68
+ * widget does; null on pages with no product context.
69
+ */
70
+ private currentProductHandle;
64
71
  private abortController;
65
72
  private impressionCleanups;
66
73
  /**