@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.cjs CHANGED
@@ -196,7 +196,7 @@ function bindDropdown(select) {
196
196
  if (rect.width === 0) return false;
197
197
  const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);
198
198
  const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
199
- const scrollable = listbox.hasAttribute("data-lb-dropdown-portal") ? null : findScrollableAncestor(trigger);
199
+ const scrollable = findScrollableAncestor(trigger);
200
200
  const clip = scrollable ? (() => {
201
201
  const r = scrollable.getBoundingClientRect();
202
202
  return { top: r.top, bottom: r.bottom };
@@ -1044,16 +1044,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1044
1044
  root.appendChild(pricingSection.el);
1045
1045
  const savingsBar = wc.savingsBar.visible ? renderSavingsBar2() : null;
1046
1046
  if (savingsBar) root.appendChild(savingsBar.el);
1047
- const placeholder = el("div", "lb-mix-match__price-placeholder", {
1048
- "data-price-placeholder": ""
1049
- });
1050
- const placeholderText = el(
1051
- "span",
1052
- "lb-mix-match__price-placeholder-text"
1053
- );
1054
- placeholderText.textContent = `Select ${requiredQty} items to see price`;
1055
- placeholder.appendChild(placeholderText);
1056
- root.appendChild(placeholder);
1057
1047
  const modal = renderModal(bundle, eligible, currency, {
1058
1048
  showSearch: wc.showSearch,
1059
1049
  showQtySelector,
@@ -1118,7 +1108,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
1118
1108
  progress.update(selections.length);
1119
1109
  pricingSection.update(selections, bundle, currency);
1120
1110
  if (savingsBar) savingsBar.update(selections, bundle, currency);
1121
- placeholder.style.display = selections.length === 0 ? "" : "none";
1122
1111
  modal.refreshCounts();
1123
1112
  updateCta();
1124
1113
  }
@@ -1558,14 +1547,12 @@ function renderModal(bundle, eligible, currency, handlers) {
1558
1547
  return { min: rule.min, max, cap };
1559
1548
  };
1560
1549
  let stepper = null;
1550
+ let qtyGroup = null;
1561
1551
  if (!ep.isOos && handlers.showQtySelector) {
1562
- const qtyGroup = el(
1552
+ qtyGroup = el(
1563
1553
  "div",
1564
1554
  "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group"
1565
1555
  );
1566
- const qtyLabel = el("span", "lb-bundle-variant-option-label");
1567
- qtyLabel.textContent = "Quantity";
1568
- qtyGroup.appendChild(qtyLabel);
1569
1556
  stepper = renderQtyStepper({
1570
1557
  initial: rule.min,
1571
1558
  getBounds: () => {
@@ -1574,7 +1561,6 @@ function renderModal(bundle, eligible, currency, handlers) {
1574
1561
  }
1575
1562
  });
1576
1563
  qtyGroup.appendChild(stepper.el);
1577
- info.appendChild(qtyGroup);
1578
1564
  }
1579
1565
  const row = {
1580
1566
  el: productEl,
@@ -1710,10 +1696,22 @@ function renderModal(bundle, eligible, currency, handlers) {
1710
1696
  } else {
1711
1697
  countBadge.hidden = true;
1712
1698
  }
1713
- addBtn.disabled = handlers.isComplete() || cap < rule.min;
1699
+ const productInBundle = handlers.selections.some(
1700
+ (s) => s.productId === ep.product.id
1701
+ );
1702
+ if (productInBundle) {
1703
+ addBtn.disabled = true;
1704
+ addBtn.textContent = "Added";
1705
+ productEl.classList.add("lb-mix-match__modal-product--in-bundle");
1706
+ } else {
1707
+ addBtn.textContent = "Add";
1708
+ productEl.classList.remove("lb-mix-match__modal-product--in-bundle");
1709
+ addBtn.disabled = handlers.isComplete() || cap < rule.min;
1710
+ }
1714
1711
  };
1715
1712
  if (!ep.isOos) {
1716
1713
  const actions = el("div", "lb-mix-match__modal-product-actions");
1714
+ if (qtyGroup) actions.appendChild(qtyGroup);
1717
1715
  addBtn = document.createElement("button");
1718
1716
  addBtn.type = "button";
1719
1717
  addBtn.className = "lb-mix-match__modal-add";
@@ -1727,7 +1725,7 @@ function renderModal(bundle, eligible, currency, handlers) {
1727
1725
  close();
1728
1726
  });
1729
1727
  actions.appendChild(addBtn);
1730
- productEl.appendChild(actions);
1728
+ info.appendChild(actions);
1731
1729
  }
1732
1730
  row.refreshFromSelections = () => {
1733
1731
  if (stepper) stepper.refresh();
@@ -2567,7 +2565,7 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2567
2565
  .lb-bundle-variant-option-groups {
2568
2566
  display: flex;
2569
2567
  flex-direction: column;
2570
- gap: 12px;
2568
+ gap: 8px;
2571
2569
  margin-top: 8px;
2572
2570
  }
2573
2571
 
@@ -2589,7 +2587,23 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
2589
2587
  }
2590
2588
 
2591
2589
  .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2592
- margin-top: 4px;
2590
+ margin-top: 8px;
2591
+ }
2592
+
2593
+ /* Desktop: lay variant option groups in a wrapping row inside the modal
2594
+ (Size + Color side-by-side). Mobile keeps the column stack from the
2595
+ base rule above. The 768px breakpoint mirrors bundle-mix-match.css. */
2596
+ @media (min-width: 768px) {
2597
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
2598
+ flex-direction: row;
2599
+ flex-wrap: wrap;
2600
+ }
2601
+ /* Direct children only \u2014 the qty-stepper-group is also a
2602
+ .lb-bundle-variant-option-group but lives in the actions wrapper
2603
+ and must keep its natural width. */
2604
+ .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups > .lb-bundle-variant-option-group {
2605
+ flex: 1;
2606
+ }
2593
2607
  }
2594
2608
 
2595
2609
  /* Focus-ring suppression for mouse users. A small JS helper \u2014 see
@@ -3118,17 +3132,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3118
3132
  outline-offset: 2px;
3119
3133
  }
3120
3134
 
3121
- /* Price placeholder */
3122
- .lb-mix-match__price-placeholder {
3123
- padding: 4px 0 16px;
3124
- text-align: center;
3125
- }
3126
-
3127
- .lb-mix-match__price-placeholder-text {
3128
- font-size: 16px;
3129
- color: var(--lb-text);
3130
- }
3131
-
3132
3135
  /* === Modal Overlay === */
3133
3136
  .lb-mix-match__modal-overlay {
3134
3137
  position: fixed;
@@ -3157,7 +3160,7 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3157
3160
  border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);
3158
3161
  box-sizing: border-box;
3159
3162
  width: 100%;
3160
- max-width: 480px;
3163
+ max-width: 520px;
3161
3164
  max-height: 70vh;
3162
3165
  display: flex;
3163
3166
  flex-direction: column;
@@ -3267,10 +3270,12 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3267
3270
  }
3268
3271
 
3269
3272
  .lb-mix-match__modal-product {
3270
- display: flex;
3271
- align-items: center;
3272
- gap: 20px;
3273
- padding: 12px 0;
3273
+ display: grid;
3274
+ grid-template-columns: auto 1fr;
3275
+ align-items: start;
3276
+ column-gap: 20px;
3277
+ padding-top: 20px;
3278
+ padding-bottom: 16px;
3274
3279
  border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);
3275
3280
  }
