@lime-bundles/widget 3.2.0 → 3.2.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.js CHANGED
@@ -188,7 +188,7 @@ function bindDropdown(select) {
188
188
  if (rect.width === 0) return false;
189
189
  const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);
190
190
  const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
191
- const scrollable = listbox.hasAttribute("data-lb-dropdown-portal") ? null : findScrollableAncestor(trigger);
191
+ const scrollable = findScrollableAncestor(trigger);
192
192
  const clip = scrollable ? (() => {
193
193
  const r = scrollable.getBoundingClientRect();
194
194
  return { top: r.top, bottom: r.bottom };
@@ -1051,16 +1051,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1051
1051
  root.appendChild(pricingSection.el);
1052
1052
  const savingsBar = wc.savingsBar.visible ? renderSavingsBar2() : null;
1053
1053
  if (savingsBar) root.appendChild(savingsBar.el);
1054
- const placeholder = el("div", "lb-mix-match__price-placeholder", {
1055
- "data-price-placeholder": ""
1056
- });
1057
- const placeholderText = el(
1058
- "span",
1059
- "lb-mix-match__price-placeholder-text"
1060
- );
1061
- placeholderText.textContent = `Select ${requiredQty} items to see price`;
1062
- placeholder.appendChild(placeholderText);
1063
- root.appendChild(placeholder);
1064
1054
  const modal = renderModal(bundle, eligible, currency, {
1065
1055
  showSearch: wc.showSearch,
1066
1056
  showQtySelector,
@@ -1125,7 +1115,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1125
1115
  progress.update(selections.length);
1126
1116
  pricingSection.update(selections, bundle, currency);
1127
1117
  if (savingsBar) savingsBar.update(selections, bundle, currency);
1128
- placeholder.style.display = selections.length === 0 ? "" : "none";
1129
1118
  modal.refreshCounts();
1130
1119
  updateCta();
1131
1120
  }
@@ -1565,14 +1554,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1565
1554
  return { min: rule.min, max, cap };
1566
1555
  };
1567
1556
  let stepper = null;
1557
+ let qtyGroup = null;
1568
1558
  if (!ep.isOos && handlers.showQtySelector) {
1569
- const qtyGroup = el(
1559
+ qtyGroup = el(
1570
1560
  "div",
1571
1561
  "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group"
1572
1562
  );
1573
- const qtyLabel = el("span", "lb-bundle-variant-option-label");
1574
- qtyLabel.textContent = "Quantity";
1575
- qtyGroup.appendChild(qtyLabel);
1576
1563
  stepper = renderQtyStepper({
1577
1564
  initial: rule.min,
1578
1565
  getBounds: () => {
@@ -1581,7 +1568,6 @@ function renderModal(bundle, eligible, currency, handlers) {
1581
1568
  }
1582
1569
  });
1583
1570
  qtyGroup.appendChild(stepper.el);
1584
- info.appendChild(qtyGroup);
1585
1571
  }
1586
1572
  const row = {
1587
1573
  el: productEl,
@@ -1717,10 +1703,22 @@ function renderModal(bundle, eligible, currency, handlers) {
1717
1703
  } else {
1718
1704
  countBadge.hidden = true;
1719
1705
  }
1720
- addBtn.disabled = handlers.isComplete() || cap < rule.min;
1706
+ const productInBundle = handlers.selections.some(
1707
+ (s) => s.productId === ep.product.id
1708
+ );
1709
+ if (productInBundle) {
1710
+ addBtn.disabled = true;
1711
+ addBtn.textContent = "Added";
1712
+ productEl.classList.add("lb-mix-match__modal-product--in-bundle");
1713
+ } else {
1714
+ addBtn.textContent = "Add";
1715
+ productEl.classList.remove("lb-mix-match__modal-product--in-bundle");
1716
+ addBtn.disabled = handlers.isComplete() || cap < rule.min;
1717
+ }
1721
1718
  };
1722
1719
  if (!ep.isOos) {
1723
1720
  const actions = el("div", "lb-mix-match__modal-product-actions");
1721
+ if (qtyGroup) actions.appendChild(qtyGroup);
1724
1722
  addBtn = document.createElement("button");
1725
1723
  addBtn.type = "button";
1726
1724
  addBtn.className = "lb-mix-match__modal-add";
@@ -1734,7 +1732,7 @@ function renderModal(bundle, eligible, currency, handlers) {
1734
1732
  close();
1735
1733
  });
1736
1734
  actions.appendChild(addBtn);
1737
- productEl.appendChild(actions);
1735
+ info.appendChild(actions);
1738
1736
  }
1739
1737
  row.refreshFromSelections = () => {
1740
1738
  if (stepper) stepper.refresh();
@@ -2574,7 +2572,7 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2574
2572
  .lb-bundle-variant-option-groups {
2575
2573
  display: flex;
2576
2574
  flex-direction: column;
2577
- gap: 12px;
2575
+ gap: 8px;
2578
2576
  margin-top: 8px;
2579
2577
  }
2580
2578
 
@@ -2596,7 +2594,23 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2596
2594
  }
2597
2595
 
2598
2596
  .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2599
- margin-top: 4px;
2597
+ margin-top: 8px;
2598
+ }
2599
+
2600
+ /* Desktop: lay variant option groups in a wrapping row inside the modal
2601
+ (Size + Color side-by-side). Mobile keeps the column stack from the
2602
+ base rule above. The 768px breakpoint mirrors bundle-mix-match.css. */
2603
+ @media (min-width: 768px) {
2604
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2605
+ flex-direction: row;
2606
+ flex-wrap: wrap;
2607
+ }
2608
+ /* Direct children only \u2014 the qty-stepper-group is also a
2609
+ .lb-bundle-variant-option-group but lives in the actions wrapper
2610
+ and must keep its natural width. */
2611
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups > .lb-bundle-variant-option-group {
2612
+ flex: 1;
2613
+ }
2600
2614
  }
2601
2615
 
2602
2616
  /* Focus-ring suppression for mouse users. A small JS helper \u2014 see
@@ -3125,17 +3139,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3125
3139
  outline-offset: 2px;
3126
3140
  }
3127
3141
 
3128
- /* Price placeholder */
3129
- .lb-mix-match__price-placeholder {
3130
- padding: 4px 0 16px;
3131
- text-align: center;
3132
- }
3133
-
3134
- .lb-mix-match__price-placeholder-text {
3135
- font-size: 16px;
3136
- color: var(--lb-text);
3137
- }
3138
-
3139
3142
  /* === Modal Overlay === */
3140
3143
  .lb-mix-match__modal-overlay {
3141
3144
  position: fixed;
@@ -3164,7 +3167,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3164
3167
  border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);
3165
3168
  box-sizing: border-box;
3166
3169
  width: 100%;
3167
- max-width: 480px;
3170
+ max-width: 520px;
3168
3171
  max-height: 70vh;
3169
3172
  display: flex;
3170
3173
  flex-direction: column;
@@ -3274,10 +3277,12 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3274
3277
  }
