@best-bundles/bundle-ui 0.0.14 → 0.0.16

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/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # @best-bundles/bundle-ui
2
+
3
+ Bundle Builder UI components and hooks for Shopify storefronts.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @best-bundles/bundle-ui lucide-react
9
+ ```
10
+
11
+ Peer deps: `react`, `react-dom`, and `lucide-react`.
12
+
13
+ ## Hydrogen setup
14
+
15
+ ### 1) Create a cart adapter (passthrough to Hydrogen cart handlers)
16
+
17
+ Create a small adapter that forwards Bundle Builder line changes to your Hydrogen cart.
18
+ Adjust the cart UI open/close helpers to match your storefront.
19
+
20
+ `app/lib/bundleCartAdapter.tsx`
21
+
22
+ ```tsx
23
+ import { useMemo } from "react";
24
+ import type { BundleCartAdapter } from "@best-bundles/bundle-ui";
25
+ import { useCart } from "@shopify/hydrogen";
26
+
27
+ export function useHydrogenBundleCartAdapter(): BundleCartAdapter {
28
+ const { linesAdd, linesUpdate, linesRemove, openCart, closeCart } = useCart();
29
+
30
+ return useMemo(
31
+ () => ({
32
+ async linesAdd(lines) {
33
+ await linesAdd(lines);
34
+ },
35
+ async linesUpdate(lines) {
36
+ await linesUpdate(lines);
37
+ },
38
+ async linesRemove(lineIds) {
39
+ await linesRemove(lineIds);
40
+ },
41
+ openCartUI() {
42
+ openCart?.();
43
+ },
44
+ closeCartUI() {
45
+ closeCart?.();
46
+ },
47
+ }),
48
+ [linesAdd, linesUpdate, linesRemove, openCart, closeCart],
49
+ );
50
+ }
51
+ ```
52
+
53
+ If your Hydrogen setup uses `@shopify/hydrogen-react`, import `useCart` from there instead.
54
+
55
+ ### 2) Wrap the app in `root.tsx` with `BundleProvider`
56
+
57
+ Place the provider inside your Hydrogen `CartProvider` so `useCart()` is available.
58
+
59
+ ```tsx
60
+ import { BundleProvider } from "@best-bundles/bundle-ui";
61
+ import { useHydrogenBundleCartAdapter } from "~/lib/bundleCartAdapter";
62
+
63
+ export default function App() {
64
+ const { env } = useLoaderData<typeof loader>();
65
+ const cartAdapter = useHydrogenBundleCartAdapter();
66
+
67
+ return (
68
+ <CartProvider>
69
+ <BundleProvider
70
+ apiBaseUrl={env.BEST_BUNDLES_API_BASE_URL}
71
+ shop={env.PUBLIC_STORE_DOMAIN}
72
+ cartAdapter={cartAdapter}
73
+ >
74
+ <Layout>
75
+ <Outlet />
76
+ </Layout>
77
+ </BundleProvider>
78
+ </CartProvider>
79
+ );
80
+ }
81
+ ```
82
+
83
+ Required props:
84
+ - `apiBaseUrl`: Base URL of the Best Bundles app (used for config + analytics).
85
+ - `shop`: Your shop domain (used to fetch the bundle config).
86
+ - `cartAdapter`: The passthrough adapter from step 1.
87
+
88
+ Optional props:
89
+ - `configHandle`: Defaults to `"default"`.
90
+ - `analyticsEndpoint`: Defaults to `${apiBaseUrl}/api/public/bundle-analytics`.
91
+
92
+ ### 3) Mount the drawer UI in `Layout.tsx`
93
+
94
+ Render the drawer once in your layout so it can be opened anywhere.
95
+
96
+ ```tsx
97
+ import { BundleBuilderDrawer, BundleButton } from "@best-bundles/bundle-ui";
98
+
99
+ export function Layout({ children }: { children: React.ReactNode }) {
100
+ return (
101
+ <>
102
+ <header>
103
+ <BundleButton>Build a bundle</BundleButton>
104
+ </header>
105
+
106
+ {children}
107
+
108
+ <BundleBuilderDrawer />
109
+ </>
110
+ );
111
+ }
112
+ ```
113
+
114
+ ## `useBundleBuilder` hook
115
+
116
+ The hook exposes drawer state, selections, and submit actions. It must be used
117
+ inside `BundleProvider`.
118
+
119
+ ```tsx
120
+ import { useBundleBuilder } from "@best-bundles/bundle-ui";
121
+
122
+ export function CustomBundleTrigger() {
123
+ const { toggle, canSubmit, bundleSize, minRequired } = useBundleBuilder();
124
+
125
+ return (
126
+ <button type="button" onClick={toggle} disabled={!canSubmit && bundleSize < minRequired}>
127
+ Build a bundle
128
+ </button>
129
+ );
130
+ }
131
+ ```
132
+
133
+ Key fields include:
134
+ - `isOpen`, `open`, `close`, `toggle`
135
+ - `loading`, `submitting`, `error`
136
+ - `config`, `eligibleVariants`
137
+ - `selections`, `selectionOrder`, `setQuantity`, `clearSelections`
138
+ - `bundleSize`, `minRequired`, `canSubmit`, `submit`
139
+
140
+ ## Bundle cart adapter shape
141
+
142
+ If you need to build your own adapter, implement `BundleCartAdapter`:
143
+
144
+ ```ts
145
+ export type BundleCartAdapter = {
146
+ linesAdd(lines: Array<{
147
+ merchandiseId: string;
148
+ quantity: number;
149
+ attributes?: { key: string; value: string }[];
150
+ }>): Promise<void> | void;
151
+ linesUpdate?(lines: Array<{ id: string; quantity: number; attributes?: { key: string; value: string }[] }>): Promise<void> | void;
152
+ linesRemove?(lineIds: string[]): Promise<void> | void;
153
+ openCartUI?(): void;
154
+ closeCartUI?(): void;
155
+ };
156
+ ```
157
+
158
+ For Liquid storefronts, `createLiquidCartAdapter()` is available.
package/dist/index.js CHANGED
@@ -1235,7 +1235,7 @@ function Hn(i) {
1235
1235
  /* @__PURE__ */ h("button", { type: "button", onClick: re, disabled: !J, className: u.cta, children: r ? "Adding…" : b > 0 ? `Add ${b} more product${b === 1 ? "" : "s"}` : "Add to Basket" }),
1236
1236
  /* @__PURE__ */ R("div", { className: u.poweredBy, children: [
1237
1237
  "powered by ",
1238
- /* @__PURE__ */ h("a", { href: "https://best-bundles.onrender.com", target: "_blank", rel: "noopener noreferrer", children: " BestBundles" })
1238
+ /* @__PURE__ */ h("a", { href: "https://apps.shopify.com/best-bundles", target: "_blank", rel: "noopener noreferrer", children: " BestBundles" })
1239
1239
  ] })
1240
1240
  ] })
1241
1241
  ] })