@best-bundles/bundle-ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import type { PublicBundleConfigResponse } from "../types";
2
+ export declare function fetchBundleConfig(params: {
3
+ apiBaseUrl: string;
4
+ shop: string;
5
+ signal?: AbortSignal;
6
+ }): Promise<PublicBundleConfigResponse>;
7
+ //# sourceMappingURL=fetchBundleConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchBundleConfig.d.ts","sourceRoot":"","sources":["../../src/api/fetchBundleConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAE3D,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAiBtC"}
@@ -0,0 +1,5 @@
1
+ export type BundleBuilderDrawerProps = {
2
+ className?: string;
3
+ };
4
+ export declare function BundleBuilderDrawer(props: BundleBuilderDrawerProps): import("react/jsx-runtime").JSX.Element | null;
5
+ //# sourceMappingURL=BundleBuilderDrawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BundleBuilderDrawer.d.ts","sourceRoot":"","sources":["../../src/components/BundleBuilderDrawer.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,kDA8iBlE"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export type BundleButtonProps = {
3
+ children?: React.ReactNode;
4
+ className?: string;
5
+ };
6
+ export declare function BundleButton(props: BundleButtonProps): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=BundleButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BundleButton.d.ts","sourceRoot":"","sources":["../../src/components/BundleButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,2CAWpD"}
@@ -0,0 +1,6 @@
1
+ export declare const BundleLineAttributeKeys: {
2
+ readonly bundleId: "_bundle_id";
3
+ readonly bundleConfig: "_bundle_config";
4
+ readonly bundleSource: "_bundle_source";
5
+ };
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB;;;;CAI1B,CAAC"}
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import type { BundleCartAdapter, BundleConfig, BundleEligibleVariant, PublicBundleConfigResponse } from "../types";
3
+ type BundleProviderData = {
4
+ currencyCode: string | null;
5
+ config: BundleConfig;
6
+ eligibleVariants: BundleEligibleVariant[];
7
+ };
8
+ type BundleSelectionState = Record<string, number>;
9
+ type BundleSelectionOrder = string[];
10
+ export type BundleProviderProps = {
11
+ apiBaseUrl: string;
12
+ shop: string;
13
+ cartAdapter: BundleCartAdapter;
14
+ /**
15
+ * v1 default is "default" to mirror the app's current single-config endpoint.
16
+ * Kept as a prop to avoid an API break when the public endpoint supports multiple handles.
17
+ */
18
+ configHandle?: string;
19
+ /**
20
+ * Optional override for Storybook/tests so we can avoid network calls.
21
+ */
22
+ initialData?: BundleProviderData;
23
+ /**
24
+ * Optional fetcher override for Storybook/tests.
25
+ */
26
+ configFetcher?: (params: {
27
+ apiBaseUrl: string;
28
+ shop: string;
29
+ signal?: AbortSignal;
30
+ }) => Promise<PublicBundleConfigResponse>;
31
+ children: React.ReactNode;
32
+ };
33
+ export type BundleBuilderContextValue = {
34
+ isOpen: boolean;
35
+ open: () => void;
36
+ close: () => void;
37
+ toggle: () => void;
38
+ loading: boolean;
39
+ submitting: boolean;
40
+ error: string | null;
41
+ currencyCode: string | null;
42
+ config: BundleConfig | null;
43
+ eligibleVariants: BundleEligibleVariant[];
44
+ selections: BundleSelectionState;
45
+ selectionOrder: BundleSelectionOrder;
46
+ setQuantity: (merchandiseId: string, quantity: number) => void;
47
+ clearSelections: () => void;
48
+ bundleSize: number;
49
+ minRequired: number;
50
+ canSubmit: boolean;
51
+ submit: () => Promise<void>;
52
+ };
53
+ export declare const BundleBuilderContext: React.Context<BundleBuilderContextValue | null>;
54
+ export declare function BundleProvider(props: BundleProviderProps): import("react/jsx-runtime").JSX.Element;
55
+ export {};
56
+ //# sourceMappingURL=BundleProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BundleProvider.d.ts","sourceRoot":"","sources":["../../src/context/BundleProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2E,MAAM,OAAO,CAAC;AAGhG,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAEnH,KAAK,kBAAkB,GAAG;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,gBAAgB,EAAE,qBAAqB,EAAE,CAAC;CAC3C,CAAC;AAEF,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACnD,KAAK,oBAAoB,GAAG,MAAM,EAAE,CAAC;AAErC,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,iBAAiB,CAAC;IAE/B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAE5H,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAErB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,qBAAqB,EAAE,CAAC;IAE1C,UAAU,EAAE,oBAAoB,CAAC;IACjC,cAAc,EAAE,oBAAoB,CAAC;IACrC,WAAW,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,eAAe,EAAE,MAAM,IAAI,CAAC;IAE5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,iDAAwD,CAAC;AAkB1F,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,2CA8MxD"}
@@ -0,0 +1,2 @@
1
+ export declare function useBundleBuilder(): import("../context/BundleProvider").BundleBuilderContextValue;
2
+ //# sourceMappingURL=useBundleBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBundleBuilder.d.ts","sourceRoot":"","sources":["../../src/hooks/useBundleBuilder.ts"],"names":[],"mappings":"AAGA,wBAAgB,gBAAgB,kEAM/B"}
@@ -0,0 +1,12 @@
1
+ export type BundleBuilderDrawerControls = {
2
+ isOpen: boolean;
3
+ open: () => void;
4
+ close: () => void;
5
+ toggle: () => void;
6
+ };
7
+ /**
8
+ * Convenience hook for controlling the drawer (alternative to using <BundleButton />).
9
+ * Must be used within <BundleProvider />.
10
+ */
11
+ export declare function useBundleBuilderDrawer(): BundleBuilderDrawerControls;
12
+ //# sourceMappingURL=useBundleBuilderDrawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBundleBuilderDrawer.d.ts","sourceRoot":"","sources":["../../src/hooks/useBundleBuilderDrawer.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,2BAA2B,CAGpE"}
@@ -0,0 +1,10 @@
1
+ export type { BundleCartAdapter } from "./types";
2
+ export type { BundleConfig, BundleEligibleVariant, BundleDiscountRule, PublicBundleConfigResponse } from "./types";
3
+ export { BundleLineAttributeKeys } from "./constants";
4
+ export { BundleProvider } from "./context/BundleProvider";
5
+ export { useBundleBuilder } from "./hooks/useBundleBuilder";
6
+ export { useBundleBuilderDrawer } from "./hooks/useBundleBuilderDrawer";
7
+ export type { BundleBuilderDrawerControls } from "./hooks/useBundleBuilderDrawer";
8
+ export { BundleButton } from "./components/BundleButton";
9
+ export { BundleBuilderDrawer } from "./components/BundleBuilderDrawer";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACnH,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,YAAY,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,687 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var t=document.createElement("style");t.appendChild(document.createTextNode('._button_18rtl_1{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--bb-font-family, inherit);border:1px solid var(--bb-button-border, rgba(0, 0, 0, .12));background:var(--bb-button-bg, #111827);color:var(--bb-button-text, #ffffff);font:inherit;line-height:1;padding:10px 14px;border-radius:var(--bb-button-radius, 10px);cursor:pointer;transition:transform .12s ease,background .12s ease,border-color .12s ease,opacity .12s ease}._button_18rtl_1:hover{background:var(--bb-button-bg-hover, #0b1220)}._button_18rtl_1:active{transform:translateY(1px)}._button_18rtl_1:disabled{opacity:.6;cursor:not-allowed}._button_18rtl_1:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._backdrop_7tuld_1{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--bb-backdrop-z-index, 9999);display:flex;justify-content:flex-end;background:var(--bb-backdrop-bg, rgba(0, 0, 0, .5));-webkit-backdrop-filter:blur(var(--bb-backdrop-blur, 2px));backdrop-filter:blur(var(--bb-backdrop-blur, 2px));padding:var(--bb-backdrop-padding, 12px);opacity:0;transition:opacity var(--bb-drawer-transition-duration, .22s) ease}._backdropOpen_7tuld_14{opacity:1}._backdropClosed_7tuld_18{opacity:0}._panel_7tuld_22{width:440px;max-width:100%;height:100%;background:var(--bb-surface-bg, #ffffff);color:var(--bb-text-color, #111827);font-family:var(--bb-font-family-body, "Cabin", system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif);border-radius:var(--bb-panel-radius, 16px);box-shadow:0 20px 60px #00000059;display:grid;grid-template-rows:auto 1fr auto;overflow:hidden;outline:none}._panelOpen_7tuld_37{animation:_bbSlideInFromRight_7tuld_1 var(--bb-drawer-transition-duration, .22s) ease forwards}._panelClosed_7tuld_41{animation:_bbSlideOutToRight_7tuld_1 var(--bb-drawer-transition-duration, .22s) ease forwards}@keyframes _bbSlideInFromRight_7tuld_1{0%{transform:translate(110%)}to{transform:translate(0)}}@keyframes _bbSlideOutToRight_7tuld_1{0%{transform:translate(0)}to{transform:translate(110%)}}@media(prefers-reduced-motion:reduce){._backdrop_7tuld_1{transition:none}._panelOpen_7tuld_37,._panelClosed_7tuld_41{animation:none}}._header_7tuld_76{display:grid;grid-template-columns:36px 1fr 36px;align-items:center;column-gap:12px;padding:var(--bb-header-padding, 16px 16px 12px 16px);border-bottom:1px solid var(--bb-border-color, rgba(17, 24, 39, .08))}._headerCenter_7tuld_85{position:relative;min-width:0;display:flex;justify-content:center;align-items:center}._title_7tuld_93{font-size:24px;margin:0;line-height:1.2;font-family:var(--bb-font-family-title, "Bebas Neue", "Cabin", system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif);letter-spacing:.02em;color:var(--bb-title-color, var(--bb-text-color, #111827))}._searchIconButton_7tuld_102{width:36px;height:36px;border-radius:10px;border:1px solid var(--bb-border-strong-color, rgba(17, 24, 39, .12));background:var(--bb-surface-bg, #ffffff);color:var(--bb-text-color, #111827);cursor:pointer;display:inline-flex;align-items:center;justify-content:center}._searchIconButton_7tuld_102:hover{background:var(--bb-control-bg-hover, rgba(17, 24, 39, .04))}._searchIconButton_7tuld_102:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._headerTitleWrap_7tuld_124{transition:opacity .18s ease,transform .22s ease}._headerSearchWrap_7tuld_128{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;justify-content:center;align-items:center;pointer-events:none;opacity:0;transform:translateY(-2px);transition:opacity .18s ease,transform .22s ease}._headerSearchOpen_7tuld_140 ._headerTitleWrap_7tuld_124{opacity:0;transform:translateY(2px);pointer-events:none}._headerSearchOpen_7tuld_140 ._headerSearchWrap_7tuld_128{opacity:1;transform:translateY(0);pointer-events:auto}._searchBar_7tuld_152{width:min(320px,100%);height:36px;display:flex;align-items:center;gap:8px;background:var(--bb-surface-bg, #ffffff);border:1px solid var(--bb-border-strong-color, rgba(17, 24, 39, .12));border-radius:10px;padding:0 8px}._searchInput_7tuld_164{width:100%;border:none;outline:none;background:transparent;font:inherit;font-size:14px;min-width:0;height:100%;line-height:36px;padding:0}._searchInput_7tuld_164::-webkit-search-cancel-button,._searchInput_7tuld_164::-webkit-search-decoration,._searchInput_7tuld_164::-webkit-search-results-button,._searchInput_7tuld_164::-webkit-search-results-decoration{-webkit-appearance:none;-moz-appearance:none;appearance:none}._searchClearButton_7tuld_185{border:none;background:transparent;color:var(--bb-text-color, #111827);font:inherit;font-size:13px;font-weight:700;cursor:pointer;padding:6px;border-radius:10px;line-height:1}._searchClearButton_7tuld_185:hover{background:var(--bb-control-bg-hover, rgba(17, 24, 39, .04))}._searchClearButton_7tuld_185:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._closeButton_7tuld_207{border:1px solid var(--bb-border-strong-color, rgba(17, 24, 39, .12));background:var(--bb-surface-bg, #ffffff);color:var(--bb-text-color, #111827);width:36px;height:36px;border-radius:10px;cursor:pointer;flex:0 0 auto}._closeButton_7tuld_207:hover{background:var(--bb-control-bg-hover, rgba(17, 24, 39, .04))}._closeButton_7tuld_207:disabled{opacity:.6;cursor:not-allowed}._closeButton_7tuld_207:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}@media(prefers-reduced-motion:reduce){._headerTitleWrap_7tuld_124,._headerSearchWrap_7tuld_128{transition:none}}._body_7tuld_240{padding:var(--bb-body-padding, 8px 16px);overflow:auto}._muted_7tuld_245{color:var(--bb-muted-strong-color, rgba(17, 24, 39, .65));margin:0 0 12px}._error_7tuld_250{color:var(--bb-error-color, #b91c1c);margin:0 0 12px}._variants_7tuld_255{margin-top:4px}._variantList_7tuld_259{list-style:none;padding:0;margin:0;display:grid;gap:10px}._variantRow_7tuld_267{display:grid;grid-template-columns:1fr auto;gap:0px;align-items:stretch;padding:5px}._variantLeft_7tuld_275{min-width:0;display:flex;gap:12px;align-items:center}._addToBundleButton_7tuld_282{height:100%;align-self:stretch;border-radius:12px;border:none;background:var(--bb-cta-bg, #91aae5);color:var(--bb-cta-text, #ffffff);font:inherit;font-weight:800;padding:0 16px;cursor:pointer;white-space:nowrap}._addToBundleButton_7tuld_282:hover{background:var(--bb-cta-bg-hover, #1d4ed8)}._addToBundleButton_7tuld_282:disabled{opacity:.6;cursor:not-allowed}._addToBundleButton_7tuld_282:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._variantThumb_7tuld_310{width:60px;height:60px;border-radius:12px;overflow:hidden;border:1px solid var(--bb-border-color, rgba(17, 24, 39, .08));background:var(--bb-surface-subtle-bg, rgba(17, 24, 39, .02));flex:0 0 auto}._variantThumbImg_7tuld_320{width:100%;height:100%;object-fit:cover;display:block}._variantThumbFallback_7tuld_327{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-weight:800;color:var(--bb-text-color, #111827)}._variantInfo_7tuld_337{min-width:0}._variantName_7tuld_341{font-size:14px;font-weight:600;margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._variantMeta_7tuld_350{font-size:12px;color:var(--bb-muted-color, rgba(17, 24, 39, .6));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._variantPriceRow_7tuld_358{display:flex;align-items:baseline;gap:8px;margin-top:4px}._variantCompareAt_7tuld_365{font-size:12px;color:var(--bb-muted-color, rgba(17, 24, 39, .6));text-decoration:line-through}._variantPrice_7tuld_358{font-size:13px;font-weight:800}._qtyControls_7tuld_376{display:inline-flex;align-items:center;gap:6px}._qtyButton_7tuld_382{width:34px;height:34px;border-radius:10px;border:1px solid var(--bb-border-strong-color, rgba(17, 24, 39, .12));background:var(--bb-surface-bg, #ffffff);cursor:pointer;line-height:1;font-size:16px}._qtyButton_7tuld_382:hover{background:var(--bb-control-bg-hover, rgba(17, 24, 39, .04))}._qtyButton_7tuld_382:disabled{opacity:.6;cursor:not-allowed}._qtyButton_7tuld_382:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._qtyValue_7tuld_407{min-width:18px;text-align:center;font-variant-numeric:tabular-nums;font-weight:700}._footer_7tuld_414{padding:var(--bb-footer-padding, 12px 16px 16px 16px);border-top:1px solid var(--bb-border-color, rgba(17, 24, 39, .08));background:var(--bb-surface-bg, #ffffff);position:relative}._boxSummaryWindow_7tuld_421{margin-bottom:6px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;padding-bottom:6px;scrollbar-width:none;-ms-overflow-style:none}._boxSummaryWindow_7tuld_421::-webkit-scrollbar{display:none}._boxSummaryRow_7tuld_438{--bb-box-gap: 10px;display:flex;align-items:flex-start;gap:var(--bb-box-gap);width:100%}._boxSlot_7tuld_446{flex:0 0 calc((100% - (4 * var(--bb-box-gap))) / 5);display:flex;flex-direction:column;align-items:stretch;gap:6px}._boxItem_7tuld_454{width:100%;aspect-ratio:1 / 1;border-radius:12px;position:relative;display:flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none;box-sizing:border-box}._boxItemClickable_7tuld_466{cursor:pointer}._boxItemClickable_7tuld_466:focus-visible{outline:2px solid var(--bb-focus-ring, #2563eb);outline-offset:2px}._boxThresholdLabel_7tuld_475{font-size:11px;line-height:1.15;font-weight:700;color:var(--bb-muted-strong-color, rgba(17, 24, 39, .65));text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._boxItemPlaceholder_7tuld_486{border:1px dashed var(--bb-border-strong-color, rgba(17, 24, 39, .18));background:var(--bb-surface-subtle-bg, rgba(17, 24, 39, .02));color:var(--bb-muted-color, rgba(17, 24, 39, .6))}._boxItemFilled_7tuld_492{border:1px solid var(--bb-border-color, rgba(17, 24, 39, .08));background:var(--bb-surface-bg, #ffffff);overflow:hidden}._boxItemThreshold_7tuld_498{border-style:solid;border-width:2px;border-color:var(--bb-tier-border, rgba(37, 99, 235, .55));background:linear-gradient(180deg,#2563eb1a,#2563eb08);color:var(--bb-tier-text, rgba(29, 78, 216, .95))}._boxItemDiscountText_7tuld_506{font-size:12px;font-weight:800;text-align:center;line-height:1.1;padding:6px}._boxItemPlus_7tuld_514{font-size:22px;font-weight:700;opacity:.5;line-height:1}._boxItemImage_7tuld_521{width:100%;height:100%;object-fit:cover;display:block}._boxItemFallback_7tuld_528{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:var(--bb-surface-subtle-bg, rgba(17, 24, 39, .04));color:var(--bb-text-color, #111827);font-weight:800}._cta_7tuld_539{width:100%;border:1px solid var(--bb-cta-border, rgba(0, 0, 0, .12));background:var(--bb-cta-bg, #2563eb);color:var(--bb-cta-text, #ffffff);font:inherit;font-weight:700;padding:12px 14px;border-radius:12px;cursor:pointer}._ctaTotals_7tuld_551{display:flex;justify-content:space-between;align-items:baseline;gap:10px;font-variant-numeric:tabular-nums}._ctaDiscountBadge_7tuld_559{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:800;background:linear-gradient(180deg,#2563eb24,#2563eb0f);border:1px solid rgba(37,99,235,.3);color:#1d4ed8f2;white-space:nowrap}._ctaTotalsWrap_7tuld_573{overflow:hidden;max-height:0;opacity:0;transform:translateY(4px);margin-bottom:0;transition:max-height .22s ease,opacity .16s ease,transform .22s ease,margin-bottom .22s ease}._ctaTotalsWrapVisible_7tuld_586{max-height:40px;opacity:1;transform:translateY(0);margin-bottom:8px}._ctaTotalsStrike_7tuld_593{opacity:.85;text-decoration:line-through;font-weight:700;color:var(--bb-muted-strong-color, rgba(17, 24, 39, .65))}._ctaTotalsPrice_7tuld_600{font-weight:900;color:var(--bb-text-color, #111827)}@media(prefers-reduced-motion:reduce){._ctaTotalsWrap_7tuld_573{transition:none}}._cta_7tuld_539:hover{background:var(--bb-cta-bg-hover, #1d4ed8)}._cta_7tuld_539:disabled{opacity:.6;cursor:not-allowed}._cta_7tuld_539:focus-visible{outline:2px solid var(--bb-cta-focus-ring, var(--bb-text-color, #111827));outline-offset:2px}._helperText_7tuld_625{margin-top:8px;font-size:12px;color:var(--bb-muted-strong-color, rgba(17, 24, 39, .65));text-align:center}@media(max-width:767px){._backdrop_7tuld_1{padding:0;justify-content:stretch}._panel_7tuld_22{width:100%;height:100%;border-radius:0}}')),document.head.appendChild(t)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
+ import { jsx as r, jsxs as N, Fragment as Te } from "react/jsx-runtime";
3
+ import { useState as k, useRef as K, useEffect as Z, useCallback as oe, useMemo as y, createContext as Ie, useContext as we, forwardRef as Be, createElement as ye } from "react";
4
+ const fe = {
5
+ bundleId: "_bundle_id",
6
+ bundleConfig: "_bundle_config",
7
+ bundleSource: "_bundle_source"
8
+ };
9
+ async function Se(o) {
10
+ const l = new URL("/api/public/bundle-config", o.apiBaseUrl);
11
+ l.searchParams.set("shop", o.shop);
12
+ const m = await (await fetch(l, {
13
+ method: "GET",
14
+ signal: o.signal,
15
+ headers: { "Content-Type": "application/json" }
16
+ })).json().catch(() => null);
17
+ return !m || typeof m != "object" ? { ok: !1, error: "Invalid response." } : m;
18
+ }
19
+ const Ne = Ie(null);
20
+ function ke() {
21
+ try {
22
+ const o = globalThis.crypto;
23
+ if (o != null && o.randomUUID) return o.randomUUID();
24
+ } catch {
25
+ }
26
+ return `bb_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
27
+ }
28
+ function ge(o) {
29
+ return Number.isFinite(o) ? Math.max(0, Math.floor(o)) : 0;
30
+ }
31
+ function Qt(o) {
32
+ const {
33
+ apiBaseUrl: l,
34
+ shop: _,
35
+ cartAdapter: m,
36
+ configHandle: T = "default",
37
+ initialData: h,
38
+ configFetcher: U,
39
+ children: x
40
+ } = o, [w, L] = k(!1), [M, G] = k(!h), [g, E] = k(!1), [J, le] = k(null), [F, X] = k((h == null ? void 0 : h.currencyCode) ?? null), [p, Y] = k((h == null ? void 0 : h.config) ?? null), [ee, te] = k(
41
+ (h == null ? void 0 : h.eligibleVariants) ?? []
42
+ ), [se, me] = k(
43
+ () => ({ selections: {}, order: [] })
44
+ ), he = K(null);
45
+ Z(() => {
46
+ var B;
47
+ if (h) return;
48
+ (B = he.current) == null || B.abort();
49
+ const d = new AbortController();
50
+ he.current = d;
51
+ const C = U ?? Se;
52
+ return G(!0), le(null), C({ apiBaseUrl: l, shop: _, signal: d.signal }).then((i) => {
53
+ if (!i.ok) {
54
+ le(i.error || "Failed to load bundle config."), Y(null), te([]), X(null);
55
+ return;
56
+ }
57
+ X(i.currencyCode ?? null), Y(i.config), te(i.eligibleVariants ?? []);
58
+ }).catch((i) => {
59
+ const D = i instanceof Error ? i.message : "Failed to load bundle config.";
60
+ le(D), Y(null), te([]), X(null);
61
+ }).finally(() => G(!1)), () => d.abort();
62
+ }, [l, _, h, U]);
63
+ const ie = oe(() => L(!0), []), I = oe(() => L(!1), []), A = oe(() => L((d) => !d), []), be = oe((d, C) => {
64
+ const B = ge(C);
65
+ me((i) => {
66
+ const D = i.selections[d] ?? 0, ne = B - D;
67
+ let j;
68
+ if (B <= 0) {
69
+ const { [d]: re, ...z } = i.selections;
70
+ j = z;
71
+ } else D === B ? j = i.selections : j = { ...i.selections, [d]: B };
72
+ let Q = i.order;
73
+ if (ne > 0)
74
+ Q = [...i.order, ...Array.from({ length: ne }, () => d)];
75
+ else if (ne < 0) {
76
+ let re = -ne;
77
+ const z = [];
78
+ for (let ae = i.order.length - 1; ae >= 0; ae--) {
79
+ const ue = i.order[ae];
80
+ if (ue === d && re > 0) {
81
+ re--;
82
+ continue;
83
+ }
84
+ z.push(ue);
85
+ }
86
+ z.reverse(), Q = z;
87
+ }
88
+ return j === i.selections && Q === i.order ? i : { selections: j, order: Q };
89
+ });
90
+ }, []), $ = oe(() => me({ selections: {}, order: [] }), []), W = se.selections, v = se.order, P = y(() => Object.values(W).reduce((d, C) => d + (C || 0), 0), [W]), V = y(() => {
91
+ const d = (p == null ? void 0 : p.rules) ?? [], C = d.length ? Math.min(...d.map((B) => B.minBundleSize)) : 2;
92
+ return Number.isFinite(C) && C > 0 ? C : 2;
93
+ }, [p]), S = y(() => !(M || g || J || !(p != null && p.isActive) || P < V), [P, p == null ? void 0 : p.isActive, J, M, V, g]), _e = oe(async () => {
94
+ var B;
95
+ if (!S || !p) return;
96
+ const d = ke(), C = Object.entries(W).map(([i, D]) => ({ merchandiseId: i, quantity: ge(D) })).filter((i) => i.quantity > 0).map((i) => ({
97
+ merchandiseId: i.merchandiseId,
98
+ quantity: i.quantity,
99
+ attributes: [
100
+ { key: fe.bundleId, value: d },
101
+ { key: fe.bundleConfig, value: T },
102
+ { key: fe.bundleSource, value: "bundle_builder" }
103
+ ]
104
+ }));
105
+ if (C.length) {
106
+ E(!0);
107
+ try {
108
+ await m.linesAdd(C), $(), I(), (B = m.openCartUI) == null || B.call(m);
109
+ } finally {
110
+ E(!1);
111
+ }
112
+ }
113
+ }, [S, m, $, I, p, T, W]), ce = y(
114
+ () => ({
115
+ isOpen: w,
116
+ open: ie,
117
+ close: I,
118
+ toggle: A,
119
+ loading: M,
120
+ submitting: g,
121
+ error: J,
122
+ currencyCode: F,
123
+ config: p,
124
+ eligibleVariants: ee,
125
+ selections: W,
126
+ selectionOrder: v,
127
+ setQuantity: be,
128
+ clearSelections: $,
129
+ bundleSize: P,
130
+ minRequired: V,
131
+ canSubmit: S,
132
+ submit: _e
133
+ }),
134
+ [
135
+ w,
136
+ ie,
137
+ I,
138
+ A,
139
+ M,
140
+ g,
141
+ J,
142
+ F,
143
+ p,
144
+ ee,
145
+ W,
146
+ v,
147
+ be,
148
+ $,
149
+ P,
150
+ V,
151
+ S,
152
+ _e
153
+ ]
154
+ );
155
+ return /* @__PURE__ */ r(Ne.Provider, { value: ce, children: x });
156
+ }
157
+ function xe() {
158
+ const o = we(Ne);
159
+ if (!o)
160
+ throw new Error("useBundleBuilder must be used within <BundleProvider />");
161
+ return o;
162
+ }
163
+ function Ht() {
164
+ const { isOpen: o, open: l, close: _, toggle: m } = xe();
165
+ return { isOpen: o, open: l, close: _, toggle: m };
166
+ }
167
+ const Fe = "_button_18rtl_1", Pe = {
168
+ button: Fe
169
+ };
170
+ function Kt(o) {
171
+ const { toggle: l } = xe();
172
+ return /* @__PURE__ */ r(
173
+ "button",
174
+ {
175
+ type: "button",
176
+ onClick: l,
177
+ className: [Pe.button, o.className].filter(Boolean).join(" "),
178
+ children: o.children ?? "Build a bundle"
179
+ }
180
+ );
181
+ }
182
+ /**
183
+ * @license lucide-react v0.515.0 - ISC
184
+ *
185
+ * This source code is licensed under the ISC license.
186
+ * See the LICENSE file in the root directory of this source tree.
187
+ */
188
+ const Re = (o) => o.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), Oe = (o) => o.replace(
189
+ /^([A-Z])|[\s-_]+(\w)/g,
190
+ (l, _, m) => m ? m.toUpperCase() : _.toLowerCase()
191
+ ), ve = (o) => {
192
+ const l = Oe(o);
193
+ return l.charAt(0).toUpperCase() + l.slice(1);
194
+ }, Ce = (...o) => o.filter((l, _, m) => !!l && l.trim() !== "" && m.indexOf(l) === _).join(" ").trim(), Le = (o) => {
195
+ for (const l in o)
196
+ if (l.startsWith("aria-") || l === "role" || l === "title")
197
+ return !0;
198
+ };
199
+ /**
200
+ * @license lucide-react v0.515.0 - ISC
201
+ *
202
+ * This source code is licensed under the ISC license.
203
+ * See the LICENSE file in the root directory of this source tree.
204
+ */
205
+ var Me = {
206
+ xmlns: "http://www.w3.org/2000/svg",
207
+ width: 24,
208
+ height: 24,
209
+ viewBox: "0 0 24 24",
210
+ fill: "none",
211
+ stroke: "currentColor",
212
+ strokeWidth: 2,
213
+ strokeLinecap: "round",
214
+ strokeLinejoin: "round"
215
+ };
216
+ /**
217
+ * @license lucide-react v0.515.0 - ISC
218
+ *
219
+ * This source code is licensed under the ISC license.
220
+ * See the LICENSE file in the root directory of this source tree.
221
+ */
222
+ const qe = Be(
223
+ ({
224
+ color: o = "currentColor",
225
+ size: l = 24,
226
+ strokeWidth: _ = 2,
227
+ absoluteStrokeWidth: m,
228
+ className: T = "",
229
+ children: h,
230
+ iconNode: U,
231
+ ...x
232
+ }, w) => ye(
233
+ "svg",
234
+ {
235
+ ref: w,
236
+ ...Me,
237
+ width: l,
238
+ height: l,
239
+ stroke: o,
240
+ strokeWidth: m ? Number(_) * 24 / Number(l) : _,
241
+ className: Ce("lucide", T),
242
+ ...!h && !Le(x) && { "aria-hidden": "true" },
243
+ ...x
244
+ },
245
+ [
246
+ ...U.map(([L, M]) => ye(L, M)),
247
+ ...Array.isArray(h) ? h : [h]
248
+ ]
249
+ )
250
+ );
251
+ /**
252
+ * @license lucide-react v0.515.0 - ISC
253
+ *
254
+ * This source code is licensed under the ISC license.
255
+ * See the LICENSE file in the root directory of this source tree.
256
+ */
257
+ const Ae = (o, l) => {
258
+ const _ = Be(
259
+ ({ className: m, ...T }, h) => ye(qe, {
260
+ ref: h,
261
+ iconNode: l,
262
+ className: Ce(
263
+ `lucide-${Re(ve(o))}`,
264
+ `lucide-${o}`,
265
+ m
266
+ ),
267
+ ...T
268
+ })
269
+ );
270
+ return _.displayName = ve(o), _;
271
+ };
272
+ /**
273
+ * @license lucide-react v0.515.0 - ISC
274
+ *
275
+ * This source code is licensed under the ISC license.
276
+ * See the LICENSE file in the root directory of this source tree.
277
+ */
278
+ const $e = [
279
+ ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
280
+ ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
281
+ ], We = Ae("search", $e), Ve = "_backdrop_7tuld_1", De = "_backdropOpen_7tuld_14", je = "_backdropClosed_7tuld_18", ze = "_panel_7tuld_22", Ue = "_panelOpen_7tuld_37", Ee = "_panelClosed_7tuld_41", Qe = "_header_7tuld_76", He = "_headerCenter_7tuld_85", Ke = "_title_7tuld_93", Ze = "_searchIconButton_7tuld_102", Ge = "_headerTitleWrap_7tuld_124", Je = "_headerSearchWrap_7tuld_128", Xe = "_headerSearchOpen_7tuld_140", Ye = "_searchBar_7tuld_152", et = "_searchInput_7tuld_164", tt = "_searchClearButton_7tuld_185", nt = "_closeButton_7tuld_207", rt = "_body_7tuld_240", at = "_muted_7tuld_245", ot = "_error_7tuld_250", lt = "_variants_7tuld_255", st = "_variantList_7tuld_259", it = "_variantRow_7tuld_267", ct = "_variantLeft_7tuld_275", ut = "_addToBundleButton_7tuld_282", dt = "_variantThumb_7tuld_310", mt = "_variantThumbImg_7tuld_320", ht = "_variantThumbFallback_7tuld_327", bt = "_variantInfo_7tuld_337", _t = "_variantName_7tuld_341", pt = "_variantMeta_7tuld_350", ft = "_variantPriceRow_7tuld_358", yt = "_variantCompareAt_7tuld_365", xt = "_variantPrice_7tuld_358", gt = "_qtyControls_7tuld_376", vt = "_qtyButton_7tuld_382", Bt = "_qtyValue_7tuld_407", Nt = "_footer_7tuld_414", Ct = "_boxSummaryWindow_7tuld_421", Tt = "_boxSummaryRow_7tuld_438", It = "_boxSlot_7tuld_446", wt = "_boxItem_7tuld_454", St = "_boxItemClickable_7tuld_466", kt = "_boxThresholdLabel_7tuld_475", Ft = "_boxItemPlaceholder_7tuld_486", Pt = "_boxItemFilled_7tuld_492", Rt = "_boxItemThreshold_7tuld_498", Ot = "_boxItemDiscountText_7tuld_506", Lt = "_boxItemPlus_7tuld_514", Mt = "_boxItemImage_7tuld_521", qt = "_boxItemFallback_7tuld_528", At = "_cta_7tuld_539", $t = "_ctaTotals_7tuld_551", Wt = "_ctaDiscountBadge_7tuld_559", Vt = "_ctaTotalsWrap_7tuld_573", Dt = "_ctaTotalsWrapVisible_7tuld_586", jt = "_ctaTotalsStrike_7tuld_593", zt = "_ctaTotalsPrice_7tuld_600", n = {
282
+ backdrop: Ve,
283
+ backdropOpen: De,
284
+ backdropClosed: je,
285
+ panel: ze,
286
+ panelOpen: Ue,
287
+ panelClosed: Ee,
288
+ header: Qe,
289
+ headerCenter: He,
290
+ title: Ke,
291
+ searchIconButton: Ze,
292
+ headerTitleWrap: Ge,
293
+ headerSearchWrap: Je,
294
+ headerSearchOpen: Xe,
295
+ searchBar: Ye,
296
+ searchInput: et,
297
+ searchClearButton: tt,
298
+ closeButton: nt,
299
+ body: rt,
300
+ muted: at,
301
+ error: ot,
302
+ variants: lt,
303
+ variantList: st,
304
+ variantRow: it,
305
+ variantLeft: ct,
306
+ addToBundleButton: ut,
307
+ variantThumb: dt,
308
+ variantThumbImg: mt,
309
+ variantThumbFallback: ht,
310
+ variantInfo: bt,
311
+ variantName: _t,
312
+ variantMeta: pt,
313
+ variantPriceRow: ft,
314
+ variantCompareAt: yt,
315
+ variantPrice: xt,
316
+ qtyControls: gt,
317
+ qtyButton: vt,
318
+ qtyValue: Bt,
319
+ footer: Nt,
320
+ boxSummaryWindow: Ct,
321
+ boxSummaryRow: Tt,
322
+ boxSlot: It,
323
+ boxItem: wt,
324
+ boxItemClickable: St,
325
+ boxThresholdLabel: kt,
326
+ boxItemPlaceholder: Ft,
327
+ boxItemFilled: Pt,
328
+ boxItemThreshold: Rt,
329
+ boxItemDiscountText: Ot,
330
+ boxItemPlus: Lt,
331
+ boxItemImage: Mt,
332
+ boxItemFallback: qt,
333
+ cta: At,
334
+ ctaTotals: $t,
335
+ ctaDiscountBadge: Wt,
336
+ ctaTotalsWrap: Vt,
337
+ ctaTotalsWrapVisible: Dt,
338
+ ctaTotalsStrike: jt,
339
+ ctaTotalsPrice: zt
340
+ };
341
+ function Zt(o) {
342
+ var ue;
343
+ const {
344
+ isOpen: l,
345
+ close: _,
346
+ loading: m,
347
+ submitting: T,
348
+ error: h,
349
+ currencyCode: U,
350
+ config: x,
351
+ eligibleVariants: w,
352
+ selections: L,
353
+ selectionOrder: M,
354
+ setQuantity: G,
355
+ bundleSize: g,
356
+ minRequired: E,
357
+ canSubmit: J,
358
+ submit: le
359
+ } = xe(), [F, X] = k(!1), [p, Y] = k(""), ee = K(null), te = (e) => {
360
+ const t = (e ?? []).filter((a) => {
361
+ var b, u;
362
+ const c = (b = a == null ? void 0 : a.name) == null ? void 0 : b.trim(), s = (u = a == null ? void 0 : a.value) == null ? void 0 : u.trim();
363
+ return !(!c || !s || c.toLowerCase() === "title");
364
+ });
365
+ return t.length ? t.map((a) => `${a.name}: ${a.value}`).join(" · ") : null;
366
+ }, se = (e) => {
367
+ var t;
368
+ return ((t = e.product) == null ? void 0 : t.title) ?? e.displayName ?? e.title;
369
+ }, me = (e) => {
370
+ const t = se(e), a = te(e.selectedOptions);
371
+ return [t, a].filter(Boolean).join(" — ");
372
+ }, [he, ie] = k(l), I = K(null), A = (e) => {
373
+ const t = Number(e);
374
+ if (!Number.isFinite(t)) return e;
375
+ if (U)
376
+ try {
377
+ return new Intl.NumberFormat(void 0, { style: "currency", currency: U }).format(t);
378
+ } catch {
379
+ }
380
+ return `$${t.toFixed(2)}`;
381
+ }, be = ((ue = x == null ? void 0 : x.title) == null ? void 0 : ue.trim()) || "Build your bundle", $ = y(() => [...w].sort((e, t) => e.displayName.localeCompare(t.displayName)), [w]), W = y(() => {
382
+ const e = p.trim().toLowerCase();
383
+ if (!e) return $;
384
+ const t = e.split(/\s+/).filter(Boolean), a = (c) => {
385
+ var u;
386
+ const s = [];
387
+ s.push(c.displayName), (u = c.product) != null && u.title && s.push(c.product.title);
388
+ for (const f of c.selectedOptions ?? [])
389
+ s.push(f.name), s.push(f.value);
390
+ const b = s.join(" ").toLowerCase();
391
+ return t.every((f) => b.includes(f));
392
+ };
393
+ return $.filter(a);
394
+ }, [p, $]), v = y(() => [...(x == null ? void 0 : x.rules) ?? []].sort((e, t) => e.minBundleSize - t.minBundleSize), [x == null ? void 0 : x.rules]), P = y(() => Math.max(0, E - g), [g, E]), V = y(() => {
395
+ let e = null;
396
+ for (const t of v)
397
+ g >= t.minBundleSize && (e = t);
398
+ return e;
399
+ }, [g, v]), S = y(() => {
400
+ if (!V) return null;
401
+ const e = Number.parseFloat(V.discountPercent);
402
+ return !Number.isFinite(e) || e <= 0 ? null : e;
403
+ }, [V]), _e = y(() => {
404
+ const e = v[0];
405
+ if (!e) return null;
406
+ const t = Number.parseFloat(e.discountPercent);
407
+ return !Number.isFinite(t) || t <= 0 ? null : t;
408
+ }, [v]), ce = S ?? _e, d = y(() => {
409
+ const e = new Map(w.map((a) => [a.id, a]));
410
+ let t = 0;
411
+ for (const [a, c] of Object.entries(L)) {
412
+ const s = c ?? 0;
413
+ if (s <= 0) continue;
414
+ const b = e.get(a), u = Number(b == null ? void 0 : b.price);
415
+ Number.isFinite(u) && (t += u * s);
416
+ }
417
+ return Math.round(t * 100) / 100;
418
+ }, [w, L]), C = y(() => {
419
+ if (!S) return d;
420
+ const e = d * (1 - S / 100);
421
+ return Math.round(e * 100) / 100;
422
+ }, [S, d]), B = y(() => v.length ? Math.max(...v.map((e) => e.minBundleSize)) : 0, [v]), i = y(() => {
423
+ const e = new Map(w.map((t) => [t.id, t]));
424
+ return M.map((t) => e.get(t)).filter((t) => !!t);
425
+ }, [w, M]), D = y(() => {
426
+ var t;
427
+ const e = /* @__PURE__ */ new Map();
428
+ for (const a of v) {
429
+ const c = Math.max(0, a.minBundleSize - 1);
430
+ e.set(c, {
431
+ discountPercent: a.discountPercent,
432
+ minBundleSize: a.minBundleSize,
433
+ label: ((t = a.label) == null ? void 0 : t.trim()) || `Buy ${a.minBundleSize}+`
434
+ });
435
+ }
436
+ return e;
437
+ }, [v]), ne = y(() => Math.max(B || E, i.length), [i.length, B, E]), j = K(null), Q = K(null), re = K(null), z = K(g), ae = (e) => {
438
+ const t = Q.current;
439
+ if (!t) return;
440
+ const a = t.querySelector(`[data-variant-row="${e}"]`);
441
+ if (!a) return;
442
+ const c = t.getBoundingClientRect(), s = a.getBoundingClientRect(), b = 8;
443
+ if (s.top >= c.top + b && s.bottom <= c.bottom - b) return;
444
+ const f = Math.max(0, t.scrollHeight - t.clientHeight), R = s.top - c.top + s.height / 2, O = t.scrollTop + R - t.clientHeight / 2, q = Math.min(f, Math.max(0, O));
445
+ t.scrollTo({ top: q, behavior: "smooth" });
446
+ };
447
+ return Z(() => {
448
+ if (!l) return;
449
+ const e = (t) => {
450
+ t.key === "Escape" && _();
451
+ };
452
+ return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
453
+ }, [_, l]), Z(() => {
454
+ if (I.current != null && (window.clearTimeout(I.current), I.current = null), l) {
455
+ ie(!0);
456
+ return;
457
+ }
458
+ I.current = window.setTimeout(() => {
459
+ ie(!1), I.current = null;
460
+ }, 220);
461
+ }, [l]), Z(() => () => {
462
+ I.current != null && window.clearTimeout(I.current);
463
+ }, []), Z(() => {
464
+ if (!l || !F) return;
465
+ const e = requestAnimationFrame(() => {
466
+ var t;
467
+ return (t = ee.current) == null ? void 0 : t.focus();
468
+ });
469
+ return () => cancelAnimationFrame(e);
470
+ }, [l, F]), Z(() => {
471
+ const e = z.current;
472
+ if (z.current = g, g <= e) return;
473
+ const t = re.current;
474
+ if (!t) return;
475
+ const a = v.some((u) => u.minBundleSize === g), c = v.find((u) => u.minBundleSize > g), s = c && a ? c.minBundleSize - 1 : c ? null : g - 1;
476
+ if (s == null || s < 0) return;
477
+ const b = requestAnimationFrame(() => {
478
+ const u = t.querySelector(`[data-box-slot="${s}"]`);
479
+ if (!u) return;
480
+ const f = t.getBoundingClientRect(), O = u.getBoundingClientRect().right - f.right;
481
+ if (O <= 1) return;
482
+ const q = Math.max(0, t.scrollWidth - t.clientWidth), H = Math.min(q, Math.max(0, t.scrollLeft + O));
483
+ t.scrollTo({ left: H, behavior: "smooth" });
484
+ });
485
+ return () => cancelAnimationFrame(b);
486
+ }, [g, v]), Z(() => {
487
+ var e;
488
+ l && ((e = j.current) == null || e.focus());
489
+ }, [l]), he ? /* @__PURE__ */ r(
490
+ "div",
491
+ {
492
+ role: "dialog",
493
+ "aria-modal": "true",
494
+ "aria-label": "Bundle builder",
495
+ className: [n.backdrop, l ? n.backdropOpen : n.backdropClosed, o.className].filter(Boolean).join(" "),
496
+ onMouseDown: (e) => {
497
+ e.target === e.currentTarget && _();
498
+ },
499
+ children: /* @__PURE__ */ N("aside", { ref: j, className: [n.panel, l ? n.panelOpen : n.panelClosed].filter(Boolean).join(" "), tabIndex: -1, children: [
500
+ /* @__PURE__ */ N("header", { className: [n.header, F ? n.headerSearchOpen : null].filter(Boolean).join(" "), children: [
501
+ /* @__PURE__ */ r(
502
+ "button",
503
+ {
504
+ type: "button",
505
+ className: n.searchIconButton,
506
+ "aria-label": F ? "Search (expanded)" : "Search",
507
+ "aria-expanded": F,
508
+ onClick: () => {
509
+ X(!0);
510
+ },
511
+ children: /* @__PURE__ */ r(We, { size: 18, "aria-hidden": "true" })
512
+ }
513
+ ),
514
+ /* @__PURE__ */ N("div", { className: n.headerCenter, children: [
515
+ /* @__PURE__ */ r("div", { className: n.headerTitleWrap, "aria-hidden": F, children: /* @__PURE__ */ r("h2", { className: n.title, children: be }) }),
516
+ /* @__PURE__ */ r("div", { className: n.headerSearchWrap, "aria-hidden": !F, children: /* @__PURE__ */ N("div", { className: n.searchBar, children: [
517
+ /* @__PURE__ */ r(
518
+ "input",
519
+ {
520
+ ref: ee,
521
+ className: n.searchInput,
522
+ type: "search",
523
+ value: p,
524
+ placeholder: "Search",
525
+ onChange: (e) => Y(e.target.value),
526
+ onBlur: () => {
527
+ p.trim() === "" && X(!1);
528
+ }
529
+ }
530
+ ),
531
+ p.trim() ? /* @__PURE__ */ r(
532
+ "button",
533
+ {
534
+ type: "button",
535
+ className: n.searchClearButton,
536
+ onMouseDown: (e) => {
537
+ e.preventDefault();
538
+ },
539
+ onClick: () => {
540
+ var e;
541
+ Y(""), (e = ee.current) == null || e.focus();
542
+ },
543
+ "aria-label": "Clear search",
544
+ children: "Clear"
545
+ }
546
+ ) : null
547
+ ] }) })
548
+ ] }),
549
+ /* @__PURE__ */ r("button", { type: "button", onClick: _, disabled: T, className: n.closeButton, "aria-label": "Close", children: "×" })
550
+ ] }),
551
+ /* @__PURE__ */ N("div", { className: n.body, ref: Q, children: [
552
+ m ? /* @__PURE__ */ r("p", { className: n.muted, children: "Loading…" }) : null,
553
+ h ? /* @__PURE__ */ r("p", { className: n.error, children: h }) : null,
554
+ !m && x && !x.isActive ? /* @__PURE__ */ r("p", { className: n.error, children: "Bundles are not active." }) : null,
555
+ /* @__PURE__ */ r("div", { className: n.variants, children: /* @__PURE__ */ r("ul", { className: n.variantList, children: W.map((e) => {
556
+ var O, q, H, de;
557
+ const t = L[e.id] ?? 0, a = se(e), c = te(e.selectedOptions), s = me(e), b = ce, u = Number(e.price), f = b != null && Number.isFinite(b) && b > 0 && Number.isFinite(u), R = f ? Math.round(u * (1 - b / 100) * 100) / 100 : null;
558
+ return /* @__PURE__ */ N("li", { className: n.variantRow, "data-variant-row": e.id, children: [
559
+ /* @__PURE__ */ N("div", { className: n.variantLeft, children: [
560
+ /* @__PURE__ */ r("div", { className: n.variantThumb, "aria-hidden": "true", children: (O = e.image) != null && O.url ? /* @__PURE__ */ r(
561
+ "img",
562
+ {
563
+ className: n.variantThumbImg,
564
+ src: e.image.url,
565
+ alt: e.image.altText ?? s,
566
+ loading: "lazy"
567
+ }
568
+ ) : /* @__PURE__ */ r("div", { className: n.variantThumbFallback, children: ((de = (H = (q = e.product) == null ? void 0 : q.title) == null ? void 0 : H.slice(0, 1)) == null ? void 0 : de.toUpperCase()) ?? "•" }) }),
569
+ /* @__PURE__ */ N("div", { className: n.variantInfo, children: [
570
+ /* @__PURE__ */ r("div", { className: n.variantName, children: a }),
571
+ c ? /* @__PURE__ */ r("div", { className: n.variantMeta, children: c }) : null,
572
+ /* @__PURE__ */ N("div", { className: n.variantPriceRow, children: [
573
+ f ? /* @__PURE__ */ r("span", { className: n.variantCompareAt, children: A(Number.isFinite(u) ? u.toFixed(2) : e.price) }) : null,
574
+ /* @__PURE__ */ r("span", { className: n.variantPrice, children: A(
575
+ R != null ? R.toFixed(2) : Number.isFinite(u) ? u.toFixed(2) : e.price
576
+ ) })
577
+ ] })
578
+ ] })
579
+ ] }),
580
+ t <= 0 ? /* @__PURE__ */ r(
581
+ "button",
582
+ {
583
+ type: "button",
584
+ onClick: () => G(e.id, 1),
585
+ disabled: T,
586
+ className: n.addToBundleButton,
587
+ "aria-label": `Add ${s} to bundle`,
588
+ children: "Add"
589
+ }
590
+ ) : /* @__PURE__ */ N("div", { className: n.qtyControls, children: [
591
+ /* @__PURE__ */ r(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ onClick: () => G(e.id, t - 1),
596
+ disabled: T || t <= 0,
597
+ className: n.qtyButton,
598
+ "aria-label": `Decrease ${s}`,
599
+ children: "−"
600
+ }
601
+ ),
602
+ /* @__PURE__ */ r("span", { className: n.qtyValue, "aria-label": `Quantity ${t}`, children: t }),
603
+ /* @__PURE__ */ r(
604
+ "button",
605
+ {
606
+ type: "button",
607
+ onClick: () => G(e.id, t + 1),
608
+ disabled: T,
609
+ className: n.qtyButton,
610
+ "aria-label": `Increase ${s}`,
611
+ children: "+"
612
+ }
613
+ )
614
+ ] })
615
+ ] }, e.id);
616
+ }) }) })
617
+ ] }),
618
+ /* @__PURE__ */ N("footer", { className: n.footer, children: [
619
+ /* @__PURE__ */ r("div", { className: n.boxSummaryWindow, ref: re, children: /* @__PURE__ */ r("div", { className: n.boxSummaryRow, "aria-label": "Bundle box summary", children: Array.from({ length: ne }).map((e, t) => {
620
+ var O, q, H, de;
621
+ const a = i[t], c = D.get(t), s = !!a, b = !!c, u = b && !s, f = c ? `${c.discountPercent}% off` : null, R = s ? a.displayName : `Slot ${t + 1}`;
622
+ return /* @__PURE__ */ N("div", { "data-box-slot": t, className: n.boxSlot, children: [
623
+ /* @__PURE__ */ r(
624
+ "div",
625
+ {
626
+ className: [
627
+ n.boxItem,
628
+ s ? n.boxItemFilled : n.boxItemPlaceholder,
629
+ b ? n.boxItemThreshold : null,
630
+ s ? n.boxItemClickable : null
631
+ ].filter(Boolean).join(" "),
632
+ role: s ? "button" : void 0,
633
+ tabIndex: s ? 0 : void 0,
634
+ onClick: s ? () => ae(a.id) : void 0,
635
+ onKeyDown: s ? (pe) => {
636
+ (pe.key === "Enter" || pe.key === " ") && (pe.preventDefault(), ae(a.id));
637
+ } : void 0,
638
+ "aria-label": u && f ? `${R}. Unlocks ${f}.` : R,
639
+ title: u && f ? `${R} • Unlocks ${f}` : R,
640
+ children: s ? (O = a.image) != null && O.url ? /* @__PURE__ */ r(
641
+ "img",
642
+ {
643
+ className: n.boxItemImage,
644
+ src: a.image.url,
645
+ alt: a.image.altText ?? a.displayName,
646
+ loading: "lazy"
647
+ }
648
+ ) : /* @__PURE__ */ r("div", { className: n.boxItemFallback, "aria-hidden": "true", children: ((de = (H = (q = a.product) == null ? void 0 : q.title) == null ? void 0 : H.slice(0, 1)) == null ? void 0 : de.toUpperCase()) ?? "•" }) : u && f ? /* @__PURE__ */ r("div", { className: n.boxItemDiscountText, children: f }) : /* @__PURE__ */ r("div", { className: n.boxItemPlus, "aria-hidden": "true", children: "+" })
649
+ }
650
+ ),
651
+ b ? /* @__PURE__ */ r("div", { className: n.boxThresholdLabel, children: c.label }) : null
652
+ ] }, t);
653
+ }) }) }),
654
+ /* @__PURE__ */ r(
655
+ "div",
656
+ {
657
+ className: [n.ctaTotalsWrap, P <= 0 ? n.ctaTotalsWrapVisible : null].filter(Boolean).join(" "),
658
+ "aria-hidden": P > 0,
659
+ children: /* @__PURE__ */ N("div", { className: n.ctaTotals, "aria-label": "Totals", children: [
660
+ ce ? /* @__PURE__ */ N("span", { className: n.ctaDiscountBadge, children: [
661
+ S ? "Unlocked " : "",
662
+ ce,
663
+ "% off"
664
+ ] }) : /* @__PURE__ */ r("span", {}),
665
+ /* @__PURE__ */ r("span", { children: S ? /* @__PURE__ */ N(Te, { children: [
666
+ /* @__PURE__ */ r("span", { className: n.ctaTotalsStrike, children: A(d.toFixed(2)) }),
667
+ " ",
668
+ /* @__PURE__ */ r("span", { className: n.ctaTotalsPrice, children: A(C.toFixed(2)) })
669
+ ] }) : /* @__PURE__ */ r("span", { className: n.ctaTotalsPrice, children: A(d.toFixed(2)) }) })
670
+ ] })
671
+ }
672
+ ),
673
+ /* @__PURE__ */ r("button", { type: "button", onClick: le, disabled: !J, className: n.cta, children: T ? "Adding…" : P > 0 ? `Add ${P} more product${P === 1 ? "" : "s"}` : "Add to Basket" })
674
+ ] })
675
+ ] })
676
+ }
677
+ ) : null;
678
+ }
679
+ export {
680
+ Zt as BundleBuilderDrawer,
681
+ Kt as BundleButton,
682
+ fe as BundleLineAttributeKeys,
683
+ Qt as BundleProvider,
684
+ xe as useBundleBuilder,
685
+ Ht as useBundleBuilderDrawer
686
+ };
687
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/constants.ts","../src/api/fetchBundleConfig.ts","../src/context/BundleProvider.tsx","../src/hooks/useBundleBuilder.ts","../src/hooks/useBundleBuilderDrawer.ts","../src/components/BundleButton.tsx","../../../node_modules/lucide-react/dist/esm/shared/src/utils.js","../../../node_modules/lucide-react/dist/esm/defaultAttributes.js","../../../node_modules/lucide-react/dist/esm/Icon.js","../../../node_modules/lucide-react/dist/esm/createLucideIcon.js","../../../node_modules/lucide-react/dist/esm/icons/search.js","../src/components/BundleBuilderDrawer.tsx"],"sourcesContent":["export const BundleLineAttributeKeys = {\n bundleId: \"_bundle_id\",\n bundleConfig: \"_bundle_config\",\n bundleSource: \"_bundle_source\",\n} as const;\n\n","import type { PublicBundleConfigResponse } from \"../types\";\n\nexport async function fetchBundleConfig(params: {\n apiBaseUrl: string;\n shop: string;\n signal?: AbortSignal;\n}): Promise<PublicBundleConfigResponse> {\n const url = new URL(\"/api/public/bundle-config\", params.apiBaseUrl);\n url.searchParams.set(\"shop\", params.shop);\n\n const resp = await fetch(url, {\n method: \"GET\",\n signal: params.signal,\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n // The endpoint uses 4xx/5xx for some errors; normalize to {ok:false}.\n const json = (await resp.json().catch(() => null)) as unknown;\n if (!json || typeof json !== \"object\") {\n return { ok: false, error: \"Invalid response.\" };\n }\n\n return json as PublicBundleConfigResponse;\n}\n\n","import React, { createContext, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { BundleLineAttributeKeys } from \"../constants\";\nimport { fetchBundleConfig as defaultFetchBundleConfig } from \"../api/fetchBundleConfig\";\nimport type { BundleCartAdapter, BundleConfig, BundleEligibleVariant, PublicBundleConfigResponse } from \"../types\";\n\ntype BundleProviderData = {\n currencyCode: string | null;\n config: BundleConfig;\n eligibleVariants: BundleEligibleVariant[];\n};\n\ntype BundleSelectionState = Record<string, number>; // merchandiseId -> qty\ntype BundleSelectionOrder = string[]; // merchandiseId (variant GID) per-item, in first-added -> last-added order\n\nexport type BundleProviderProps = {\n apiBaseUrl: string;\n shop: string;\n cartAdapter: BundleCartAdapter;\n\n /**\n * v1 default is \"default\" to mirror the app's current single-config endpoint.\n * Kept as a prop to avoid an API break when the public endpoint supports multiple handles.\n */\n configHandle?: string;\n\n /**\n * Optional override for Storybook/tests so we can avoid network calls.\n */\n initialData?: BundleProviderData;\n\n /**\n * Optional fetcher override for Storybook/tests.\n */\n configFetcher?: (params: { apiBaseUrl: string; shop: string; signal?: AbortSignal }) => Promise<PublicBundleConfigResponse>;\n\n children: React.ReactNode;\n};\n\nexport type BundleBuilderContextValue = {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n\n loading: boolean;\n submitting: boolean;\n error: string | null;\n\n currencyCode: string | null;\n config: BundleConfig | null;\n eligibleVariants: BundleEligibleVariant[];\n\n selections: BundleSelectionState;\n selectionOrder: BundleSelectionOrder;\n setQuantity: (merchandiseId: string, quantity: number) => void;\n clearSelections: () => void;\n\n bundleSize: number;\n minRequired: number;\n canSubmit: boolean;\n submit: () => Promise<void>;\n};\n\nexport const BundleBuilderContext = createContext<BundleBuilderContextValue | null>(null);\n\nfunction safeRandomUUID() {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const cryptoAny = (globalThis as any).crypto as Crypto | undefined;\n if (cryptoAny?.randomUUID) return cryptoAny.randomUUID();\n } catch {\n // ignore\n }\n return `bb_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction clampQty(n: number) {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.floor(n));\n}\n\nexport function BundleProvider(props: BundleProviderProps) {\n const {\n apiBaseUrl,\n shop,\n cartAdapter,\n configHandle = \"default\",\n initialData,\n configFetcher,\n children,\n } = props;\n\n const [isOpen, setIsOpen] = useState(false);\n const [loading, setLoading] = useState(!initialData);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const [currencyCode, setCurrencyCode] = useState<string | null>(initialData?.currencyCode ?? null);\n const [config, setConfig] = useState<BundleConfig | null>(initialData?.config ?? null);\n const [eligibleVariants, setEligibleVariants] = useState<BundleEligibleVariant[]>(\n initialData?.eligibleVariants ?? [],\n );\n\n const [selectionsState, setSelectionsState] = useState<{ selections: BundleSelectionState; order: BundleSelectionOrder }>(\n () => ({ selections: {}, order: [] }),\n );\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => {\n if (initialData) return;\n\n abortRef.current?.abort();\n const ac = new AbortController();\n abortRef.current = ac;\n\n const fetcher = configFetcher ?? defaultFetchBundleConfig;\n\n setLoading(true);\n setError(null);\n fetcher({ apiBaseUrl, shop, signal: ac.signal })\n .then((json) => {\n if (!json.ok) {\n setError(json.error || \"Failed to load bundle config.\");\n setConfig(null);\n setEligibleVariants([]);\n setCurrencyCode(null);\n return;\n }\n setCurrencyCode(json.currencyCode ?? null);\n setConfig(json.config);\n setEligibleVariants(json.eligibleVariants ?? []);\n })\n .catch((e: unknown) => {\n const msg = e instanceof Error ? e.message : \"Failed to load bundle config.\";\n setError(msg);\n setConfig(null);\n setEligibleVariants([]);\n setCurrencyCode(null);\n })\n .finally(() => setLoading(false));\n\n return () => ac.abort();\n }, [apiBaseUrl, shop, initialData, configFetcher]);\n\n const open = useCallback(() => setIsOpen(true), []);\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen((v) => !v), []);\n\n const setQuantity = useCallback((merchandiseId: string, quantity: number) => {\n const q = clampQty(quantity);\n setSelectionsState((prev) => {\n const prevQty = prev.selections[merchandiseId] ?? 0;\n const delta = q - prevQty;\n\n // selections\n let nextSelections: BundleSelectionState;\n if (q <= 0) {\n const { [merchandiseId]: _, ...rest } = prev.selections;\n nextSelections = rest;\n } else if (prevQty === q) {\n nextSelections = prev.selections;\n } else {\n nextSelections = { ...prev.selections, [merchandiseId]: q };\n }\n\n // order\n let nextOrder = prev.order;\n if (delta > 0) {\n // Add N items to the end.\n nextOrder = [...prev.order, ...Array.from({ length: delta }, () => merchandiseId)];\n } else if (delta < 0) {\n // Remove N most-recent occurrences of this variant.\n let remainingToRemove = -delta;\n const keptReversed: string[] = [];\n for (let i = prev.order.length - 1; i >= 0; i--) {\n const id = prev.order[i];\n if (id === merchandiseId && remainingToRemove > 0) {\n remainingToRemove--;\n continue;\n }\n keptReversed.push(id);\n }\n keptReversed.reverse();\n nextOrder = keptReversed;\n }\n\n if (nextSelections === prev.selections && nextOrder === prev.order) return prev;\n return { selections: nextSelections, order: nextOrder };\n });\n }, []);\n\n const clearSelections = useCallback(() => setSelectionsState({ selections: {}, order: [] }), []);\n\n const selections = selectionsState.selections;\n const selectionOrder = selectionsState.order;\n\n const bundleSize = useMemo(() => Object.values(selections).reduce((sum, q) => sum + (q || 0), 0), [selections]);\n\n const minRequired = useMemo(() => {\n const rules = config?.rules ?? [];\n const min = rules.length ? Math.min(...rules.map((r) => r.minBundleSize)) : 2;\n return Number.isFinite(min) && min > 0 ? min : 2;\n }, [config]);\n\n const canSubmit = useMemo(() => {\n if (loading || submitting) return false;\n if (error) return false;\n if (!config?.isActive) return false;\n if (bundleSize < minRequired) return false;\n return true;\n }, [bundleSize, config?.isActive, error, loading, minRequired, submitting]);\n\n const submit = useCallback(async () => {\n if (!canSubmit || !config) return;\n\n const bundleId = safeRandomUUID();\n const lines = Object.entries(selections)\n .map(([merchandiseId, quantity]) => ({ merchandiseId, quantity: clampQty(quantity) }))\n .filter((l) => l.quantity > 0)\n .map((l) => ({\n merchandiseId: l.merchandiseId,\n quantity: l.quantity,\n attributes: [\n { key: BundleLineAttributeKeys.bundleId, value: bundleId },\n { key: BundleLineAttributeKeys.bundleConfig, value: configHandle },\n { key: BundleLineAttributeKeys.bundleSource, value: \"bundle_builder\" },\n ],\n }));\n\n if (!lines.length) return;\n\n setSubmitting(true);\n try {\n await cartAdapter.linesAdd(lines);\n clearSelections();\n close();\n cartAdapter.openCartUI?.();\n } finally {\n setSubmitting(false);\n }\n }, [canSubmit, cartAdapter, clearSelections, close, config, configHandle, selections]);\n\n const value: BundleBuilderContextValue = useMemo(\n () => ({\n isOpen,\n open,\n close,\n toggle,\n loading,\n submitting,\n error,\n currencyCode,\n config,\n eligibleVariants,\n selections,\n selectionOrder,\n setQuantity,\n clearSelections,\n bundleSize,\n minRequired,\n canSubmit,\n submit,\n }),\n [\n isOpen,\n open,\n close,\n toggle,\n loading,\n submitting,\n error,\n currencyCode,\n config,\n eligibleVariants,\n selections,\n selectionOrder,\n setQuantity,\n clearSelections,\n bundleSize,\n minRequired,\n canSubmit,\n submit,\n ],\n );\n\n return <BundleBuilderContext.Provider value={value}>{children}</BundleBuilderContext.Provider>;\n}\n\n","import { useContext } from \"react\";\nimport { BundleBuilderContext } from \"../context/BundleProvider\";\n\nexport function useBundleBuilder() {\n const ctx = useContext(BundleBuilderContext);\n if (!ctx) {\n throw new Error(\"useBundleBuilder must be used within <BundleProvider />\");\n }\n return ctx;\n}\n\n","import { useBundleBuilder } from \"./useBundleBuilder\";\n\nexport type BundleBuilderDrawerControls = {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n};\n\n/**\n * Convenience hook for controlling the drawer (alternative to using <BundleButton />).\n * Must be used within <BundleProvider />.\n */\nexport function useBundleBuilderDrawer(): BundleBuilderDrawerControls {\n const { isOpen, open, close, toggle } = useBundleBuilder();\n return { isOpen, open, close, toggle };\n}\n\n","import React from \"react\";\nimport { useBundleBuilder } from \"../hooks/useBundleBuilder\";\nimport styles from \"./BundleButton.module.css\";\n\nexport type BundleButtonProps = {\n children?: React.ReactNode;\n className?: string;\n};\n\nexport function BundleButton(props: BundleButtonProps) {\n const { toggle } = useBundleBuilder();\n return (\n <button\n type=\"button\"\n onClick={toggle}\n className={[styles.button, props.className].filter(Boolean).join(\" \")}\n >\n {props.children ?? \"Build a bundle\"}\n </button>\n );\n}\n\n","/**\n * @license lucide-react v0.515.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n};\n\nexport { hasA11yProp, mergeClasses, toCamelCase, toKebabCase, toPascalCase };\n//# sourceMappingURL=utils.js.map\n","/**\n * @license lucide-react v0.515.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.js.map\n","/**\n * @license lucide-react v0.515.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.js';\nimport { mergeClasses, hasA11yProp } from './shared/src/utils.js';\n\nconst Icon = forwardRef(\n ({\n color = \"currentColor\",\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = \"\",\n children,\n iconNode,\n ...rest\n }, ref) => createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,\n className: mergeClasses(\"lucide\", className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n )\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.js.map\n","/**\n * @license lucide-react v0.515.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses, toKebabCase, toPascalCase } from './shared/src/utils.js';\nimport Icon from './Icon.js';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.js.map\n","/**\n * @license lucide-react v0.515.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"m21 21-4.34-4.34\", key: \"14j7rj\" }],\n [\"circle\", { cx: \"11\", cy: \"11\", r: \"8\", key: \"4ej97u\" }]\n];\nconst Search = createLucideIcon(\"search\", __iconNode);\n\nexport { __iconNode, Search as default };\n//# sourceMappingURL=search.js.map\n","import React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Search } from \"lucide-react\";\nimport { useBundleBuilder } from \"../hooks/useBundleBuilder\";\nimport styles from \"./BundleBuilderDrawer.module.css\";\n\nexport type BundleBuilderDrawerProps = {\n className?: string;\n};\n\nexport function BundleBuilderDrawer(props: BundleBuilderDrawerProps) {\n const {\n isOpen,\n close,\n loading,\n submitting,\n error,\n currencyCode,\n config,\n eligibleVariants,\n selections,\n selectionOrder,\n setQuantity,\n bundleSize,\n minRequired,\n canSubmit,\n submit,\n } = useBundleBuilder();\n\n const [isSearchOpen, setIsSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n\n const formatVariantOptions = (selectedOptions: Array<{ name: string; value: string }> | null | undefined) => {\n const opts = (selectedOptions ?? []).filter((o) => {\n const name = o?.name?.trim();\n const value = o?.value?.trim();\n if (!name || !value) return false;\n if (name.toLowerCase() === \"title\") return false;\n return true;\n });\n if (!opts.length) return null;\n return opts.map((o) => `${o.name}: ${o.value}`).join(\" · \");\n };\n\n const getVariantPrimaryName = (v: (typeof eligibleVariants)[number]) => {\n return v.product?.title ?? v.displayName ?? v.title;\n };\n\n const getVariantA11yLabel = (v: (typeof eligibleVariants)[number]) => {\n const name = getVariantPrimaryName(v);\n const opts = formatVariantOptions(v.selectedOptions);\n return [name, opts].filter(Boolean).join(\" — \");\n };\n\n const [isMounted, setIsMounted] = useState(isOpen);\n const closeTimerRef = useRef<number | null>(null);\n\n const formatMoney = (amount: string) => {\n const n = Number(amount);\n if (!Number.isFinite(n)) return amount;\n\n if (currencyCode) {\n try {\n return new Intl.NumberFormat(undefined, { style: \"currency\", currency: currencyCode }).format(n);\n } catch {\n // ignore and fall through\n }\n }\n\n return `$${n.toFixed(2)}`;\n };\n\n const title = config?.title?.trim() || \"Build your bundle\";\n\n const sorted = useMemo(() => {\n return [...eligibleVariants].sort((a, b) => a.displayName.localeCompare(b.displayName));\n }, [eligibleVariants]);\n\n const filteredVariants = useMemo(() => {\n const q = searchQuery.trim().toLowerCase();\n if (!q) return sorted;\n\n const tokens = q.split(/\\s+/).filter(Boolean);\n const matches = (v: (typeof sorted)[number]) => {\n const parts: string[] = [];\n parts.push(v.displayName);\n if (v.product?.title) parts.push(v.product.title);\n for (const o of v.selectedOptions ?? []) {\n parts.push(o.name);\n parts.push(o.value);\n }\n const haystack = parts.join(\" \").toLowerCase();\n return tokens.every((t) => haystack.includes(t));\n };\n\n return sorted.filter(matches);\n }, [searchQuery, sorted]);\n\n const rulesSorted = useMemo(() => {\n return [...(config?.rules ?? [])].sort((a, b) => a.minBundleSize - b.minBundleSize);\n }, [config?.rules]);\n\n const itemsRemaining = useMemo(() => Math.max(0, minRequired - bundleSize), [bundleSize, minRequired]);\n\n const appliedRule = useMemo(() => {\n let best = null as (typeof rulesSorted)[number] | null;\n for (const r of rulesSorted) {\n if (bundleSize >= r.minBundleSize) best = r;\n }\n return best;\n }, [bundleSize, rulesSorted]);\n\n const appliedDiscountPercent = useMemo(() => {\n if (!appliedRule) return null;\n const pct = Number.parseFloat(appliedRule.discountPercent);\n if (!Number.isFinite(pct) || pct <= 0) return null;\n return pct;\n }, [appliedRule]);\n\n const minRuleDiscountPercent = useMemo(() => {\n const r = rulesSorted[0];\n if (!r) return null;\n const pct = Number.parseFloat(r.discountPercent);\n if (!Number.isFinite(pct) || pct <= 0) return null;\n return pct;\n }, [rulesSorted]);\n\n const badgeDiscountPercent = appliedDiscountPercent ?? minRuleDiscountPercent;\n\n const subtotal = useMemo(() => {\n const byId = new Map(eligibleVariants.map((v) => [v.id, v] as const));\n let total = 0;\n for (const [variantId, qty] of Object.entries(selections)) {\n const q = qty ?? 0;\n if (q <= 0) continue;\n const v = byId.get(variantId);\n const price = Number(v?.price);\n if (!Number.isFinite(price)) continue;\n total += price * q;\n }\n // Keep cents stable for display.\n return Math.round(total * 100) / 100;\n }, [eligibleVariants, selections]);\n\n const discountedTotal = useMemo(() => {\n if (!appliedDiscountPercent) return subtotal;\n const total = subtotal * (1 - appliedDiscountPercent / 100);\n return Math.round(total * 100) / 100;\n }, [appliedDiscountPercent, subtotal]);\n\n const maxRuleSize = useMemo(() => {\n if (!rulesSorted.length) return 0;\n return Math.max(...rulesSorted.map((r) => r.minBundleSize));\n }, [rulesSorted]);\n\n const filledVariants = useMemo(() => {\n const byId = new Map(eligibleVariants.map((v) => [v.id, v] as const));\n return selectionOrder\n .map((variantId) => byId.get(variantId))\n .filter((v): v is (typeof eligibleVariants)[number] => Boolean(v));\n }, [eligibleVariants, selectionOrder]);\n\n const thresholdByIndex = useMemo(() => {\n // Slot index (0-based) => rule that becomes active at that slot.\n const map = new Map<number, { discountPercent: string; minBundleSize: number; label: string }>();\n for (const r of rulesSorted) {\n const idx = Math.max(0, r.minBundleSize - 1);\n map.set(idx, {\n discountPercent: r.discountPercent,\n minBundleSize: r.minBundleSize,\n label: r.label?.trim() || `Buy ${r.minBundleSize}+`,\n });\n }\n return map;\n }, [rulesSorted]);\n\n const slotCount = useMemo(() => {\n // Placeholders show until highest tier; if user adds more, extend (scroll).\n return Math.max(maxRuleSize || minRequired, filledVariants.length);\n }, [filledVariants.length, maxRuleSize, minRequired]);\n\n const panelRef = useRef<HTMLDivElement | null>(null);\n const bodyRef = useRef<HTMLDivElement | null>(null);\n const boxSummaryWindowRef = useRef<HTMLDivElement | null>(null);\n const prevBundleSizeRef = useRef<number>(bundleSize);\n\n const scrollVariantRowIntoView = (variantId: string) => {\n const container = bodyRef.current;\n if (!container) return;\n const row = container.querySelector<HTMLElement>(`[data-variant-row=\"${variantId}\"]`);\n if (!row) return;\n\n const c = container.getBoundingClientRect();\n const r = row.getBoundingClientRect();\n const padding = 8;\n const isFullyVisible = r.top >= c.top + padding && r.bottom <= c.bottom - padding;\n if (isFullyVisible) return;\n\n const maxTop = Math.max(0, container.scrollHeight - container.clientHeight);\n const rowCenterOffset = (r.top - c.top) + r.height / 2;\n const targetTop = container.scrollTop + rowCenterOffset - container.clientHeight / 2;\n const nextTop = Math.min(maxTop, Math.max(0, targetTop));\n container.scrollTo({ top: nextTop, behavior: \"smooth\" });\n };\n\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [close, isOpen]);\n\n useEffect(() => {\n if (closeTimerRef.current != null) {\n window.clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n\n if (isOpen) {\n setIsMounted(true);\n return;\n }\n\n // Keep mounted long enough to play exit animation.\n closeTimerRef.current = window.setTimeout(() => {\n setIsMounted(false);\n closeTimerRef.current = null;\n }, 220);\n }, [isOpen]);\n\n useEffect(() => {\n return () => {\n if (closeTimerRef.current != null) window.clearTimeout(closeTimerRef.current);\n };\n }, []);\n\n useEffect(() => {\n if (!isOpen) return;\n if (!isSearchOpen) return;\n const raf = requestAnimationFrame(() => searchInputRef.current?.focus());\n return () => cancelAnimationFrame(raf);\n }, [isOpen, isSearchOpen]);\n\n useEffect(() => {\n const prev = prevBundleSizeRef.current;\n prevBundleSizeRef.current = bundleSize;\n\n // Only respond to adds (qty increases).\n if (bundleSize <= prev) return;\n\n const container = boxSummaryWindowRef.current;\n if (!container) return;\n\n const hitThreshold = rulesSorted.some((r) => r.minBundleSize === bundleSize);\n const nextThreshold = rulesSorted.find((r) => r.minBundleSize > bundleSize);\n\n // When hitting a threshold, try to reveal the NEXT threshold (aligned right) if it's off-screen to the right.\n // If no remaining thresholds, scroll the newest added item into view.\n const targetIdx = nextThreshold && hitThreshold ? nextThreshold.minBundleSize - 1 : !nextThreshold ? bundleSize - 1 : null;\n if (targetIdx == null || targetIdx < 0) return;\n\n const raf = requestAnimationFrame(() => {\n const el = container.querySelector<HTMLElement>(`[data-box-slot=\"${targetIdx}\"]`);\n if (!el) return;\n\n const containerRect = container.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n const deltaRight = elRect.right - containerRect.right;\n\n // Only scroll rightward when the target is off-screen to the right.\n if (deltaRight <= 1) return;\n\n const maxLeft = Math.max(0, container.scrollWidth - container.clientWidth);\n const nextLeft = Math.min(maxLeft, Math.max(0, container.scrollLeft + deltaRight));\n container.scrollTo({ left: nextLeft, behavior: \"smooth\" });\n });\n\n return () => cancelAnimationFrame(raf);\n }, [bundleSize, rulesSorted]);\n\n useEffect(() => {\n if (!isOpen) return;\n panelRef.current?.focus();\n }, [isOpen]);\n\n if (!isMounted) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Bundle builder\"\n className={[styles.backdrop, isOpen ? styles.backdropOpen : styles.backdropClosed, props.className].filter(Boolean).join(\" \")}\n onMouseDown={(e) => {\n if (e.target === e.currentTarget) close();\n }}\n >\n <aside ref={panelRef} className={[styles.panel, isOpen ? styles.panelOpen : styles.panelClosed].filter(Boolean).join(\" \")} tabIndex={-1}>\n <header className={[styles.header, isSearchOpen ? styles.headerSearchOpen : null].filter(Boolean).join(\" \")}>\n <button\n type=\"button\"\n className={styles.searchIconButton}\n aria-label={isSearchOpen ? \"Search (expanded)\" : \"Search\"}\n aria-expanded={isSearchOpen}\n onClick={() => {\n setIsSearchOpen(true);\n }}\n >\n <Search size={18} aria-hidden=\"true\" />\n </button>\n\n <div className={styles.headerCenter}>\n <div className={styles.headerTitleWrap} aria-hidden={isSearchOpen}>\n <h2 className={styles.title}>{title}</h2>\n </div>\n\n <div className={styles.headerSearchWrap} aria-hidden={!isSearchOpen}>\n <div className={styles.searchBar}>\n <input\n ref={searchInputRef}\n className={styles.searchInput}\n type=\"search\"\n value={searchQuery}\n placeholder=\"Search\"\n onChange={(e) => setSearchQuery(e.target.value)}\n onBlur={() => {\n if (searchQuery.trim() === \"\") setIsSearchOpen(false);\n }}\n />\n {searchQuery.trim() ? (\n <button\n type=\"button\"\n className={styles.searchClearButton}\n onMouseDown={(e) => {\n // Prevent input blur so the bar stays open and focused.\n e.preventDefault();\n }}\n onClick={() => {\n setSearchQuery(\"\");\n searchInputRef.current?.focus();\n }}\n aria-label=\"Clear search\"\n >\n Clear\n </button>\n ) : null}\n </div>\n </div>\n </div>\n\n <button type=\"button\" onClick={close} disabled={submitting} className={styles.closeButton} aria-label=\"Close\">\n ×\n </button>\n </header>\n\n <div className={styles.body} ref={bodyRef}>\n {loading ? <p className={styles.muted}>Loading…</p> : null}\n {error ? <p className={styles.error}>{error}</p> : null}\n {!loading && config && !config.isActive ? <p className={styles.error}>Bundles are not active.</p> : null}\n\n <div className={styles.variants}>\n <ul className={styles.variantList}>\n {filteredVariants.map((v) => {\n const qty = selections[v.id] ?? 0;\n const variantPrimaryName = getVariantPrimaryName(v);\n const variantOptionText = formatVariantOptions(v.selectedOptions);\n const variantA11y = getVariantA11yLabel(v);\n\n // Price display:\n // - Always show a \"discounted\" price based on the unlocked discount, falling back to the minimum rule discount.\n // - Show the original price with a strikethrough when a discount applies.\n const discountPercentToUse = badgeDiscountPercent;\n const originalPriceNumber = Number(v.price);\n const hasDiscount =\n discountPercentToUse != null &&\n Number.isFinite(discountPercentToUse) &&\n discountPercentToUse > 0 &&\n Number.isFinite(originalPriceNumber);\n const discountedPriceNumber = hasDiscount\n ? Math.round(originalPriceNumber * (1 - discountPercentToUse / 100) * 100) / 100\n : null;\n\n return (\n <li key={v.id} className={styles.variantRow} data-variant-row={v.id}>\n <div className={styles.variantLeft}>\n <div className={styles.variantThumb} aria-hidden=\"true\">\n {v.image?.url ? (\n <img\n className={styles.variantThumbImg}\n src={v.image.url}\n alt={v.image.altText ?? variantA11y}\n loading=\"lazy\"\n />\n ) : (\n <div className={styles.variantThumbFallback}>{v.product?.title?.slice(0, 1)?.toUpperCase() ?? \"•\"}</div>\n )}\n </div>\n\n <div className={styles.variantInfo}>\n <div className={styles.variantName}>{variantPrimaryName}</div>\n {variantOptionText ? <div className={styles.variantMeta}>{variantOptionText}</div> : null}\n <div className={styles.variantPriceRow}>\n {hasDiscount ? (\n <span className={styles.variantCompareAt}>\n {formatMoney(Number.isFinite(originalPriceNumber) ? originalPriceNumber.toFixed(2) : v.price)}\n </span>\n ) : null}\n <span className={styles.variantPrice}>\n {formatMoney(\n discountedPriceNumber != null\n ? discountedPriceNumber.toFixed(2)\n : Number.isFinite(originalPriceNumber)\n ? originalPriceNumber.toFixed(2)\n : v.price,\n )}\n </span>\n </div>\n </div>\n </div>\n {qty <= 0 ? (\n <button\n type=\"button\"\n onClick={() => setQuantity(v.id, 1)}\n disabled={submitting}\n className={styles.addToBundleButton}\n aria-label={`Add ${variantA11y} to bundle`}\n >\n Add\n </button>\n ) : (\n <div className={styles.qtyControls}>\n <button\n type=\"button\"\n onClick={() => setQuantity(v.id, qty - 1)}\n disabled={submitting || qty <= 0}\n className={styles.qtyButton}\n aria-label={`Decrease ${variantA11y}`}\n >\n −\n </button>\n <span className={styles.qtyValue} aria-label={`Quantity ${qty}`}>\n {qty}\n </span>\n <button\n type=\"button\"\n onClick={() => setQuantity(v.id, qty + 1)}\n disabled={submitting}\n className={styles.qtyButton}\n aria-label={`Increase ${variantA11y}`}\n >\n +\n </button>\n </div>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n </div>\n\n <footer className={styles.footer}>\n <div className={styles.boxSummaryWindow} ref={boxSummaryWindowRef}>\n <div className={styles.boxSummaryRow} aria-label=\"Bundle box summary\">\n {Array.from({ length: slotCount }).map((_, idx) => {\n const variant = filledVariants[idx];\n const threshold = thresholdByIndex.get(idx);\n const isFilled = Boolean(variant);\n\n // Per your preference: show discount text only while it's still a placeholder (unlocked display comes later elsewhere).\n const isThreshold = Boolean(threshold);\n const showThreshold = isThreshold && !isFilled;\n const discountText = threshold ? `${threshold.discountPercent}% off` : null;\n\n const titleText = isFilled ? variant!.displayName : `Slot ${idx + 1}`;\n\n return (\n <div key={idx} data-box-slot={idx} className={styles.boxSlot}>\n <div\n className={[\n styles.boxItem,\n isFilled ? styles.boxItemFilled : styles.boxItemPlaceholder,\n isThreshold ? styles.boxItemThreshold : null,\n isFilled ? styles.boxItemClickable : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n role={isFilled ? \"button\" : undefined}\n tabIndex={isFilled ? 0 : undefined}\n onClick={isFilled ? () => scrollVariantRowIntoView(variant!.id) : undefined}\n onKeyDown={\n isFilled\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n scrollVariantRowIntoView(variant!.id);\n }\n }\n : undefined\n }\n aria-label={showThreshold && discountText ? `${titleText}. Unlocks ${discountText}.` : titleText}\n title={showThreshold && discountText ? `${titleText} • Unlocks ${discountText}` : titleText}\n >\n {isFilled ? (\n variant!.image?.url ? (\n <img\n className={styles.boxItemImage}\n src={variant!.image!.url}\n alt={variant!.image!.altText ?? variant!.displayName}\n loading=\"lazy\"\n />\n ) : (\n <div className={styles.boxItemFallback} aria-hidden=\"true\">\n {variant!.product?.title?.slice(0, 1)?.toUpperCase() ?? \"•\"}\n </div>\n )\n ) : showThreshold && discountText ? (\n <div className={styles.boxItemDiscountText}>{discountText}</div>\n ) : (\n <div className={styles.boxItemPlus} aria-hidden=\"true\">\n +\n </div>\n )}\n </div>\n\n {isThreshold ? <div className={styles.boxThresholdLabel}>{threshold!.label}</div> : null}\n </div>\n );\n })}\n </div>\n </div>\n <div\n className={[styles.ctaTotalsWrap, itemsRemaining <= 0 ? styles.ctaTotalsWrapVisible : null].filter(Boolean).join(\" \")}\n aria-hidden={itemsRemaining > 0}\n >\n <div className={styles.ctaTotals} aria-label=\"Totals\">\n {badgeDiscountPercent ? (\n <span className={styles.ctaDiscountBadge}>\n {appliedDiscountPercent ? \"Unlocked \" : \"\"}\n {badgeDiscountPercent}% off\n </span>\n ) : (\n <span />\n )}\n\n <span>\n {appliedDiscountPercent ? (\n <>\n <span className={styles.ctaTotalsStrike}>{formatMoney(subtotal.toFixed(2))}</span>{\" \"}\n <span className={styles.ctaTotalsPrice}>{formatMoney(discountedTotal.toFixed(2))}</span>\n </>\n ) : (\n <span className={styles.ctaTotalsPrice}>{formatMoney(subtotal.toFixed(2))}</span>\n )}\n </span>\n </div>\n </div>\n\n <button type=\"button\" onClick={submit} disabled={!canSubmit} className={styles.cta}>\n {submitting ? \"Adding…\" : itemsRemaining > 0 ? `Add ${itemsRemaining} more product${itemsRemaining === 1 ? \"\" : \"s\"}` : \"Add to Basket\"}\n </button>\n </footer>\n </aside>\n </div>\n );\n}\n\n"],"names":["BundleLineAttributeKeys","fetchBundleConfig","params","url","json","BundleBuilderContext","createContext","safeRandomUUID","cryptoAny","clampQty","n","BundleProvider","props","apiBaseUrl","shop","cartAdapter","configHandle","initialData","configFetcher","children","isOpen","setIsOpen","useState","loading","setLoading","submitting","setSubmitting","error","setError","currencyCode","setCurrencyCode","config","setConfig","eligibleVariants","setEligibleVariants","selectionsState","setSelectionsState","abortRef","useRef","useEffect","_a","ac","fetcher","defaultFetchBundleConfig","e","msg","open","useCallback","close","toggle","v","setQuantity","merchandiseId","quantity","q","prev","prevQty","delta","nextSelections","_","rest","nextOrder","remainingToRemove","keptReversed","i","id","clearSelections","selections","selectionOrder","bundleSize","useMemo","sum","minRequired","rules","min","r","canSubmit","submit","bundleId","lines","l","value","jsx","useBundleBuilder","ctx","useContext","useBundleBuilderDrawer","BundleButton","styles","toKebabCase","string","toCamelCase","match","p1","p2","toPascalCase","camelCase","mergeClasses","classes","className","index","array","hasA11yProp","prop","defaultAttributes","Icon","forwardRef","color","size","strokeWidth","absoluteStrokeWidth","iconNode","ref","createElement","tag","attrs","createLucideIcon","iconName","Component","__iconNode","Search","BundleBuilderDrawer","isSearchOpen","setIsSearchOpen","searchQuery","setSearchQuery","searchInputRef","formatVariantOptions","selectedOptions","opts","o","name","_b","getVariantPrimaryName","getVariantA11yLabel","isMounted","setIsMounted","closeTimerRef","formatMoney","amount","title","sorted","a","b","filteredVariants","tokens","matches","parts","haystack","t","rulesSorted","itemsRemaining","appliedRule","best","appliedDiscountPercent","pct","minRuleDiscountPercent","badgeDiscountPercent","subtotal","byId","total","variantId","qty","price","discountedTotal","maxRuleSize","filledVariants","thresholdByIndex","map","idx","slotCount","panelRef","bodyRef","boxSummaryWindowRef","prevBundleSizeRef","scrollVariantRowIntoView","container","row","padding","maxTop","rowCenterOffset","targetTop","nextTop","onKeyDown","raf","hitThreshold","nextThreshold","targetIdx","el","containerRect","deltaRight","maxLeft","nextLeft","jsxs","variantPrimaryName","variantOptionText","variantA11y","discountPercentToUse","originalPriceNumber","hasDiscount","discountedPriceNumber","_d","_c","variant","threshold","isFilled","isThreshold","showThreshold","discountText","titleText","Fragment"],"mappings":"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","x_google_ignoreList":[6,7,8,9,10]}
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ export declare const FullIntegration: StoryObj;
5
+ export declare const HookControlledDrawer: StoryObj;
6
+ export declare const HeaderSearchFiltering: StoryObj;
7
+ //# sourceMappingURL=BundleUI.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BundleUI.stories.d.ts","sourceRoot":"","sources":["../../src/stories/BundleUI.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,QAAA,MAAM,IAAI,EAAE,IAEX,CAAC;AAEF,eAAe,IAAI,CAAC;AA6JpB,eAAO,MAAM,eAAe,EAAE,QAwB7B,CAAC;AAwBF,eAAO,MAAM,oBAAoB,EAAE,QAyBlC,CAAC;AAUF,eAAO,MAAM,qBAAqB,EAAE,QAyBnC,CAAC"}
@@ -0,0 +1,67 @@
1
+ export type BundleCartLineAttribute = {
2
+ key: string;
3
+ value: string;
4
+ };
5
+ export type BundleCartAdapter = {
6
+ linesAdd(lines: Array<{
7
+ merchandiseId: string;
8
+ quantity: number;
9
+ attributes?: BundleCartLineAttribute[];
10
+ }>): Promise<void> | void;
11
+ linesUpdate?(lines: Array<{
12
+ id: string;
13
+ quantity: number;
14
+ attributes?: BundleCartLineAttribute[];
15
+ }>): Promise<void> | void;
16
+ linesRemove?(lineIds: string[]): Promise<void> | void;
17
+ openCartUI?(): void;
18
+ closeCartUI?(): void;
19
+ };
20
+ export type BundleDiscountRule = {
21
+ id: string;
22
+ handle: string;
23
+ minBundleSize: number;
24
+ discountPercent: string;
25
+ label: string;
26
+ sortOrder: number | null;
27
+ };
28
+ export type BundleConfig = {
29
+ id: string;
30
+ title: string;
31
+ isActive: boolean;
32
+ eligibleVariantIds: string[];
33
+ rules: BundleDiscountRule[];
34
+ };
35
+ export type BundleEligibleVariant = {
36
+ id: string;
37
+ title: string;
38
+ displayName: string;
39
+ selectedOptions: Array<{
40
+ name: string;
41
+ value: string;
42
+ }>;
43
+ price: string;
44
+ compareAtPrice: string | null;
45
+ image: null | {
46
+ url: string;
47
+ altText: string | null;
48
+ width: number | null;
49
+ height: number | null;
50
+ };
51
+ product: null | {
52
+ id: string;
53
+ title: string;
54
+ handle: string;
55
+ };
56
+ };
57
+ export type PublicBundleConfigResponse = {
58
+ ok: true;
59
+ shop: string;
60
+ currencyCode: string | null;
61
+ config: BundleConfig;
62
+ eligibleVariants: BundleEligibleVariant[];
63
+ } | {
64
+ ok: false;
65
+ error: string;
66
+ };
67
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,uBAAuB,EAAE,CAAC;KACxC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE1B,WAAW,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC;QACxB,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,uBAAuB,EAAE,CAAC;KACxC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE1B,WAAW,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEtD,UAAU,CAAC,IAAI,IAAI,CAAC;IACpB,WAAW,CAAC,IAAI,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,IAAI,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnG,OAAO,EAAE,IAAI,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,gBAAgB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GACxH;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@best-bundles/bundle-ui",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "sideEffects": [
10
+ "**/*.css"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "scripts": {
21
+ "build": "npm run build:js && npm run build:types",
22
+ "build:js": "vite build",
23
+ "build:types": "tsc -p tsconfig.build.json",
24
+ "prepack": "npm run build",
25
+ "storybook": "STORYBOOK_DISABLE_TELEMETRY=1 CI=1 storybook dev -p 6007",
26
+ "build-storybook": "STORYBOOK_DISABLE_TELEMETRY=1 CI=1 storybook build"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "peerDependencies": {
32
+ "lucide-react": ">=0.300.0",
33
+ "react": ">=18",
34
+ "react-dom": ">=18"
35
+ },
36
+ "devDependencies": {
37
+ "@vitejs/plugin-react": "^4.7.0",
38
+ "@storybook/addon-essentials": "^8.6.0",
39
+ "@storybook/addon-interactions": "^8.6.0",
40
+ "@storybook/addon-links": "^8.6.0",
41
+ "@storybook/blocks": "^8.6.0",
42
+ "@storybook/react-vite": "^8.6.0",
43
+ "@storybook/test": "^8.6.0",
44
+ "@types/react": "^18.3.25",
45
+ "@types/react-dom": "^18.3.7",
46
+ "lucide-react": "^0.515.0",
47
+ "storybook": "^8.6.0",
48
+ "typescript": "^5.9.3",
49
+ "vite": "^6.3.6",
50
+ "vite-plugin-css-injected-by-js": "^3.5.2"
51
+ }
52
+ }