@lime-bundles/react 2.3.1 → 2.5.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 +737 -238
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -2
- package/dist/index.d.ts +50 -2
- package/dist/index.js +701 -191
- package/dist/index.js.map +1 -1
- package/docs/css-variables.md +14 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,56 +20,58 @@ 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: () =>
|
|
25
|
-
BundleParseError: () =>
|
|
23
|
+
BUNDLES_FOR_PRODUCT_QUERY: () => import_core13.BUNDLES_FOR_PRODUCT_QUERY,
|
|
24
|
+
BUNDLE_METAOBJECT_QUERY: () => import_core13.BUNDLE_METAOBJECT_QUERY,
|
|
25
|
+
BundleParseError: () => import_core12.BundleParseError,
|
|
26
26
|
FixedBundle: () => FixedBundle,
|
|
27
27
|
MixMatchBundle: () => MixMatchBundle,
|
|
28
|
-
SHOP_CUSTOM_CSS_QUERY: () =>
|
|
29
|
-
StorefrontApiError: () =>
|
|
28
|
+
SHOP_CUSTOM_CSS_QUERY: () => import_core13.SHOP_CUSTOM_CSS_QUERY,
|
|
29
|
+
StorefrontApiError: () => import_core12.StorefrontApiError,
|
|
30
|
+
VariantDropdown: () => VariantDropdown,
|
|
30
31
|
VolumeBundle: () => VolumeBundle,
|
|
31
|
-
WIDGET_CONFIG_DEFAULTS: () =>
|
|
32
|
-
applyABVariantB: () =>
|
|
33
|
-
applyWidgetConfigVars: () =>
|
|
34
|
-
calculateDiscount: () =>
|
|
35
|
-
calculateTierSavings: () =>
|
|
36
|
-
computeBundleSaleCents: () =>
|
|
37
|
-
computeFixedPricing: () =>
|
|
38
|
-
createStorefrontClient: () =>
|
|
32
|
+
WIDGET_CONFIG_DEFAULTS: () => import_core13.WIDGET_CONFIG_DEFAULTS,
|
|
33
|
+
applyABVariantB: () => import_core13.applyABVariantB,
|
|
34
|
+
applyWidgetConfigVars: () => import_core13.applyWidgetConfigVars,
|
|
35
|
+
calculateDiscount: () => import_core13.calculateDiscount,
|
|
36
|
+
calculateTierSavings: () => import_core13.calculateTierSavings,
|
|
37
|
+
computeBundleSaleCents: () => import_core13.computeBundleSaleCents,
|
|
38
|
+
computeFixedPricing: () => import_core13.computeFixedPricing,
|
|
39
|
+
createStorefrontClient: () => import_core13.createStorefrontClient,
|
|
39
40
|
fetchBundleData: () => fetchBundleData,
|
|
40
|
-
fetchBundlesForProduct: () =>
|
|
41
|
+
fetchBundlesForProduct: () => import_core13.fetchBundlesForProduct,
|
|
41
42
|
fetchShopCustomCss: () => fetchShopCustomCss,
|
|
42
|
-
formatCents: () =>
|
|
43
|
-
formatCountdown: () =>
|
|
44
|
-
formatMoney: () =>
|
|
45
|
-
formatUnitPrice: () =>
|
|
46
|
-
getABTestAssignment: () =>
|
|
47
|
-
getActiveTier: () =>
|
|
48
|
-
hasConsent: () =>
|
|
49
|
-
injectCustomCss: () =>
|
|
50
|
-
mergeWidgetConfig: () =>
|
|
51
|
-
observeImpression: () =>
|
|
52
|
-
parseCents: () =>
|
|
53
|
-
parseMetaobjectBundle: () =>
|
|
54
|
-
parseMetaobjectBundleStrict: () =>
|
|
55
|
-
percentageDiscountUnit: () =>
|
|
56
|
-
reportAddToCart: () =>
|
|
57
|
-
reportImpression: () =>
|
|
58
|
-
sanitizeCustomCss: () =>
|
|
59
|
-
setConsent: () =>
|
|
60
|
-
thumbnailRatioVars: () =>
|
|
61
|
-
transformImageUrl: () =>
|
|
43
|
+
formatCents: () => import_core13.formatCents,
|
|
44
|
+
formatCountdown: () => import_core13.formatCountdown,
|
|
45
|
+
formatMoney: () => import_core13.formatMoney,
|
|
46
|
+
formatUnitPrice: () => import_core13.formatUnitPrice,
|
|
47
|
+
getABTestAssignment: () => import_core13.getABTestAssignment,
|
|
48
|
+
getActiveTier: () => import_core13.getActiveTier,
|
|
49
|
+
hasConsent: () => import_core13.hasConsent,
|
|
50
|
+
injectCustomCss: () => import_core13.injectCustomCss,
|
|
51
|
+
mergeWidgetConfig: () => import_core13.mergeWidgetConfig,
|
|
52
|
+
observeImpression: () => import_core13.observeImpression,
|
|
53
|
+
parseCents: () => import_core13.parseCents,
|
|
54
|
+
parseMetaobjectBundle: () => import_core13.parseMetaobjectBundle,
|
|
55
|
+
parseMetaobjectBundleStrict: () => import_core13.parseMetaobjectBundleStrict,
|
|
56
|
+
percentageDiscountUnit: () => import_core13.percentageDiscountUnit,
|
|
57
|
+
reportAddToCart: () => import_core13.reportAddToCart,
|
|
58
|
+
reportImpression: () => import_core13.reportImpression,
|
|
59
|
+
sanitizeCustomCss: () => import_core13.sanitizeCustomCss,
|
|
60
|
+
setConsent: () => import_core13.setConsent,
|
|
61
|
+
thumbnailRatioVars: () => import_core13.thumbnailRatioVars,
|
|
62
|
+
transformImageUrl: () => import_core13.transformImageUrl,
|
|
62
63
|
useAnalytics: () => useAnalytics,
|
|
63
64
|
useBundleData: () => useBundleData,
|
|
64
65
|
useBundlesForProduct: () => useBundlesForProduct,
|
|
66
|
+
useVariantSelection: () => useVariantSelection,
|
|
65
67
|
useWidgetConfigVars: () => useWidgetConfigVars,
|
|
66
|
-
validateQuantity: () =>
|
|
68
|
+
validateQuantity: () => import_core13.validateQuantity
|
|
67
69
|
});
|
|
68
70
|
module.exports = __toCommonJS(index_exports);
|
|
69
71
|
|
|
70
72
|
// src/components/FixedBundle.tsx
|
|
71
|
-
var
|
|
72
|
-
var
|
|
73
|
+
var import_react6 = require("react");
|
|
74
|
+
var import_core8 = require("@lime-bundles/core");
|
|
73
75
|
|
|
74
76
|
// src/hooks/useBundleData.ts
|
|
75
77
|
var import_react = require("react");
|
|
@@ -260,8 +262,333 @@ function useWidgetConfigVars(config) {
|
|
|
260
262
|
);
|
|
261
263
|
}
|
|
262
264
|
|
|
263
|
-
// src/
|
|
265
|
+
// src/hooks/useVariantSelection.ts
|
|
266
|
+
var import_react4 = require("react");
|
|
267
|
+
var import_core6 = require("@lime-bundles/core");
|
|
268
|
+
var { findVariantByOptions, isOptionValueAvailable, toPickerVariant } = import_core6.variants;
|
|
269
|
+
function defaultInitial(variants) {
|
|
270
|
+
return variants.find((v) => v.availableForSale) ?? variants[0] ?? null;
|
|
271
|
+
}
|
|
272
|
+
function useVariantSelection({
|
|
273
|
+
variants,
|
|
274
|
+
optionNames,
|
|
275
|
+
initialVariant,
|
|
276
|
+
optionValuesByPosition
|
|
277
|
+
}) {
|
|
278
|
+
const seed = (0, import_react4.useMemo)(
|
|
279
|
+
() => initialVariant ?? defaultInitial(variants),
|
|
280
|
+
[initialVariant, variants]
|
|
281
|
+
);
|
|
282
|
+
const [selectedValues, setSelectedValues] = (0, import_react4.useState)(
|
|
283
|
+
() => seed ? seed.selectedOptions.map((o) => o.value) : optionNames.map(() => "")
|
|
284
|
+
);
|
|
285
|
+
(0, import_react4.useEffect)(() => {
|
|
286
|
+
if (!seed) return;
|
|
287
|
+
setSelectedValues(seed.selectedOptions.map((o) => o.value));
|
|
288
|
+
}, [seed?.id]);
|
|
289
|
+
const pickerVariants = (0, import_react4.useMemo)(
|
|
290
|
+
() => variants.map((v) => toPickerVariant(v)),
|
|
291
|
+
[variants]
|
|
292
|
+
);
|
|
293
|
+
const selectedVariant = (0, import_react4.useMemo)(() => {
|
|
294
|
+
if (selectedValues.length !== optionNames.length) return null;
|
|
295
|
+
const match = findVariantByOptions(pickerVariants, selectedValues);
|
|
296
|
+
if (!match) return null;
|
|
297
|
+
return variants.find((v) => v.id === match.id) ?? null;
|
|
298
|
+
}, [pickerVariants, selectedValues, variants, optionNames.length]);
|
|
299
|
+
const setOptionValue = (0, import_react4.useCallback)(
|
|
300
|
+
(optionIndex, value) => {
|
|
301
|
+
setSelectedValues((prev) => {
|
|
302
|
+
const next = prev.slice();
|
|
303
|
+
next[optionIndex] = value;
|
|
304
|
+
return next;
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
[]
|
|
308
|
+
);
|
|
309
|
+
const optionsByPosition = (0, import_react4.useMemo)(() => {
|
|
310
|
+
if (optionValuesByPosition) {
|
|
311
|
+
return optionValuesByPosition.map((values) => values.slice());
|
|
312
|
+
}
|
|
313
|
+
const out = optionNames.map(() => []);
|
|
314
|
+
for (const v of variants) {
|
|
315
|
+
v.selectedOptions.forEach((o, i) => {
|
|
316
|
+
if (i < out.length && !out[i].includes(o.value)) out[i].push(o.value);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}, [variants, optionNames, optionValuesByPosition]);
|
|
321
|
+
const optionsFor = (0, import_react4.useCallback)(
|
|
322
|
+
(optionIndex) => {
|
|
323
|
+
const values = optionsByPosition[optionIndex] ?? [];
|
|
324
|
+
return values.map((value) => ({
|
|
325
|
+
value,
|
|
326
|
+
disabled: !isOptionValueAvailable(
|
|
327
|
+
pickerVariants,
|
|
328
|
+
optionIndex,
|
|
329
|
+
value,
|
|
330
|
+
selectedValues
|
|
331
|
+
)
|
|
332
|
+
}));
|
|
333
|
+
},
|
|
334
|
+
[optionsByPosition, pickerVariants, selectedValues]
|
|
335
|
+
);
|
|
336
|
+
return {
|
|
337
|
+
selectedValues,
|
|
338
|
+
selectedVariant,
|
|
339
|
+
setOptionValue,
|
|
340
|
+
optionsFor
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/components/VariantDropdown.tsx
|
|
345
|
+
var import_react5 = require("react");
|
|
346
|
+
var import_core7 = require("@lime-bundles/core");
|
|
264
347
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
348
|
+
var { computePosition, emptyTypeAheadState, handleKey, pushTypeAheadChar } = import_core7.dropdown;
|
|
349
|
+
var ITEM_HEIGHT_PX = 32;
|
|
350
|
+
var LIST_PAD_Y = 8;
|
|
351
|
+
var MAX_VISIBLE_ITEMS = 8;
|
|
352
|
+
function VariantDropdown({
|
|
353
|
+
options,
|
|
354
|
+
value,
|
|
355
|
+
onChange,
|
|
356
|
+
ariaLabel,
|
|
357
|
+
className
|
|
358
|
+
}) {
|
|
359
|
+
const idBase = (0, import_react5.useId)();
|
|
360
|
+
const triggerRef = (0, import_react5.useRef)(null);
|
|
361
|
+
const listboxRef = (0, import_react5.useRef)(null);
|
|
362
|
+
const typeAheadRef = (0, import_react5.useRef)(emptyTypeAheadState());
|
|
363
|
+
const [isOpen, setIsOpen] = (0, import_react5.useState)(false);
|
|
364
|
+
const [activeIndex, setActiveIndex] = (0, import_react5.useState)(-1);
|
|
365
|
+
const [position, setPosition] = (0, import_react5.useState)(null);
|
|
366
|
+
const selectedIndex = (0, import_react5.useMemo)(
|
|
367
|
+
() => options.findIndex((o) => o.value === value),
|
|
368
|
+
[options, value]
|
|
369
|
+
);
|
|
370
|
+
const optionsState = (0, import_react5.useMemo)(
|
|
371
|
+
() => options.map((o) => ({ disabled: !!o.disabled, label: o.label })),
|
|
372
|
+
[options]
|
|
373
|
+
);
|
|
374
|
+
const selectedLabel = selectedIndex >= 0 ? options[selectedIndex].label : "";
|
|
375
|
+
function open() {
|
|
376
|
+
if (isOpen) return;
|
|
377
|
+
setIsOpen(true);
|
|
378
|
+
setActiveIndex(
|
|
379
|
+
selectedIndex >= 0 && !optionsState[selectedIndex]?.disabled ? selectedIndex : optionsState.findIndex((o) => !o.disabled)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
function close(restoreFocus) {
|
|
383
|
+
setIsOpen(false);
|
|
384
|
+
setActiveIndex(-1);
|
|
385
|
+
if (restoreFocus) triggerRef.current?.focus();
|
|
386
|
+
}
|
|
387
|
+
function commit(index) {
|
|
388
|
+
const opt = options[index];
|
|
389
|
+
if (!opt || opt.disabled) return;
|
|
390
|
+
if (opt.value !== value) onChange(opt.value);
|
|
391
|
+
close(true);
|
|
392
|
+
}
|
|
393
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
394
|
+
if (!isOpen) return;
|
|
395
|
+
const trigger = triggerRef.current;
|
|
396
|
+
if (!trigger) return;
|
|
397
|
+
const rect = trigger.getBoundingClientRect();
|
|
398
|
+
if (rect.width === 0) return;
|
|
399
|
+
const visibleCount = Math.min(options.length || 1, MAX_VISIBLE_ITEMS);
|
|
400
|
+
const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
|
|
401
|
+
setPosition(
|
|
402
|
+
computePosition({
|
|
403
|
+
trigger: {
|
|
404
|
+
top: rect.top,
|
|
405
|
+
bottom: rect.bottom,
|
|
406
|
+
left: rect.left,
|
|
407
|
+
width: rect.width
|
|
408
|
+
},
|
|
409
|
+
viewportHeight: window.innerHeight,
|
|
410
|
+
desiredHeight
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
}, [isOpen, options.length]);
|
|
414
|
+
(0, import_react5.useEffect)(() => {
|
|
415
|
+
if (!isOpen) return;
|
|
416
|
+
const isInsideDropdown = (event) => {
|
|
417
|
+
const path = event.composedPath();
|
|
418
|
+
const trigger = triggerRef.current;
|
|
419
|
+
const listbox = listboxRef.current;
|
|
420
|
+
return trigger != null && path.includes(trigger) || listbox != null && path.includes(listbox);
|
|
421
|
+
};
|
|
422
|
+
const onPointerdown = (event) => {
|
|
423
|
+
if (isInsideDropdown(event)) return;
|
|
424
|
+
close(false);
|
|
425
|
+
};
|
|
426
|
+
const onScroll = (event) => {
|
|
427
|
+
if (isInsideDropdown(event)) return;
|
|
428
|
+
close(false);
|
|
429
|
+
};
|
|
430
|
+
const onResize = () => close(false);
|
|
431
|
+
document.addEventListener("pointerdown", onPointerdown, true);
|
|
432
|
+
window.addEventListener("scroll", onScroll, true);
|
|
433
|
+
window.addEventListener("resize", onResize);
|
|
434
|
+
return () => {
|
|
435
|
+
document.removeEventListener("pointerdown", onPointerdown, true);
|
|
436
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
437
|
+
window.removeEventListener("resize", onResize);
|
|
438
|
+
};
|
|
439
|
+
}, [isOpen]);
|
|
440
|
+
(0, import_react5.useEffect)(() => {
|
|
441
|
+
if (!isOpen || activeIndex < 0) return;
|
|
442
|
+
const listbox = listboxRef.current;
|
|
443
|
+
if (!listbox) return;
|
|
444
|
+
const li = listbox.querySelector(
|
|
445
|
+
`#${CSS.escape(`${idBase}-opt-${activeIndex}`)}`
|
|
446
|
+
);
|
|
447
|
+
if (!li) return;
|
|
448
|
+
const liTop = li.offsetTop;
|
|
449
|
+
const liBottom = liTop + li.offsetHeight;
|
|
450
|
+
const visTop = listbox.scrollTop;
|
|
451
|
+
const visBottom = visTop + listbox.clientHeight;
|
|
452
|
+
if (liTop < visTop) {
|
|
453
|
+
listbox.scrollTop = liTop;
|
|
454
|
+
} else if (liBottom > visBottom) {
|
|
455
|
+
listbox.scrollTop = liBottom - listbox.clientHeight;
|
|
456
|
+
}
|
|
457
|
+
}, [activeIndex, isOpen, idBase]);
|
|
458
|
+
function onKeyDown(event) {
|
|
459
|
+
const action = handleKey(
|
|
460
|
+
{
|
|
461
|
+
key: event.key,
|
|
462
|
+
ctrlKey: event.ctrlKey,
|
|
463
|
+
metaKey: event.metaKey,
|
|
464
|
+
altKey: event.altKey,
|
|
465
|
+
shiftKey: event.shiftKey
|
|
466
|
+
},
|
|
467
|
+
{ isOpen, activeIndex, selectedIndex, options: optionsState }
|
|
468
|
+
);
|
|
469
|
+
if (action.preventDefault) event.preventDefault();
|
|
470
|
+
switch (action.type) {
|
|
471
|
+
case "open":
|
|
472
|
+
open();
|
|
473
|
+
if (action.activeIndex >= 0) setActiveIndex(action.activeIndex);
|
|
474
|
+
return;
|
|
475
|
+
case "close":
|
|
476
|
+
close(action.restoreFocus);
|
|
477
|
+
return;
|
|
478
|
+
case "move-active":
|
|
479
|
+
setActiveIndex(action.activeIndex);
|
|
480
|
+
return;
|
|
481
|
+
case "commit":
|
|
482
|
+
commit(action.index);
|
|
483
|
+
return;
|
|
484
|
+
case "type-ahead": {
|
|
485
|
+
const result = pushTypeAheadChar(
|
|
486
|
+
typeAheadRef.current,
|
|
487
|
+
action.char,
|
|
488
|
+
Date.now(),
|
|
489
|
+
optionsState
|
|
490
|
+
);
|
|
491
|
+
typeAheadRef.current = result.newState;
|
|
492
|
+
if (result.matchedIndex !== null) {
|
|
493
|
+
if (!isOpen) open();
|
|
494
|
+
setActiveIndex(result.matchedIndex);
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
case "passthrough":
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function onShellBlur(event) {
|
|
503
|
+
const next = event.relatedTarget;
|
|
504
|
+
if (next && event.currentTarget.contains(next)) return;
|
|
505
|
+
setTimeout(() => {
|
|
506
|
+
if (!isOpen) return;
|
|
507
|
+
const active = document.activeElement;
|
|
508
|
+
if (!triggerRef.current?.contains(active) && !listboxRef.current?.contains(active)) {
|
|
509
|
+
close(false);
|
|
510
|
+
}
|
|
511
|
+
}, 0);
|
|
512
|
+
}
|
|
513
|
+
const listboxId = `${idBase}-listbox`;
|
|
514
|
+
const activeDescendantId = activeIndex >= 0 ? `${idBase}-opt-${activeIndex}` : void 0;
|
|
515
|
+
const panelStyle = position ? { maxHeight: position.maxHeight } : void 0;
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
517
|
+
"div",
|
|
518
|
+
{
|
|
519
|
+
className: `lb-dropdown${className ? ` ${className}` : ""}`,
|
|
520
|
+
"data-lb-dropdown": "",
|
|
521
|
+
onBlur: onShellBlur,
|
|
522
|
+
children: [
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
524
|
+
"button",
|
|
525
|
+
{
|
|
526
|
+
ref: triggerRef,
|
|
527
|
+
type: "button",
|
|
528
|
+
className: "lb-dropdown-trigger",
|
|
529
|
+
role: "combobox",
|
|
530
|
+
"aria-haspopup": "listbox",
|
|
531
|
+
"aria-expanded": isOpen,
|
|
532
|
+
"aria-controls": listboxId,
|
|
533
|
+
"aria-label": ariaLabel,
|
|
534
|
+
"aria-activedescendant": activeDescendantId,
|
|
535
|
+
onClick: (e) => {
|
|
536
|
+
e.preventDefault();
|
|
537
|
+
if (isOpen) close(false);
|
|
538
|
+
else open();
|
|
539
|
+
},
|
|
540
|
+
onKeyDown,
|
|
541
|
+
children: [
|
|
542
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lb-dropdown-trigger-value", children: selectedLabel }),
|
|
543
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lb-dropdown-chevron", "aria-hidden": "true" })
|
|
544
|
+
]
|
|
545
|
+
}
|
|
546
|
+
),
|
|
547
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
548
|
+
"ul",
|
|
549
|
+
{
|
|
550
|
+
ref: listboxRef,
|
|
551
|
+
id: listboxId,
|
|
552
|
+
className: "lb-dropdown-listbox",
|
|
553
|
+
role: "listbox",
|
|
554
|
+
"aria-label": ariaLabel,
|
|
555
|
+
hidden: !isOpen,
|
|
556
|
+
"data-placement": position?.placement,
|
|
557
|
+
style: panelStyle,
|
|
558
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
559
|
+
children: options.map((opt, i) => (
|
|
560
|
+
// Keyboard activation lives on the trigger combobox via
|
|
561
|
+
// aria-activedescendant — the WAI-ARIA combobox pattern keeps
|
|
562
|
+
// focus on the button and tracks the active option by id, so
|
|
563
|
+
// the option <li> elements deliberately have no key handlers.
|
|
564
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
566
|
+
"li",
|
|
567
|
+
{
|
|
568
|
+
id: `${idBase}-opt-${i}`,
|
|
569
|
+
className: `lb-dropdown-option${i === activeIndex ? " is-active" : ""}`,
|
|
570
|
+
role: "option",
|
|
571
|
+
"aria-selected": i === selectedIndex,
|
|
572
|
+
"aria-disabled": opt.disabled || void 0,
|
|
573
|
+
"data-value": opt.value,
|
|
574
|
+
onClick: () => commit(i),
|
|
575
|
+
onMouseMove: () => {
|
|
576
|
+
if (!opt.disabled && i !== activeIndex) setActiveIndex(i);
|
|
577
|
+
},
|
|
578
|
+
children: opt.label
|
|
579
|
+
},
|
|
580
|
+
opt.value
|
|
581
|
+
)
|
|
582
|
+
))
|
|
583
|
+
}
|
|
584
|
+
)
|
|
585
|
+
]
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/components/FixedBundle.tsx
|
|
591
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
265
592
|
function FixedBundle(props) {
|
|
266
593
|
const {
|
|
267
594
|
shopDomain,
|
|
@@ -285,11 +612,21 @@ function FixedBundle(props) {
|
|
|
285
612
|
bundleType: "fixed",
|
|
286
613
|
enabled: analyticsEnabled !== false
|
|
287
614
|
});
|
|
288
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
289
|
-
const [cartError, setCartError] = (0,
|
|
615
|
+
const [addingToCart, setAddingToCart] = (0, import_react6.useState)(false);
|
|
616
|
+
const [cartError, setCartError] = (0, import_react6.useState)(null);
|
|
617
|
+
const [selectedVariants, setSelectedVariants] = (0, import_react6.useState)({});
|
|
618
|
+
const handleVariantChange = (0, import_react6.useCallback)(
|
|
619
|
+
(productId, variant) => {
|
|
620
|
+
setSelectedVariants((prev) => {
|
|
621
|
+
if (prev[productId] === variant) return prev;
|
|
622
|
+
return { ...prev, [productId]: variant };
|
|
623
|
+
});
|
|
624
|
+
},
|
|
625
|
+
[]
|
|
626
|
+
);
|
|
290
627
|
const bundle = result.status === "success" && result.bundle.bundleType === "fixed" ? result.bundle : null;
|
|
291
628
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
292
|
-
(0,
|
|
629
|
+
(0, import_react6.useEffect)(() => {
|
|
293
630
|
if (result.status === "error") {
|
|
294
631
|
onError?.(result.error);
|
|
295
632
|
return;
|
|
@@ -302,14 +639,20 @@ function FixedBundle(props) {
|
|
|
302
639
|
);
|
|
303
640
|
}
|
|
304
641
|
}, [result, onError]);
|
|
305
|
-
const handleAddToCart = (0,
|
|
642
|
+
const handleAddToCart = (0, import_react6.useCallback)(async () => {
|
|
306
643
|
if (!bundle) return;
|
|
307
|
-
const bundleLines = bundle.products.filter(
|
|
308
|
-
|
|
644
|
+
const bundleLines = bundle.products.filter(
|
|
645
|
+
(p) => p.variants.nodes.some(
|
|
646
|
+
(v) => (0, import_core8.isVariantFulfillable)(v, (0, import_core8.resolveBundleQty)(bundle, p.id, v.id))
|
|
647
|
+
)
|
|
648
|
+
).map((p) => {
|
|
649
|
+
const variant = selectedVariants[p.id] ?? p.variants.nodes.find(
|
|
650
|
+
(v) => (0, import_core8.isVariantFulfillable)(v, (0, import_core8.resolveBundleQty)(bundle, p.id, v.id))
|
|
651
|
+
);
|
|
309
652
|
return {
|
|
310
653
|
productId: p.id,
|
|
311
654
|
variant,
|
|
312
|
-
quantity: (0,
|
|
655
|
+
quantity: (0, import_core8.resolveBundleQty)(bundle, p.id, variant.id)
|
|
313
656
|
};
|
|
314
657
|
}).filter((line) => line.quantity > 0);
|
|
315
658
|
if (bundleLines.length === 0) return;
|
|
@@ -327,7 +670,7 @@ function FixedBundle(props) {
|
|
|
327
670
|
await onAddToCart(lines);
|
|
328
671
|
const totalPrice = bundleLines.reduce((sum, line) => {
|
|
329
672
|
const price = parseFloat(line.variant.price.amount);
|
|
330
|
-
const discounted = (0,
|
|
673
|
+
const discounted = (0, import_core8.calculateDiscount)(
|
|
331
674
|
price,
|
|
332
675
|
bundle.discountConfig.discountType,
|
|
333
676
|
bundle.discountConfig.discountValue
|
|
@@ -349,17 +692,17 @@ function FixedBundle(props) {
|
|
|
349
692
|
} finally {
|
|
350
693
|
setAddingToCart(false);
|
|
351
694
|
}
|
|
352
|
-
}, [bundle, onAddToCart, onError, trackAddToCart]);
|
|
695
|
+
}, [bundle, onAddToCart, onError, trackAddToCart, selectedVariants]);
|
|
353
696
|
if (result.status === "loading") {
|
|
354
|
-
return /* @__PURE__ */ (0,
|
|
355
|
-
/* @__PURE__ */ (0,
|
|
356
|
-
/* @__PURE__ */ (0,
|
|
697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
|
|
699
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
|
|
357
700
|
] });
|
|
358
701
|
}
|
|
359
702
|
if (result.status === "error") return null;
|
|
360
703
|
if (!bundle) return null;
|
|
361
704
|
const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
362
|
-
return /* @__PURE__ */ (0,
|
|
705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
363
706
|
"div",
|
|
364
707
|
{
|
|
365
708
|
ref: (el) => {
|
|
@@ -370,38 +713,20 @@ function FixedBundle(props) {
|
|
|
370
713
|
role: "region",
|
|
371
714
|
"aria-label": bundle.title,
|
|
372
715
|
children: [
|
|
373
|
-
/* @__PURE__ */ (0,
|
|
374
|
-
bundle.discountLabel && /* @__PURE__ */ (0,
|
|
375
|
-
/* @__PURE__ */ (0,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
product.featuredImage && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
388
|
-
"img",
|
|
389
|
-
{
|
|
390
|
-
src: product.featuredImage.url,
|
|
391
|
-
alt: product.featuredImage.altText ?? product.title,
|
|
392
|
-
className: "lb-bundle__product-image",
|
|
393
|
-
loading: "lazy"
|
|
394
|
-
}
|
|
395
|
-
),
|
|
396
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
397
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
398
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lb-bundle__product-price", children: priceText }),
|
|
399
|
-
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText })
|
|
400
|
-
] })
|
|
401
|
-
] }, product.id);
|
|
402
|
-
}) }),
|
|
403
|
-
cartError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
404
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
716
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "lb-bundle__title", children: bundle.title }),
|
|
717
|
+
bundle.discountLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle__discount-badge", children: bundle.discountLabel }),
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle__products", children: bundle.products.map((product) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
719
|
+
FixedProductRow,
|
|
720
|
+
{
|
|
721
|
+
bundle,
|
|
722
|
+
product,
|
|
723
|
+
currency,
|
|
724
|
+
onVariantChange: handleVariantChange
|
|
725
|
+
},
|
|
726
|
+
product.id
|
|
727
|
+
)) }),
|
|
728
|
+
cartError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
405
730
|
"button",
|
|
406
731
|
{
|
|
407
732
|
className: "lb-bundle__cta",
|
|
@@ -415,11 +740,80 @@ function FixedBundle(props) {
|
|
|
415
740
|
}
|
|
416
741
|
);
|
|
417
742
|
}
|
|
743
|
+
function FixedProductRow({ bundle, product, currency, onVariantChange }) {
|
|
744
|
+
const variants = product.variants.nodes;
|
|
745
|
+
const optionNames = (0, import_react6.useMemo)(() => {
|
|
746
|
+
const first = variants[0];
|
|
747
|
+
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
748
|
+
}, [variants]);
|
|
749
|
+
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
750
|
+
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({
|
|
751
|
+
variants,
|
|
752
|
+
optionNames
|
|
753
|
+
});
|
|
754
|
+
(0, import_react6.useEffect)(() => {
|
|
755
|
+
onVariantChange(product.id, selectedVariant ?? null);
|
|
756
|
+
}, [selectedVariant, product.id, onVariantChange]);
|
|
757
|
+
const displayVariant = selectedVariant ?? variants.find(
|
|
758
|
+
(v) => (0, import_core8.isVariantFulfillable)(v, (0, import_core8.resolveBundleQty)(bundle, product.id, v.id))
|
|
759
|
+
) ?? variants[0];
|
|
760
|
+
const priceText = displayVariant ? (0, import_core8.formatMoney)(displayVariant.price.amount, currency) : (0, import_core8.formatMoney)(product.priceRange.minVariantPrice.amount, currency);
|
|
761
|
+
const unitPriceText = displayVariant ? (0, import_core8.formatUnitPrice)(
|
|
762
|
+
displayVariant.unitPrice,
|
|
763
|
+
displayVariant.unitPriceMeasurement,
|
|
764
|
+
currency
|
|
765
|
+
) : null;
|
|
766
|
+
const thumbImage = displayVariant?.image ?? product.featuredImage;
|
|
767
|
+
const showLowStock = !!displayVariant && (0, import_core8.shouldShowLowStockBadge)(
|
|
768
|
+
displayVariant,
|
|
769
|
+
(0, import_core8.resolveBundleQty)(bundle, product.id, displayVariant.id),
|
|
770
|
+
bundle.widgetConfig.lowStockThreshold,
|
|
771
|
+
bundle.widgetConfig.showLowStockBadge
|
|
772
|
+
);
|
|
773
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle__product", part: "product", children: [
|
|
774
|
+
thumbImage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
775
|
+
"img",
|
|
776
|
+
{
|
|
777
|
+
src: thumbImage.url,
|
|
778
|
+
alt: thumbImage.altText ?? product.title,
|
|
779
|
+
className: "lb-bundle__product-image",
|
|
780
|
+
loading: "lazy"
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-price", children: priceText }),
|
|
786
|
+
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
|
|
787
|
+
showLowStock && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
|
|
788
|
+
"Only ",
|
|
789
|
+
displayVariant.quantityAvailable,
|
|
790
|
+
" left"
|
|
791
|
+
] }),
|
|
792
|
+
showPicker && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
|
|
793
|
+
const dropdownOptions = optionsFor(optionIndex).map((o) => ({
|
|
794
|
+
value: o.value,
|
|
795
|
+
label: o.value,
|
|
796
|
+
disabled: o.disabled
|
|
797
|
+
}));
|
|
798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
799
|
+
VariantDropdown,
|
|
800
|
+
{
|
|
801
|
+
options: dropdownOptions,
|
|
802
|
+
value: selectedValues[optionIndex] ?? null,
|
|
803
|
+
onChange: (v) => setOptionValue(optionIndex, v),
|
|
804
|
+
ariaLabel: optionName
|
|
805
|
+
},
|
|
806
|
+
optionName
|
|
807
|
+
);
|
|
808
|
+
}) })
|
|
809
|
+
] })
|
|
810
|
+
] });
|
|
811
|
+
}
|
|
418
812
|
|
|
419
813
|
// src/components/MixMatchBundle.tsx
|
|
420
|
-
var
|
|
421
|
-
var
|
|
422
|
-
var
|
|
814
|
+
var import_react7 = require("react");
|
|
815
|
+
var import_core9 = require("@lime-bundles/core");
|
|
816
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
423
817
|
function MixMatchBundle(props) {
|
|
424
818
|
const {
|
|
425
819
|
shopDomain,
|
|
@@ -443,14 +837,14 @@ function MixMatchBundle(props) {
|
|
|
443
837
|
bundleType: "mix_match",
|
|
444
838
|
enabled: analyticsEnabled !== false
|
|
445
839
|
});
|
|
446
|
-
const [selections, setSelections] = (0,
|
|
840
|
+
const [selections, setSelections] = (0, import_react7.useState)(
|
|
447
841
|
/* @__PURE__ */ new Map()
|
|
448
842
|
);
|
|
449
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
450
|
-
const [cartError, setCartError] = (0,
|
|
843
|
+
const [addingToCart, setAddingToCart] = (0, import_react7.useState)(false);
|
|
844
|
+
const [cartError, setCartError] = (0, import_react7.useState)(null);
|
|
451
845
|
const bundle = result.status === "success" && result.bundle.bundleType === "mix_match" ? result.bundle : null;
|
|
452
846
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
453
|
-
(0,
|
|
847
|
+
(0, import_react7.useEffect)(() => {
|
|
454
848
|
if (result.status === "error") {
|
|
455
849
|
onError?.(result.error);
|
|
456
850
|
return;
|
|
@@ -467,8 +861,8 @@ function MixMatchBundle(props) {
|
|
|
467
861
|
(sum, s) => sum + s.quantity,
|
|
468
862
|
0
|
|
469
863
|
);
|
|
470
|
-
const validation = bundle ? (0,
|
|
471
|
-
const toggleProduct = (0,
|
|
864
|
+
const validation = bundle ? (0, import_core9.validateQuantity)(totalQuantity, bundle.minQuantity, bundle.maxQuantity) : { valid: false, totalQuantity: 0, message: null };
|
|
865
|
+
const toggleProduct = (0, import_react7.useCallback)(
|
|
472
866
|
(productId, variant) => {
|
|
473
867
|
setSelections((prev) => {
|
|
474
868
|
const next = new Map(prev);
|
|
@@ -476,7 +870,7 @@ function MixMatchBundle(props) {
|
|
|
476
870
|
if (next.has(key)) {
|
|
477
871
|
next.delete(key);
|
|
478
872
|
} else {
|
|
479
|
-
const configuredQty = bundle ? (0,
|
|
873
|
+
const configuredQty = bundle ? (0, import_core9.resolveBundleQty)(bundle, productId, variant.id) : 1;
|
|
480
874
|
next.set(key, {
|
|
481
875
|
productId,
|
|
482
876
|
variantId: variant.id,
|
|
@@ -488,7 +882,7 @@ function MixMatchBundle(props) {
|
|
|
488
882
|
},
|
|
489
883
|
[bundle]
|
|
490
884
|
);
|
|
491
|
-
const updateQuantity = (0,
|
|
885
|
+
const updateQuantity = (0, import_react7.useCallback)(
|
|
492
886
|
(productId, variantId, quantity) => {
|
|
493
887
|
setSelections((prev) => {
|
|
494
888
|
const next = new Map(prev);
|
|
@@ -503,7 +897,7 @@ function MixMatchBundle(props) {
|
|
|
503
897
|
},
|
|
504
898
|
[]
|
|
505
899
|
);
|
|
506
|
-
const handleAddToCart = (0,
|
|
900
|
+
const handleAddToCart = (0, import_react7.useCallback)(async () => {
|
|
507
901
|
if (!bundle || !validation.valid) return;
|
|
508
902
|
const lines = Array.from(selections.values()).map((s) => ({
|
|
509
903
|
merchandiseId: s.variantId,
|
|
@@ -548,15 +942,15 @@ function MixMatchBundle(props) {
|
|
|
548
942
|
totalQuantity
|
|
549
943
|
]);
|
|
550
944
|
if (result.status === "loading") {
|
|
551
|
-
return /* @__PURE__ */ (0,
|
|
552
|
-
/* @__PURE__ */ (0,
|
|
553
|
-
/* @__PURE__ */ (0,
|
|
945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
|
|
554
948
|
] });
|
|
555
949
|
}
|
|
556
950
|
if (result.status === "error") return null;
|
|
557
951
|
if (!bundle) return null;
|
|
558
952
|
const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
559
|
-
return /* @__PURE__ */ (0,
|
|
953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
560
954
|
"div",
|
|
561
955
|
{
|
|
562
956
|
ref: (el) => {
|
|
@@ -567,83 +961,24 @@ function MixMatchBundle(props) {
|
|
|
567
961
|
role: "region",
|
|
568
962
|
"aria-label": bundle.title,
|
|
569
963
|
children: [
|
|
570
|
-
/* @__PURE__ */ (0,
|
|
571
|
-
bundle.discountLabel && /* @__PURE__ */ (0,
|
|
572
|
-
/* @__PURE__ */ (0,
|
|
573
|
-
/* @__PURE__ */ (0,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
className: "lb-bundle__product-image",
|
|
589
|
-
loading: "lazy"
|
|
590
|
-
}
|
|
591
|
-
),
|
|
592
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
593
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
594
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-price", children: (0, import_core7.formatMoney)(defaultVariant.price.amount, currency) }),
|
|
595
|
-
(() => {
|
|
596
|
-
const unitPriceText = (0, import_core7.formatUnitPrice)(
|
|
597
|
-
defaultVariant.unitPrice,
|
|
598
|
-
defaultVariant.unitPriceMeasurement,
|
|
599
|
-
currency
|
|
600
|
-
);
|
|
601
|
-
return unitPriceText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }) : null;
|
|
602
|
-
})()
|
|
603
|
-
] }),
|
|
604
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle__product-actions", children: selected ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle__quantity-control", children: [
|
|
605
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
606
|
-
"button",
|
|
607
|
-
{
|
|
608
|
-
"aria-label": `Decrease ${product.title}`,
|
|
609
|
-
onClick: () => updateQuantity(
|
|
610
|
-
product.id,
|
|
611
|
-
defaultVariant.id,
|
|
612
|
-
selected.quantity - 1
|
|
613
|
-
),
|
|
614
|
-
children: "\u2212"
|
|
615
|
-
}
|
|
616
|
-
),
|
|
617
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: selected.quantity }),
|
|
618
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
619
|
-
"button",
|
|
620
|
-
{
|
|
621
|
-
"aria-label": `Increase ${product.title}`,
|
|
622
|
-
onClick: () => updateQuantity(
|
|
623
|
-
product.id,
|
|
624
|
-
defaultVariant.id,
|
|
625
|
-
selected.quantity + 1
|
|
626
|
-
),
|
|
627
|
-
children: "+"
|
|
628
|
-
}
|
|
629
|
-
)
|
|
630
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
631
|
-
"button",
|
|
632
|
-
{
|
|
633
|
-
className: "lb-bundle__select-btn",
|
|
634
|
-
onClick: () => toggleProduct(product.id, defaultVariant),
|
|
635
|
-
disabled: !defaultVariant.availableForSale,
|
|
636
|
-
children: defaultVariant.availableForSale ? "Select" : "Sold out"
|
|
637
|
-
}
|
|
638
|
-
) })
|
|
639
|
-
]
|
|
640
|
-
},
|
|
641
|
-
product.id
|
|
642
|
-
);
|
|
643
|
-
}) }),
|
|
644
|
-
validation.message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__validation", role: "status", children: validation.message }),
|
|
645
|
-
cartError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
646
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
964
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "lb-bundle__title", children: bundle.title }),
|
|
965
|
+
bundle.discountLabel && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle__discount-badge", children: bundle.discountLabel }),
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__instructions", children: bundle.minQuantity && bundle.maxQuantity ? `Select ${bundle.minQuantity}\u2013${bundle.maxQuantity} items` : bundle.minQuantity ? `Select at least ${bundle.minQuantity} items` : bundle.maxQuantity ? `Select up to ${bundle.maxQuantity} items` : "Select your items" }),
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle__products lb-bundle__products--selectable", children: bundle.products.map((product) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
968
|
+
MixMatchProductRow,
|
|
969
|
+
{
|
|
970
|
+
bundle,
|
|
971
|
+
product,
|
|
972
|
+
currency,
|
|
973
|
+
selections,
|
|
974
|
+
onToggle: toggleProduct,
|
|
975
|
+
onUpdateQuantity: updateQuantity
|
|
976
|
+
},
|
|
977
|
+
product.id
|
|
978
|
+
)) }),
|
|
979
|
+
validation.message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__validation", role: "status", children: validation.message }),
|
|
980
|
+
cartError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
981
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
647
982
|
"button",
|
|
648
983
|
{
|
|
649
984
|
className: "lb-bundle__cta",
|
|
@@ -657,11 +992,132 @@ function MixMatchBundle(props) {
|
|
|
657
992
|
}
|
|
658
993
|
);
|
|
659
994
|
}
|
|
995
|
+
function MixMatchProductRow({
|
|
996
|
+
bundle,
|
|
997
|
+
product,
|
|
998
|
+
currency,
|
|
999
|
+
selections,
|
|
1000
|
+
onToggle,
|
|
1001
|
+
onUpdateQuantity
|
|
1002
|
+
}) {
|
|
1003
|
+
const variants = product.variants.nodes;
|
|
1004
|
+
const optionNames = (0, import_react7.useMemo)(() => {
|
|
1005
|
+
const first = variants[0];
|
|
1006
|
+
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
1007
|
+
}, [variants]);
|
|
1008
|
+
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
1009
|
+
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
|
|
1010
|
+
const displayVariant = selectedVariant ?? variants.find(
|
|
1011
|
+
(v) => (0, import_core9.isVariantFulfillable)(v, (0, import_core9.resolveBundleQty)(bundle, product.id, v.id))
|
|
1012
|
+
) ?? variants[0] ?? null;
|
|
1013
|
+
if (!displayVariant) return null;
|
|
1014
|
+
const displayVariantQty = (0, import_core9.resolveBundleQty)(
|
|
1015
|
+
bundle,
|
|
1016
|
+
product.id,
|
|
1017
|
+
displayVariant.id
|
|
1018
|
+
);
|
|
1019
|
+
const displayVariantFulfillable = (0, import_core9.isVariantFulfillable)(
|
|
1020
|
+
displayVariant,
|
|
1021
|
+
displayVariantQty
|
|
1022
|
+
);
|
|
1023
|
+
const key = `${product.id}:${displayVariant.id}`;
|
|
1024
|
+
const selected = selections.get(key);
|
|
1025
|
+
const thumbImage = displayVariant.image ?? product.featuredImage;
|
|
1026
|
+
const unitPriceText = (0, import_core9.formatUnitPrice)(
|
|
1027
|
+
displayVariant.unitPrice,
|
|
1028
|
+
displayVariant.unitPriceMeasurement,
|
|
1029
|
+
currency
|
|
1030
|
+
);
|
|
1031
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1032
|
+
"div",
|
|
1033
|
+
{
|
|
1034
|
+
className: `lb-bundle__product lb-bundle__product--selectable ${selected ? "lb-bundle__product--selected" : ""}`,
|
|
1035
|
+
children: [
|
|
1036
|
+
thumbImage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1037
|
+
"img",
|
|
1038
|
+
{
|
|
1039
|
+
src: thumbImage.url,
|
|
1040
|
+
alt: thumbImage.altText ?? product.title,
|
|
1041
|
+
className: "lb-bundle__product-image",
|
|
1042
|
+
loading: "lazy"
|
|
1043
|
+
}
|
|
1044
|
+
),
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__product-price", children: (0, import_core9.formatMoney)(displayVariant.price.amount, currency) }),
|
|
1048
|
+
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
|
|
1049
|
+
(0, import_core9.shouldShowLowStockBadge)(
|
|
1050
|
+
displayVariant,
|
|
1051
|
+
displayVariantQty,
|
|
1052
|
+
bundle.widgetConfig.lowStockThreshold,
|
|
1053
|
+
bundle.widgetConfig.showLowStockBadge
|
|
1054
|
+
) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
|
|
1055
|
+
"Only ",
|
|
1056
|
+
displayVariant.quantityAvailable,
|
|
1057
|
+
" left"
|
|
1058
|
+
] }),
|
|
1059
|
+
showPicker && !selected && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
|
|
1060
|
+
const dropdownOptions = optionsFor(optionIndex).map((o) => ({
|
|
1061
|
+
value: o.value,
|
|
1062
|
+
label: o.value,
|
|
1063
|
+
disabled: o.disabled
|
|
1064
|
+
}));
|
|
1065
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1066
|
+
VariantDropdown,
|
|
1067
|
+
{
|
|
1068
|
+
options: dropdownOptions,
|
|
1069
|
+
value: selectedValues[optionIndex] ?? null,
|
|
1070
|
+
onChange: (v) => setOptionValue(optionIndex, v),
|
|
1071
|
+
ariaLabel: optionName
|
|
1072
|
+
},
|
|
1073
|
+
optionName
|
|
1074
|
+
);
|
|
1075
|
+
}) })
|
|
1076
|
+
] }),
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle__product-actions", children: selected ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle__quantity-control", children: [
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1079
|
+
"button",
|
|
1080
|
+
{
|
|
1081
|
+
"aria-label": `Decrease ${product.title}`,
|
|
1082
|
+
onClick: () => onUpdateQuantity(
|
|
1083
|
+
product.id,
|
|
1084
|
+
displayVariant.id,
|
|
1085
|
+
selected.quantity - 1
|
|
1086
|
+
),
|
|
1087
|
+
children: "\u2212"
|
|
1088
|
+
}
|
|
1089
|
+
),
|
|
1090
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: selected.quantity }),
|
|
1091
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1092
|
+
"button",
|
|
1093
|
+
{
|
|
1094
|
+
"aria-label": `Increase ${product.title}`,
|
|
1095
|
+
onClick: () => onUpdateQuantity(
|
|
1096
|
+
product.id,
|
|
1097
|
+
displayVariant.id,
|
|
1098
|
+
selected.quantity + 1
|
|
1099
|
+
),
|
|
1100
|
+
children: "+"
|
|
1101
|
+
}
|
|
1102
|
+
)
|
|
1103
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1104
|
+
"button",
|
|
1105
|
+
{
|
|
1106
|
+
className: "lb-bundle__select-btn",
|
|
1107
|
+
onClick: () => onToggle(product.id, displayVariant),
|
|
1108
|
+
disabled: !displayVariantFulfillable,
|
|
1109
|
+
children: displayVariantFulfillable ? "Select" : "Sold out"
|
|
1110
|
+
}
|
|
1111
|
+
) })
|
|
1112
|
+
]
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
660
1116
|
|
|
661
1117
|
// src/components/VolumeBundle.tsx
|
|
662
|
-
var
|
|
663
|
-
var
|
|
664
|
-
var
|
|
1118
|
+
var import_react8 = require("react");
|
|
1119
|
+
var import_core10 = require("@lime-bundles/core");
|
|
1120
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
665
1121
|
function VolumeBundle(props) {
|
|
666
1122
|
const {
|
|
667
1123
|
shopDomain,
|
|
@@ -685,12 +1141,12 @@ function VolumeBundle(props) {
|
|
|
685
1141
|
bundleType: "volume",
|
|
686
1142
|
enabled: analyticsEnabled !== false
|
|
687
1143
|
});
|
|
688
|
-
const [quantity, setQuantity] = (0,
|
|
689
|
-
const [addingToCart, setAddingToCart] = (0,
|
|
690
|
-
const [cartError, setCartError] = (0,
|
|
1144
|
+
const [quantity, setQuantity] = (0, import_react8.useState)(1);
|
|
1145
|
+
const [addingToCart, setAddingToCart] = (0, import_react8.useState)(false);
|
|
1146
|
+
const [cartError, setCartError] = (0, import_react8.useState)(null);
|
|
691
1147
|
const bundle = result.status === "success" && result.bundle.bundleType === "volume" ? result.bundle : null;
|
|
692
1148
|
const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
|
|
693
|
-
(0,
|
|
1149
|
+
(0, import_react8.useEffect)(() => {
|
|
694
1150
|
if (result.status === "error") {
|
|
695
1151
|
onError?.(result.error);
|
|
696
1152
|
return;
|
|
@@ -704,18 +1160,38 @@ function VolumeBundle(props) {
|
|
|
704
1160
|
}
|
|
705
1161
|
}, [result, onError]);
|
|
706
1162
|
const product = bundle?.products[0];
|
|
707
|
-
const
|
|
1163
|
+
const variants = (0, import_react8.useMemo)(() => product?.variants.nodes ?? [], [product]);
|
|
1164
|
+
const optionNames = (0, import_react8.useMemo)(() => {
|
|
1165
|
+
const first = variants[0];
|
|
1166
|
+
return first ? first.selectedOptions.map((o) => o.name) : [];
|
|
1167
|
+
}, [variants]);
|
|
1168
|
+
const showPicker = variants.length > 1 && optionNames.length > 0;
|
|
1169
|
+
const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
|
|
1170
|
+
const minTierQty = bundle?.volumeTiers[0]?.minQuantity ?? 1;
|
|
1171
|
+
const displayVariant = selectedVariant ?? variants.find((v) => (0, import_core10.isVariantFulfillable)(v, minTierQty)) ?? variants[0];
|
|
1172
|
+
const basePrice = displayVariant ? parseFloat(displayVariant.price.amount) : product ? parseFloat(product.priceRange.minVariantPrice.amount) : 0;
|
|
708
1173
|
const currency = product?.priceRange.minVariantPrice.currencyCode ?? "USD";
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
) :
|
|
715
|
-
const
|
|
716
|
-
|
|
1174
|
+
const thumbImage = displayVariant?.image ?? product?.featuredImage ?? null;
|
|
1175
|
+
const unitPriceText = displayVariant ? (0, import_core10.formatUnitPrice)(
|
|
1176
|
+
displayVariant.unitPrice,
|
|
1177
|
+
displayVariant.unitPriceMeasurement,
|
|
1178
|
+
currency
|
|
1179
|
+
) : null;
|
|
1180
|
+
const tierSavings = (0, import_react8.useMemo)(
|
|
1181
|
+
() => bundle ? (0, import_core10.calculateTierSavings)(
|
|
1182
|
+
bundle.volumeTiers,
|
|
1183
|
+
basePrice,
|
|
1184
|
+
quantity,
|
|
1185
|
+
bundle.discountConfig.discountType
|
|
1186
|
+
) : [],
|
|
1187
|
+
[bundle, basePrice, quantity]
|
|
1188
|
+
);
|
|
1189
|
+
const activeTier = bundle ? (0, import_core10.getActiveTier)(bundle.volumeTiers, quantity) : null;
|
|
1190
|
+
const handleAddToCart = (0, import_react8.useCallback)(async () => {
|
|
717
1191
|
if (!bundle || !product) return;
|
|
718
|
-
const variant = product.variants.nodes.find(
|
|
1192
|
+
const variant = selectedVariant ?? product.variants.nodes.find(
|
|
1193
|
+
(v) => (0, import_core10.isVariantFulfillable)(v, quantity)
|
|
1194
|
+
);
|
|
719
1195
|
if (!variant) return;
|
|
720
1196
|
const lines = [
|
|
721
1197
|
{
|
|
@@ -752,18 +1228,20 @@ function VolumeBundle(props) {
|
|
|
752
1228
|
basePrice,
|
|
753
1229
|
onAddToCart,
|
|
754
1230
|
onError,
|
|
755
|
-
trackAddToCart
|
|
1231
|
+
trackAddToCart,
|
|
1232
|
+
selectedVariant,
|
|
1233
|
+
tierSavings
|
|
756
1234
|
]);
|
|
757
1235
|
if (result.status === "loading") {
|
|
758
|
-
return /* @__PURE__ */ (0,
|
|
759
|
-
/* @__PURE__ */ (0,
|
|
760
|
-
/* @__PURE__ */ (0,
|
|
1236
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-skeleton lb-skeleton--tiers" })
|
|
761
1239
|
] });
|
|
762
1240
|
}
|
|
763
1241
|
if (result.status === "error") return null;
|
|
764
1242
|
if (!bundle) return null;
|
|
765
1243
|
if (!product) return null;
|
|
766
|
-
return /* @__PURE__ */ (0,
|
|
1244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
767
1245
|
"div",
|
|
768
1246
|
{
|
|
769
1247
|
ref: (el) => {
|
|
@@ -774,55 +1252,64 @@ function VolumeBundle(props) {
|
|
|
774
1252
|
role: "region",
|
|
775
1253
|
"aria-label": bundle.title,
|
|
776
1254
|
children: [
|
|
777
|
-
/* @__PURE__ */ (0,
|
|
778
|
-
/* @__PURE__ */ (0,
|
|
779
|
-
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "lb-bundle__title", children: bundle.title }),
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle__product lb-bundle__product--volume", children: [
|
|
1257
|
+
thumbImage && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
780
1258
|
"img",
|
|
781
1259
|
{
|
|
782
|
-
src:
|
|
783
|
-
alt:
|
|
1260
|
+
src: thumbImage.url,
|
|
1261
|
+
alt: thumbImage.altText ?? product.title,
|
|
784
1262
|
className: "lb-bundle__product-image",
|
|
785
1263
|
loading: "lazy"
|
|
786
1264
|
}
|
|
787
1265
|
),
|
|
788
|
-
/* @__PURE__ */ (0,
|
|
789
|
-
/* @__PURE__ */ (0,
|
|
790
|
-
/* @__PURE__ */ (0,
|
|
791
|
-
(0,
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle__product-info", children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "lb-bundle__product-price", children: [
|
|
1269
|
+
(0, import_core10.formatMoney)(basePrice, currency),
|
|
792
1270
|
" each"
|
|
793
1271
|
] }),
|
|
794
|
-
((
|
|
795
|
-
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
)
|
|
801
|
-
return
|
|
802
|
-
|
|
1272
|
+
unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
|
|
1273
|
+
showPicker && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
|
|
1274
|
+
const dropdownOptions = optionsFor(optionIndex).map((o) => ({
|
|
1275
|
+
value: o.value,
|
|
1276
|
+
label: o.value,
|
|
1277
|
+
disabled: o.disabled
|
|
1278
|
+
}));
|
|
1279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1280
|
+
VariantDropdown,
|
|
1281
|
+
{
|
|
1282
|
+
options: dropdownOptions,
|
|
1283
|
+
value: selectedValues[optionIndex] ?? null,
|
|
1284
|
+
onChange: (v) => setOptionValue(optionIndex, v),
|
|
1285
|
+
ariaLabel: optionName
|
|
1286
|
+
},
|
|
1287
|
+
optionName
|
|
1288
|
+
);
|
|
1289
|
+
}) })
|
|
803
1290
|
] })
|
|
804
1291
|
] }),
|
|
805
|
-
/* @__PURE__ */ (0,
|
|
1292
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
806
1293
|
"div",
|
|
807
1294
|
{
|
|
808
1295
|
className: "lb-bundle__tiers",
|
|
809
1296
|
role: "table",
|
|
810
1297
|
"aria-label": "Volume discounts",
|
|
811
|
-
children: tierSavings.map((ts) => /* @__PURE__ */ (0,
|
|
1298
|
+
children: tierSavings.map((ts) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
812
1299
|
"div",
|
|
813
1300
|
{
|
|
814
1301
|
className: `lb-bundle__tier ${ts.isActive ? "lb-bundle__tier--active" : ""}`,
|
|
815
1302
|
role: "row",
|
|
816
1303
|
children: [
|
|
817
|
-
/* @__PURE__ */ (0,
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-bundle__tier-quantity", role: "cell", children: [
|
|
818
1305
|
ts.tier.minQuantity,
|
|
819
1306
|
"+ items"
|
|
820
1307
|
] }),
|
|
821
|
-
/* @__PURE__ */ (0,
|
|
822
|
-
(0,
|
|
1308
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-bundle__tier-price", role: "cell", children: [
|
|
1309
|
+
(0, import_core10.formatMoney)(ts.unitPrice, currency),
|
|
823
1310
|
" each"
|
|
824
1311
|
] }),
|
|
825
|
-
/* @__PURE__ */ (0,
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-bundle__tier-savings", role: "cell", children: [
|
|
826
1313
|
"Save ",
|
|
827
1314
|
ts.savingsPercent.toFixed(0),
|
|
828
1315
|
"%"
|
|
@@ -833,10 +1320,10 @@ function VolumeBundle(props) {
|
|
|
833
1320
|
))
|
|
834
1321
|
}
|
|
835
1322
|
),
|
|
836
|
-
/* @__PURE__ */ (0,
|
|
837
|
-
/* @__PURE__ */ (0,
|
|
838
|
-
/* @__PURE__ */ (0,
|
|
839
|
-
/* @__PURE__ */ (0,
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle__quantity-selector", children: [
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: `lb-qty-${bundle.id}`, children: "Quantity" }),
|
|
1325
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle__quantity-control", children: [
|
|
1326
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
840
1327
|
"button",
|
|
841
1328
|
{
|
|
842
1329
|
"aria-label": "Decrease quantity",
|
|
@@ -844,7 +1331,7 @@ function VolumeBundle(props) {
|
|
|
844
1331
|
children: "\u2212"
|
|
845
1332
|
}
|
|
846
1333
|
),
|
|
847
|
-
/* @__PURE__ */ (0,
|
|
1334
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
848
1335
|
"input",
|
|
849
1336
|
{
|
|
850
1337
|
id: `lb-qty-${bundle.id}`,
|
|
@@ -858,7 +1345,7 @@ function VolumeBundle(props) {
|
|
|
858
1345
|
className: "lb-bundle__quantity-input"
|
|
859
1346
|
}
|
|
860
1347
|
),
|
|
861
|
-
/* @__PURE__ */ (0,
|
|
1348
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
862
1349
|
"button",
|
|
863
1350
|
{
|
|
864
1351
|
"aria-label": "Increase quantity",
|
|
@@ -868,8 +1355,18 @@ function VolumeBundle(props) {
|
|
|
868
1355
|
)
|
|
869
1356
|
] })
|
|
870
1357
|
] }),
|
|
871
|
-
cartError && /* @__PURE__ */ (0,
|
|
872
|
-
|
|
1358
|
+
cartError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
|
|
1359
|
+
bundle && displayVariant && (0, import_core10.shouldShowLowStockBadge)(
|
|
1360
|
+
displayVariant,
|
|
1361
|
+
minTierQty,
|
|
1362
|
+
bundle.widgetConfig.lowStockThreshold,
|
|
1363
|
+
bundle.widgetConfig.showLowStockBadge
|
|
1364
|
+
) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
|
|
1365
|
+
"Only ",
|
|
1366
|
+
displayVariant.quantityAvailable,
|
|
1367
|
+
" left"
|
|
1368
|
+
] }),
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
873
1370
|
"button",
|
|
874
1371
|
{
|
|
875
1372
|
className: "lb-bundle__cta",
|
|
@@ -885,21 +1382,21 @@ function VolumeBundle(props) {
|
|
|
885
1382
|
}
|
|
886
1383
|
|
|
887
1384
|
// src/hooks/useBundlesForProduct.ts
|
|
888
|
-
var
|
|
889
|
-
var
|
|
1385
|
+
var import_react9 = require("react");
|
|
1386
|
+
var import_core11 = require("@lime-bundles/core");
|
|
890
1387
|
var INITIAL_STATE2 = {
|
|
891
1388
|
status: "loading",
|
|
892
1389
|
bundles: null,
|
|
893
1390
|
error: null
|
|
894
1391
|
};
|
|
895
1392
|
function useBundlesForProduct(options) {
|
|
896
|
-
const [state, setState] = (0,
|
|
1393
|
+
const [state, setState] = (0, import_react9.useState)(
|
|
897
1394
|
INITIAL_STATE2
|
|
898
1395
|
);
|
|
899
|
-
(0,
|
|
1396
|
+
(0, import_react9.useEffect)(() => {
|
|
900
1397
|
const controller = new AbortController();
|
|
901
1398
|
setState(INITIAL_STATE2);
|
|
902
|
-
(0,
|
|
1399
|
+
(0, import_core11.fetchBundlesForProduct)({
|
|
903
1400
|
shopDomain: options.shopDomain,
|
|
904
1401
|
storefrontAccessToken: options.storefrontAccessToken,
|
|
905
1402
|
productHandle: options.productHandle,
|
|
@@ -922,7 +1419,7 @@ function useBundlesForProduct(options) {
|
|
|
922
1419
|
signal: controller.signal
|
|
923
1420
|
}).then((css) => {
|
|
924
1421
|
if (controller.signal.aborted) return;
|
|
925
|
-
(0,
|
|
1422
|
+
(0, import_core11.injectCustomCss)(options.shopDomain, css);
|
|
926
1423
|
}).catch(() => {
|
|
927
1424
|
});
|
|
928
1425
|
return () => {
|
|
@@ -938,8 +1435,8 @@ function useBundlesForProduct(options) {
|
|
|
938
1435
|
}
|
|
939
1436
|
|
|
940
1437
|
// src/index.ts
|
|
941
|
-
var
|
|
942
|
-
var
|
|
1438
|
+
var import_core12 = require("@lime-bundles/core");
|
|
1439
|
+
var import_core13 = require("@lime-bundles/core");
|
|
943
1440
|
// Annotate the CommonJS export names for ESM import in node:
|
|
944
1441
|
0 && (module.exports = {
|
|
945
1442
|
BUNDLES_FOR_PRODUCT_QUERY,
|
|
@@ -949,6 +1446,7 @@ var import_core11 = require("@lime-bundles/core");
|
|
|
949
1446
|
MixMatchBundle,
|
|
950
1447
|
SHOP_CUSTOM_CSS_QUERY,
|
|
951
1448
|
StorefrontApiError,
|
|
1449
|
+
VariantDropdown,
|
|
952
1450
|
VolumeBundle,
|
|
953
1451
|
WIDGET_CONFIG_DEFAULTS,
|
|
954
1452
|
applyABVariantB,
|
|
@@ -984,6 +1482,7 @@ var import_core11 = require("@lime-bundles/core");
|
|
|
984
1482
|
useAnalytics,
|
|
985
1483
|
useBundleData,
|
|
986
1484
|
useBundlesForProduct,
|
|
1485
|
+
useVariantSelection,
|
|
987
1486
|
useWidgetConfigVars,
|
|
988
1487
|
validateQuantity
|
|
989
1488
|
});
|