@lime-bundles/react 2.4.0 → 3.0.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.js CHANGED
@@ -4,7 +4,9 @@ import {
4
4
  formatMoney,
5
5
  formatUnitPrice,
6
6
  calculateDiscount,
7
- resolveBundleQty
7
+ resolveBundleQty,
8
+ isVariantFulfillable,
9
+ shouldShowLowStockBadge
8
10
  } from "@lime-bundles/core";
9
11
 
10
12
  // src/hooks/useBundleData.ts
@@ -302,6 +304,20 @@ var { computePosition, emptyTypeAheadState, handleKey, pushTypeAheadChar } = dro
302
304
  var ITEM_HEIGHT_PX = 32;
303
305
  var LIST_PAD_Y = 8;
304
306
  var MAX_VISIBLE_ITEMS = 8;
307
+ function findScrollableAncestor(el) {
308
+ const win = el.ownerDocument?.defaultView;
309
+ if (!win) return null;
310
+ let cur = el.parentElement;
311
+ while (cur && cur !== el.ownerDocument.body) {
312
+ const style = win.getComputedStyle(cur);
313
+ const overflowY = style.overflowY;
314
+ if (overflowY === "auto" || overflowY === "scroll" || overflowY === "hidden") {
315
+ return cur;
316
+ }
317
+ cur = cur.parentElement;
318
+ }
319
+ return null;
320
+ }
305
321
  function VariantDropdown({
306
322
  options,
307
323
  value,
@@ -351,6 +367,11 @@ function VariantDropdown({
351
367
  if (rect.width === 0) return;
352
368
  const visibleCount = Math.min(options.length || 1, MAX_VISIBLE_ITEMS);
353
369
  const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
370
+ const scrollable = findScrollableAncestor(trigger);
371
+ const clip = scrollable ? (() => {
372
+ const r = scrollable.getBoundingClientRect();
373
+ return { top: r.top, bottom: r.bottom };
374
+ })() : void 0;
354
375
  setPosition(
355
376
  computePosition({
356
377
  trigger: {
@@ -360,7 +381,8 @@ function VariantDropdown({
360
381
  width: rect.width
361
382
  },
362
383
  viewportHeight: window.innerHeight,
363
- desiredHeight
384
+ desiredHeight,
385
+ clip
364
386
  })
365
387
  );
366
388
  }, [isOpen, options.length]);
@@ -594,8 +616,14 @@ function FixedBundle(props) {
594
616
  }, [result, onError]);
595
617
  const handleAddToCart = useCallback4(async () => {
596
618
  if (!bundle) return;
597
- const bundleLines = bundle.products.filter((p) => p.variants.nodes.some((v) => v.availableForSale)).map((p) => {
598
- const variant = selectedVariants[p.id] ?? p.variants.nodes.find((v) => v.availableForSale);
619
+ const bundleLines = bundle.products.filter(
620
+ (p) => p.variants.nodes.some(
621
+ (v) => isVariantFulfillable(v, resolveBundleQty(bundle, p.id, v.id))
622
+ )
623
+ ).map((p) => {
624
+ const variant = selectedVariants[p.id] ?? p.variants.nodes.find(
625
+ (v) => isVariantFulfillable(v, resolveBundleQty(bundle, p.id, v.id))
626
+ );
599
627
  return {
600
628
  productId: p.id,
601
629
  variant,
@@ -665,6 +693,7 @@ function FixedBundle(props) {
665
693
  /* @__PURE__ */ jsx2("div", { className: "lb-bundle__products", children: bundle.products.map((product) => /* @__PURE__ */ jsx2(
666
694
  FixedProductRow,
667
695
  {
696
+ bundle,
668
697
  product,
669
698
  currency,
670
699
  onVariantChange: handleVariantChange
@@ -686,7 +715,7 @@ function FixedBundle(props) {
686
715
  }
687
716
  );
688
717
  }
689
- function FixedProductRow({ product, currency, onVariantChange }) {
718
+ function FixedProductRow({ bundle, product, currency, onVariantChange }) {
690
719
  const variants = product.variants.nodes;
691
720
  const optionNames = useMemo4(() => {
692
721
  const first = variants[0];
@@ -700,7 +729,9 @@ function FixedProductRow({ product, currency, onVariantChange }) {
700
729
  useEffect6(() => {
701
730
  onVariantChange(product.id, selectedVariant ?? null);
702
731
  }, [selectedVariant, product.id, onVariantChange]);
703
- const displayVariant = selectedVariant ?? variants.find((v) => v.availableForSale) ?? variants[0];
732
+ const displayVariant = selectedVariant ?? variants.find(
733
+ (v) => isVariantFulfillable(v, resolveBundleQty(bundle, product.id, v.id))
734
+ ) ?? variants[0];
704
735
  const priceText = displayVariant ? formatMoney(displayVariant.price.amount, currency) : formatMoney(product.priceRange.minVariantPrice.amount, currency);
705
736
  const unitPriceText = displayVariant ? formatUnitPrice(
706
737
  displayVariant.unitPrice,
@@ -708,6 +739,12 @@ function FixedProductRow({ product, currency, onVariantChange }) {
708
739
  currency
709
740
  ) : null;
710
741
  const thumbImage = displayVariant?.image ?? product.featuredImage;
742
+ const showLowStock = !!displayVariant && shouldShowLowStockBadge(
743
+ displayVariant,
744
+ resolveBundleQty(bundle, product.id, displayVariant.id),
745
+ bundle.widgetConfig.lowStockThreshold,
746
+ bundle.widgetConfig.showLowStockBadge
747
+ );
711
748
  return /* @__PURE__ */ jsxs2("div", { className: "lb-bundle__product", part: "product", children: [
712
749
  thumbImage && /* @__PURE__ */ jsx2(
713
750
  "img",
@@ -722,6 +759,11 @@ function FixedProductRow({ product, currency, onVariantChange }) {
722
759
  /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-title", children: product.title }),
723
760
  /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-price", children: priceText }),
724
761
  unitPriceText && /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
762
+ showLowStock && /* @__PURE__ */ jsxs2("span", { className: "lb-bundle-low-stock-badge", children: [
763
+ "Only ",
764
+ displayVariant.quantityAvailable,
765
+ " left"
766
+ ] }),
725
767
  showPicker && /* @__PURE__ */ jsx2("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
726
768
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
727
769
  value: o.value,
@@ -748,10 +790,15 @@ import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMem
748
790
  import {
749
791
  formatMoney as formatMoney2,
750
792
  formatUnitPrice as formatUnitPrice2,
751
- validateQuantity,
752
- resolveBundleQty as resolveBundleQty2
793
+ isVariantFulfillable as isVariantFulfillable2,
794
+ shouldShowLowStockBadge as shouldShowLowStockBadge2,
795
+ maxAddableQuantity,
796
+ DEFAULT_PRODUCT_RULE
753
797
  } from "@lime-bundles/core";
754
798
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
799
+ function ruleFor(bundle, productId) {
800
+ return bundle.productRules[productId] ?? DEFAULT_PRODUCT_RULE;
801
+ }
755
802
  function MixMatchBundle(props) {
756
803
  const {
757
804
  shopDomain,
@@ -795,56 +842,92 @@ function MixMatchBundle(props) {
795
842
  );
796
843
  }
797
844
  }, [result, onError]);
798
- const totalQuantity = Array.from(selections.values()).reduce(
799
- (sum, s) => sum + s.quantity,
800
- 0
845
+ const showStepper = bundle?.widgetConfig.mixMatchShowQuantitySelector !== false;
846
+ const distinctSelectedProducts = useMemo5(() => {
847
+ const ids = /* @__PURE__ */ new Set();
848
+ for (const sel of selections.values()) {
849
+ if (sel.quantity > 0) ids.add(sel.productId);
850
+ }
851
+ return ids;
852
+ }, [selections]);
853
+ const totalQuantity = useMemo5(
854
+ () => Array.from(selections.values()).reduce((sum, s) => sum + s.quantity, 0),
855
+ [selections]
801
856
  );
802
- const validation = bundle ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity) : { valid: false, totalQuantity: 0, message: null };
803
- const toggleProduct = useCallback5(
857
+ const requiredPicks = bundle?.minQuantity ?? 0;
858
+ const meetsMinPicks = distinctSelectedProducts.size >= requiredPicks;
859
+ const valid = !!bundle && meetsMinPicks;
860
+ const remaining = Math.max(0, requiredPicks - distinctSelectedProducts.size);
861
+ const validationMessage = valid ? null : remaining > 0 ? `Pick ${remaining} more product${remaining === 1 ? "" : "s"}` : null;
862
+ const selectVariant = useCallback5(
804
863
  (productId, variant) => {
864
+ if (!bundle) return;
865
+ const rule = ruleFor(bundle, productId);
866
+ const maxAddable = maxAddableQuantity(variant, rule.max, 0);
867
+ if (maxAddable < rule.min) return;
805
868
  setSelections((prev) => {
806
869
  const next = new Map(prev);
807
- const key = `${productId}:${variant.id}`;
808
- if (next.has(key)) {
809
- next.delete(key);
810
- } else {
811
- const configuredQty = bundle ? resolveBundleQty2(bundle, productId, variant.id) : 1;
812
- next.set(key, {
813
- productId,
814
- variantId: variant.id,
815
- quantity: configuredQty
816
- });
817
- }
870
+ next.set(`${productId}:${variant.id}`, {
871
+ productId,
872
+ variantId: variant.id,
873
+ quantity: rule.min
874
+ });
818
875
  return next;
819
876
  });
820
877
  },
821
878
  [bundle]
822
879
  );
823
- const updateQuantity = useCallback5(
824
- (productId, variantId, quantity) => {
880
+ const deselect = useCallback5((productId, variantId) => {
881
+ setSelections((prev) => {
882
+ if (!prev.has(`${productId}:${variantId}`)) return prev;
883
+ const next = new Map(prev);
884
+ next.delete(`${productId}:${variantId}`);
885
+ return next;
886
+ });
887
+ }, []);
888
+ const setQuantity = useCallback5(
889
+ (productId, variant, quantity) => {
890
+ if (!bundle) return;
891
+ const rule = ruleFor(bundle, productId);
892
+ const maxAddable = maxAddableQuantity(variant, rule.max, 0);
893
+ const key = `${productId}:${variant.id}`;
825
894
  setSelections((prev) => {
826
895
  const next = new Map(prev);
827
- const key = `${productId}:${variantId}`;
828
- if (quantity <= 0) {
896
+ if (quantity < rule.min || maxAddable < rule.min) {
829
897
  next.delete(key);
830
- } else {
831
- next.set(key, { productId, variantId, quantity });
898
+ return next;
832
899
  }
900
+ const clamped = Math.min(quantity, maxAddable);
901
+ next.set(key, {
902
+ productId,
903
+ variantId: variant.id,
904
+ quantity: clamped
905
+ });
833
906
  return next;
834
907
  });
835
908
  },
836
- []
909
+ [bundle]
837
910
  );
838
911
  const handleAddToCart = useCallback5(async () => {
839
- if (!bundle || !validation.valid) return;
840
- const lines = Array.from(selections.values()).map((s) => ({
841
- merchandiseId: s.variantId,
842
- quantity: s.quantity,
843
- attributes: [
844
- { key: "_lime_bundle_gid", value: bundle.id },
845
- { key: "_lime_bundle_type", value: bundle.bundleType }
846
- ]
847
- }));
912
+ if (!bundle || !valid) return;
913
+ const byVariant = /* @__PURE__ */ new Map();
914
+ for (const sel of selections.values()) {
915
+ if (sel.quantity <= 0) continue;
916
+ byVariant.set(
917
+ sel.variantId,
918
+ (byVariant.get(sel.variantId) ?? 0) + sel.quantity
919
+ );
920
+ }
921
+ const lines = Array.from(byVariant.entries()).map(
922
+ ([variantId, quantity]) => ({
923
+ merchandiseId: variantId,
924
+ quantity,
925
+ attributes: [
926
+ { key: "_lime_bundle_gid", value: bundle.id },
927
+ { key: "_lime_bundle_type", value: bundle.bundleType }
928
+ ]
929
+ })
930
+ );
848
931
  setAddingToCart(true);
849
932
  setCartError(null);
850
933
  try {
@@ -870,15 +953,7 @@ function MixMatchBundle(props) {
870
953
  } finally {
871
954
  setAddingToCart(false);
872
955
  }
873
- }, [
874
- bundle,
875
- selections,
876
- validation.valid,
877
- onAddToCart,
878
- onError,
879
- trackAddToCart,
880
- totalQuantity
881
- ]);
956
+ }, [bundle, selections, valid, onAddToCart, onError, trackAddToCart, totalQuantity]);
882
957
  if (result.status === "loading") {
883
958
  return /* @__PURE__ */ jsxs3("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
884
959
  /* @__PURE__ */ jsx3("div", { className: "lb-skeleton lb-skeleton--title" }),
@@ -901,26 +976,29 @@ function MixMatchBundle(props) {
901
976
  children: [
902
977
  /* @__PURE__ */ jsx3("h3", { className: "lb-bundle__title", children: bundle.title }),
903
978
  bundle.discountLabel && /* @__PURE__ */ jsx3("span", { className: "lb-bundle__discount-badge", children: bundle.discountLabel }),
904
- /* @__PURE__ */ jsx3("p", { className: "lb-bundle__instructions", children: bundle.minQuantity && bundle.maxQuantity ? `Select ${bundle.minQuantity}\u2013${bundle.maxQuantity} items` : bundle.minQuantity ? `Select at least ${bundle.minQuantity} items` : bundle.maxQuantity ? `Select up to ${bundle.maxQuantity} items` : "Select your items" }),
979
+ /* @__PURE__ */ jsx3("p", { className: "lb-bundle__instructions", children: requiredPicks > 0 ? `Pick ${requiredPicks} product${requiredPicks === 1 ? "" : "s"}` : "Pick your products" }),
905
980
  /* @__PURE__ */ jsx3("div", { className: "lb-bundle__products lb-bundle__products--selectable", children: bundle.products.map((product) => /* @__PURE__ */ jsx3(
906
981
  MixMatchProductRow,
907
982
  {
983
+ bundle,
908
984
  product,
909
985
  currency,
986
+ showStepper,
910
987
  selections,
911
- onToggle: toggleProduct,
912
- onUpdateQuantity: updateQuantity
988
+ onSelect: selectVariant,
989
+ onDeselect: deselect,
990
+ onSetQuantity: setQuantity
913
991
  },
914
992
  product.id
915
993
  )) }),
916
- validation.message && /* @__PURE__ */ jsx3("p", { className: "lb-bundle__validation", role: "status", children: validation.message }),
994
+ validationMessage && /* @__PURE__ */ jsx3("p", { className: "lb-bundle__validation", role: "status", children: validationMessage }),
917
995
  cartError && /* @__PURE__ */ jsx3("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
918
996
  /* @__PURE__ */ jsx3(
919
997
  "button",
920
998
  {
921
999
  className: "lb-bundle__cta",
922
1000
  onClick: handleAddToCart,
923
- disabled: addingToCart || !validation.valid,
1001
+ disabled: addingToCart || !valid,
924
1002
  "aria-busy": addingToCart,
925
1003
  children: addingToCart ? "Adding..." : bundle.widgetConfig.cta.ctaText ?? `Add ${totalQuantity} Items to Cart`
926
1004
  }
@@ -930,11 +1008,14 @@ function MixMatchBundle(props) {
930
1008
  );
931
1009
  }
932
1010
  function MixMatchProductRow({
1011
+ bundle,
933
1012
  product,
934
1013
  currency,
1014
+ showStepper,
935
1015
  selections,
936
- onToggle,
937
- onUpdateQuantity
1016
+ onSelect,
1017
+ onDeselect,
1018
+ onSetQuantity
938
1019
  }) {
939
1020
  const variants = product.variants.nodes;
940
1021
  const optionNames = useMemo5(() => {
@@ -942,9 +1023,12 @@ function MixMatchProductRow({
942
1023
  return first ? first.selectedOptions.map((o) => o.name) : [];
943
1024
  }, [variants]);
944
1025
  const showPicker = variants.length > 1 && optionNames.length > 0;
1026
+ const rule = ruleFor(bundle, product.id);
945
1027
  const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
946
- const displayVariant = selectedVariant ?? variants.find((v) => v.availableForSale) ?? variants[0] ?? null;
1028
+ const displayVariant = selectedVariant ?? variants.find((v) => isVariantFulfillable2(v, rule.min)) ?? variants[0] ?? null;
947
1029
  if (!displayVariant) return null;
1030
+ const fulfillable = isVariantFulfillable2(displayVariant, rule.min);
1031
+ const maxAddable = maxAddableQuantity(displayVariant, rule.max, 0);
948
1032
  const key = `${product.id}:${displayVariant.id}`;
949
1033
  const selected = selections.get(key);
950
1034
  const thumbImage = displayVariant.image ?? product.featuredImage;
@@ -971,6 +1055,16 @@ function MixMatchProductRow({
971
1055
  /* @__PURE__ */ jsx3("p", { className: "lb-bundle__product-title", children: product.title }),
972
1056
  /* @__PURE__ */ jsx3("p", { className: "lb-bundle__product-price", children: formatMoney2(displayVariant.price.amount, currency) }),
973
1057
  unitPriceText && /* @__PURE__ */ jsx3("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
1058
+ shouldShowLowStockBadge2(
1059
+ displayVariant,
1060
+ rule.min,
1061
+ bundle.widgetConfig.lowStockThreshold,
1062
+ bundle.widgetConfig.showLowStockBadge
1063
+ ) && /* @__PURE__ */ jsxs3("span", { className: "lb-bundle-low-stock-badge", children: [
1064
+ "Only ",
1065
+ displayVariant.quantityAvailable,
1066
+ " left"
1067
+ ] }),
974
1068
  showPicker && !selected && /* @__PURE__ */ jsx3("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
975
1069
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
976
1070
  value: o.value,
@@ -989,39 +1083,58 @@ function MixMatchProductRow({
989
1083
  );
990
1084
  }) })
991
1085
  ] }),
992
- /* @__PURE__ */ jsx3("div", { className: "lb-bundle__product-actions", children: selected ? /* @__PURE__ */ jsxs3("div", { className: "lb-bundle__quantity-control", children: [
1086
+ /* @__PURE__ */ jsx3("div", { className: "lb-bundle__product-actions", children: selected ? showStepper ? /* @__PURE__ */ jsxs3("div", { className: "lb-bundle__quantity-control", children: [
993
1087
  /* @__PURE__ */ jsx3(
994
1088
  "button",
995
1089
  {
996
1090
  "aria-label": `Decrease ${product.title}`,
997
- onClick: () => onUpdateQuantity(
1091
+ onClick: () => onSetQuantity(
998
1092
  product.id,
999
- displayVariant.id,
1093
+ displayVariant,
1000
1094
  selected.quantity - 1
1001
1095
  ),
1096
+ disabled: selected.quantity <= rule.min,
1002
1097
  children: "\u2212"
1003
1098
  }
1004
1099
  ),
1005
- /* @__PURE__ */ jsx3("span", { children: selected.quantity }),
1100
+ /* @__PURE__ */ jsx3("span", { "aria-live": "polite", children: selected.quantity }),
1006
1101
  /* @__PURE__ */ jsx3(
1007
1102
  "button",
1008
1103
  {
1009
1104
  "aria-label": `Increase ${product.title}`,
1010
- onClick: () => onUpdateQuantity(
1105
+ onClick: () => onSetQuantity(
1011
1106
  product.id,
1012
- displayVariant.id,
1107
+ displayVariant,
1013
1108
  selected.quantity + 1
1014
1109
  ),
1110
+ disabled: selected.quantity >= maxAddable,
1015
1111
  children: "+"
1016
1112
  }
1113
+ ),
1114
+ /* @__PURE__ */ jsx3(
1115
+ "button",
1116
+ {
1117
+ className: "lb-bundle__remove-btn",
1118
+ "aria-label": `Remove ${product.title}`,
1119
+ onClick: () => onDeselect(product.id, displayVariant.id),
1120
+ children: "Remove"
1121
+ }
1017
1122
  )
1018
1123
  ] }) : /* @__PURE__ */ jsx3(
1124
+ "button",
1125
+ {
1126
+ className: "lb-bundle__select-btn lb-bundle__select-btn--selected",
1127
+ "aria-label": `Remove ${product.title}`,
1128
+ onClick: () => onDeselect(product.id, displayVariant.id),
1129
+ children: "Selected"
1130
+ }
1131
+ ) : /* @__PURE__ */ jsx3(
1019
1132
  "button",
1020
1133
  {
1021
1134
  className: "lb-bundle__select-btn",
1022
- onClick: () => onToggle(product.id, displayVariant),
1023
- disabled: !displayVariant.availableForSale,
1024
- children: displayVariant.availableForSale ? "Select" : "Sold out"
1135
+ onClick: () => onSelect(product.id, displayVariant),
1136
+ disabled: !fulfillable || maxAddable <= 0,
1137
+ children: fulfillable && maxAddable > 0 ? "Select" : "Sold out"
1025
1138
  }
1026
1139
  ) })
1027
1140
  ]
@@ -1035,7 +1148,9 @@ import {
1035
1148
  formatMoney as formatMoney3,
1036
1149
  formatUnitPrice as formatUnitPrice3,
1037
1150
  calculateTierSavings,
1038
- getActiveTier
1151
+ getActiveTier,
1152
+ isVariantFulfillable as isVariantFulfillable3,
1153
+ shouldShowLowStockBadge as shouldShowLowStockBadge3
1039
1154
  } from "@lime-bundles/core";
1040
1155
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1041
1156
  function VolumeBundle(props) {
@@ -1087,7 +1202,8 @@ function VolumeBundle(props) {
1087
1202
  }, [variants]);
1088
1203
  const showPicker = variants.length > 1 && optionNames.length > 0;
1089
1204
  const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
1090
- const displayVariant = selectedVariant ?? variants.find((v) => v.availableForSale) ?? variants[0];
1205
+ const minTierQty = bundle?.volumeTiers[0]?.minQuantity ?? 1;
1206
+ const displayVariant = selectedVariant ?? variants.find((v) => isVariantFulfillable3(v, minTierQty)) ?? variants[0];
1091
1207
  const basePrice = displayVariant ? parseFloat(displayVariant.price.amount) : product ? parseFloat(product.priceRange.minVariantPrice.amount) : 0;
1092
1208
  const currency = product?.priceRange.minVariantPrice.currencyCode ?? "USD";
1093
1209
  const thumbImage = displayVariant?.image ?? product?.featuredImage ?? null;
@@ -1108,7 +1224,9 @@ function VolumeBundle(props) {
1108
1224
  const activeTier = bundle ? getActiveTier(bundle.volumeTiers, quantity) : null;
1109
1225
  const handleAddToCart = useCallback6(async () => {
1110
1226
  if (!bundle || !product) return;
1111
- const variant = selectedVariant ?? product.variants.nodes.find((v) => v.availableForSale);
1227
+ const variant = selectedVariant ?? product.variants.nodes.find(
1228
+ (v) => isVariantFulfillable3(v, quantity)
1229
+ );
1112
1230
  if (!variant) return;
1113
1231
  const lines = [
1114
1232
  {
@@ -1273,6 +1391,16 @@ function VolumeBundle(props) {
1273
1391
  ] })
1274
1392
  ] }),
1275
1393
  cartError && /* @__PURE__ */ jsx4("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
1394
+ bundle && displayVariant && shouldShowLowStockBadge3(
1395
+ displayVariant,
1396
+ minTierQty,
1397
+ bundle.widgetConfig.lowStockThreshold,
1398
+ bundle.widgetConfig.showLowStockBadge
1399
+ ) && /* @__PURE__ */ jsxs4("span", { className: "lb-bundle-low-stock-badge", children: [
1400
+ "Only ",
1401
+ displayVariant.quantityAvailable,
1402
+ " left"
1403
+ ] }),
1276
1404
  /* @__PURE__ */ jsx4(
1277
1405
  "button",
1278
1406
  {
@@ -1359,7 +1487,7 @@ import {
1359
1487
  computeBundleSaleCents,
1360
1488
  calculateTierSavings as calculateTierSavings2,
1361
1489
  getActiveTier as getActiveTier2,
1362
- validateQuantity as validateQuantity2,
1490
+ validateQuantity,
1363
1491
  transformImageUrl,
1364
1492
  formatCountdown,
1365
1493
  formatMoney as formatMoney4,
@@ -1427,6 +1555,6 @@ export {
1427
1555
  useBundlesForProduct,
1428
1556
  useVariantSelection,
1429
1557
  useWidgetConfigVars,
1430
- validateQuantity2 as validateQuantity
1558
+ validateQuantity
1431
1559
  };
1432
1560
  //# sourceMappingURL=index.js.map