@lime-bundles/react 6.2.0 → 7.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.cjs +304 -303
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -41
- package/dist/index.d.ts +15 -41
- package/dist/index.js +138 -140
- package/dist/index.js.map +1 -1
- package/dist/styles.css +35 -7
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,58 +20,55 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
BUNDLES_FOR_PRODUCT_QUERY: () =>
|
|
24
|
-
BUNDLE_METAOBJECT_QUERY: () =>
|
|
23
|
+
BUNDLES_FOR_PRODUCT_QUERY: () => import_core17.BUNDLES_FOR_PRODUCT_QUERY,
|
|
24
|
+
BUNDLE_METAOBJECT_QUERY: () => import_core17.BUNDLE_METAOBJECT_QUERY,
|
|
25
25
|
BogoBundle: () => BogoBundle,
|
|
26
26
|
BundleCountdown: () => BundleCountdown,
|
|
27
|
-
BundleParseError: () =>
|
|
28
|
-
DEFAULT_OUT_OF_STOCK_BEHAVIOR: () => import_core18.DEFAULT_OUT_OF_STOCK_BEHAVIOR,
|
|
27
|
+
BundleParseError: () => import_core16.BundleParseError,
|
|
29
28
|
FixedBundle: () => FixedBundle,
|
|
30
29
|
MixMatchBundle: () => MixMatchBundle,
|
|
31
30
|
MultiStepBundle: () => MultiStepBundle,
|
|
32
|
-
SHOP_SETTINGS_QUERY: () =>
|
|
33
|
-
StorefrontApiError: () =>
|
|
31
|
+
SHOP_SETTINGS_QUERY: () => import_core17.SHOP_SETTINGS_QUERY,
|
|
32
|
+
StorefrontApiError: () => import_core16.StorefrontApiError,
|
|
34
33
|
VariantDropdown: () => VariantDropdown,
|
|
35
34
|
VolumeBundle: () => VolumeBundle,
|
|
36
|
-
WIDGET_CONFIG_DEFAULTS: () =>
|
|
37
|
-
applyWidgetConfigVars: () =>
|
|
38
|
-
calculateDiscount: () =>
|
|
39
|
-
calculateTierSavings: () =>
|
|
40
|
-
computeBundleSaleCents: () =>
|
|
41
|
-
computeFixedPricing: () =>
|
|
42
|
-
createStorefrontClient: () =>
|
|
35
|
+
WIDGET_CONFIG_DEFAULTS: () => import_core17.WIDGET_CONFIG_DEFAULTS,
|
|
36
|
+
applyWidgetConfigVars: () => import_core17.applyWidgetConfigVars,
|
|
37
|
+
calculateDiscount: () => import_core17.calculateDiscount,
|
|
38
|
+
calculateTierSavings: () => import_core17.calculateTierSavings,
|
|
39
|
+
computeBundleSaleCents: () => import_core17.computeBundleSaleCents,
|
|
40
|
+
computeFixedPricing: () => import_core17.computeFixedPricing,
|
|
41
|
+
createStorefrontClient: () => import_core17.createStorefrontClient,
|
|
43
42
|
fetchBundleData: () => fetchBundleData,
|
|
44
|
-
fetchBundlesForProduct: () =>
|
|
43
|
+
fetchBundlesForProduct: () => import_core17.fetchBundlesForProduct,
|
|
45
44
|
fetchShopCustomCss: () => fetchShopCustomCss,
|
|
46
45
|
fetchShopSettings: () => fetchShopSettings,
|
|
47
46
|
fetchShopSettingsOrDefault: () => fetchShopSettingsOrDefault,
|
|
48
|
-
formatCents: () =>
|
|
49
|
-
formatCountdown: () =>
|
|
50
|
-
formatMoney: () =>
|
|
51
|
-
formatUnitPrice: () =>
|
|
52
|
-
getActiveTier: () =>
|
|
53
|
-
injectCustomCss: () =>
|
|
54
|
-
mergeWidgetConfig: () =>
|
|
55
|
-
observeImpression: () =>
|
|
56
|
-
parseCents: () =>
|
|
57
|
-
parseMetaobjectBundle: () =>
|
|
58
|
-
parseMetaobjectBundleStrict: () =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
transformImageUrl: () => import_core18.transformImageUrl,
|
|
47
|
+
formatCents: () => import_core17.formatCents,
|
|
48
|
+
formatCountdown: () => import_core17.formatCountdown,
|
|
49
|
+
formatMoney: () => import_core17.formatMoney,
|
|
50
|
+
formatUnitPrice: () => import_core17.formatUnitPrice,
|
|
51
|
+
getActiveTier: () => import_core17.getActiveTier,
|
|
52
|
+
injectCustomCss: () => import_core17.injectCustomCss,
|
|
53
|
+
mergeWidgetConfig: () => import_core17.mergeWidgetConfig,
|
|
54
|
+
observeImpression: () => import_core17.observeImpression,
|
|
55
|
+
parseCents: () => import_core17.parseCents,
|
|
56
|
+
parseMetaobjectBundle: () => import_core17.parseMetaobjectBundle,
|
|
57
|
+
parseMetaobjectBundleStrict: () => import_core17.parseMetaobjectBundleStrict,
|
|
58
|
+
percentageDiscountUnit: () => import_core17.percentageDiscountUnit,
|
|
59
|
+
productsForStep: () => import_core17.productsForStep,
|
|
60
|
+
reportAddToCart: () => import_core17.reportAddToCart,
|
|
61
|
+
reportImpression: () => import_core17.reportImpression,
|
|
62
|
+
sanitizeCustomCss: () => import_core17.sanitizeCustomCss,
|
|
63
|
+
stepHeadroom: () => import_core17.stepHeadroom,
|
|
64
|
+
thumbnailRatioVars: () => import_core17.thumbnailRatioVars,
|
|
65
|
+
transformImageUrl: () => import_core17.transformImageUrl,
|
|
68
66
|
useAnalytics: () => useAnalytics,
|
|
69
67
|
useBundleData: () => useBundleData,
|
|
70
68
|
useBundlesForProduct: () => useBundlesForProduct,
|
|
71
|
-
useShopSettings: () => useShopSettings,
|
|
72
69
|
useVariantSelection: () => useVariantSelection,
|
|
73
70
|
useWidgetConfigVars: () => useWidgetConfigVars,
|
|
74
|
-
validateQuantity: () =>
|
|
71
|
+
validateQuantity: () => import_core17.validateQuantity
|
|
75
72
|
});
|
|
76
73
|
module.exports = __toCommonJS(index_exports);
|
|
77
74
|
|
|
@@ -141,8 +138,7 @@ async function fetchBundleDataWithWarnings(options) {
|
|
|
141
138
|
// src/fetchShopSettings.ts
|
|
142
139
|
var import_core2 = require("@lime-bundles/core");
|
|
143
140
|
var FALLBACK = {
|
|
144
|
-
customCss: null
|
|
145
|
-
outOfStockBehavior: import_core2.DEFAULT_OUT_OF_STOCK_BEHAVIOR
|
|
141
|
+
customCss: null
|
|
146
142
|
};
|
|
147
143
|
var cache = /* @__PURE__ */ new Map();
|
|
148
144
|
async function fetchShopSettings(options) {
|
|
@@ -160,10 +156,7 @@ async function fetchShopSettings(options) {
|
|
|
160
156
|
{ signal: options.signal }
|
|
161
157
|
);
|
|
162
158
|
return {
|
|
163
|
-
customCss: data.shop?.customCss?.value ?? null
|
|
164
|
-
outOfStockBehavior: (0, import_core2.parseOutOfStockBehavior)(
|
|
165
|
-
data.shop?.outOfStockBehavior?.value
|
|
166
|
-
)
|
|
159
|
+
customCss: data.shop?.customCss?.value ?? null
|
|
167
160
|
};
|
|
168
161
|
})();
|
|
169
162
|
cache.set(options.shopDomain, promise);
|
|
@@ -317,19 +310,25 @@ function useWidgetConfigVars(config) {
|
|
|
317
310
|
var import_react4 = require("react");
|
|
318
311
|
var import_core6 = require("@lime-bundles/core");
|
|
319
312
|
var { findVariantByOptions, isOptionValueAvailable, toPickerVariant } = import_core6.variants;
|
|
320
|
-
function defaultInitial(variants) {
|
|
321
|
-
return variants.find((v) => v.availableForSale) ?? variants[0] ?? null;
|
|
322
|
-
}
|
|
323
313
|
function useVariantSelection({
|
|
324
314
|
variants,
|
|
325
315
|
optionNames,
|
|
326
316
|
initialVariant,
|
|
327
|
-
optionValuesByPosition
|
|
317
|
+
optionValuesByPosition,
|
|
318
|
+
requiredQtyFor
|
|
328
319
|
}) {
|
|
329
|
-
const
|
|
330
|
-
() =>
|
|
331
|
-
[
|
|
320
|
+
const pickerVariants = (0, import_react4.useMemo)(
|
|
321
|
+
() => variants.map((v) => toPickerVariant(v, requiredQtyFor?.(v.id))),
|
|
322
|
+
[variants, requiredQtyFor]
|
|
332
323
|
);
|
|
324
|
+
const seed = (0, import_react4.useMemo)(() => {
|
|
325
|
+
if (initialVariant) return initialVariant;
|
|
326
|
+
const covered = pickerVariants.find((p) => p.available);
|
|
327
|
+
if (covered) {
|
|
328
|
+
return variants.find((v) => v.id === covered.id) ?? null;
|
|
329
|
+
}
|
|
330
|
+
return variants.find((v) => v.availableForSale) ?? variants[0] ?? null;
|
|
331
|
+
}, [initialVariant, variants, pickerVariants]);
|
|
333
332
|
const [selectedValues, setSelectedValues] = (0, import_react4.useState)(
|
|
334
333
|
() => seed ? seed.selectedOptions.map((o) => o.value) : optionNames.map(() => "")
|
|
335
334
|
);
|
|
@@ -337,10 +336,6 @@ function useVariantSelection({
|
|
|
337
336
|
if (!seed) return;
|
|
338
337
|
setSelectedValues(seed.selectedOptions.map((o) => o.value));
|
|
339
338
|
}, [seed?.id]);
|
|
340
|
-
const pickerVariants = (0, import_react4.useMemo)(
|
|
341
|
-
() => variants.map((v) => toPickerVariant(v)),
|
|
342
|
-
[variants]
|
|
343
|
-
);
|
|
344
339
|
const selectedVariant = (0, import_react4.useMemo)(() => {
|
|
345
340
|
if (selectedValues.length !== optionNames.length) return null;
|
|
346
341
|
const match = findVariantByOptions(pickerVariants, selectedValues);
|
|
@@ -968,7 +963,11 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
|
|
|
968
963
|
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
969
964
|
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({
|
|
970
965
|
variants,
|
|
971
|
-
optionNames
|
|
966
|
+
optionNames,
|
|
967
|
+
// Quantity-aware dropdown: a variant that can't cover this row's
|
|
968
|
+
// bundle quantity greys out instead of walking the shopper into a
|
|
969
|
+
// cart Shopify caps at add time.
|
|
970
|
+
requiredQtyFor: (variantId) => (0, import_core8.resolveBundleQty)(bundle, product.id, variantId)
|
|
972
971
|
});
|
|
973
972
|
(0, import_react8.useEffect)(() => {
|
|
974
973
|
onVariantChange(product.id, selectedVariant ?? null);
|
|
@@ -1063,41 +1062,16 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
|
|
|
1063
1062
|
}
|
|
1064
1063
|
|
|
1065
1064
|
// src/components/MixMatchBundle.tsx
|
|
1066
|
-
var
|
|
1067
|
-
var
|
|
1068
|
-
|
|
1069
|
-
// src/hooks/useShopSettings.ts
|
|
1070
|
-
var import_react9 = require("react");
|
|
1071
|
-
var import_core9 = require("@lime-bundles/core");
|
|
1072
|
-
function useShopSettings(options) {
|
|
1073
|
-
const [outOfStockBehavior, setOutOfStockBehavior] = (0, import_react9.useState)(import_core9.DEFAULT_OUT_OF_STOCK_BEHAVIOR);
|
|
1074
|
-
const { shopDomain, storefrontAccessToken } = options;
|
|
1075
|
-
(0, import_react9.useEffect)(() => {
|
|
1076
|
-
const controller = new AbortController();
|
|
1077
|
-
let active = true;
|
|
1078
|
-
fetchShopSettingsOrDefault({
|
|
1079
|
-
shopDomain,
|
|
1080
|
-
storefrontAccessToken,
|
|
1081
|
-
signal: controller.signal
|
|
1082
|
-
}).then((settings) => {
|
|
1083
|
-
if (!active || controller.signal.aborted) return;
|
|
1084
|
-
setOutOfStockBehavior(settings.outOfStockBehavior);
|
|
1085
|
-
});
|
|
1086
|
-
return () => {
|
|
1087
|
-
active = false;
|
|
1088
|
-
controller.abort();
|
|
1089
|
-
};
|
|
1090
|
-
}, [shopDomain, storefrontAccessToken]);
|
|
1091
|
-
return { outOfStockBehavior };
|
|
1092
|
-
}
|
|
1065
|
+
var import_react12 = require("react");
|
|
1066
|
+
var import_core10 = require("@lime-bundles/core");
|
|
1093
1067
|
|
|
1094
1068
|
// src/components/MixMatchPicker.tsx
|
|
1095
|
-
var
|
|
1069
|
+
var import_react11 = require("react");
|
|
1096
1070
|
var import_react_dom = require("react-dom");
|
|
1097
|
-
var
|
|
1071
|
+
var import_core9 = require("@lime-bundles/core");
|
|
1098
1072
|
|
|
1099
1073
|
// src/hooks/useScrollLock.ts
|
|
1100
|
-
var
|
|
1074
|
+
var import_react9 = require("react");
|
|
1101
1075
|
var lockCount = 0;
|
|
1102
1076
|
var savedY = 0;
|
|
1103
1077
|
function lock() {
|
|
@@ -1126,7 +1100,7 @@ function unlock() {
|
|
|
1126
1100
|
window.scrollTo(0, savedY);
|
|
1127
1101
|
}
|
|
1128
1102
|
function useScrollLock(active) {
|
|
1129
|
-
(0,
|
|
1103
|
+
(0, import_react9.useEffect)(() => {
|
|
1130
1104
|
if (!active) return;
|
|
1131
1105
|
lock();
|
|
1132
1106
|
return unlock;
|
|
@@ -1134,7 +1108,7 @@ function useScrollLock(active) {
|
|
|
1134
1108
|
}
|
|
1135
1109
|
|
|
1136
1110
|
// src/hooks/useFocusTrap.ts
|
|
1137
|
-
var
|
|
1111
|
+
var import_react10 = require("react");
|
|
1138
1112
|
var FOCUSABLE_SELECTOR = [
|
|
1139
1113
|
"a[href]",
|
|
1140
1114
|
"button:not([disabled])",
|
|
@@ -1157,14 +1131,16 @@ function useFocusTrap({
|
|
|
1157
1131
|
active,
|
|
1158
1132
|
containerRef,
|
|
1159
1133
|
onEscape,
|
|
1134
|
+
initialFocusRef,
|
|
1160
1135
|
returnFocusRef
|
|
1161
1136
|
}) {
|
|
1162
|
-
const previouslyFocused = (0,
|
|
1163
|
-
(0,
|
|
1137
|
+
const previouslyFocused = (0, import_react10.useRef)(null);
|
|
1138
|
+
(0, import_react10.useEffect)(() => {
|
|
1164
1139
|
if (!active) return;
|
|
1165
1140
|
const container = containerRef.current;
|
|
1166
1141
|
previouslyFocused.current = document.activeElement ?? null;
|
|
1167
|
-
container
|
|
1142
|
+
const initial = initialFocusRef?.current ?? container;
|
|
1143
|
+
initial?.focus();
|
|
1168
1144
|
function onKeyDown(e) {
|
|
1169
1145
|
if (e.key === "Escape") {
|
|
1170
1146
|
e.preventDefault();
|
|
@@ -1208,7 +1184,7 @@ function ruleFor(bundle, productId, variantId) {
|
|
|
1208
1184
|
const vRule = bundle.variantRules[variantId];
|
|
1209
1185
|
if (vRule) return vRule;
|
|
1210
1186
|
}
|
|
1211
|
-
return bundle.productRules[productId] ??
|
|
1187
|
+
return bundle.productRules[productId] ?? import_core9.DEFAULT_PRODUCT_RULE;
|
|
1212
1188
|
}
|
|
1213
1189
|
function variantRuleFor(bundle, variantId) {
|
|
1214
1190
|
return variantId ? bundle.variantRules[variantId] : void 0;
|
|
@@ -1216,7 +1192,7 @@ function variantRuleFor(bundle, variantId) {
|
|
|
1216
1192
|
function sanitizeId(gid) {
|
|
1217
1193
|
return gid.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
1218
1194
|
}
|
|
1219
|
-
function buildEligibleProducts(bundle
|
|
1195
|
+
function buildEligibleProducts(bundle) {
|
|
1220
1196
|
const result = [];
|
|
1221
1197
|
const seen = /* @__PURE__ */ new Set();
|
|
1222
1198
|
for (const product of bundle.products) {
|
|
@@ -1224,10 +1200,9 @@ function buildEligibleProducts(bundle, oosBehavior) {
|
|
|
1224
1200
|
seen.add(product.id);
|
|
1225
1201
|
const rule = ruleFor(bundle, product.id);
|
|
1226
1202
|
const available = product.variants.nodes.filter(
|
|
1227
|
-
(v) => (0,
|
|
1203
|
+
(v) => (0, import_core9.isVariantFulfillable)(v, rule.min)
|
|
1228
1204
|
);
|
|
1229
1205
|
const isOos = available.length === 0;
|
|
1230
|
-
if (isOos && oosBehavior === "hide") continue;
|
|
1231
1206
|
result.push({
|
|
1232
1207
|
product,
|
|
1233
1208
|
variants: product.variants.nodes,
|
|
@@ -1242,7 +1217,6 @@ function normalizeText(str) {
|
|
|
1242
1217
|
}
|
|
1243
1218
|
var FILTERS_ALL = "__all__";
|
|
1244
1219
|
function MixMatchPicker({
|
|
1245
|
-
outOfStockBehavior,
|
|
1246
1220
|
bundle,
|
|
1247
1221
|
currency,
|
|
1248
1222
|
requiredQty,
|
|
@@ -1258,17 +1232,18 @@ function MixMatchPicker({
|
|
|
1258
1232
|
styleVarsRef
|
|
1259
1233
|
}) {
|
|
1260
1234
|
const wc = bundle.widgetConfig;
|
|
1261
|
-
const overlayRef = (0,
|
|
1262
|
-
const dialogRef = (0,
|
|
1263
|
-
const
|
|
1264
|
-
const
|
|
1265
|
-
const [
|
|
1266
|
-
const [
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1235
|
+
const overlayRef = (0, import_react11.useRef)(null);
|
|
1236
|
+
const dialogRef = (0, import_react11.useRef)(null);
|
|
1237
|
+
const closeBtnRef = (0, import_react11.useRef)(null);
|
|
1238
|
+
const mouseDownTarget = (0, import_react11.useRef)(null);
|
|
1239
|
+
const [open, setOpen] = (0, import_react11.useState)(false);
|
|
1240
|
+
const [query, setQuery] = (0, import_react11.useState)("");
|
|
1241
|
+
const [activeType, setActiveType] = (0, import_react11.useState)(FILTERS_ALL);
|
|
1242
|
+
const eligible = (0, import_react11.useMemo)(
|
|
1243
|
+
() => buildEligibleProducts(bundle),
|
|
1244
|
+
[bundle]
|
|
1270
1245
|
);
|
|
1271
|
-
const productTypes = (0,
|
|
1246
|
+
const productTypes = (0, import_react11.useMemo)(() => {
|
|
1272
1247
|
const types = [];
|
|
1273
1248
|
const seen = /* @__PURE__ */ new Set();
|
|
1274
1249
|
for (const ep of eligible) {
|
|
@@ -1284,7 +1259,7 @@ function MixMatchPicker({
|
|
|
1284
1259
|
const totalUnits = selections.reduce((sum, s) => sum + s.quantity, 0);
|
|
1285
1260
|
const atCapacity = totalUnits >= requiredQty;
|
|
1286
1261
|
const shownUnits = Math.min(totalUnits, requiredQty);
|
|
1287
|
-
(0,
|
|
1262
|
+
(0, import_react11.useEffect)(() => {
|
|
1288
1263
|
const id = requestAnimationFrame(() => setOpen(true));
|
|
1289
1264
|
return () => cancelAnimationFrame(id);
|
|
1290
1265
|
}, []);
|
|
@@ -1292,6 +1267,7 @@ function MixMatchPicker({
|
|
|
1292
1267
|
useFocusTrap({
|
|
1293
1268
|
active: true,
|
|
1294
1269
|
containerRef: dialogRef,
|
|
1270
|
+
initialFocusRef: closeBtnRef,
|
|
1295
1271
|
onEscape: onClose
|
|
1296
1272
|
});
|
|
1297
1273
|
const remaining = Math.max(0, requiredQty - shownUnits);
|
|
@@ -1300,14 +1276,25 @@ function MixMatchPicker({
|
|
|
1300
1276
|
const pct = bundle.discountConfig.discountType === "percentage" && bundle.discountConfig.discountValue ? Math.round(bundle.discountConfig.discountValue) : 0;
|
|
1301
1277
|
return pct > 0 ? `Choose ${remaining} more to unlock ${pct}% off` : `Choose ${remaining} more to complete your bundle`;
|
|
1302
1278
|
})();
|
|
1303
|
-
const
|
|
1279
|
+
const [selectedAtOpen] = (0, import_react11.useState)(
|
|
1280
|
+
() => new Set(selections.map((s) => s.productId))
|
|
1281
|
+
);
|
|
1282
|
+
const ordered = (0, import_react11.useMemo)(() => {
|
|
1283
|
+
const front = [];
|
|
1284
|
+
const back = [];
|
|
1285
|
+
for (const ep of eligible) {
|
|
1286
|
+
(selectedAtOpen.has(ep.product.id) ? back : front).push(ep);
|
|
1287
|
+
}
|
|
1288
|
+
return front.concat(back);
|
|
1289
|
+
}, [eligible, selectedAtOpen]);
|
|
1290
|
+
const filtered = (0, import_react11.useMemo)(() => {
|
|
1304
1291
|
const nq = normalizeText(query.trim());
|
|
1305
|
-
return
|
|
1292
|
+
return ordered.filter((ep) => {
|
|
1306
1293
|
const matchesQuery = !nq || normalizeText(ep.product.title).includes(nq);
|
|
1307
1294
|
const matchesType = activeType === FILTERS_ALL || (ep.product.productType ?? "") === activeType;
|
|
1308
1295
|
return matchesQuery && matchesType;
|
|
1309
1296
|
});
|
|
1310
|
-
}, [
|
|
1297
|
+
}, [ordered, query, activeType]);
|
|
1311
1298
|
const showEmpty = filtered.length === 0 && query.trim().length > 0;
|
|
1312
1299
|
function onOverlayMouseDown(e) {
|
|
1313
1300
|
mouseDownTarget.current = e.target;
|
|
@@ -1356,6 +1343,7 @@ function MixMatchPicker({
|
|
|
1356
1343
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1357
1344
|
"button",
|
|
1358
1345
|
{
|
|
1346
|
+
ref: closeBtnRef,
|
|
1359
1347
|
type: "button",
|
|
1360
1348
|
className: "lb-mix-match__modal-close",
|
|
1361
1349
|
"data-modal-close": true,
|
|
@@ -1510,16 +1498,16 @@ function MixMatchPickerRow({
|
|
|
1510
1498
|
}) {
|
|
1511
1499
|
const { product, variants, isOos } = eligible;
|
|
1512
1500
|
const rule = ruleFor(bundle, product.id);
|
|
1513
|
-
const optionNames = (0,
|
|
1501
|
+
const optionNames = (0, import_react11.useMemo)(() => {
|
|
1514
1502
|
const first = variants[0];
|
|
1515
1503
|
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
1516
1504
|
}, [variants]);
|
|
1517
1505
|
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
1518
1506
|
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
|
|
1519
|
-
const currentVariant = selectedVariant ?? variants.find((v) => (0,
|
|
1507
|
+
const currentVariant = selectedVariant ?? variants.find((v) => (0, import_core9.isVariantFulfillable)(v, rule.min)) ?? eligible.firstAvailableVariant ?? variants[0] ?? null;
|
|
1520
1508
|
const vRule = ruleFor(bundle, product.id, currentVariant?.id);
|
|
1521
1509
|
const variantMax = variantRuleFor(bundle, currentVariant?.id)?.max;
|
|
1522
|
-
const alreadyInBundle = (0,
|
|
1510
|
+
const alreadyInBundle = (0, import_react11.useMemo)(() => {
|
|
1523
1511
|
if (!currentVariant) return 0;
|
|
1524
1512
|
let sum = 0;
|
|
1525
1513
|
for (const s of selections) {
|
|
@@ -1529,13 +1517,13 @@ function MixMatchPickerRow({
|
|
|
1529
1517
|
}
|
|
1530
1518
|
return sum;
|
|
1531
1519
|
}, [selections, product.id, currentVariant]);
|
|
1532
|
-
const committedSelection = (0,
|
|
1520
|
+
const committedSelection = (0, import_react11.useMemo)(
|
|
1533
1521
|
() => currentVariant ? selections.find((s) => s.variantId === currentVariant.id) ?? null : null,
|
|
1534
1522
|
[selections, currentVariant]
|
|
1535
1523
|
);
|
|
1536
1524
|
const variantInBundle = committedSelection !== null;
|
|
1537
1525
|
const committedQty = committedSelection?.quantity ?? 0;
|
|
1538
|
-
const productOtherUnits = (0,
|
|
1526
|
+
const productOtherUnits = (0, import_react11.useMemo)(() => {
|
|
1539
1527
|
if (!currentVariant) return 0;
|
|
1540
1528
|
let sum = 0;
|
|
1541
1529
|
for (const s of selections) {
|
|
@@ -1552,23 +1540,23 @@ function MixMatchPickerRow({
|
|
|
1552
1540
|
committedQty + remainingUnits,
|
|
1553
1541
|
variantMax ?? Infinity
|
|
1554
1542
|
);
|
|
1555
|
-
const cap = currentVariant ? (0,
|
|
1543
|
+
const cap = currentVariant ? (0, import_core9.maxAddableQuantity)(
|
|
1556
1544
|
currentVariant,
|
|
1557
1545
|
spotCeiling,
|
|
1558
1546
|
Math.max(0, alreadyInBundle - committedQty)
|
|
1559
1547
|
) : 0;
|
|
1560
1548
|
const stepperMax = Math.max(vRule.min, cap);
|
|
1561
|
-
const [qty, setQty] = (0,
|
|
1562
|
-
(0,
|
|
1549
|
+
const [qty, setQty] = (0, import_react11.useState)(vRule.min);
|
|
1550
|
+
(0, import_react11.useEffect)(() => {
|
|
1563
1551
|
setQty((prev) => Math.max(vRule.min, Math.min(prev, stepperMax)));
|
|
1564
1552
|
}, [stepperMax, vRule.min]);
|
|
1565
1553
|
const currentVariantId = currentVariant?.id ?? null;
|
|
1566
|
-
(0,
|
|
1554
|
+
(0, import_react11.useEffect)(() => {
|
|
1567
1555
|
if (!variantInBundle) setQty(vRule.min);
|
|
1568
1556
|
}, [variantInBundle, currentVariantId, vRule.min]);
|
|
1569
1557
|
if (!currentVariant) return null;
|
|
1570
1558
|
const thumbImage = currentVariant.image ?? product.featuredImage ?? null;
|
|
1571
|
-
const unitPriceText = (0,
|
|
1559
|
+
const unitPriceText = (0, import_core9.formatUnitPrice)(
|
|
1572
1560
|
currentVariant.unitPrice,
|
|
1573
1561
|
currentVariant.unitPriceMeasurement,
|
|
1574
1562
|
currency
|
|
@@ -1638,8 +1626,8 @@ function MixMatchPickerRow({
|
|
|
1638
1626
|
}
|
|
1639
1627
|
) }),
|
|
1640
1628
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
|
|
1641
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-row-price-sale": true, children: (0,
|
|
1642
|
-
compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0,
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-row-price-sale": true, children: (0, import_core9.formatMoney)(priceCents, currency) }),
|
|
1630
|
+
compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core9.formatMoney)(compareCents, currency) })
|
|
1643
1631
|
] }),
|
|
1644
1632
|
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
|
|
1645
1633
|
showPicker && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
|
|
@@ -1790,7 +1778,7 @@ function ruleFor2(bundle, productId, variantId) {
|
|
|
1790
1778
|
const vRule = bundle.variantRules[variantId];
|
|
1791
1779
|
if (vRule) return vRule;
|
|
1792
1780
|
}
|
|
1793
|
-
return bundle.productRules[productId] ??
|
|
1781
|
+
return bundle.productRules[productId] ?? import_core10.DEFAULT_PRODUCT_RULE;
|
|
1794
1782
|
}
|
|
1795
1783
|
function variantRuleFor2(bundle, variantId) {
|
|
1796
1784
|
return variantId ? bundle.variantRules[variantId] : void 0;
|
|
@@ -1825,10 +1813,6 @@ function MixMatchBundle(props) {
|
|
|
1825
1813
|
storefrontAccessToken,
|
|
1826
1814
|
bundleGid
|
|
1827
1815
|
});
|
|
1828
|
-
const { outOfStockBehavior } = useShopSettings({
|
|
1829
|
-
shopDomain,
|
|
1830
|
-
storefrontAccessToken
|
|
1831
|
-
});
|
|
1832
1816
|
const { elementRef, trackAddToCart } = useAnalytics({
|
|
1833
1817
|
shopDomain,
|
|
1834
1818
|
appUrl: appUrl ?? `https://${shopDomain}`,
|
|
@@ -1836,16 +1820,16 @@ function MixMatchBundle(props) {
|
|
|
1836
1820
|
bundleType: "mix_match",
|
|
1837
1821
|
enabled: analyticsEnabled !== false
|
|
1838
1822
|
});
|
|
1839
|
-
const [selections, setSelections] = (0,
|
|
1840
|
-
const [seededFor, setSeededFor] = (0,
|
|
1841
|
-
const [pickerOpen, setPickerOpen] = (0,
|
|
1842
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
1843
|
-
const [cartError, setCartError] = (0,
|
|
1823
|
+
const [selections, setSelections] = (0, import_react12.useState)([]);
|
|
1824
|
+
const [seededFor, setSeededFor] = (0, import_react12.useState)(null);
|
|
1825
|
+
const [pickerOpen, setPickerOpen] = (0, import_react12.useState)(false);
|
|
1826
|
+
const [addingToCart, setAddingToCart] = (0, import_react12.useState)(false);
|
|
1827
|
+
const [cartError, setCartError] = (0, import_react12.useState)(null);
|
|
1844
1828
|
const bundle = result.status === "success" && result.bundle.bundleType === "mix_match" ? result.bundle : null;
|
|
1845
1829
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
1846
1830
|
const setModalConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
1847
1831
|
const slotsEdgeFadeRef = useEdgeFade();
|
|
1848
|
-
(0,
|
|
1832
|
+
(0, import_react12.useEffect)(() => {
|
|
1849
1833
|
if (result.status === "error") {
|
|
1850
1834
|
onError?.(result.error);
|
|
1851
1835
|
return;
|
|
@@ -1858,7 +1842,7 @@ function MixMatchBundle(props) {
|
|
|
1858
1842
|
);
|
|
1859
1843
|
}
|
|
1860
1844
|
}, [result, onError]);
|
|
1861
|
-
(0,
|
|
1845
|
+
(0, import_react12.useEffect)(() => {
|
|
1862
1846
|
if (!bundle || seededFor === bundle.id) return;
|
|
1863
1847
|
setSeededFor(bundle.id);
|
|
1864
1848
|
const seedCurrency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
@@ -1871,7 +1855,7 @@ function MixMatchBundle(props) {
|
|
|
1871
1855
|
featuredImage: variant.image?.url ?? product.featuredImage?.url ?? null,
|
|
1872
1856
|
price: parseFloat(variant.price.amount),
|
|
1873
1857
|
compareAtPrice: variant.compareAtPrice ? parseFloat(variant.compareAtPrice.amount) : null,
|
|
1874
|
-
unitPrice: (0,
|
|
1858
|
+
unitPrice: (0, import_core10.formatUnitPrice)(
|
|
1875
1859
|
variant.unitPrice,
|
|
1876
1860
|
variant.unitPriceMeasurement,
|
|
1877
1861
|
seedCurrency
|
|
@@ -1884,7 +1868,7 @@ function MixMatchBundle(props) {
|
|
|
1884
1868
|
for (const variant2 of product.variants.nodes) {
|
|
1885
1869
|
const vRule = bundle.variantRules[variant2.id];
|
|
1886
1870
|
if (!vRule?.required) continue;
|
|
1887
|
-
if (!(0,
|
|
1871
|
+
if (!(0, import_core10.isVariantFulfillable)(variant2, Math.max(1, vRule.min))) continue;
|
|
1888
1872
|
seeds.push(pick(product, variant2, Math.max(1, vRule.min)));
|
|
1889
1873
|
seededVariant = true;
|
|
1890
1874
|
}
|
|
@@ -1892,7 +1876,7 @@ function MixMatchBundle(props) {
|
|
|
1892
1876
|
const rule = bundle.productRules[product.id];
|
|
1893
1877
|
if (!rule?.required) continue;
|
|
1894
1878
|
const variant = product.variants.nodes.find(
|
|
1895
|
-
(v) => (0,
|
|
1879
|
+
(v) => (0, import_core10.isVariantFulfillable)(v, rule.min)
|
|
1896
1880
|
);
|
|
1897
1881
|
if (!variant) continue;
|
|
1898
1882
|
seeds.push(pick(product, variant, rule.min));
|
|
@@ -1901,7 +1885,7 @@ function MixMatchBundle(props) {
|
|
|
1901
1885
|
const requiredUnits2 = bundle.minQuantity ?? 0;
|
|
1902
1886
|
const qualifies = (p) => {
|
|
1903
1887
|
const min = bundle.productRules[p.id]?.min ?? 1;
|
|
1904
|
-
return min <= requiredUnits2 && p.variants.nodes.some((v) => (0,
|
|
1888
|
+
return min <= requiredUnits2 && p.variants.nodes.some((v) => (0, import_core10.isVariantFulfillable)(v, min));
|
|
1905
1889
|
};
|
|
1906
1890
|
const seed = bundle.products.find(
|
|
1907
1891
|
(p) => p.handle === productHandle && qualifies(p)
|
|
@@ -1909,7 +1893,7 @@ function MixMatchBundle(props) {
|
|
|
1909
1893
|
if (seed) {
|
|
1910
1894
|
const min = bundle.productRules[seed.id]?.min ?? 1;
|
|
1911
1895
|
const variant = seed.variants.nodes.find(
|
|
1912
|
-
(v) => (0,
|
|
1896
|
+
(v) => (0, import_core10.isVariantFulfillable)(v, min)
|
|
1913
1897
|
);
|
|
1914
1898
|
if (variant) seeds.push(pick(seed, variant, min));
|
|
1915
1899
|
}
|
|
@@ -1918,7 +1902,7 @@ function MixMatchBundle(props) {
|
|
|
1918
1902
|
}, [bundle, seededFor, productHandle]);
|
|
1919
1903
|
const showStepper = bundle?.widgetConfig.mixMatchShowQuantitySelector !== false;
|
|
1920
1904
|
const requiredUnits = bundle?.minQuantity ?? 0;
|
|
1921
|
-
const totalQuantity = (0,
|
|
1905
|
+
const totalQuantity = (0, import_react12.useMemo)(
|
|
1922
1906
|
() => selections.reduce((sum, s) => sum + s.quantity, 0),
|
|
1923
1907
|
[selections]
|
|
1924
1908
|
);
|
|
@@ -1926,14 +1910,14 @@ function MixMatchBundle(props) {
|
|
|
1926
1910
|
const valid = !!bundle && meetsMinUnits;
|
|
1927
1911
|
const shownUnits = Math.min(totalQuantity, requiredUnits);
|
|
1928
1912
|
const remaining = Math.max(0, requiredUnits - shownUnits);
|
|
1929
|
-
const summary = (0,
|
|
1913
|
+
const summary = (0, import_react12.useMemo)(() => {
|
|
1930
1914
|
let comparePrice = 0;
|
|
1931
1915
|
let salePrice = 0;
|
|
1932
1916
|
for (const sel of selections) {
|
|
1933
1917
|
if (sel.quantity <= 0) continue;
|
|
1934
1918
|
comparePrice += sel.price * sel.quantity;
|
|
1935
1919
|
if (bundle) {
|
|
1936
|
-
salePrice += (0,
|
|
1920
|
+
salePrice += (0, import_core10.calculateDiscount)(
|
|
1937
1921
|
sel.price,
|
|
1938
1922
|
bundle.discountConfig.discountType,
|
|
1939
1923
|
bundle.discountConfig.discountValue
|
|
@@ -1944,7 +1928,7 @@ function MixMatchBundle(props) {
|
|
|
1944
1928
|
const savingsPercent = comparePrice > 0 && savings > 0 ? Math.round(savings / comparePrice * 100) : 0;
|
|
1945
1929
|
return { comparePrice, salePrice, savings, savingsPercent };
|
|
1946
1930
|
}, [selections, bundle]);
|
|
1947
|
-
const addSelection = (0,
|
|
1931
|
+
const addSelection = (0, import_react12.useCallback)(
|
|
1948
1932
|
(item) => {
|
|
1949
1933
|
setSelections((prev) => {
|
|
1950
1934
|
const units = prev.reduce((sum, s) => sum + s.quantity, 0);
|
|
@@ -1955,7 +1939,7 @@ function MixMatchBundle(props) {
|
|
|
1955
1939
|
},
|
|
1956
1940
|
[requiredUnits]
|
|
1957
1941
|
);
|
|
1958
|
-
const updateQuantity = (0,
|
|
1942
|
+
const updateQuantity = (0, import_react12.useCallback)(
|
|
1959
1943
|
(productId, variantId, quantity) => {
|
|
1960
1944
|
setSelections((prev) => {
|
|
1961
1945
|
const idx = prev.findIndex(
|
|
@@ -1978,7 +1962,7 @@ function MixMatchBundle(props) {
|
|
|
1978
1962
|
},
|
|
1979
1963
|
[requiredUnits]
|
|
1980
1964
|
);
|
|
1981
|
-
const swapSelection = (0,
|
|
1965
|
+
const swapSelection = (0, import_react12.useCallback)(
|
|
1982
1966
|
(item) => {
|
|
1983
1967
|
setSelections((prev) => {
|
|
1984
1968
|
if (!bundle) return prev;
|
|
@@ -2010,7 +1994,7 @@ function MixMatchBundle(props) {
|
|
|
2010
1994
|
},
|
|
2011
1995
|
[bundle]
|
|
2012
1996
|
);
|
|
2013
|
-
const removeVariant = (0,
|
|
1997
|
+
const removeVariant = (0, import_react12.useCallback)(
|
|
2014
1998
|
(variantId) => {
|
|
2015
1999
|
setSelections((prev) => {
|
|
2016
2000
|
const idx = prev.findIndex((s) => s.variantId === variantId);
|
|
@@ -2023,7 +2007,7 @@ function MixMatchBundle(props) {
|
|
|
2023
2007
|
},
|
|
2024
2008
|
[bundle]
|
|
2025
2009
|
);
|
|
2026
|
-
const removeSlot = (0,
|
|
2010
|
+
const removeSlot = (0, import_react12.useCallback)(
|
|
2027
2011
|
(index) => {
|
|
2028
2012
|
setSelections((prev) => {
|
|
2029
2013
|
if (index < 0 || index >= prev.length) return prev;
|
|
@@ -2035,7 +2019,7 @@ function MixMatchBundle(props) {
|
|
|
2035
2019
|
},
|
|
2036
2020
|
[bundle]
|
|
2037
2021
|
);
|
|
2038
|
-
const handleAddToCart = (0,
|
|
2022
|
+
const handleAddToCart = (0, import_react12.useCallback)(async () => {
|
|
2039
2023
|
if (!bundle || !valid) return;
|
|
2040
2024
|
const grouped = /* @__PURE__ */ new Map();
|
|
2041
2025
|
for (const sel of selections) {
|
|
@@ -2089,7 +2073,7 @@ function MixMatchBundle(props) {
|
|
|
2089
2073
|
if (!rule.required) return false;
|
|
2090
2074
|
const product = bundle.products.find((p) => p.id === pid);
|
|
2091
2075
|
return !product?.variants.nodes.some(
|
|
2092
|
-
(v) => (0,
|
|
2076
|
+
(v) => (0, import_core10.isVariantFulfillable)(v, rule.min)
|
|
2093
2077
|
);
|
|
2094
2078
|
}
|
|
2095
2079
|
);
|
|
@@ -2189,8 +2173,8 @@ function MixMatchBundle(props) {
|
|
|
2189
2173
|
),
|
|
2190
2174
|
item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
|
|
2191
2175
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
|
|
2192
|
-
item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0,
|
|
2193
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-product-price", children: (0,
|
|
2176
|
+
item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core10.formatMoney)(item.compareAtPrice, currency) }),
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core10.formatMoney)(item.price, currency) }),
|
|
2194
2178
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
|
|
2195
2179
|
] }),
|
|
2196
2180
|
item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
|
|
@@ -2240,7 +2224,7 @@ function MixMatchBundle(props) {
|
|
|
2240
2224
|
bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
|
|
2241
2225
|
"You save",
|
|
2242
2226
|
" ",
|
|
2243
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-savings-amount": true, children: (0,
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-savings-amount": true, children: (0, import_core10.formatMoney)(summary.savings, currency) }),
|
|
2244
2228
|
" ",
|
|
2245
2229
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "data-savings-percent": true, children: [
|
|
2246
2230
|
"(",
|
|
@@ -2250,8 +2234,8 @@ function MixMatchBundle(props) {
|
|
|
2250
2234
|
] })
|
|
2251
2235
|
] }),
|
|
2252
2236
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
|
|
2253
|
-
bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0,
|
|
2254
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0,
|
|
2237
|
+
bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core10.formatMoney)(summary.comparePrice, currency) }),
|
|
2238
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core10.formatMoney)(summary.salePrice, currency) })
|
|
2255
2239
|
] })
|
|
2256
2240
|
] }),
|
|
2257
2241
|
cartError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
@@ -2268,7 +2252,6 @@ function MixMatchBundle(props) {
|
|
|
2268
2252
|
pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2269
2253
|
MixMatchPicker,
|
|
2270
2254
|
{
|
|
2271
|
-
outOfStockBehavior,
|
|
2272
2255
|
bundle,
|
|
2273
2256
|
currency,
|
|
2274
2257
|
requiredQty: requiredUnits,
|
|
@@ -2299,7 +2282,7 @@ function countInStockProducts(bundle) {
|
|
|
2299
2282
|
seen.add(product.id);
|
|
2300
2283
|
const rule = ruleFor2(bundle, product.id);
|
|
2301
2284
|
const fulfillable = product.variants.nodes.some(
|
|
2302
|
-
(v) => (0,
|
|
2285
|
+
(v) => (0, import_core10.isVariantFulfillable)(v, rule.min)
|
|
2303
2286
|
);
|
|
2304
2287
|
if (fulfillable) count += 1;
|
|
2305
2288
|
}
|
|
@@ -2341,8 +2324,8 @@ function CloseIcon2() {
|
|
|
2341
2324
|
}
|
|
2342
2325
|
|
|
2343
2326
|
// src/components/VolumeBundle.tsx
|
|
2344
|
-
var
|
|
2345
|
-
var
|
|
2327
|
+
var import_react13 = require("react");
|
|
2328
|
+
var import_core11 = require("@lime-bundles/core");
|
|
2346
2329
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2347
2330
|
function VolumeBundle(props) {
|
|
2348
2331
|
const {
|
|
@@ -2367,21 +2350,21 @@ function VolumeBundle(props) {
|
|
|
2367
2350
|
bundleType: "volume",
|
|
2368
2351
|
enabled: analyticsEnabled !== false
|
|
2369
2352
|
});
|
|
2370
|
-
const [quantity, setQuantity] = (0,
|
|
2371
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
2372
|
-
const [cartError, setCartError] = (0,
|
|
2373
|
-
const [seededFor, setSeededFor] = (0,
|
|
2353
|
+
const [quantity, setQuantity] = (0, import_react13.useState)(1);
|
|
2354
|
+
const [addingToCart, setAddingToCart] = (0, import_react13.useState)(false);
|
|
2355
|
+
const [cartError, setCartError] = (0, import_react13.useState)(null);
|
|
2356
|
+
const [seededFor, setSeededFor] = (0, import_react13.useState)(null);
|
|
2374
2357
|
const bundle = result.status === "success" && result.bundle.bundleType === "volume" ? result.bundle : null;
|
|
2375
|
-
(0,
|
|
2358
|
+
(0, import_react13.useEffect)(() => {
|
|
2376
2359
|
if (!bundle || seededFor === bundle.id) return;
|
|
2377
2360
|
setSeededFor(bundle.id);
|
|
2378
2361
|
const tiers = bundle.volumeTiers;
|
|
2379
2362
|
if (tiers.length === 0) return;
|
|
2380
|
-
const best = (0,
|
|
2363
|
+
const best = (0, import_core11.bestValueTierIndex)(
|
|
2381
2364
|
tiers,
|
|
2382
2365
|
bundle.discountConfig.discountType
|
|
2383
2366
|
);
|
|
2384
|
-
const idx = (0,
|
|
2367
|
+
const idx = (0, import_core11.resolveDefaultTierIndex)(
|
|
2385
2368
|
bundle.widgetConfig.defaultTier,
|
|
2386
2369
|
tiers.length,
|
|
2387
2370
|
best
|
|
@@ -2390,7 +2373,7 @@ function VolumeBundle(props) {
|
|
|
2390
2373
|
}, [bundle, seededFor]);
|
|
2391
2374
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
2392
2375
|
const tiersEdgeFadeRef = useEdgeFade();
|
|
2393
|
-
(0,
|
|
2376
|
+
(0, import_react13.useEffect)(() => {
|
|
2394
2377
|
if (result.status === "error") {
|
|
2395
2378
|
onError?.(result.error);
|
|
2396
2379
|
return;
|
|
@@ -2404,26 +2387,31 @@ function VolumeBundle(props) {
|
|
|
2404
2387
|
}
|
|
2405
2388
|
}, [result, onError]);
|
|
2406
2389
|
const product = bundle?.products[0];
|
|
2407
|
-
const variants = (0,
|
|
2408
|
-
const optionNames = (0,
|
|
2390
|
+
const variants = (0, import_react13.useMemo)(() => product?.variants.nodes ?? [], [product]);
|
|
2391
|
+
const optionNames = (0, import_react13.useMemo)(() => {
|
|
2409
2392
|
const first = variants[0];
|
|
2410
2393
|
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
2411
2394
|
}, [variants]);
|
|
2412
2395
|
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
2413
2396
|
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
|
|
2414
|
-
const minTierQty = bundle ? (0,
|
|
2415
|
-
const isSoldOut = variants.length > 0 && !variants.some((v) => (0,
|
|
2416
|
-
const displayVariant = selectedVariant ?? variants.find((v) => (0,
|
|
2397
|
+
const minTierQty = bundle ? (0, import_core11.getMinTierQuantity)(bundle.volumeTiers) : 1;
|
|
2398
|
+
const isSoldOut = variants.length > 0 && !variants.some((v) => (0, import_core11.isVariantFulfillable)(v, minTierQty));
|
|
2399
|
+
const displayVariant = selectedVariant ?? variants.find((v) => (0, import_core11.isVariantFulfillable)(v, minTierQty)) ?? variants[0];
|
|
2400
|
+
const tierAvailable = (0, import_react13.useCallback)(
|
|
2401
|
+
(tierQty) => Boolean(displayVariant && (0, import_core11.isVariantFulfillable)(displayVariant, tierQty)),
|
|
2402
|
+
[displayVariant]
|
|
2403
|
+
);
|
|
2404
|
+
const selectedTierUnavailable = !isSoldOut && !tierAvailable(quantity);
|
|
2417
2405
|
const basePrice = displayVariant ? parseFloat(displayVariant.price.amount) : product ? parseFloat(product.priceRange.minVariantPrice.amount) : 0;
|
|
2418
2406
|
const currency = product?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
2419
2407
|
const thumbImage = displayVariant?.image ?? product?.featuredImage ?? null;
|
|
2420
|
-
const unitPriceText = displayVariant ? (0,
|
|
2408
|
+
const unitPriceText = displayVariant ? (0, import_core11.formatUnitPrice)(
|
|
2421
2409
|
displayVariant.unitPrice,
|
|
2422
2410
|
displayVariant.unitPriceMeasurement,
|
|
2423
2411
|
currency
|
|
2424
2412
|
) : null;
|
|
2425
|
-
const tierSavings = (0,
|
|
2426
|
-
() => bundle ? (0,
|
|
2413
|
+
const tierSavings = (0, import_react13.useMemo)(
|
|
2414
|
+
() => bundle ? (0, import_core11.calculateTierSavings)(
|
|
2427
2415
|
bundle.volumeTiers,
|
|
2428
2416
|
basePrice,
|
|
2429
2417
|
quantity,
|
|
@@ -2431,13 +2419,13 @@ function VolumeBundle(props) {
|
|
|
2431
2419
|
) : [],
|
|
2432
2420
|
[bundle, basePrice, quantity]
|
|
2433
2421
|
);
|
|
2434
|
-
const activeTier = bundle ? (0,
|
|
2435
|
-
const popularTierIndex = (0,
|
|
2422
|
+
const activeTier = bundle ? (0, import_core11.getActiveTier)(bundle.volumeTiers, quantity) : null;
|
|
2423
|
+
const popularTierIndex = (0, import_react13.useMemo)(() => {
|
|
2436
2424
|
if (!bundle || bundle.volumeTiers.length === 0) return -1;
|
|
2437
|
-
return (0,
|
|
2425
|
+
return (0, import_core11.resolvePopularTierIndex)(
|
|
2438
2426
|
bundle.widgetConfig.popularBadge.tierIndex,
|
|
2439
2427
|
bundle.widgetConfig.popularBadge.visible !== false,
|
|
2440
|
-
(0,
|
|
2428
|
+
(0, import_core11.bestValueTierIndex)(
|
|
2441
2429
|
bundle.volumeTiers,
|
|
2442
2430
|
bundle.discountConfig.discountType
|
|
2443
2431
|
)
|
|
@@ -2448,10 +2436,10 @@ function VolumeBundle(props) {
|
|
|
2448
2436
|
const volumeTotal = activeUnitPrice * quantity;
|
|
2449
2437
|
const volumeSavings = Math.max(0, undiscountedTotal - volumeTotal);
|
|
2450
2438
|
const volumeSavingsPercent = undiscountedTotal > 0 && volumeSavings > 0 ? Math.round(volumeSavings / undiscountedTotal * 100) : 0;
|
|
2451
|
-
const handleAddToCart = (0,
|
|
2439
|
+
const handleAddToCart = (0, import_react13.useCallback)(async () => {
|
|
2452
2440
|
if (!bundle || !product) return;
|
|
2453
2441
|
const variant = selectedVariant ?? product.variants.nodes.find(
|
|
2454
|
-
(v) => (0,
|
|
2442
|
+
(v) => (0, import_core11.isVariantFulfillable)(v, quantity)
|
|
2455
2443
|
);
|
|
2456
2444
|
if (!variant) return;
|
|
2457
2445
|
const lines = [
|
|
@@ -2537,7 +2525,7 @@ function VolumeBundle(props) {
|
|
|
2537
2525
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
2538
2526
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
2539
2527
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "lb-bundle__product-price", children: [
|
|
2540
|
-
(0,
|
|
2528
|
+
(0, import_core11.formatMoney)(basePrice, currency),
|
|
2541
2529
|
" each"
|
|
2542
2530
|
] }),
|
|
2543
2531
|
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
|
|
@@ -2571,16 +2559,20 @@ function VolumeBundle(props) {
|
|
|
2571
2559
|
const savingsPercent = Math.round(ts.savingsPercent);
|
|
2572
2560
|
const showCompare = bundle.widgetConfig.pricing.showComparePrice && ts.savings > 0;
|
|
2573
2561
|
const isChecked = activeTier === ts.tier;
|
|
2562
|
+
const available = tierAvailable(ts.tier.minQuantity);
|
|
2574
2563
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2575
2564
|
"div",
|
|
2576
2565
|
{
|
|
2577
|
-
className: `lb-bundle__tier ${ts.isActive ? "lb-bundle__tier--active" : ""}`,
|
|
2566
|
+
className: `lb-bundle__tier ${ts.isActive ? "lb-bundle__tier--active" : ""} ${available ? "" : "lb-bundle__tier--oos"}`,
|
|
2578
2567
|
role: "radio",
|
|
2579
2568
|
"aria-checked": isChecked,
|
|
2569
|
+
"aria-disabled": available ? void 0 : true,
|
|
2580
2570
|
tabIndex: isChecked ? 0 : -1,
|
|
2581
|
-
onClick: () =>
|
|
2571
|
+
onClick: () => {
|
|
2572
|
+
if (available) setQuantity(ts.tier.minQuantity);
|
|
2573
|
+
},
|
|
2582
2574
|
onKeyDown: (e) => {
|
|
2583
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
2575
|
+
if ((e.key === "Enter" || e.key === " ") && available) {
|
|
2584
2576
|
e.preventDefault();
|
|
2585
2577
|
setQuantity(ts.tier.minQuantity);
|
|
2586
2578
|
}
|
|
@@ -2593,8 +2585,8 @@ function VolumeBundle(props) {
|
|
|
2593
2585
|
ts.tier.minQuantity
|
|
2594
2586
|
] }),
|
|
2595
2587
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-price", children: [
|
|
2596
|
-
showCompare && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-compare", children: (0,
|
|
2597
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: (0,
|
|
2588
|
+
showCompare && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-compare", children: (0, import_core11.formatMoney)(basePrice, currency) }),
|
|
2589
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: (0, import_core11.formatMoney)(ts.unitPrice, currency) }),
|
|
2598
2590
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-unit", children: "each" })
|
|
2599
2591
|
] })
|
|
2600
2592
|
] }),
|
|
@@ -2649,7 +2641,7 @@ function VolumeBundle(props) {
|
|
|
2649
2641
|
] })
|
|
2650
2642
|
] }),
|
|
2651
2643
|
cartError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
2652
|
-
bundle && displayVariant && (0,
|
|
2644
|
+
bundle && displayVariant && (0, import_core11.shouldShowLowStockBadge)(
|
|
2653
2645
|
displayVariant,
|
|
2654
2646
|
minTierQty,
|
|
2655
2647
|
bundle.widgetConfig.lowStockThreshold,
|
|
@@ -2676,7 +2668,7 @@ function VolumeBundle(props) {
|
|
|
2676
2668
|
bundle.widgetConfig.savingsBar.visible && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
|
|
2677
2669
|
"You save",
|
|
2678
2670
|
" ",
|
|
2679
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "data-savings-amount": true, children: (0,
|
|
2671
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "data-savings-amount": true, children: (0, import_core11.formatMoney)(volumeSavings, currency) }),
|
|
2680
2672
|
" ",
|
|
2681
2673
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { "data-savings-percent": true, children: [
|
|
2682
2674
|
"(",
|
|
@@ -2686,8 +2678,8 @@ function VolumeBundle(props) {
|
|
|
2686
2678
|
] })
|
|
2687
2679
|
] }),
|
|
2688
2680
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
|
|
2689
|
-
bundle.widgetConfig.pricing.showComparePrice && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0,
|
|
2690
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-sale-price", "data-total-price": true, children: (0,
|
|
2681
|
+
bundle.widgetConfig.pricing.showComparePrice && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core11.formatMoney)(undiscountedTotal, currency) }),
|
|
2682
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-sale-price", "data-total-price": true, children: (0, import_core11.formatMoney)(volumeTotal, currency) })
|
|
2691
2683
|
] })
|
|
2692
2684
|
] }),
|
|
2693
2685
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -2695,9 +2687,9 @@ function VolumeBundle(props) {
|
|
|
2695
2687
|
{
|
|
2696
2688
|
className: "lb-bundle__cta",
|
|
2697
2689
|
onClick: handleAddToCart,
|
|
2698
|
-
disabled: addingToCart || isSoldOut,
|
|
2690
|
+
disabled: addingToCart || isSoldOut || selectedTierUnavailable,
|
|
2699
2691
|
"aria-busy": addingToCart,
|
|
2700
|
-
children: addingToCart ? "Adding..." : isSoldOut ? "Sold out" : bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`
|
|
2692
|
+
children: addingToCart ? "Adding..." : isSoldOut || selectedTierUnavailable ? "Sold out" : bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`
|
|
2701
2693
|
}
|
|
2702
2694
|
)
|
|
2703
2695
|
]
|
|
@@ -2706,8 +2698,8 @@ function VolumeBundle(props) {
|
|
|
2706
2698
|
}
|
|
2707
2699
|
|
|
2708
2700
|
// src/components/BogoBundle.tsx
|
|
2709
|
-
var
|
|
2710
|
-
var
|
|
2701
|
+
var import_react14 = require("react");
|
|
2702
|
+
var import_core12 = require("@lime-bundles/core");
|
|
2711
2703
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2712
2704
|
function discountedUnit(price, percent) {
|
|
2713
2705
|
const cents = Math.round(price * 100);
|
|
@@ -2737,10 +2729,10 @@ function BogoBundle(props) {
|
|
|
2737
2729
|
bundleType: "bogo",
|
|
2738
2730
|
enabled: analyticsEnabled !== false
|
|
2739
2731
|
});
|
|
2740
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
2741
|
-
const [cartError, setCartError] = (0,
|
|
2742
|
-
const [selectedVariants, setSelectedVariants] = (0,
|
|
2743
|
-
const handleVariantChange = (0,
|
|
2732
|
+
const [addingToCart, setAddingToCart] = (0, import_react14.useState)(false);
|
|
2733
|
+
const [cartError, setCartError] = (0, import_react14.useState)(null);
|
|
2734
|
+
const [selectedVariants, setSelectedVariants] = (0, import_react14.useState)({ buy: null, get: null });
|
|
2735
|
+
const handleVariantChange = (0, import_react14.useCallback)(
|
|
2744
2736
|
(role, variant) => {
|
|
2745
2737
|
setSelectedVariants((prev) => {
|
|
2746
2738
|
if (prev[role] === variant) return prev;
|
|
@@ -2751,7 +2743,7 @@ function BogoBundle(props) {
|
|
|
2751
2743
|
);
|
|
2752
2744
|
const bundle = result.status === "success" && result.bundle.bundleType === "bogo" ? result.bundle : null;
|
|
2753
2745
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
2754
|
-
(0,
|
|
2746
|
+
(0, import_react14.useEffect)(() => {
|
|
2755
2747
|
if (result.status === "error") {
|
|
2756
2748
|
onError?.(result.error);
|
|
2757
2749
|
return;
|
|
@@ -2764,28 +2756,28 @@ function BogoBundle(props) {
|
|
|
2764
2756
|
);
|
|
2765
2757
|
}
|
|
2766
2758
|
}, [result, onError]);
|
|
2767
|
-
const buyProduct = (0,
|
|
2759
|
+
const buyProduct = (0, import_react14.useMemo)(
|
|
2768
2760
|
() => bundle?.products.find((p) => p.id === bundle.buyProductId) ?? null,
|
|
2769
2761
|
[bundle]
|
|
2770
2762
|
);
|
|
2771
|
-
const getProduct = (0,
|
|
2763
|
+
const getProduct = (0, import_react14.useMemo)(
|
|
2772
2764
|
() => bundle?.products.find((p) => p.id === bundle.getProductId) ?? null,
|
|
2773
2765
|
[bundle]
|
|
2774
2766
|
);
|
|
2775
2767
|
const percent = bundle ? Math.min(100, Math.max(0, bundle.discountConfig.discountValue)) : 0;
|
|
2776
|
-
const resolveVariant = (0,
|
|
2768
|
+
const resolveVariant = (0, import_react14.useCallback)(
|
|
2777
2769
|
(product, selected, qty) => {
|
|
2778
2770
|
if (!product) return null;
|
|
2779
|
-
return selected ?? product.variants.nodes.find((v) => (0,
|
|
2771
|
+
return selected ?? product.variants.nodes.find((v) => (0, import_core12.isVariantFulfillable)(v, qty)) ?? product.variants.nodes[0] ?? null;
|
|
2780
2772
|
},
|
|
2781
2773
|
[]
|
|
2782
2774
|
);
|
|
2783
|
-
const oosSides = (0,
|
|
2775
|
+
const oosSides = (0, import_react14.useMemo)(() => {
|
|
2784
2776
|
if (!bundle) return 0;
|
|
2785
|
-
const short = (product, qty) => !!product && !product.variants.nodes.some((v) => (0,
|
|
2777
|
+
const short = (product, qty) => !!product && !product.variants.nodes.some((v) => (0, import_core12.isVariantFulfillable)(v, qty));
|
|
2786
2778
|
return (short(buyProduct, bundle.buyQuantity) ? 1 : 0) + (short(getProduct, bundle.getQuantity) ? 1 : 0);
|
|
2787
2779
|
}, [bundle, buyProduct, getProduct]);
|
|
2788
|
-
const handleAddToCart = (0,
|
|
2780
|
+
const handleAddToCart = (0, import_react14.useCallback)(async () => {
|
|
2789
2781
|
if (!bundle || !buyProduct || !getProduct) return;
|
|
2790
2782
|
const buyVariant2 = resolveVariant(buyProduct, selectedVariants.buy, bundle.buyQuantity);
|
|
2791
2783
|
const getVariant2 = resolveVariant(getProduct, selectedVariants.get, bundle.getQuantity);
|
|
@@ -2910,7 +2902,7 @@ function BogoBundle(props) {
|
|
|
2910
2902
|
showSavings && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
|
|
2911
2903
|
"You save",
|
|
2912
2904
|
" ",
|
|
2913
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-savings-amount": true, children: (0,
|
|
2905
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-savings-amount": true, children: (0, import_core12.formatMoney)(savings, currency) }),
|
|
2914
2906
|
" ",
|
|
2915
2907
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-savings-percent": true, children: [
|
|
2916
2908
|
"(",
|
|
@@ -2920,8 +2912,8 @@ function BogoBundle(props) {
|
|
|
2920
2912
|
] })
|
|
2921
2913
|
] }),
|
|
2922
2914
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
|
|
2923
|
-
showCompare && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0,
|
|
2924
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0,
|
|
2915
|
+
showCompare && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core12.formatMoney)(comparePrice, currency) }),
|
|
2916
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core12.formatMoney)(salePrice, currency) })
|
|
2925
2917
|
] })
|
|
2926
2918
|
] }),
|
|
2927
2919
|
cartError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
@@ -2941,7 +2933,7 @@ function BogoBundle(props) {
|
|
|
2941
2933
|
}
|
|
2942
2934
|
function BogoProductRow({ side, product, quantity, percent, badge, currency, onVariantChange }) {
|
|
2943
2935
|
const variants = product.variants.nodes;
|
|
2944
|
-
const optionNames = (0,
|
|
2936
|
+
const optionNames = (0, import_react14.useMemo)(() => {
|
|
2945
2937
|
const first = variants[0];
|
|
2946
2938
|
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
2947
2939
|
}, [variants]);
|
|
@@ -2950,15 +2942,15 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
|
|
|
2950
2942
|
variants,
|
|
2951
2943
|
optionNames
|
|
2952
2944
|
});
|
|
2953
|
-
(0,
|
|
2945
|
+
(0, import_react14.useEffect)(() => {
|
|
2954
2946
|
onVariantChange(side, selectedVariant ?? null);
|
|
2955
2947
|
}, [selectedVariant, side, onVariantChange]);
|
|
2956
|
-
const displayVariant = selectedVariant ?? variants.find((v) => (0,
|
|
2948
|
+
const displayVariant = selectedVariant ?? variants.find((v) => (0, import_core12.isVariantFulfillable)(v, quantity)) ?? variants[0];
|
|
2957
2949
|
const unit = displayVariant ? parseFloat(displayVariant.price.amount) : 0;
|
|
2958
2950
|
const isGet = side === "get" && percent > 0;
|
|
2959
|
-
const priceText = isGet ? (0,
|
|
2960
|
-
const compareAt = isGet ? (0,
|
|
2961
|
-
const unitPriceText = displayVariant ? (0,
|
|
2951
|
+
const priceText = isGet ? (0, import_core12.formatMoney)(discountedUnit(unit, percent), currency) : (0, import_core12.formatMoney)(unit, currency);
|
|
2952
|
+
const compareAt = isGet ? (0, import_core12.formatMoney)(unit, currency) : displayVariant?.compareAtPrice && parseFloat(displayVariant.compareAtPrice.amount) > unit ? (0, import_core12.formatMoney)(displayVariant.compareAtPrice.amount, currency) : null;
|
|
2953
|
+
const unitPriceText = displayVariant ? (0, import_core12.formatUnitPrice)(
|
|
2962
2954
|
displayVariant.unitPrice,
|
|
2963
2955
|
displayVariant.unitPriceMeasurement,
|
|
2964
2956
|
currency
|
|
@@ -3023,20 +3015,20 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
|
|
|
3023
3015
|
}
|
|
3024
3016
|
|
|
3025
3017
|
// src/components/MultiStepBundle.tsx
|
|
3026
|
-
var
|
|
3027
|
-
var
|
|
3018
|
+
var import_react16 = require("react");
|
|
3019
|
+
var import_core14 = require("@lime-bundles/core");
|
|
3028
3020
|
|
|
3029
3021
|
// src/components/MultiStepPicker.tsx
|
|
3030
|
-
var
|
|
3022
|
+
var import_react15 = require("react");
|
|
3031
3023
|
var import_react_dom2 = require("react-dom");
|
|
3032
|
-
var
|
|
3024
|
+
var import_core13 = require("@lime-bundles/core");
|
|
3033
3025
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3034
3026
|
function ruleFor3(bundle, productId, variantId) {
|
|
3035
3027
|
if (variantId) {
|
|
3036
3028
|
const vRule = bundle.variantRules[variantId];
|
|
3037
3029
|
if (vRule) return vRule;
|
|
3038
3030
|
}
|
|
3039
|
-
return bundle.productRules[productId] ??
|
|
3031
|
+
return bundle.productRules[productId] ?? import_core13.DEFAULT_PRODUCT_RULE;
|
|
3040
3032
|
}
|
|
3041
3033
|
function variantRuleFor3(bundle, variantId) {
|
|
3042
3034
|
return variantId ? bundle.variantRules[variantId] : void 0;
|
|
@@ -3044,15 +3036,14 @@ function variantRuleFor3(bundle, variantId) {
|
|
|
3044
3036
|
function sanitizeId2(gid) {
|
|
3045
3037
|
return gid.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
3046
3038
|
}
|
|
3047
|
-
function buildEligibleProducts2(bundle, stepIndex
|
|
3039
|
+
function buildEligibleProducts2(bundle, stepIndex) {
|
|
3048
3040
|
const result = [];
|
|
3049
|
-
for (const product of (0,
|
|
3041
|
+
for (const product of (0, import_core13.productsForStep)(bundle, stepIndex)) {
|
|
3050
3042
|
const rule = ruleFor3(bundle, product.id);
|
|
3051
3043
|
const available = product.variants.nodes.filter(
|
|
3052
|
-
(v) => (0,
|
|
3044
|
+
(v) => (0, import_core13.isVariantFulfillable)(v, rule.min)
|
|
3053
3045
|
);
|
|
3054
3046
|
const isOos = available.length === 0;
|
|
3055
|
-
if (isOos && oosBehavior === "hide") continue;
|
|
3056
3047
|
result.push({
|
|
3057
3048
|
product,
|
|
3058
3049
|
variants: product.variants.nodes,
|
|
@@ -3067,7 +3058,6 @@ function normalizeText2(str) {
|
|
|
3067
3058
|
}
|
|
3068
3059
|
var FILTERS_ALL2 = "__all__";
|
|
3069
3060
|
function MultiStepPicker({
|
|
3070
|
-
outOfStockBehavior,
|
|
3071
3061
|
bundle,
|
|
3072
3062
|
currency,
|
|
3073
3063
|
initialStep,
|
|
@@ -3084,17 +3074,19 @@ function MultiStepPicker({
|
|
|
3084
3074
|
}) {
|
|
3085
3075
|
const wc = bundle.widgetConfig;
|
|
3086
3076
|
const steps = bundle.steps;
|
|
3087
|
-
const overlayRef = (0,
|
|
3088
|
-
const dialogRef = (0,
|
|
3089
|
-
const titleRef = (0,
|
|
3090
|
-
const
|
|
3091
|
-
const
|
|
3092
|
-
const
|
|
3077
|
+
const overlayRef = (0, import_react15.useRef)(null);
|
|
3078
|
+
const dialogRef = (0, import_react15.useRef)(null);
|
|
3079
|
+
const titleRef = (0, import_react15.useRef)(null);
|
|
3080
|
+
const closeBtnRef = (0, import_react15.useRef)(null);
|
|
3081
|
+
const listRef = (0, import_react15.useRef)(null);
|
|
3082
|
+
const mouseDownTarget = (0, import_react15.useRef)(null);
|
|
3083
|
+
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
3084
|
+
const [stepIndex, setStepIndex] = (0, import_react15.useState)(
|
|
3093
3085
|
() => Math.max(0, Math.min(steps.length - 1, initialStep))
|
|
3094
3086
|
);
|
|
3095
|
-
const [query, setQuery] = (0,
|
|
3096
|
-
const [activeType, setActiveType] = (0,
|
|
3097
|
-
const [announcement, setAnnouncement] = (0,
|
|
3087
|
+
const [query, setQuery] = (0, import_react15.useState)("");
|
|
3088
|
+
const [activeType, setActiveType] = (0, import_react15.useState)(FILTERS_ALL2);
|
|
3089
|
+
const [announcement, setAnnouncement] = (0, import_react15.useState)("");
|
|
3098
3090
|
const step = steps[stepIndex];
|
|
3099
3091
|
const stepMin = step?.minQuantity ?? 1;
|
|
3100
3092
|
const stepPicks = selections[stepIndex] ?? [];
|
|
@@ -3102,11 +3094,11 @@ function MultiStepPicker({
|
|
|
3102
3094
|
const stepIsSatisfied = unitsInStep >= stepMin;
|
|
3103
3095
|
const isLastStep = stepIndex === steps.length - 1;
|
|
3104
3096
|
const shownUnits = Math.min(unitsInStep, stepMin);
|
|
3105
|
-
const eligible = (0,
|
|
3106
|
-
() => buildEligibleProducts2(bundle, stepIndex
|
|
3107
|
-
[bundle, stepIndex
|
|
3097
|
+
const eligible = (0, import_react15.useMemo)(
|
|
3098
|
+
() => buildEligibleProducts2(bundle, stepIndex),
|
|
3099
|
+
[bundle, stepIndex]
|
|
3108
3100
|
);
|
|
3109
|
-
const productTypes = (0,
|
|
3101
|
+
const productTypes = (0, import_react15.useMemo)(() => {
|
|
3110
3102
|
const types = [];
|
|
3111
3103
|
const seen = /* @__PURE__ */ new Set();
|
|
3112
3104
|
for (const ep of eligible) {
|
|
@@ -3119,7 +3111,7 @@ function MultiStepPicker({
|
|
|
3119
3111
|
return types;
|
|
3120
3112
|
}, [eligible]);
|
|
3121
3113
|
const showFilters = wc.mixMatchShowTypeFilters && productTypes.length >= 2;
|
|
3122
|
-
(0,
|
|
3114
|
+
(0, import_react15.useEffect)(() => {
|
|
3123
3115
|
const id = requestAnimationFrame(() => setOpen(true));
|
|
3124
3116
|
return () => cancelAnimationFrame(id);
|
|
3125
3117
|
}, []);
|
|
@@ -3127,8 +3119,23 @@ function MultiStepPicker({
|
|
|
3127
3119
|
useFocusTrap({
|
|
3128
3120
|
active: true,
|
|
3129
3121
|
containerRef: dialogRef,
|
|
3122
|
+
initialFocusRef: closeBtnRef,
|
|
3130
3123
|
onEscape: onClose
|
|
3131
3124
|
});
|
|
3125
|
+
(0, import_react15.useEffect)(() => {
|
|
3126
|
+
if (listRef.current) listRef.current.scrollTop = 0;
|
|
3127
|
+
}, [stepIndex]);
|
|
3128
|
+
const [selectedAtLoad, setSelectedAtLoad] = (0, import_react15.useState)(
|
|
3129
|
+
() => new Set((selections[stepIndex] ?? []).map((s) => s.productId))
|
|
3130
|
+
);
|
|
3131
|
+
const ordered = (0, import_react15.useMemo)(() => {
|
|
3132
|
+
const front = [];
|
|
3133
|
+
const back = [];
|
|
3134
|
+
for (const ep of eligible) {
|
|
3135
|
+
(selectedAtLoad.has(ep.product.id) ? back : front).push(ep);
|
|
3136
|
+
}
|
|
3137
|
+
return front.concat(back);
|
|
3138
|
+
}, [eligible, selectedAtLoad]);
|
|
3132
3139
|
function goToStep(i) {
|
|
3133
3140
|
const clamped = Math.max(0, Math.min(steps.length - 1, i));
|
|
3134
3141
|
setStepIndex(clamped);
|
|
@@ -3136,6 +3143,7 @@ function MultiStepPicker({
|
|
|
3136
3143
|
setActiveType(FILTERS_ALL2);
|
|
3137
3144
|
const target = steps[clamped];
|
|
3138
3145
|
const targetPicks = selections[clamped] ?? [];
|
|
3146
|
+
setSelectedAtLoad(new Set(targetPicks.map((s) => s.productId)));
|
|
3139
3147
|
const targetUnits = targetPicks.reduce((sum, s) => sum + s.quantity, 0);
|
|
3140
3148
|
setAnnouncement(
|
|
3141
3149
|
`Step ${clamped + 1} of ${steps.length}. ${target.name}. ${Math.min(targetUnits, target.minQuantity)} of ${target.minQuantity} added`
|
|
@@ -3143,14 +3151,14 @@ function MultiStepPicker({
|
|
|
3143
3151
|
titleRef.current?.focus();
|
|
3144
3152
|
}
|
|
3145
3153
|
const subtitle = `Step ${stepIndex + 1} of ${steps.length} \xB7 ${step?.name ?? ""}`;
|
|
3146
|
-
const filtered = (0,
|
|
3154
|
+
const filtered = (0, import_react15.useMemo)(() => {
|
|
3147
3155
|
const nq = normalizeText2(query.trim());
|
|
3148
|
-
return
|
|
3156
|
+
return ordered.filter((ep) => {
|
|
3149
3157
|
const matchesQuery = !nq || normalizeText2(ep.product.title).includes(nq);
|
|
3150
3158
|
const matchesType = activeType === FILTERS_ALL2 || (ep.product.productType ?? "") === activeType;
|
|
3151
3159
|
return matchesQuery && matchesType;
|
|
3152
3160
|
});
|
|
3153
|
-
}, [
|
|
3161
|
+
}, [ordered, query, activeType]);
|
|
3154
3162
|
const showEmpty = filtered.length === 0 && query.trim().length > 0;
|
|
3155
3163
|
function onOverlayMouseDown(e) {
|
|
3156
3164
|
mouseDownTarget.current = e.target;
|
|
@@ -3208,6 +3216,7 @@ function MultiStepPicker({
|
|
|
3208
3216
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3209
3217
|
"button",
|
|
3210
3218
|
{
|
|
3219
|
+
ref: closeBtnRef,
|
|
3211
3220
|
type: "button",
|
|
3212
3221
|
className: "lb-mix-match__modal-close",
|
|
3213
3222
|
"data-modal-close": true,
|
|
@@ -3295,7 +3304,7 @@ function MultiStepPicker({
|
|
|
3295
3304
|
})
|
|
3296
3305
|
}
|
|
3297
3306
|
),
|
|
3298
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3307
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, ref: listRef, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3299
3308
|
MultiStepPickerRow,
|
|
3300
3309
|
{
|
|
3301
3310
|
bundle,
|
|
@@ -3386,27 +3395,27 @@ function MultiStepPickerRow({
|
|
|
3386
3395
|
const { product, variants, isOos } = eligible;
|
|
3387
3396
|
const rule = ruleFor3(bundle, product.id);
|
|
3388
3397
|
const step = bundle.steps[stepIndex];
|
|
3389
|
-
const optionNames = (0,
|
|
3398
|
+
const optionNames = (0, import_react15.useMemo)(() => {
|
|
3390
3399
|
const first = variants[0];
|
|
3391
3400
|
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
3392
3401
|
}, [variants]);
|
|
3393
3402
|
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
3394
3403
|
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
|
|
3395
|
-
const currentVariant = selectedVariant ?? variants.find((v) => (0,
|
|
3404
|
+
const currentVariant = selectedVariant ?? variants.find((v) => (0, import_core13.isVariantFulfillable)(v, rule.min)) ?? eligible.firstAvailableVariant ?? variants[0] ?? null;
|
|
3396
3405
|
const vRule = ruleFor3(bundle, product.id, currentVariant?.id);
|
|
3397
3406
|
const variantMax = variantRuleFor3(bundle, currentVariant?.id)?.max;
|
|
3398
|
-
const stepPicks = (0,
|
|
3407
|
+
const stepPicks = (0, import_react15.useMemo)(
|
|
3399
3408
|
() => selections[stepIndex] ?? [],
|
|
3400
3409
|
[selections, stepIndex]
|
|
3401
3410
|
);
|
|
3402
3411
|
const unitsInStep = stepPicks.reduce((sum, s) => sum + s.quantity, 0);
|
|
3403
|
-
const committedSelection = (0,
|
|
3412
|
+
const committedSelection = (0, import_react15.useMemo)(
|
|
3404
3413
|
() => currentVariant ? stepPicks.find((s) => s.variantId === currentVariant.id) ?? null : null,
|
|
3405
3414
|
[stepPicks, currentVariant]
|
|
3406
3415
|
);
|
|
3407
3416
|
const variantInStep = committedSelection !== null;
|
|
3408
3417
|
const committedQty = committedSelection?.quantity ?? 0;
|
|
3409
|
-
const productOtherUnits = (0,
|
|
3418
|
+
const productOtherUnits = (0, import_react15.useMemo)(() => {
|
|
3410
3419
|
if (!currentVariant) return 0;
|
|
3411
3420
|
let sum = 0;
|
|
3412
3421
|
for (let i = 0; i < selections.length; i++) {
|
|
@@ -3418,7 +3427,7 @@ function MultiStepPickerRow({
|
|
|
3418
3427
|
}
|
|
3419
3428
|
return sum;
|
|
3420
3429
|
}, [selections, product.id, currentVariant, stepIndex]);
|
|
3421
|
-
const variantOtherUnits = (0,
|
|
3430
|
+
const variantOtherUnits = (0, import_react15.useMemo)(() => {
|
|
3422
3431
|
if (!currentVariant) return 0;
|
|
3423
3432
|
let sum = 0;
|
|
3424
3433
|
for (const picks of selections) {
|
|
@@ -3430,7 +3439,7 @@ function MultiStepPickerRow({
|
|
|
3430
3439
|
}, [selections, currentVariant, committedQty]);
|
|
3431
3440
|
const swapUnits = currentVariant ? swapUnitsFor?.(stepIndex, product.id, currentVariant.id) ?? 0 : 0;
|
|
3432
3441
|
const inSwapState = swapUnits >= vRule.min;
|
|
3433
|
-
const room = (0,
|
|
3442
|
+
const room = (0, import_core13.stepHeadroom)(step?.maxQuantity ?? null, unitsInStep);
|
|
3434
3443
|
const productHeadroom = Math.min(
|
|
3435
3444
|
rule.max - productOtherUnits,
|
|
3436
3445
|
variantMax ?? Infinity
|
|
@@ -3439,20 +3448,20 @@ function MultiStepPickerRow({
|
|
|
3439
3448
|
productHeadroom,
|
|
3440
3449
|
room === Number.POSITIVE_INFINITY ? productHeadroom : committedQty + room
|
|
3441
3450
|
);
|
|
3442
|
-
const stockRemaining = currentVariant ? (0,
|
|
3451
|
+
const stockRemaining = currentVariant ? (0, import_core13.maxAddableQuantity)(currentVariant, Number.MAX_SAFE_INTEGER, variantOtherUnits) : 0;
|
|
3443
3452
|
const cap = Math.max(0, Math.min(spotCeiling, stockRemaining));
|
|
3444
3453
|
const stepperMax = Math.max(vRule.min, cap);
|
|
3445
|
-
const [qty, setQty] = (0,
|
|
3446
|
-
(0,
|
|
3454
|
+
const [qty, setQty] = (0, import_react15.useState)(vRule.min);
|
|
3455
|
+
(0, import_react15.useEffect)(() => {
|
|
3447
3456
|
setQty((prev) => Math.max(vRule.min, Math.min(prev, stepperMax)));
|
|
3448
3457
|
}, [stepperMax, vRule.min]);
|
|
3449
3458
|
const currentVariantId = currentVariant?.id ?? null;
|
|
3450
|
-
(0,
|
|
3459
|
+
(0, import_react15.useEffect)(() => {
|
|
3451
3460
|
if (!variantInStep) setQty(vRule.min);
|
|
3452
3461
|
}, [variantInStep, currentVariantId, vRule.min]);
|
|
3453
3462
|
if (!currentVariant || !step) return null;
|
|
3454
3463
|
const thumbImage = currentVariant.image ?? product.featuredImage ?? null;
|
|
3455
|
-
const unitPriceText = (0,
|
|
3464
|
+
const unitPriceText = (0, import_core13.formatUnitPrice)(
|
|
3456
3465
|
currentVariant.unitPrice,
|
|
3457
3466
|
currentVariant.unitPriceMeasurement,
|
|
3458
3467
|
currency
|
|
@@ -3522,8 +3531,8 @@ function MultiStepPickerRow({
|
|
|
3522
3531
|
}
|
|
3523
3532
|
) }),
|
|
3524
3533
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
|
|
3525
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-row-price-sale": true, children: (0,
|
|
3526
|
-
compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0,
|
|
3534
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-row-price-sale": true, children: (0, import_core13.formatMoney)(priceCents, currency) }),
|
|
3535
|
+
compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core13.formatMoney)(compareCents, currency) })
|
|
3527
3536
|
] }),
|
|
3528
3537
|
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
|
|
3529
3538
|
showPicker && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
|
|
@@ -3676,7 +3685,7 @@ function ruleFor4(bundle, productId, variantId) {
|
|
|
3676
3685
|
const vRule = bundle.variantRules[variantId];
|
|
3677
3686
|
if (vRule) return vRule;
|
|
3678
3687
|
}
|
|
3679
|
-
return bundle.productRules[productId] ??
|
|
3688
|
+
return bundle.productRules[productId] ?? import_core14.DEFAULT_PRODUCT_RULE;
|
|
3680
3689
|
}
|
|
3681
3690
|
function variantRuleFor4(bundle, variantId) {
|
|
3682
3691
|
return variantId ? bundle.variantRules[variantId] : void 0;
|
|
@@ -3718,10 +3727,6 @@ function MultiStepBundle(props) {
|
|
|
3718
3727
|
storefrontAccessToken,
|
|
3719
3728
|
bundleGid
|
|
3720
3729
|
});
|
|
3721
|
-
const { outOfStockBehavior } = useShopSettings({
|
|
3722
|
-
shopDomain,
|
|
3723
|
-
storefrontAccessToken
|
|
3724
|
-
});
|
|
3725
3730
|
const { elementRef, trackAddToCart } = useAnalytics({
|
|
3726
3731
|
shopDomain,
|
|
3727
3732
|
appUrl: appUrl ?? `https://${shopDomain}`,
|
|
@@ -3729,14 +3734,14 @@ function MultiStepBundle(props) {
|
|
|
3729
3734
|
bundleType: "multi_step",
|
|
3730
3735
|
enabled: analyticsEnabled !== false
|
|
3731
3736
|
});
|
|
3732
|
-
const [selections, setSelections] = (0,
|
|
3733
|
-
const [pickerOpenAt, setPickerOpenAt] = (0,
|
|
3734
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
3735
|
-
const [cartError, setCartError] = (0,
|
|
3737
|
+
const [selections, setSelections] = (0, import_react16.useState)([]);
|
|
3738
|
+
const [pickerOpenAt, setPickerOpenAt] = (0, import_react16.useState)(null);
|
|
3739
|
+
const [addingToCart, setAddingToCart] = (0, import_react16.useState)(false);
|
|
3740
|
+
const [cartError, setCartError] = (0, import_react16.useState)(null);
|
|
3736
3741
|
const bundle = result.status === "success" && result.bundle.bundleType === "multi_step" ? result.bundle : null;
|
|
3737
3742
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
3738
3743
|
const setModalConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
3739
|
-
(0,
|
|
3744
|
+
(0, import_react16.useEffect)(() => {
|
|
3740
3745
|
if (result.status === "error") {
|
|
3741
3746
|
onError?.(result.error);
|
|
3742
3747
|
return;
|
|
@@ -3749,17 +3754,17 @@ function MultiStepBundle(props) {
|
|
|
3749
3754
|
);
|
|
3750
3755
|
}
|
|
3751
3756
|
}, [result, onError]);
|
|
3752
|
-
const steps = (0,
|
|
3753
|
-
(0,
|
|
3757
|
+
const steps = (0, import_react16.useMemo)(() => bundle?.steps ?? [], [bundle]);
|
|
3758
|
+
(0, import_react16.useEffect)(() => {
|
|
3754
3759
|
const seeded = steps.map(() => []);
|
|
3755
3760
|
if (bundle) {
|
|
3756
3761
|
const seedCurrency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
3757
3762
|
for (const [pid, rule] of Object.entries(bundle.productRules)) {
|
|
3758
3763
|
if (!rule.required) continue;
|
|
3759
3764
|
for (let i = 0; i < steps.length; i++) {
|
|
3760
|
-
const product = (0,
|
|
3765
|
+
const product = (0, import_core14.productsForStep)(bundle, i).find((p) => p.id === pid);
|
|
3761
3766
|
const variant = product?.variants.nodes.find(
|
|
3762
|
-
(v) => (0,
|
|
3767
|
+
(v) => (0, import_core14.isVariantFulfillable)(v, rule.min)
|
|
3763
3768
|
);
|
|
3764
3769
|
if (!product || !variant) continue;
|
|
3765
3770
|
seeded[i].push({
|
|
@@ -3771,7 +3776,7 @@ function MultiStepBundle(props) {
|
|
|
3771
3776
|
featuredImage: variant.image?.url ?? product.featuredImage?.url ?? null,
|
|
3772
3777
|
price: parseFloat(variant.price.amount),
|
|
3773
3778
|
compareAtPrice: variant.compareAtPrice ? parseFloat(variant.compareAtPrice.amount) : null,
|
|
3774
|
-
unitPrice: (0,
|
|
3779
|
+
unitPrice: (0, import_core14.formatUnitPrice)(
|
|
3775
3780
|
variant.unitPrice,
|
|
3776
3781
|
variant.unitPriceMeasurement,
|
|
3777
3782
|
seedCurrency
|
|
@@ -3785,26 +3790,26 @@ function MultiStepBundle(props) {
|
|
|
3785
3790
|
setSelections(seeded);
|
|
3786
3791
|
}, [bundle, steps]);
|
|
3787
3792
|
const showStepper = bundle?.widgetConfig.mixMatchShowQuantitySelector !== false;
|
|
3788
|
-
const totalMin = (0,
|
|
3793
|
+
const totalMin = (0, import_react16.useMemo)(
|
|
3789
3794
|
() => steps.reduce((sum, s) => sum + s.minQuantity, 0),
|
|
3790
3795
|
[steps]
|
|
3791
3796
|
);
|
|
3792
|
-
const unitsByStep = (0,
|
|
3797
|
+
const unitsByStep = (0, import_react16.useMemo)(
|
|
3793
3798
|
() => selections.map((picks) => picks.reduce((sum, s) => sum + s.quantity, 0)),
|
|
3794
3799
|
[selections]
|
|
3795
3800
|
);
|
|
3796
|
-
const totalQuantity = (0,
|
|
3801
|
+
const totalQuantity = (0, import_react16.useMemo)(
|
|
3797
3802
|
() => unitsByStep.reduce((sum, u) => sum + u, 0),
|
|
3798
3803
|
[unitsByStep]
|
|
3799
3804
|
);
|
|
3800
|
-
const stepSatisfied = (0,
|
|
3805
|
+
const stepSatisfied = (0, import_react16.useCallback)(
|
|
3801
3806
|
(i) => (unitsByStep[i] ?? 0) >= (steps[i]?.minQuantity ?? 1),
|
|
3802
3807
|
[unitsByStep, steps]
|
|
3803
3808
|
);
|
|
3804
3809
|
const allSatisfied = steps.length > 0 && steps.every((_, i) => stepSatisfied(i));
|
|
3805
3810
|
const valid = !!bundle && allSatisfied;
|
|
3806
3811
|
const completedSteps = steps.filter((_, i) => stepSatisfied(i)).length;
|
|
3807
|
-
const summary = (0,
|
|
3812
|
+
const summary = (0, import_react16.useMemo)(() => {
|
|
3808
3813
|
let comparePrice = 0;
|
|
3809
3814
|
let salePrice = 0;
|
|
3810
3815
|
for (const stepPicks of selections) {
|
|
@@ -3812,7 +3817,7 @@ function MultiStepBundle(props) {
|
|
|
3812
3817
|
if (sel.quantity <= 0) continue;
|
|
3813
3818
|
comparePrice += sel.price * sel.quantity;
|
|
3814
3819
|
if (bundle) {
|
|
3815
|
-
salePrice += (0,
|
|
3820
|
+
salePrice += (0, import_core14.calculateDiscount)(
|
|
3816
3821
|
sel.price,
|
|
3817
3822
|
bundle.discountConfig.discountType,
|
|
3818
3823
|
bundle.discountConfig.discountValue
|
|
@@ -3824,7 +3829,7 @@ function MultiStepBundle(props) {
|
|
|
3824
3829
|
const savingsPercent = comparePrice > 0 && savings > 0 ? Math.round(savings / comparePrice * 100) : 0;
|
|
3825
3830
|
return { comparePrice, salePrice, savings, savingsPercent };
|
|
3826
3831
|
}, [selections, bundle]);
|
|
3827
|
-
const addSelection = (0,
|
|
3832
|
+
const addSelection = (0, import_react16.useCallback)((step, item) => {
|
|
3828
3833
|
setSelections((prev) => {
|
|
3829
3834
|
if (step < 0 || step >= prev.length) return prev;
|
|
3830
3835
|
if (prev[step].some((s) => s.variantId === item.variantId)) return prev;
|
|
@@ -3833,7 +3838,7 @@ function MultiStepBundle(props) {
|
|
|
3833
3838
|
return next;
|
|
3834
3839
|
});
|
|
3835
3840
|
}, []);
|
|
3836
|
-
const swapSelection = (0,
|
|
3841
|
+
const swapSelection = (0, import_react16.useCallback)(
|
|
3837
3842
|
(step, item) => {
|
|
3838
3843
|
setSelections((prev) => {
|
|
3839
3844
|
if (!bundle || step < 0 || step >= prev.length) return prev;
|
|
@@ -3868,7 +3873,7 @@ function MultiStepBundle(props) {
|
|
|
3868
3873
|
},
|
|
3869
3874
|
[bundle]
|
|
3870
3875
|
);
|
|
3871
|
-
const updateQuantity = (0,
|
|
3876
|
+
const updateQuantity = (0, import_react16.useCallback)(
|
|
3872
3877
|
(step, productId, variantId, quantity) => {
|
|
3873
3878
|
setSelections((prev) => {
|
|
3874
3879
|
if (step < 0 || step >= prev.length) return prev;
|
|
@@ -3887,7 +3892,7 @@ function MultiStepBundle(props) {
|
|
|
3887
3892
|
},
|
|
3888
3893
|
[]
|
|
3889
3894
|
);
|
|
3890
|
-
const removeVariant = (0,
|
|
3895
|
+
const removeVariant = (0, import_react16.useCallback)(
|
|
3891
3896
|
(step, variantId) => {
|
|
3892
3897
|
setSelections((prev) => {
|
|
3893
3898
|
if (step < 0 || step >= prev.length) return prev;
|
|
@@ -3903,7 +3908,7 @@ function MultiStepBundle(props) {
|
|
|
3903
3908
|
},
|
|
3904
3909
|
[bundle]
|
|
3905
3910
|
);
|
|
3906
|
-
const removeSlot = (0,
|
|
3911
|
+
const removeSlot = (0, import_react16.useCallback)(
|
|
3907
3912
|
(step, index) => {
|
|
3908
3913
|
setSelections((prev) => {
|
|
3909
3914
|
if (step < 0 || step >= prev.length) return prev;
|
|
@@ -3919,7 +3924,7 @@ function MultiStepBundle(props) {
|
|
|
3919
3924
|
},
|
|
3920
3925
|
[bundle]
|
|
3921
3926
|
);
|
|
3922
|
-
const handleAddToCart = (0,
|
|
3927
|
+
const handleAddToCart = (0, import_react16.useCallback)(async () => {
|
|
3923
3928
|
if (!bundle || !valid) return;
|
|
3924
3929
|
const grouped = /* @__PURE__ */ new Map();
|
|
3925
3930
|
for (const stepPicks of selections) {
|
|
@@ -3974,8 +3979,8 @@ function MultiStepBundle(props) {
|
|
|
3974
3979
|
([pid, rule]) => {
|
|
3975
3980
|
if (!rule.required) return false;
|
|
3976
3981
|
return !steps.some(
|
|
3977
|
-
(_, i) => (0,
|
|
3978
|
-
(p) => p.id === pid && p.variants.nodes.some((v) => (0,
|
|
3982
|
+
(_, i) => (0, import_core14.productsForStep)(bundle, i).some(
|
|
3983
|
+
(p) => p.id === pid && p.variants.nodes.some((v) => (0, import_core14.isVariantFulfillable)(v, rule.min))
|
|
3979
3984
|
)
|
|
3980
3985
|
);
|
|
3981
3986
|
}
|
|
@@ -4099,8 +4104,8 @@ function MultiStepBundle(props) {
|
|
|
4099
4104
|
),
|
|
4100
4105
|
item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
|
|
4101
4106
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
|
|
4102
|
-
item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0,
|
|
4103
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-product-price", children: (0,
|
|
4107
|
+
item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core14.formatMoney)(item.compareAtPrice, currency) }),
|
|
4108
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core14.formatMoney)(item.price, currency) }),
|
|
4104
4109
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
|
|
4105
4110
|
] }),
|
|
4106
4111
|
item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
|
|
@@ -4130,7 +4135,7 @@ function MultiStepBundle(props) {
|
|
|
4130
4135
|
))
|
|
4131
4136
|
}
|
|
4132
4137
|
),
|
|
4133
|
-
(0,
|
|
4138
|
+
(0, import_core14.stepHeadroom)(step.maxQuantity, unitsByStep[i] ?? 0) !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4134
4139
|
"button",
|
|
4135
4140
|
{
|
|
4136
4141
|
type: "button",
|
|
@@ -4162,7 +4167,7 @@ function MultiStepBundle(props) {
|
|
|
4162
4167
|
bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
|
|
4163
4168
|
"You save",
|
|
4164
4169
|
" ",
|
|
4165
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-savings-amount": true, children: (0,
|
|
4170
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-savings-amount": true, children: (0, import_core14.formatMoney)(summary.savings, currency) }),
|
|
4166
4171
|
" ",
|
|
4167
4172
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { "data-savings-percent": true, children: [
|
|
4168
4173
|
"(",
|
|
@@ -4172,8 +4177,8 @@ function MultiStepBundle(props) {
|
|
|
4172
4177
|
] })
|
|
4173
4178
|
] }),
|
|
4174
4179
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
|
|
4175
|
-
bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0,
|
|
4176
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0,
|
|
4180
|
+
bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core14.formatMoney)(summary.comparePrice, currency) }),
|
|
4181
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core14.formatMoney)(summary.salePrice, currency) })
|
|
4177
4182
|
] })
|
|
4178
4183
|
] }),
|
|
4179
4184
|
cartError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
@@ -4190,7 +4195,6 @@ function MultiStepBundle(props) {
|
|
|
4190
4195
|
pickerOpenAt !== null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
4191
4196
|
MultiStepPicker,
|
|
4192
4197
|
{
|
|
4193
|
-
outOfStockBehavior,
|
|
4194
4198
|
bundle,
|
|
4195
4199
|
currency,
|
|
4196
4200
|
initialStep: pickerOpenAt,
|
|
@@ -4217,9 +4221,9 @@ function MultiStepBundle(props) {
|
|
|
4217
4221
|
);
|
|
4218
4222
|
}
|
|
4219
4223
|
function stepHasInStockProduct(bundle, stepIndex) {
|
|
4220
|
-
return (0,
|
|
4224
|
+
return (0, import_core14.productsForStep)(bundle, stepIndex).some((product) => {
|
|
4221
4225
|
const rule = ruleFor4(bundle, product.id);
|
|
4222
|
-
return product.variants.nodes.some((v) => (0,
|
|
4226
|
+
return product.variants.nodes.some((v) => (0, import_core14.isVariantFulfillable)(v, rule.min));
|
|
4223
4227
|
});
|
|
4224
4228
|
}
|
|
4225
4229
|
function PlusIcon2() {
|
|
@@ -4264,21 +4268,21 @@ async function fetchShopCustomCss(options) {
|
|
|
4264
4268
|
}
|
|
4265
4269
|
|
|
4266
4270
|
// src/hooks/useBundlesForProduct.ts
|
|
4267
|
-
var
|
|
4268
|
-
var
|
|
4271
|
+
var import_react17 = require("react");
|
|
4272
|
+
var import_core15 = require("@lime-bundles/core");
|
|
4269
4273
|
var INITIAL_STATE2 = {
|
|
4270
4274
|
status: "loading",
|
|
4271
4275
|
bundles: null,
|
|
4272
4276
|
error: null
|
|
4273
4277
|
};
|
|
4274
4278
|
function useBundlesForProduct(options) {
|
|
4275
|
-
const [state, setState] = (0,
|
|
4279
|
+
const [state, setState] = (0, import_react17.useState)(
|
|
4276
4280
|
INITIAL_STATE2
|
|
4277
4281
|
);
|
|
4278
|
-
(0,
|
|
4282
|
+
(0, import_react17.useEffect)(() => {
|
|
4279
4283
|
const controller = new AbortController();
|
|
4280
4284
|
setState(INITIAL_STATE2);
|
|
4281
|
-
(0,
|
|
4285
|
+
(0, import_core15.fetchBundlesForProduct)({
|
|
4282
4286
|
shopDomain: options.shopDomain,
|
|
4283
4287
|
storefrontAccessToken: options.storefrontAccessToken,
|
|
4284
4288
|
productHandle: options.productHandle,
|
|
@@ -4301,7 +4305,7 @@ function useBundlesForProduct(options) {
|
|
|
4301
4305
|
signal: controller.signal
|
|
4302
4306
|
}).then((settings) => {
|
|
4303
4307
|
if (controller.signal.aborted) return;
|
|
4304
|
-
(0,
|
|
4308
|
+
(0, import_core15.injectCustomCss)(options.shopDomain, settings.customCss);
|
|
4305
4309
|
}).catch(() => {
|
|
4306
4310
|
});
|
|
4307
4311
|
return () => {
|
|
@@ -4317,8 +4321,8 @@ function useBundlesForProduct(options) {
|
|
|
4317
4321
|
}
|
|
4318
4322
|
|
|
4319
4323
|
// src/index.ts
|
|
4324
|
+
var import_core16 = require("@lime-bundles/core");
|
|
4320
4325
|
var import_core17 = require("@lime-bundles/core");
|
|
4321
|
-
var import_core18 = require("@lime-bundles/core");
|
|
4322
4326
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4323
4327
|
0 && (module.exports = {
|
|
4324
4328
|
BUNDLES_FOR_PRODUCT_QUERY,
|
|
@@ -4326,7 +4330,6 @@ var import_core18 = require("@lime-bundles/core");
|
|
|
4326
4330
|
BogoBundle,
|
|
4327
4331
|
BundleCountdown,
|
|
4328
4332
|
BundleParseError,
|
|
4329
|
-
DEFAULT_OUT_OF_STOCK_BEHAVIOR,
|
|
4330
4333
|
FixedBundle,
|
|
4331
4334
|
MixMatchBundle,
|
|
4332
4335
|
MultiStepBundle,
|
|
@@ -4357,7 +4360,6 @@ var import_core18 = require("@lime-bundles/core");
|
|
|
4357
4360
|
parseCents,
|
|
4358
4361
|
parseMetaobjectBundle,
|
|
4359
4362
|
parseMetaobjectBundleStrict,
|
|
4360
|
-
parseOutOfStockBehavior,
|
|
4361
4363
|
percentageDiscountUnit,
|
|
4362
4364
|
productsForStep,
|
|
4363
4365
|
reportAddToCart,
|
|
@@ -4369,7 +4371,6 @@ var import_core18 = require("@lime-bundles/core");
|
|
|
4369
4371
|
useAnalytics,
|
|
4370
4372
|
useBundleData,
|
|
4371
4373
|
useBundlesForProduct,
|
|
4372
|
-
useShopSettings,
|
|
4373
4374
|
useVariantSelection,
|
|
4374
4375
|
useWidgetConfigVars,
|
|
4375
4376
|
validateQuantity
|