3276
3281
 
@@ -3383,10 +3388,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3383
3388
  group from the unit-price / low-stock-badge above; the stepper itself
3384
3389
  has no top margin so the group can be repositioned without coupling. */
3385
3390
  .lb-mix-match__qty-stepper-group {
3386
- margin-top: 8px;
3387
- /* Group is a flex column; without an explicit width it stretches to
3388
- fill the info column so the \u2212/value/+ cells get pulled apart. Pin
3389
- to fit-content so the wrapper hugs the stepper's natural width. */
3391
+ /* Override the base .lb-bundle-variant-option-group column flex so the
3392
+ stepper child stretches on the cross-axis (vertical) \u2014 this is what
3393
+ lets the group track the Add button's height in the action row
3394
+ without pinning a fixed pixel value. */
3395
+ flex-direction: row;
3390
3396
  width: fit-content;
3391
3397
  }
3392
3398
 
@@ -3394,7 +3400,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3394
3400
  display: inline-flex;
3395
3401
  align-items: stretch;
3396
3402
  flex-shrink: 0;
3397
- height: 32px;
3398
3403
  border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);
3399
3404
  border-radius: var(--lb-picker-qty-stepper-radius);
3400
3405
  background-color: var(--lb-picker-bg);
@@ -3448,8 +3453,24 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3448
3453
  box-sizing: border-box;
3449
3454
  }
3450
3455
 
3456
+ /* Action row \u2014 qty stepper (left) + Add button (right). Sits at the
3457
+ bottom of the info column with a 12px top margin so it visually
3458
+ separates from the variant pickers / price block above. When the
3459
+ stepper is hidden, Add fills the row via flex: 1 below.
3460
+ align-items: stretch so the stepper-group tracks the Add button's
3461
+ height (driven by font size + button padding) without a pinned px. */
3462
+ .lb-mix-match__modal-product-actions {
3463
+ display: flex;
3464
+ flex-wrap: wrap;
3465
+ align-items: stretch;
3466
+ row-gap: 12px;
3467
+ column-gap: 8px;
3468
+ margin-top: 12px;
3469
+ }
3470
+
3451
3471
  .lb-mix-match__modal-add {
3452
- padding: 8px 20px;
3472
+ flex: 1;
3473
+ padding: 10px 20px;
3453
3474
  background: var(--lb-picker-add-bg);
3454
3475
  color: var(--lb-picker-add-label);
3455
3476
  border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);
@@ -3458,8 +3479,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3458
3479
  font-size: 12px;
3459
3480
  font-weight: 600;
3460
3481
  cursor: pointer;
3461
- white-space: nowrap;
3462
- flex-shrink: 0;
3463
3482
  }
3464
3483
 
3465
3484
  .lb-mix-match__modal-add:hover {
@@ -3482,6 +3501,14 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
3482
3501
  opacity: 0.5;
3483
3502
  }
3484
3503
 
3504
+ /* Already in a slot \u2014 prevents shoppers from filling multiple slots
3505
+ with the same product (which wouldn't qualify the discount: it
3506
+ counts distinct products, not slot occupancy). The thumb keeps
3507
+ its qty badge so the shopper sees what's already in the bundle. */
3508
+ .lb-mix-match__modal-product--in-bundle {
3509
+ opacity: 0.5;
3510
+ }
3511
+
3485
3512
  .lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {
3486
3513
  font-size: 12px;
3487
3514
  color: inherit;