@lime-bundles/widget 2.4.0 → 2.4.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 +326 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +305 -71
- package/dist/index.js.map +1 -1
- package/dist/lime-bundle.global.js +82 -25
- package/dist/lime-bundle.global.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
LimeBundleElement: () => LimeBundleElement
|
|
23
|
+
LimeBundleElement: () => LimeBundleElement,
|
|
24
|
+
trackInputMode: () => trackInputMode
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(index_exports);
|
|
26
27
|
|
|
27
28
|
// src/lime-bundle.ts
|
|
28
|
-
var
|
|
29
|
+
var import_core6 = require("@lime-bundles/core");
|
|
29
30
|
|
|
30
31
|
// src/renderers/fixed.ts
|
|
31
32
|
var import_core4 = require("@lime-bundles/core");
|
|
@@ -211,12 +212,11 @@ function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
211
212
|
}
|
|
212
213
|
function buildRowState(bundle, product, productIndex) {
|
|
213
214
|
const selectedVariantIds = bundle.selectedVariantIds?.[productIndex] ?? null;
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const selected = eligibleVariants[0] ?? null;
|
|
215
|
+
const merchantScoped = selectedVariantIds && selectedVariantIds.length > 0 ? product.variants.nodes.filter((v) => selectedVariantIds.includes(v.id)) : product.variants.nodes;
|
|
216
|
+
const firstInStock = merchantScoped.find((v) => v.availableForSale) ?? null;
|
|
217
|
+
const eligibleVariants = merchantScoped;
|
|
218
|
+
const isOos = !firstInStock;
|
|
219
|
+
const selected = firstInStock ?? merchantScoped[0] ?? null;
|
|
220
220
|
const qty = selected ? (0, import_core4.resolveBundleQty)(bundle, product.id, selected.id) : 1;
|
|
221
221
|
return { product, eligibleVariants, selected, qty, isOos };
|
|
222
222
|
}
|
|
@@ -365,33 +365,85 @@ function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
|
365
365
|
};
|
|
366
366
|
applyVariantToRow(state.selected);
|
|
367
367
|
if (state.eligibleVariants.length > 1) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
select.setAttribute("data-variant-select", "");
|
|
371
|
-
select.name = `lb-variant-${state.product.id.replace(/^.*\//, "")}`;
|
|
372
|
-
select.setAttribute(
|
|
373
|
-
"aria-label",
|
|
374
|
-
`Select variant for ${state.product.title}`
|
|
368
|
+
const optionNames = state.eligibleVariants[0].selectedOptions.map(
|
|
369
|
+
(o) => o.name
|
|
375
370
|
);
|
|
376
|
-
state.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
371
|
+
const productIdTail = state.product.id.replace(/^.*\//, "");
|
|
372
|
+
const optionSelects = [];
|
|
373
|
+
const resolveVariant = (values) => state.eligibleVariants.find(
|
|
374
|
+
(v) => v.selectedOptions.every((o, i) => o.value === values[i]) && v.selectedOptions.length === values.length
|
|
375
|
+
) ?? null;
|
|
376
|
+
const syncSelectsToVariant = (variant) => {
|
|
377
|
+
variant.selectedOptions.forEach((o, i) => {
|
|
378
|
+
const sel = optionSelects[i];
|
|
379
|
+
if (sel && sel.value !== o.value) sel.value = o.value;
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
const isValueAvailable = (optionIndex, value, selected) => state.eligibleVariants.some((v) => {
|
|
383
|
+
if (!v.availableForSale) return false;
|
|
384
|
+
if (v.selectedOptions[optionIndex]?.value !== value) return false;
|
|
385
|
+
return v.selectedOptions.every(
|
|
386
|
+
(o, i) => i === optionIndex || o.value === selected[i]
|
|
386
387
|
);
|
|
387
|
-
|
|
388
|
+
});
|
|
389
|
+
const recomputeDisabled = (selected) => {
|
|
390
|
+
optionSelects.forEach((sel, i) => {
|
|
391
|
+
Array.from(sel.options).forEach((opt) => {
|
|
392
|
+
opt.disabled = !isValueAvailable(i, opt.value, selected);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
const handleChange = () => {
|
|
397
|
+
const values = optionSelects.map((s) => s.value);
|
|
398
|
+
const variant = resolveVariant(values);
|
|
399
|
+
if (!variant) {
|
|
400
|
+
if (state.selected) {
|
|
401
|
+
syncSelectsToVariant(state.selected);
|
|
402
|
+
recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));
|
|
403
|
+
}
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
388
406
|
state.selected = variant;
|
|
389
407
|
state.qty = qtyFor(state.product.id, variant.id);
|
|
390
408
|
if (qtyBadgeRef) qtyBadgeRef.textContent = String(state.qty);
|
|
391
409
|
applyVariantToRow(variant);
|
|
410
|
+
recomputeDisabled(variant.selectedOptions.map((o) => o.value));
|
|
392
411
|
onVariantChange();
|
|
412
|
+
};
|
|
413
|
+
const groupsContainer = el("div", "lb-bundle-variant-option-groups");
|
|
414
|
+
optionNames.forEach((name2, position) => {
|
|
415
|
+
const group = el("div", "lb-bundle-variant-option-group");
|
|
416
|
+
const label = el("span", "lb-bundle-variant-option-label");
|
|
417
|
+
label.textContent = name2;
|
|
418
|
+
group.appendChild(label);
|
|
419
|
+
const select = document.createElement("select");
|
|
420
|
+
select.className = "lb-bundle-variant-select";
|
|
421
|
+
select.setAttribute("data-variant-option", "");
|
|
422
|
+
select.setAttribute("data-option-position", String(position + 1));
|
|
423
|
+
select.name = `lb-variant-${productIdTail}-${position + 1}`;
|
|
424
|
+
select.setAttribute("aria-label", name2);
|
|
425
|
+
const seen = /* @__PURE__ */ new Set();
|
|
426
|
+
state.eligibleVariants.forEach((v) => {
|
|
427
|
+
const value = v.selectedOptions[position]?.value;
|
|
428
|
+
if (!value || seen.has(value)) return;
|
|
429
|
+
seen.add(value);
|
|
430
|
+
const opt = document.createElement("option");
|
|
431
|
+
opt.value = value;
|
|
432
|
+
opt.textContent = value;
|
|
433
|
+
if (state.selected?.selectedOptions[position]?.value === value) {
|
|
434
|
+
opt.selected = true;
|
|
435
|
+
}
|
|
436
|
+
select.appendChild(opt);
|
|
437
|
+
});
|
|
438
|
+
select.addEventListener("change", handleChange);
|
|
439
|
+
optionSelects.push(select);
|
|
440
|
+
group.appendChild(select);
|
|
441
|
+
groupsContainer.appendChild(group);
|
|
393
442
|
});
|
|
394
|
-
info.appendChild(
|
|
443
|
+
info.appendChild(groupsContainer);
|
|
444
|
+
if (state.selected) {
|
|
445
|
+
recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));
|
|
446
|
+
}
|
|
395
447
|
} else if (state.eligibleVariants.length === 1 && state.product.variants.nodes.length > 1) {
|
|
396
448
|
const badge = el("span", "lb-bundle-variant-badge");
|
|
397
449
|
badge.textContent = state.eligibleVariants[0].title;
|
|
@@ -480,6 +532,7 @@ function computeSale(totalCents, discount, rows) {
|
|
|
480
532
|
}
|
|
481
533
|
|
|
482
534
|
// src/renderers/mix-match.ts
|
|
535
|
+
var import_core5 = require("@lime-bundles/core");
|
|
483
536
|
var PLACEHOLDER_THUMB_SVG2 = `
|
|
484
537
|
<svg class="lb-bundle-placeholder-icon" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
485
538
|
<rect x="4" y="4" width="20" height="20" rx="3"></rect>
|
|
@@ -547,9 +600,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
547
600
|
showSearch: wc.showSearch,
|
|
548
601
|
onAdd: (product, variant) => addSelection(product, variant),
|
|
549
602
|
onRemove: (productId, variantId) => removeSelection(productId, variantId),
|
|
550
|
-
countFor: (productId, variantId) => selections.filter(
|
|
551
|
-
(s) => s.productId === productId && s.variantId === variantId
|
|
552
|
-
).length,
|
|
553
603
|
isOverMax: () => selections.length >= maxQty
|
|
554
604
|
});
|
|
555
605
|
root.appendChild(modal.el);
|
|
@@ -559,7 +609,7 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
559
609
|
if (cta.disabled) return;
|
|
560
610
|
const lines = selections.map((s) => ({
|
|
561
611
|
merchandiseId: s.variantId,
|
|
562
|
-
quantity:
|
|
612
|
+
quantity: s.quantity,
|
|
563
613
|
attributes: [
|
|
564
614
|
{ key: "_lime_bundle_gid", value: bundle.id },
|
|
565
615
|
{ key: "_lime_bundle_type", value: bundle.bundleType }
|
|
@@ -593,7 +643,8 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
593
643
|
firstVariant.unitPrice,
|
|
594
644
|
firstVariant.unitPriceMeasurement,
|
|
595
645
|
currency
|
|
596
|
-
)
|
|
646
|
+
),
|
|
647
|
+
quantity: (0, import_core5.resolveBundleQty)(bundle, firstEligible.product.id, firstVariant.id)
|
|
597
648
|
});
|
|
598
649
|
}
|
|
599
650
|
afterMutation();
|
|
@@ -611,7 +662,8 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
611
662
|
variant.unitPrice,
|
|
612
663
|
variant.unitPriceMeasurement,
|
|
613
664
|
currency
|
|
614
|
-
)
|
|
665
|
+
),
|
|
666
|
+
quantity: (0, import_core5.resolveBundleQty)(bundle, product.id, variant.id)
|
|
615
667
|
});
|
|
616
668
|
afterMutation();
|
|
617
669
|
}
|
|
@@ -778,6 +830,9 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
778
830
|
} else {
|
|
779
831
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG2);
|
|
780
832
|
}
|
|
833
|
+
const qtyBadge = el("span", "lb-bundle-qty-badge");
|
|
834
|
+
qtyBadge.textContent = String(selection.quantity);
|
|
835
|
+
thumb.appendChild(qtyBadge);
|
|
781
836
|
slot.appendChild(thumb);
|
|
782
837
|
const info = el("div", "lb-mix-match__filled-info");
|
|
783
838
|
const title = el("span", "lb-mix-match__filled-title");
|
|
@@ -788,14 +843,16 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
788
843
|
variant.textContent = selection.variantTitle;
|
|
789
844
|
info.appendChild(variant);
|
|
790
845
|
}
|
|
846
|
+
const linePrice = selection.priceCents * selection.quantity;
|
|
847
|
+
const lineCompare = selection.compareCents !== null ? selection.compareCents * selection.quantity : null;
|
|
791
848
|
const priceWrap = el("span", "lb-mix-match__filled-price");
|
|
792
|
-
if (
|
|
849
|
+
if (lineCompare !== null && lineCompare > linePrice) {
|
|
793
850
|
const compare = el("span", "lb-mix-match__filled-compare");
|
|
794
|
-
compare.textContent = (0, import_core.formatCents)(
|
|
851
|
+
compare.textContent = (0, import_core.formatCents)(lineCompare, currency);
|
|
795
852
|
priceWrap.appendChild(compare);
|
|
796
853
|
}
|
|
797
854
|
const priceEl = document.createElement("span");
|
|
798
|
-
priceEl.textContent = (0, import_core.formatCents)(
|
|
855
|
+
priceEl.textContent = (0, import_core.formatCents)(linePrice, currency);
|
|
799
856
|
priceWrap.appendChild(priceEl);
|
|
800
857
|
info.appendChild(priceWrap);
|
|
801
858
|
if (selection.unitPriceLabel) {
|
|
@@ -836,7 +893,10 @@ function renderPricingSection(showCompareAtPrice) {
|
|
|
836
893
|
return;
|
|
837
894
|
}
|
|
838
895
|
wrap.style.display = "";
|
|
839
|
-
const totalCents = selections.reduce(
|
|
896
|
+
const totalCents = selections.reduce(
|
|
897
|
+
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
898
|
+
0
|
|
899
|
+
);
|
|
840
900
|
const saleCents = (0, import_core.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
841
901
|
if (showCompareAtPrice && totalCents > saleCents) {
|
|
842
902
|
compare.textContent = (0, import_core.formatCents)(totalCents, currency);
|
|
@@ -863,7 +923,10 @@ function renderSavingsBar2() {
|
|
|
863
923
|
wrap.style.display = "none";
|
|
864
924
|
return;
|
|
865
925
|
}
|
|
866
|
-
const totalCents = selections.reduce(
|
|
926
|
+
const totalCents = selections.reduce(
|
|
927
|
+
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
928
|
+
0
|
|
929
|
+
);
|
|
867
930
|
const saleCents = (0, import_core.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
868
931
|
const savings = Math.max(0, totalCents - saleCents);
|
|
869
932
|
if (savings <= 0) {
|
|
@@ -957,8 +1020,8 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
957
1020
|
rowsBuilt = true;
|
|
958
1021
|
list.innerHTML = "";
|
|
959
1022
|
eligible.forEach((ep) => {
|
|
960
|
-
const availableVariants = ep.variants
|
|
961
|
-
const firstAvailVariant =
|
|
1023
|
+
const availableVariants = ep.variants;
|
|
1024
|
+
const firstAvailVariant = ep.variants.find((v) => v.availableForSale) ?? ep.firstAvailableVariant ?? ep.variants[0];
|
|
962
1025
|
if (!firstAvailVariant) return;
|
|
963
1026
|
let currentVariant = firstAvailVariant;
|
|
964
1027
|
const productEl = el(
|
|
@@ -983,7 +1046,9 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
983
1046
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG2);
|
|
984
1047
|
}
|
|
985
1048
|
const countBadge = el("span", "lb-bundle-qty-badge");
|
|
986
|
-
countBadge.
|
|
1049
|
+
countBadge.textContent = String(
|
|
1050
|
+
(0, import_core5.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1051
|
+
);
|
|
987
1052
|
thumb.appendChild(countBadge);
|
|
988
1053
|
productEl.appendChild(thumb);
|
|
989
1054
|
const info = el("div", "lb-mix-match__modal-product-info");
|
|
@@ -992,7 +1057,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
992
1057
|
info.appendChild(title);
|
|
993
1058
|
const price = el("p", "lb-mix-match__modal-product-price");
|
|
994
1059
|
price.textContent = (0, import_core.formatCents)(
|
|
995
|
-
(0, import_core.parseCents)(currentVariant.price.amount),
|
|
1060
|
+
(0, import_core.parseCents)(currentVariant.price.amount) * (0, import_core5.resolveBundleQty)(bundle, ep.product.id, currentVariant.id),
|
|
996
1061
|
currency
|
|
997
1062
|
);
|
|
998
1063
|
info.appendChild(price);
|
|
@@ -1019,28 +1084,53 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1019
1084
|
}
|
|
1020
1085
|
};
|
|
1021
1086
|
if (availableVariants.length > 1) {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
select.setAttribute("data-variant-select", "");
|
|
1025
|
-
select.name = `lb-variant-${ep.product.id.replace(/^.*\//, "")}`;
|
|
1026
|
-
select.setAttribute(
|
|
1027
|
-
"aria-label",
|
|
1028
|
-
`Select variant for ${ep.product.title}`
|
|
1087
|
+
const optionNames = availableVariants[0].selectedOptions.map(
|
|
1088
|
+
(o) => o.name
|
|
1029
1089
|
);
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1090
|
+
const productIdTail = ep.product.id.replace(/^.*\//, "");
|
|
1091
|
+
const optionSelects = [];
|
|
1092
|
+
const resolveVariant = (values) => availableVariants.find(
|
|
1093
|
+
(v) => v.selectedOptions.length === values.length && v.selectedOptions.every((o, i) => o.value === values[i])
|
|
1094
|
+
) ?? null;
|
|
1095
|
+
const syncSelectsToVariant = (v) => {
|
|
1096
|
+
v.selectedOptions.forEach((o, i) => {
|
|
1097
|
+
const sel = optionSelects[i];
|
|
1098
|
+
if (sel && sel.value !== o.value) sel.value = o.value;
|
|
1099
|
+
});
|
|
1100
|
+
};
|
|
1101
|
+
const isValueAvailable = (optionIndex, value, selected) => availableVariants.some((v) => {
|
|
1102
|
+
if (!v.availableForSale) return false;
|
|
1103
|
+
if (v.selectedOptions[optionIndex]?.value !== value) return false;
|
|
1104
|
+
return v.selectedOptions.every(
|
|
1105
|
+
(o, i) => i === optionIndex || o.value === selected[i]
|
|
1106
|
+
);
|
|
1036
1107
|
});
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1108
|
+
const recomputeDisabled = (selected) => {
|
|
1109
|
+
optionSelects.forEach((sel, i) => {
|
|
1110
|
+
Array.from(sel.options).forEach((opt) => {
|
|
1111
|
+
opt.disabled = !isValueAvailable(i, opt.value, selected);
|
|
1112
|
+
});
|
|
1113
|
+
});
|
|
1114
|
+
};
|
|
1115
|
+
const handleChange = () => {
|
|
1116
|
+
const values = optionSelects.map((s) => s.value);
|
|
1117
|
+
const next = resolveVariant(values);
|
|
1118
|
+
if (!next) {
|
|
1119
|
+
syncSelectsToVariant(currentVariant);
|
|
1120
|
+
recomputeDisabled(
|
|
1121
|
+
currentVariant.selectedOptions.map((o) => o.value)
|
|
1122
|
+
);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1040
1125
|
currentVariant = next;
|
|
1041
1126
|
row.variant = next;
|
|
1127
|
+
const nextQty = (0, import_core5.resolveBundleQty)(
|
|
1128
|
+
bundle,
|
|
1129
|
+
ep.product.id,
|
|
1130
|
+
currentVariant.id
|
|
1131
|
+
);
|
|
1042
1132
|
price.textContent = (0, import_core.formatCents)(
|
|
1043
|
-
(0, import_core.parseCents)(currentVariant.price.amount),
|
|
1133
|
+
(0, import_core.parseCents)(currentVariant.price.amount) * nextQty,
|
|
1044
1134
|
currency
|
|
1045
1135
|
);
|
|
1046
1136
|
const nextUnitText = (0, import_core.formatUnitPrice)(
|
|
@@ -1055,9 +1145,43 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1055
1145
|
unitPrice.textContent = "";
|
|
1056
1146
|
unitPrice.hidden = true;
|
|
1057
1147
|
}
|
|
1148
|
+
recomputeDisabled(next.selectedOptions.map((o) => o.value));
|
|
1058
1149
|
rowUpdateCount();
|
|
1150
|
+
};
|
|
1151
|
+
const groupsContainer = el("div", "lb-bundle-variant-option-groups");
|
|
1152
|
+
optionNames.forEach((name, position) => {
|
|
1153
|
+
const group = el("div", "lb-bundle-variant-option-group");
|
|
1154
|
+
const label = el("span", "lb-bundle-variant-option-label");
|
|
1155
|
+
label.textContent = name;
|
|
1156
|
+
group.appendChild(label);
|
|
1157
|
+
const select = document.createElement("select");
|
|
1158
|
+
select.className = "lb-mix-match__variant-select";
|
|
1159
|
+
select.setAttribute("data-variant-option", "");
|
|
1160
|
+
select.setAttribute("data-option-position", String(position + 1));
|
|
1161
|
+
select.name = `lb-variant-${productIdTail}-${position + 1}`;
|
|
1162
|
+
select.setAttribute("aria-label", name);
|
|
1163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1164
|
+
availableVariants.forEach((v) => {
|
|
1165
|
+
const value = v.selectedOptions[position]?.value;
|
|
1166
|
+
if (!value || seen.has(value)) return;
|
|
1167
|
+
seen.add(value);
|
|
1168
|
+
const opt = document.createElement("option");
|
|
1169
|
+
opt.value = value;
|
|
1170
|
+
opt.textContent = value;
|
|
1171
|
+
if (firstAvailVariant.selectedOptions[position]?.value === value) {
|
|
1172
|
+
opt.selected = true;
|
|
1173
|
+
}
|
|
1174
|
+
select.appendChild(opt);
|
|
1175
|
+
});
|
|
1176
|
+
select.addEventListener("change", handleChange);
|
|
1177
|
+
optionSelects.push(select);
|
|
1178
|
+
group.appendChild(select);
|
|
1179
|
+
groupsContainer.appendChild(group);
|
|
1059
1180
|
});
|
|
1060
|
-
info.appendChild(
|
|
1181
|
+
info.appendChild(groupsContainer);
|
|
1182
|
+
recomputeDisabled(
|
|
1183
|
+
firstAvailVariant.selectedOptions.map((o) => o.value)
|
|
1184
|
+
);
|
|
1061
1185
|
} else if (availableVariants.length === 1 && firstAvailVariant.title !== "Default Title") {
|
|
1062
1186
|
const variantLabel = el("span", "lb-mix-match__filled-variant");
|
|
1063
1187
|
variantLabel.textContent = firstAvailVariant.title;
|
|
@@ -1070,13 +1194,9 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1070
1194
|
}
|
|
1071
1195
|
productEl.appendChild(info);
|
|
1072
1196
|
const rowUpdateCount = () => {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
countBadge.style.display = "";
|
|
1077
|
-
} else {
|
|
1078
|
-
countBadge.style.display = "none";
|
|
1079
|
-
}
|
|
1197
|
+
countBadge.textContent = String(
|
|
1198
|
+
(0, import_core5.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1199
|
+
);
|
|
1080
1200
|
};
|
|
1081
1201
|
if (!ep.isOos) {
|
|
1082
1202
|
const addBtn = document.createElement("button");
|
|
@@ -1086,6 +1206,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1086
1206
|
addBtn.addEventListener("click", () => {
|
|
1087
1207
|
if (handlers.isOverMax()) return;
|
|
1088
1208
|
handlers.onAdd(ep.product, currentVariant);
|
|
1209
|
+
close();
|
|
1089
1210
|
});
|
|
1090
1211
|
productEl.appendChild(addBtn);
|
|
1091
1212
|
}
|
|
@@ -1489,7 +1610,62 @@ function clamp(n, min, max) {
|
|
|
1489
1610
|
}
|
|
1490
1611
|
|
|
1491
1612
|
// src/lime-bundle.ts
|
|
1492
|
-
var
|
|
1613
|
+
var import_core7 = require("@lime-bundles/core");
|
|
1614
|
+
|
|
1615
|
+
// src/utils/input-mode.ts
|
|
1616
|
+
var NAV_KEYS = /* @__PURE__ */ new Set([
|
|
1617
|
+
"Tab",
|
|
1618
|
+
"ArrowUp",
|
|
1619
|
+
"ArrowDown",
|
|
1620
|
+
"ArrowLeft",
|
|
1621
|
+
"ArrowRight",
|
|
1622
|
+
"Home",
|
|
1623
|
+
"End",
|
|
1624
|
+
"PageUp",
|
|
1625
|
+
"PageDown",
|
|
1626
|
+
"Enter",
|
|
1627
|
+
" ",
|
|
1628
|
+
"Escape"
|
|
1629
|
+
]);
|
|
1630
|
+
var targets = /* @__PURE__ */ new Set();
|
|
1631
|
+
var listenersAttached = false;
|
|
1632
|
+
function setAll(on) {
|
|
1633
|
+
const off = on === "using-mouse" ? "using-keyboard" : "using-mouse";
|
|
1634
|
+
for (const el2 of targets) {
|
|
1635
|
+
el2.classList.add(on);
|
|
1636
|
+
el2.classList.remove(off);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
function onKeyDown(e) {
|
|
1640
|
+
if (NAV_KEYS.has(e.key)) setAll("using-keyboard");
|
|
1641
|
+
}
|
|
1642
|
+
function onPointerDown() {
|
|
1643
|
+
setAll("using-mouse");
|
|
1644
|
+
}
|
|
1645
|
+
function attachListeners() {
|
|
1646
|
+
if (listenersAttached) return;
|
|
1647
|
+
listenersAttached = true;
|
|
1648
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
1649
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
1650
|
+
}
|
|
1651
|
+
function detachListeners() {
|
|
1652
|
+
if (!listenersAttached) return;
|
|
1653
|
+
listenersAttached = false;
|
|
1654
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
1655
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
1656
|
+
}
|
|
1657
|
+
function trackInputMode(target) {
|
|
1658
|
+
target.classList.add("using-mouse");
|
|
1659
|
+
target.classList.remove("using-keyboard");
|
|
1660
|
+
targets.add(target);
|
|
1661
|
+
attachListeners();
|
|
1662
|
+
return () => {
|
|
1663
|
+
targets.delete(target);
|
|
1664
|
+
target.classList.remove("using-mouse");
|
|
1665
|
+
target.classList.remove("using-keyboard");
|
|
1666
|
+
if (targets.size === 0) detachListeners();
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1493
1669
|
|
|
1494
1670
|
// src/styles/bundle-css.ts
|
|
1495
1671
|
var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle widget types */
|
|
@@ -1775,6 +1951,56 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
|
|
|
1775
1951
|
margin-top: 8px;
|
|
1776
1952
|
}
|
|
1777
1953
|
|
|
1954
|
+
/* Per-option variant pickers. Each option's label + select sit inside a
|
|
1955
|
+
.lb-bundle-variant-option-group (flex column, 2px gap between label and
|
|
1956
|
+
select); the groups stack inside a .lb-bundle-variant-option-groups
|
|
1957
|
+
parent (flex column, 12px gap between groups). The parent owns the top
|
|
1958
|
+
offset from the preceding unit-price line, so individual labels and
|
|
1959
|
+
selects don't carry their own vertical margins. */
|
|
1960
|
+
.lb-bundle-variant-option-groups {
|
|
1961
|
+
display: flex;
|
|
1962
|
+
flex-direction: column;
|
|
1963
|
+
gap: 12px;
|
|
1964
|
+
margin-top: 8px;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
.lb-bundle-variant-option-group {
|
|
1968
|
+
display: flex;
|
|
1969
|
+
flex-direction: column;
|
|
1970
|
+
gap: 2px;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
.lb-bundle-variant-option-label {
|
|
1974
|
+
display: block;
|
|
1975
|
+
margin: 0;
|
|
1976
|
+
font-size: 12px;
|
|
1977
|
+
line-height: 16px;
|
|
1978
|
+
font-weight: 600;
|
|
1979
|
+
letter-spacing: 0.05em;
|
|
1980
|
+
text-transform: uppercase;
|
|
1981
|
+
color: color-mix(in srgb, var(--lb-text) 60%, transparent);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
.lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
|
|
1985
|
+
margin-top: 4px;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/* Focus-ring suppression for mouse users. A small JS helper \u2014 see
|
|
1989
|
+
packages/widget/src/utils/input-mode.ts and bundle-widget.js \u2014 toggles
|
|
1990
|
+
.using-mouse / .using-keyboard on the widget root (or html in the Liquid
|
|
1991
|
+
path) based on the customer's current input device. Default is mouse, so
|
|
1992
|
+
click-to-focus doesn't leave a keyboard-style ring. The modal overlay
|
|
1993
|
+
gets its own selector because the Liquid path reparents it to body,
|
|
1994
|
+
outside the widget root. */
|
|
1995
|
+
.using-mouse .lb-bundle-widget :focus,
|
|
1996
|
+
.using-mouse .lb-bundle-widget :focus-visible,
|
|
1997
|
+
.using-mouse .lb-mix-match__modal-overlay :focus,
|
|
1998
|
+
.using-mouse .lb-mix-match__modal-overlay :focus-visible {
|
|
1999
|
+
outline: none;
|
|
2000
|
+
outline-offset: 0;
|
|
2001
|
+
box-shadow: none;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1778
2004
|
.lb-bundle-quantity {
|
|
1779
2005
|
font-size: 12px;
|
|
1780
2006
|
line-height: 16px;
|
|
@@ -2032,9 +2258,10 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2032
2258
|
border: none;
|
|
2033
2259
|
}
|
|
2034
2260
|
|
|
2035
|
-
/* Variant picker select \u2014 styled to match the variant badge aesthetic
|
|
2261
|
+
/* Variant picker select \u2014 styled to match the variant badge aesthetic.
|
|
2262
|
+
Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
|
|
2263
|
+
by the group/groups flex gap, not the select itself. */
|
|
2036
2264
|
.lb-bundle-variant-select {
|
|
2037
|
-
margin-top: 8px;
|
|
2038
2265
|
display: inline-block;
|
|
2039
2266
|
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
2040
2267
|
border-radius: var(--lb-variant-radius);
|
|
@@ -2051,7 +2278,8 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2051
2278
|
background-repeat: no-repeat;
|
|
2052
2279
|
background-position: right 8px center;
|
|
2053
2280
|
background-size: 12px;
|
|
2054
|
-
|
|
2281
|
+
width: 50%;
|
|
2282
|
+
max-width: 50%;
|
|
2055
2283
|
}
|
|
2056
2284
|
|
|
2057
2285
|
.lb-bundle-variant-select:focus-visible {
|
|
@@ -2458,7 +2686,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2458
2686
|
|
|
2459
2687
|
.lb-mix-match__variant-select {
|
|
2460
2688
|
font-size: 12px;
|
|
2461
|
-
margin: 4px 0 0;
|
|
2462
2689
|
padding: 4px 24px 4px 8px;
|
|
2463
2690
|
border: var(--lb-picker-variant-border-width) solid var(--lb-picker-variant-border-color);
|
|
2464
2691
|
border-radius: var(--lb-picker-variant-radius);
|
|
@@ -2471,7 +2698,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2471
2698
|
font-family: inherit;
|
|
2472
2699
|
min-height: 32px;
|
|
2473
2700
|
cursor: pointer;
|
|
2474
|
-
|
|
2701
|
+
width: 50%;
|
|
2702
|
+
max-width: 50%;
|
|
2475
2703
|
appearance: none;
|
|
2476
2704
|
-webkit-appearance: none;
|
|
2477
2705
|
}
|
|
@@ -2555,6 +2783,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2555
2783
|
.lb-mix-match__modal-overlay--open .lb-mix-match__modal {
|
|
2556
2784
|
transform: translateY(0);
|
|
2557
2785
|
}
|
|
2786
|
+
|
|
2787
|
+
.lb-mix-match__variant-select {
|
|
2788
|
+
width: 80%;
|
|
2789
|
+
max-width: 80%;
|
|
2790
|
+
}
|
|
2558
2791
|
}
|
|
2559
2792
|
|
|
2560
2793
|
/* === Reduced Motion === */
|
|
@@ -2874,7 +3107,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2874
3107
|
const controller = new AbortController();
|
|
2875
3108
|
this.abortController = controller;
|
|
2876
3109
|
this.renderLoading();
|
|
2877
|
-
const client = (0,
|
|
3110
|
+
const client = (0, import_core6.createStorefrontClient)({
|
|
2878
3111
|
shopDomain: this.shopDomain,
|
|
2879
3112
|
accessToken: this.storefrontToken
|
|
2880
3113
|
});
|
|
@@ -2899,7 +3132,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2899
3132
|
handle
|
|
2900
3133
|
);
|
|
2901
3134
|
}
|
|
2902
|
-
const cssPromise = client.query(
|
|
3135
|
+
const cssPromise = client.query(import_core6.SHOP_CUSTOM_CSS_QUERY, void 0, {
|
|
2903
3136
|
signal: controller.signal
|
|
2904
3137
|
}).catch(() => null);
|
|
2905
3138
|
await bundlePromise;
|
|
@@ -2911,8 +3144,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2911
3144
|
}
|
|
2912
3145
|
const css = await cssPromise;
|
|
2913
3146
|
if (css?.shop?.metafield?.value) {
|
|
2914
|
-
(0,
|
|
2915
|
-
const sanitized = (0,
|
|
3147
|
+
(0, import_core6.injectCustomCss)(this.shopDomain, css.shop.metafield.value);
|
|
3148
|
+
const sanitized = (0, import_core6.sanitizeCustomCss)(css.shop.metafield.value);
|
|
2916
3149
|
if (sanitized.ok) this.shopCustomCss = sanitized.css;
|
|
2917
3150
|
}
|
|
2918
3151
|
await this.applyABVariants();
|
|
@@ -2940,14 +3173,14 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2940
3173
|
this.bundles.map(async (bundle) => {
|
|
2941
3174
|
if (!bundle.abTestId || !bundle.abVariantB) return bundle;
|
|
2942
3175
|
try {
|
|
2943
|
-
const assignment = await (0,
|
|
3176
|
+
const assignment = await (0, import_core6.getABTestAssignment)(
|
|
2944
3177
|
this.appUrl,
|
|
2945
3178
|
this.shopDomain,
|
|
2946
3179
|
bundle.abTestId,
|
|
2947
3180
|
bundle.id
|
|
2948
3181
|
);
|
|
2949
3182
|
if (assignment?.variant === "B") {
|
|
2950
|
-
return (0,
|
|
3183
|
+
return (0, import_core6.applyABVariantB)(bundle);
|
|
2951
3184
|
}
|
|
2952
3185
|
} catch (err) {
|
|
2953
3186
|
console.warn(
|
|
@@ -2962,7 +3195,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2962
3195
|
}
|
|
2963
3196
|
async fetchSingleBundle(client, signal) {
|
|
2964
3197
|
const data = await client.query(
|
|
2965
|
-
|
|
3198
|
+
import_core6.BUNDLE_METAOBJECT_QUERY,
|
|
2966
3199
|
{ id: this.bundleGid },
|
|
2967
3200
|
{ signal }
|
|
2968
3201
|
);
|
|
@@ -2970,7 +3203,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2970
3203
|
this.bundles = [];
|
|
2971
3204
|
return;
|
|
2972
3205
|
}
|
|
2973
|
-
const parsed = (0,
|
|
3206
|
+
const parsed = (0, import_core6.parseMetaobjectBundle)(
|
|
2974
3207
|
data.metaobject.id,
|
|
2975
3208
|
data.metaobject.fields
|
|
2976
3209
|
);
|
|
@@ -2978,7 +3211,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2978
3211
|
}
|
|
2979
3212
|
async fetchProductBundles(client, signal, productHandle) {
|
|
2980
3213
|
const data = await client.query(
|
|
2981
|
-
|
|
3214
|
+
import_core6.BUNDLES_FOR_PRODUCT_QUERY,
|
|
2982
3215
|
{ handle: productHandle },
|
|
2983
3216
|
{ signal }
|
|
2984
3217
|
);
|
|
@@ -2989,7 +3222,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2989
3222
|
const refs = data.product.metafield?.references?.nodes ?? [];
|
|
2990
3223
|
const bundles = [];
|
|
2991
3224
|
for (const ref of refs) {
|
|
2992
|
-
const parsed = (0,
|
|
3225
|
+
const parsed = (0, import_core6.parseMetaobjectBundle)(ref.id, ref.fields);
|
|
2993
3226
|
if (parsed) bundles.push(parsed);
|
|
2994
3227
|
}
|
|
2995
3228
|
this.bundles = bundles;
|
|
@@ -3022,7 +3255,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3022
3255
|
*/
|
|
3023
3256
|
async defaultAddToCart(lines) {
|
|
3024
3257
|
if (typeof window === "undefined") return;
|
|
3025
|
-
const client = (0,
|
|
3258
|
+
const client = (0, import_core6.createStorefrontClient)({
|
|
3026
3259
|
shopDomain: this.shopDomain,
|
|
3027
3260
|
accessToken: this.storefrontToken
|
|
3028
3261
|
});
|
|
@@ -3033,7 +3266,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3033
3266
|
let checkoutUrl = null;
|
|
3034
3267
|
if (existingCartId) {
|
|
3035
3268
|
const res = await client.query(
|
|
3036
|
-
|
|
3269
|
+
import_core6.CART_LINES_ADD_MUTATION,
|
|
3037
3270
|
{ cartId: existingCartId, lines }
|
|
3038
3271
|
);
|
|
3039
3272
|
const payload = res.cartLinesAdd;
|
|
@@ -3045,7 +3278,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3045
3278
|
}
|
|
3046
3279
|
if (!checkoutUrl) {
|
|
3047
3280
|
const res = await client.query(
|
|
3048
|
-
|
|
3281
|
+
import_core6.CART_CREATE_MUTATION,
|
|
3049
3282
|
{ input: { lines } }
|
|
3050
3283
|
);
|
|
3051
3284
|
const payload = res.cartCreate;
|
|
@@ -3091,7 +3324,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3091
3324
|
const price = variant ? parseFloat(variant.price.amount) : 0;
|
|
3092
3325
|
return sum + price * line.quantity;
|
|
3093
3326
|
}, 0);
|
|
3094
|
-
(0,
|
|
3327
|
+
(0, import_core6.reportAddToCart)(
|
|
3095
3328
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3096
3329
|
{
|
|
3097
3330
|
bundleGid: bundle.id,
|
|
@@ -3127,7 +3360,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3127
3360
|
container.setAttribute("aria-label", bundle.title);
|
|
3128
3361
|
container.setAttribute("data-bundle-type", bundle.bundleType);
|
|
3129
3362
|
container.setAttribute("data-bundle-gid", bundle.id);
|
|
3130
|
-
(0,
|
|
3363
|
+
(0, import_core7.applyWidgetConfigVars)(container, bundle.widgetConfig);
|
|
3364
|
+
this.renderCleanups.push(trackInputMode(container));
|
|
3131
3365
|
const dispatch = (lines) => this.handleAddToCart(bundle, lines);
|
|
3132
3366
|
const registerCleanup = (fn) => this.renderCleanups.push(fn);
|
|
3133
3367
|
switch (bundle.bundleType) {
|
|
@@ -3162,8 +3396,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3162
3396
|
}
|
|
3163
3397
|
setupImpressionFor(bundle, element) {
|
|
3164
3398
|
if (!this.analyticsEnabled || !this.appUrl) return;
|
|
3165
|
-
const cleanup = (0,
|
|
3166
|
-
(0,
|
|
3399
|
+
const cleanup = (0, import_core6.observeImpression)(element, () => {
|
|
3400
|
+
(0, import_core6.reportImpression)(
|
|
3167
3401
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3168
3402
|
{
|
|
3169
3403
|
bundleGid: bundle.id,
|
|
@@ -3230,6 +3464,7 @@ if (typeof customElements !== "undefined" && !customElements.get("lime-bundle"))
|
|
|
3230
3464
|
}
|
|
3231
3465
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3232
3466
|
0 && (module.exports = {
|
|
3233
|
-
LimeBundleElement
|
|
3467
|
+
LimeBundleElement,
|
|
3468
|
+
trackInputMode
|
|
3234
3469
|
});
|
|
3235
3470
|
//# sourceMappingURL=index.cjs.map
|