3275
3278
 
3276
3279
  .lb-mix-match__modal-product {
3277
- display: flex;
3278
- align-items: center;
3279
- gap: 20px;
3280
- padding: 12px 0;
3280
+ display: grid;
3281
+ grid-template-columns: auto 1fr;
3282
+ align-items: start;
3283
+ column-gap: 20px;
3284
+ padding-top: 20px;
3285
+ padding-bottom: 16px;
3281
3286
  border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);
3282
3287
  }
3283
3288
 
@@ -3390,10 +3395,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3390
3395
  group from the unit-price / low-stock-badge above; the stepper itself
3391
3396
  has no top margin so the group can be repositioned without coupling. */
3392
3397
  .lb-mix-match__qty-stepper-group {
3393
- margin-top: 8px;
3394
- /* Group is a flex column; without an explicit width it stretches to
3395
- fill the info column so the \u2212/value/+ cells get pulled apart. Pin
3396
- to fit-content so the wrapper hugs the stepper's natural width. */
3398
+ /* Override the base .lb-bundle-variant-option-group column flex so the
3399
+ stepper child stretches on the cross-axis (vertical) \u2014 this is what
3400
+ lets the group track the Add button's height in the action row
3401
+ without pinning a fixed pixel value. */
3402
+ flex-direction: row;
3397
3403
  width: fit-content;
3398
3404
  }
3399
3405
 
@@ -3401,7 +3407,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3401
3407
  display: inline-flex;
3402
3408
  align-items: stretch;
3403
3409
  flex-shrink: 0;
3404
- height: 32px;
3405
3410
  border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);
3406
3411
  border-radius: var(--lb-picker-qty-stepper-radius);
3407
3412
  background-color: var(--lb-picker-bg);
@@ -3455,8 +3460,24 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3455
3460
  box-sizing: border-box;
3456
3461
  }
3457
3462
 
3463
+ /* Action row \u2014 qty stepper (left) + Add button (right). Sits at the
3464
+ bottom of the info column with a 12px top margin so it visually
3465
+ separates from the variant pickers / price block above. When the
3466
+ stepper is hidden, Add fills the row via flex: 1 below.
3467
+ align-items: stretch so the stepper-group tracks the Add button's
3468
+ height (driven by font size + button padding) without a pinned px. */
3469
+ .lb-mix-match__modal-product-actions {
3470
+ display: flex;
3471
+ flex-wrap: wrap;
3472
+ align-items: stretch;
3473
+ row-gap: 12px;
3474
+ column-gap: 8px;
3475
+ margin-top: 12px;
3476
+ }
3477
+
3458
3478
  .lb-mix-match__modal-add {
3459
- padding: 8px 20px;
3479
+ flex: 1;
3480
+ padding: 10px 20px;
3460
3481
  background: var(--lb-picker-add-bg);
3461
3482
  color: var(--lb-picker-add-label);
3462
3483
  border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);
@@ -3465,8 +3486,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3465
3486
  font-size: 12px;
3466
3487
  font-weight: 600;
3467
3488
  cursor: pointer;
3468
- white-space: nowrap;
3469
- flex-shrink: 0;
3470
3489
  }
3471
3490
 
3472
3491
  .lb-mix-match__modal-add:hover {
@@ -3489,6 +3508,14 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3489
3508
  opacity: 0.5;
3490
3509
  }
3491
3510
 
3511
+ /* Already in a slot \u2014 prevents shoppers from filling multiple slots
3512
+ with the same product (which wouldn't qualify the discount: it
3513
+ counts distinct products, not slot occupancy). The thumb keeps
3514
+ its qty badge so the shopper sees what's already in the bundle. */
3515
+ .lb-mix-match__modal-product--in-bundle {
3516
+ opacity: 0.5;
3517
+ }
3518
+
3492
3519
  .lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {
3493
3520
  font-size: 12px;
3494
3521
  color: inherit;