@lime-bundles/widget 2.0.0 → 2.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 CHANGED
@@ -546,7 +546,20 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
546
546
  })
547
547
  );
548
548
  container.appendChild(root);
549
- renderSlots();
549
+ const firstEligible = eligible.find((ep) => !ep.isOos);
550
+ const firstVariant = firstEligible?.firstAvailableVariant ?? firstEligible?.variants[0];
551
+ if (firstEligible && firstVariant) {
552
+ selections.push({
553
+ productId: firstEligible.product.id,
554
+ productTitle: firstEligible.product.title,
555
+ variantId: firstVariant.id,
556
+ variantTitle: firstVariant.title,
557
+ imageUrl: firstEligible.product.featuredImage?.url ?? null,
558
+ priceCents: (0, import_core.parseCents)(firstVariant.price.amount),
559
+ compareCents: firstVariant.compareAtPrice ? (0, import_core.parseCents)(firstVariant.compareAtPrice.amount) : null
560
+ });
561
+ }
562
+ afterMutation();
550
563
  function addSelection(product, variant) {
551
564
  if (selections.length >= maxQty) return;
552
565
  selections.push({
@@ -1091,15 +1104,15 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
1091
1104
  if (!variant && wc.outOfStockBehavior === "hide") return;
1092
1105
  const basePriceCents = variant ? (0, import_core.parseCents)(variant.price.amount) : 0;
1093
1106
  const currency = variant?.price.currencyCode ?? "USD";
1107
+ const discountType = bundle.discountConfig.discountType;
1094
1108
  const resolved = bundle.volumeTiers.map((tier, index) => {
1095
1109
  let perUnit;
1096
- if (tier.discountType === "fixed_amount") {
1097
- const amt = Math.round(tier.discountValue * 100);
1110
+ if (discountType === "fixed_amount") {
1111
+ const amt = Math.round((tier.amount ?? 0) * 100);
1098
1112
  perUnit = Math.max(0, basePriceCents - amt);
1099
1113
  } else {
1100
- const discount = Math.floor(
1101
- basePriceCents * tier.discountValue / 100
1102
- );
1114
+ const pct = tier.percentage ?? 0;
1115
+ const discount = Math.floor(basePriceCents * pct / 100);
1103
1116
  perUnit = Math.max(0, basePriceCents - discount);
1104
1117
  }
1105
1118
  return {
@@ -1117,7 +1130,9 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
1117
1130
  }
1118
1131
  const popularIndex = wc.popularBadge.tierIndex !== void 0 ? clamp(wc.popularBadge.tierIndex, 0, resolved.length - 1) : bestTierIndex;
1119
1132
  const root = el("div", "lb-volume");
1120
- root.appendChild(renderHeader3(bundle, resolved, selectedIndex, currency));
1133
+ root.appendChild(
1134
+ renderHeader3(bundle, resolved, selectedIndex, currency, discountType)
1135
+ );
1121
1136
  if (wc.countdown.showCountdown && bundle.endsAt) {
1122
1137
  const countdown = renderCountdown(bundle.endsAt);
1123
1138
  if (countdown) {
@@ -1218,7 +1233,7 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
1218
1233
  }
1219
1234
  const badgeEl = root.querySelector("[data-header-badge]");
1220
1235
  if (badgeEl) {
1221
- const label = badgeFor(resolved[idx], currency);
1236
+ const label = badgeFor(resolved[idx], currency, discountType);
1222
1237
  if (label) {
1223
1238
  badgeEl.textContent = label;
1224
1239
  badgeEl.style.display = "";
@@ -1228,7 +1243,7 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
1228
1243
  }
1229
1244
  }
1230
1245
  }
1231
- function renderHeader3(bundle, resolved, selectedIndex, currency) {
1246
+ function renderHeader3(bundle, resolved, selectedIndex, currency, discountType) {
1232
1247
  const wc = bundle.widgetConfig;
1233
1248
  const header = el("div", "lb-bundle-header");
1234
1249
  const content = el("div", "lb-bundle-header__content");
@@ -1237,7 +1252,7 @@ function renderHeader3(bundle, resolved, selectedIndex, currency) {
1237
1252
  content.appendChild(title);
1238
1253
  header.appendChild(content);
1239
1254
  if (wc.pricing.showSaveBadge) {
1240
- const badge = badgeFor(resolved[selectedIndex], currency);
1255
+ const badge = badgeFor(resolved[selectedIndex], currency, discountType);
1241
1256
  if (badge) {
1242
1257
  const badgeEl = el("span", "lb-bundle-header__badge", {
1243
1258
  "data-header-badge": ""
@@ -1350,14 +1365,18 @@ function renderCta2(bundle, onClick) {
1350
1365
  });
1351
1366
  return button;
1352
1367
  }
1353
- function badgeFor(resolved, currency) {
1368
+ function badgeFor(resolved, currency, discountType) {
1354
1369
  if (!resolved) return null;
1355
1370
  const { tier } = resolved;
1356
- if (tier.discountType === "fixed_amount" && tier.discountValue > 0) {
1357
- return `-${(0, import_core.formatCents)(Math.round(tier.discountValue * 100), currency)}`;
1371
+ if (discountType === "fixed_amount") {
1372
+ const amount = tier.amount ?? 0;
1373
+ if (amount > 0) return `-${(0, import_core.formatCents)(Math.round(amount * 100), currency)}`;
1374
+ return null;
1358
1375
  }
1359
- if (tier.discountType === "percentage" && tier.discountValue > 0) {
1360
- return `-${Math.round(tier.discountValue)}%`;
1376
+ if (discountType === "percentage") {
1377
+ const pct = tier.percentage ?? 0;
1378
+ if (pct > 0) return `-${Math.round(pct)}%`;
1379
+ return null;
1361
1380
  }
1362
1381
  return null;
1363
1382
  }
@@ -1402,6 +1421,10 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
1402
1421
  border-radius: var(--lb-radius);
1403
1422
  padding: var(--lb-widget-pad) var(--lb-widget-pad) 20px;
1404
1423
  box-sizing: border-box;
1424
+ /* Cap the widget at a comfortable reading width on desktop. Below
1425
+ 440px viewports the container is already narrower than the cap,
1426
+ so the rule is inert on mobile. */
1427
+ max-width: 440px;
1405
1428
  }
1406
1429
 
1407
1430
  /* Countdown timer bar \u2014 sits below the gradient header */
@@ -2466,6 +2489,103 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
2466
2489
  }
2467
2490
 
2468
2491
 
2492
+ `;
2493
+ var BUNDLE_SKELETON_CSS = `/* Lime Bundles \u2014 web-component loading skeleton (not mirrored to theme assets) */
2494
+
2495
+ .lb-bundle-widget--loading {
2496
+ display: block;
2497
+ padding: var(--lb-widget-pad, 20px);
2498
+ border: 1px solid var(--lb-border, #E5E5E5);
2499
+ border-radius: var(--lb-radius, 12px);
2500
+ background: var(--lb-bg, #FFFFFF);
2501
+ /* Contain layout/paint so the skeleton doesn't influence ancestor
2502
+ layout once the real content swaps in. */
2503
+ contain: layout paint;
2504
+ }
2505
+
2506
+ .lb-bundle-widget--loading .lb-skeleton {
2507
+ background: linear-gradient(
2508
+ 90deg,
2509
+ rgba(0, 0, 0, 0.06) 0%,
2510
+ rgba(0, 0, 0, 0.10) 50%,
2511
+ rgba(0, 0, 0, 0.06) 100%
2512
+ );
2513
+ background-size: 200% 100%;
2514
+ border-radius: 6px;
2515
+ animation: lb-skeleton-pulse 1.4s ease-in-out infinite;
2516
+ }
2517
+
2518
+ .lb-bundle-widget--loading .lb-skeleton--title {
2519
+ height: 28px;
2520
+ width: 60%;
2521
+ margin-bottom: 16px;
2522
+ }
2523
+
2524
+ .lb-bundle-widget--loading .lb-skeleton--products {
2525
+ display: grid;
2526
+ gap: 12px;
2527
+ margin-bottom: 16px;
2528
+ }
2529
+
2530
+ .lb-bundle-widget--loading .lb-skeleton--row {
2531
+ display: grid;
2532
+ grid-template-columns: 56px 1fr 60px;
2533
+ gap: 12px;
2534
+ align-items: center;
2535
+ }
2536
+
2537
+ .lb-bundle-widget--loading .lb-skeleton--thumb {
2538
+ height: 56px;
2539
+ width: 56px;
2540
+ border-radius: 8px;
2541
+ }
2542
+
2543
+ .lb-bundle-widget--loading .lb-skeleton--line {
2544
+ height: 14px;
2545
+ border-radius: 4px;
2546
+ }
2547
+
2548
+ .lb-bundle-widget--loading .lb-skeleton--line + .lb-skeleton--line {
2549
+ margin-top: 8px;
2550
+ width: 70%;
2551
+ }
2552
+
2553
+ .lb-bundle-widget--loading .lb-skeleton--price {
2554
+ height: 20px;
2555
+ width: 60px;
2556
+ }
2557
+
2558
+ .lb-bundle-widget--loading .lb-skeleton--footer {
2559
+ margin-top: 16px;
2560
+ padding-top: 16px;
2561
+ border-top: 1px solid var(--lb-border, #E5E5E5);
2562
+ display: grid;
2563
+ grid-template-columns: 1fr auto;
2564
+ gap: 12px;
2565
+ align-items: center;
2566
+ }
2567
+
2568
+ .lb-bundle-widget--loading .lb-skeleton--total {
2569
+ height: 24px;
2570
+ width: 40%;
2571
+ }
2572
+
2573
+ .lb-bundle-widget--loading .lb-skeleton--cta {
2574
+ height: 44px;
2575
+ width: 140px;
2576
+ border-radius: var(--lb-cta-radius, 8px);
2577
+ }
2578
+
2579
+ @keyframes lb-skeleton-pulse {
2580
+ 0% { background-position: 0% 50%; }
2581
+ 100% { background-position: -200% 50%; }
2582
+ }
2583
+
2584
+ @media (prefers-reduced-motion: reduce) {
2585
+ .lb-bundle-widget--loading .lb-skeleton {
2586
+ animation: none;
2587
+ }
2588
+ }
2469
2589
  `;
2470
2590
 
2471
2591
  // src/lime-bundle.ts
@@ -2515,7 +2635,6 @@ var LimeBundleElement = class extends HTMLElement {
2515
2635
  this.shadow = this.attachShadow({ mode: "open" });
2516
2636
  }
2517
2637
  connectedCallback() {
2518
- this.render();
2519
2638
  this.fetchBundle();
2520
2639
  }
2521
2640
  disconnectedCallback() {
@@ -2870,9 +2989,39 @@ var LimeBundleElement = class extends HTMLElement {
2870
2989
  renderLoading() {
2871
2990
  this.shadow.innerHTML = `
2872
2991
  <style>${BUNDLE_BASE_CSS}</style>
2873
- <div class="lb-bundle-widget lb-bundle-widget--loading" aria-busy="true">
2992
+ <style>${BUNDLE_SKELETON_CSS}</style>
2993
+ <div class="lb-bundle-widget lb-bundle-widget--loading" aria-busy="true" aria-label="Loading bundle">
2874
2994
  <div class="lb-skeleton lb-skeleton--title"></div>
2875
- <div class="lb-skeleton lb-skeleton--products"></div>
2995
+ <div class="lb-skeleton--products">
2996
+ <div class="lb-skeleton--row">
2997
+ <div class="lb-skeleton lb-skeleton--thumb"></div>
2998
+ <div>
2999
+ <div class="lb-skeleton lb-skeleton--line"></div>
3000
+ <div class="lb-skeleton lb-skeleton--line"></div>
3001
+ </div>
3002
+ <div class="lb-skeleton lb-skeleton--price"></div>
3003
+ </div>
3004
+ <div class="lb-skeleton--row">
3005
+ <div class="lb-skeleton lb-skeleton--thumb"></div>
3006
+ <div>
3007
+ <div class="lb-skeleton lb-skeleton--line"></div>
3008
+ <div class="lb-skeleton lb-skeleton--line"></div>
3009
+ </div>
3010
+ <div class="lb-skeleton lb-skeleton--price"></div>
3011
+ </div>
3012
+ <div class="lb-skeleton--row">
3013
+ <div class="lb-skeleton lb-skeleton--thumb"></div>
3014
+ <div>
3015
+ <div class="lb-skeleton lb-skeleton--line"></div>
3016
+ <div class="lb-skeleton lb-skeleton--line"></div>
3017
+ </div>
3018
+ <div class="lb-skeleton lb-skeleton--price"></div>
3019
+ </div>
3020
+ </div>
3021
+ <div class="lb-skeleton--footer">
3022
+ <div class="lb-skeleton lb-skeleton--total"></div>
3023
+ <div class="lb-skeleton lb-skeleton--cta"></div>
3024
+ </div>
2876
3025
  </div>
2877
3026
  `;
2878
3027
  }
@@ -2886,9 +3035,6 @@ var LimeBundleElement = class extends HTMLElement {
2886
3035
  })
2887
3036
  );
2888
3037
  }
2889
- render() {
2890
- this.shadow.innerHTML = `<style>${BUNDLE_BASE_CSS}</style>`;
2891
- }
2892
3038
  };
2893
3039
 
2894
3040
  // src/index.ts