@shopify/shop-minis-react 0.0.26 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/commerce/product-link-skeleton.js +30 -0
- package/dist/components/commerce/product-link-skeleton.js.map +1 -0
- package/dist/components/commerce/product-link.js +68 -68
- package/dist/components/commerce/product-link.js.map +1 -1
- package/dist/components/commerce/search.js +144 -0
- package/dist/components/commerce/search.js.map +1 -0
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/hooks/product/useProductSearch.js +24 -23
- package/dist/hooks/product/useProductSearch.js.map +1 -1
- package/dist/index.js +228 -223
- package/dist/index.js.map +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/search.js +16 -0
- package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/search.js.map +1 -0
- package/dist/shop-minis-react.css +1 -1
- package/package.json +1 -1
- package/src/components/commerce/product-link-skeleton.tsx +30 -0
- package/src/components/commerce/product-link.tsx +8 -5
- package/src/components/commerce/search.tsx +264 -0
- package/src/components/index.ts +1 -0
- package/src/components/ui/input.tsx +1 -1
- package/src/hooks/product/useProductSearch.ts +10 -1
- package/src/styles/utilities.css +9 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as e, jsxs as l } from "react/jsx-runtime";
|
|
2
|
+
import { cn as m } from "../../lib/utils.js";
|
|
3
|
+
import { Skeleton as s } from "../ui/skeleton.js";
|
|
4
|
+
function n({
|
|
5
|
+
className: t,
|
|
6
|
+
...r
|
|
7
|
+
}) {
|
|
8
|
+
return /* @__PURE__ */ e(
|
|
9
|
+
"div",
|
|
10
|
+
{
|
|
11
|
+
className: m(
|
|
12
|
+
"relative w-full shadow-sm rounded-lg p-4 items-center",
|
|
13
|
+
t
|
|
14
|
+
),
|
|
15
|
+
...r,
|
|
16
|
+
children: /* @__PURE__ */ l("div", { className: "flex flex-row items-center justify-center w-full", children: [
|
|
17
|
+
/* @__PURE__ */ e(s, { className: "aspect-square w-15 h-15" }),
|
|
18
|
+
/* @__PURE__ */ l("div", { className: "flex flex-col justify-center items-start ml-2 w-full pt-2", children: [
|
|
19
|
+
/* @__PURE__ */ e(s, { className: "mb-3 h-3 w-3/4" }),
|
|
20
|
+
/* @__PURE__ */ e(s, { className: "mb-2 h-3 w-1/4" }),
|
|
21
|
+
/* @__PURE__ */ e(s, { className: "mb-2 h-3 w-1/3" })
|
|
22
|
+
] })
|
|
23
|
+
] })
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
n as ProductLinkSkeleton
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=product-link-skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-link-skeleton.js","sources":["../../../src/components/commerce/product-link-skeleton.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {cn} from '../../lib/utils'\nimport {Skeleton} from '../ui/skeleton'\n\nfunction ProductLinkSkeleton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n className={cn(\n 'relative w-full shadow-sm rounded-lg p-4 items-center',\n className\n )}\n {...props}\n >\n <div className=\"flex flex-row items-center justify-center w-full\">\n <Skeleton className=\"aspect-square w-15 h-15\" />\n <div className=\"flex flex-col justify-center items-start ml-2 w-full pt-2\">\n <Skeleton className=\"mb-3 h-3 w-3/4\" />\n <Skeleton className=\"mb-2 h-3 w-1/4\" />\n <Skeleton className=\"mb-2 h-3 w-1/3\" />\n </div>\n </div>\n </div>\n )\n}\n\nexport {ProductLinkSkeleton}\n"],"names":["ProductLinkSkeleton","className","props","jsx","cn","jsxs","Skeleton"],"mappings":";;;AAKA,SAASA,EAAoB;AAAA,EAC3B,WAAAC;AAAA,EACA,GAAGC;AACL,GAAgC;AAE5B,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAH;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,MAEJ,UAAA,gBAAAG,EAAC,OAAI,EAAA,WAAU,oDACb,UAAA;AAAA,QAAC,gBAAAF,EAAAG,GAAA,EAAS,WAAU,0BAA0B,CAAA;AAAA,QAC9C,gBAAAD,EAAC,OAAI,EAAA,WAAU,6DACb,UAAA;AAAA,UAAC,gBAAAF,EAAAG,GAAA,EAAS,WAAU,iBAAiB,CAAA;AAAA,UACrC,gBAAAH,EAACG,GAAS,EAAA,WAAU,iBAAiB,CAAA;AAAA,UACrC,gBAAAH,EAACG,GAAS,EAAA,WAAU,iBAAiB,CAAA;AAAA,QAAA,EACvC,CAAA;AAAA,MAAA,EACF,CAAA;AAAA,IAAA;AAAA,EACF;AAEJ;"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { jsxs as c, jsx as t, Fragment as
|
|
1
|
+
import { jsxs as c, jsx as t, Fragment as F } from "react/jsx-runtime";
|
|
2
2
|
import * as w from "react";
|
|
3
|
-
import { cva as
|
|
4
|
-
import { useShopNavigation as
|
|
5
|
-
import { useSavedProductsActions as
|
|
6
|
-
import { formatMoney as
|
|
3
|
+
import { cva as j } from "../../shop-minis-react/node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.js";
|
|
4
|
+
import { useShopNavigation as O } from "../../hooks/navigation/useShopNavigation.js";
|
|
5
|
+
import { useSavedProductsActions as M } from "../../hooks/user/useSavedProductsActions.js";
|
|
6
|
+
import { formatMoney as A } from "../../lib/formatMoney.js";
|
|
7
7
|
import { cn as n } from "../../lib/utils.js";
|
|
8
|
-
import { FavoriteButton as
|
|
9
|
-
import { Touchable as
|
|
10
|
-
import { Card as
|
|
11
|
-
import
|
|
12
|
-
import { Root as
|
|
13
|
-
const
|
|
8
|
+
import { FavoriteButton as D } from "../atoms/favorite-button.js";
|
|
9
|
+
import { Touchable as z } from "../atoms/touchable.js";
|
|
10
|
+
import { Card as q, CardContent as B, CardAction as U } from "../ui/card.js";
|
|
11
|
+
import _ from "../../shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/star.js";
|
|
12
|
+
import { Root as E } from "../../shop-minis-react/node_modules/.pnpm/@radix-ui_react-slot@1.2.3_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-slot/dist/index.js";
|
|
13
|
+
const G = j("", {
|
|
14
14
|
variants: {
|
|
15
15
|
layout: {
|
|
16
16
|
horizontal: "w-full !flex-row items-center gap-3 px-4 py-3",
|
|
@@ -27,37 +27,37 @@ const E = F("", {
|
|
|
27
27
|
discount: "none"
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
-
function
|
|
30
|
+
function H({
|
|
31
31
|
className: o,
|
|
32
32
|
layout: e,
|
|
33
33
|
discount: r,
|
|
34
34
|
asChild: i = !1,
|
|
35
|
-
onPress:
|
|
36
|
-
...
|
|
35
|
+
onPress: l,
|
|
36
|
+
...a
|
|
37
37
|
}) {
|
|
38
38
|
return /* @__PURE__ */ t(
|
|
39
|
-
|
|
39
|
+
z,
|
|
40
40
|
{
|
|
41
|
-
onClick:
|
|
41
|
+
onClick: l,
|
|
42
42
|
whileTap: { opacity: 0.7 },
|
|
43
43
|
transition: {
|
|
44
44
|
opacity: { type: "tween", duration: 0.08, ease: "easeInOut" }
|
|
45
45
|
},
|
|
46
46
|
children: /* @__PURE__ */ t(
|
|
47
|
-
i ?
|
|
47
|
+
i ? E : q,
|
|
48
48
|
{
|
|
49
49
|
className: n(
|
|
50
|
-
|
|
50
|
+
G({ layout: e, discount: r }),
|
|
51
51
|
"border-0 bg-white rounded-xl shadow-lg shadow-black/10",
|
|
52
52
|
o
|
|
53
53
|
),
|
|
54
|
-
...
|
|
54
|
+
...a
|
|
55
55
|
}
|
|
56
56
|
)
|
|
57
57
|
}
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
|
-
function
|
|
60
|
+
function J({
|
|
61
61
|
className: o,
|
|
62
62
|
layout: e = "horizontal",
|
|
63
63
|
...r
|
|
@@ -75,13 +75,13 @@ function H({
|
|
|
75
75
|
}
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
|
-
function
|
|
78
|
+
function K({
|
|
79
79
|
className: o,
|
|
80
80
|
layout: e = "horizontal",
|
|
81
81
|
...r
|
|
82
82
|
}) {
|
|
83
83
|
return /* @__PURE__ */ t(
|
|
84
|
-
|
|
84
|
+
B,
|
|
85
85
|
{
|
|
86
86
|
className: n(
|
|
87
87
|
e === "horizontal" ? "flex-1 min-w-0 space-y-1 px-0 py-0" : "space-y-2",
|
|
@@ -91,7 +91,7 @@ function J({
|
|
|
91
91
|
}
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
|
-
function
|
|
94
|
+
function Q({
|
|
95
95
|
className: o,
|
|
96
96
|
children: e,
|
|
97
97
|
...r
|
|
@@ -109,7 +109,7 @@ function K({
|
|
|
109
109
|
}
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
|
-
function
|
|
112
|
+
function W({ className: o, ...e }) {
|
|
113
113
|
return /* @__PURE__ */ t(
|
|
114
114
|
"div",
|
|
115
115
|
{
|
|
@@ -119,7 +119,7 @@ function Q({ className: o, ...e }) {
|
|
|
119
119
|
}
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
|
-
function
|
|
122
|
+
function X({
|
|
123
123
|
className: o,
|
|
124
124
|
...e
|
|
125
125
|
}) {
|
|
@@ -132,7 +132,7 @@ function W({
|
|
|
132
132
|
}
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
|
-
function
|
|
135
|
+
function Y({
|
|
136
136
|
className: o,
|
|
137
137
|
...e
|
|
138
138
|
}) {
|
|
@@ -145,7 +145,7 @@ function X({
|
|
|
145
145
|
}
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
|
-
function
|
|
148
|
+
function Z({
|
|
149
149
|
className: o,
|
|
150
150
|
...e
|
|
151
151
|
}) {
|
|
@@ -158,7 +158,7 @@ function Y({
|
|
|
158
158
|
}
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
|
-
function
|
|
161
|
+
function $({ className: o, ...e }) {
|
|
162
162
|
return /* @__PURE__ */ t(
|
|
163
163
|
"div",
|
|
164
164
|
{
|
|
@@ -171,19 +171,19 @@ function Z({ className: o, ...e }) {
|
|
|
171
171
|
}
|
|
172
172
|
);
|
|
173
173
|
}
|
|
174
|
-
function
|
|
174
|
+
function tt({
|
|
175
175
|
className: o,
|
|
176
176
|
onPress: e,
|
|
177
177
|
filled: r = !1,
|
|
178
178
|
...i
|
|
179
179
|
}) {
|
|
180
180
|
return /* @__PURE__ */ t(
|
|
181
|
-
|
|
181
|
+
U,
|
|
182
182
|
{
|
|
183
183
|
className: n("flex-shrink-0 self-center px-0 py-0", o),
|
|
184
184
|
...i,
|
|
185
185
|
children: /* @__PURE__ */ t(
|
|
186
|
-
|
|
186
|
+
z,
|
|
187
187
|
{
|
|
188
188
|
stopPropagation: !0,
|
|
189
189
|
onClick: e,
|
|
@@ -192,43 +192,43 @@ function $({
|
|
|
192
192
|
opacity: { type: "tween", duration: 0.08, ease: "easeInOut" },
|
|
193
193
|
scale: { type: "tween", duration: 0.08, ease: "easeInOut" }
|
|
194
194
|
},
|
|
195
|
-
children: /* @__PURE__ */ t(
|
|
195
|
+
children: /* @__PURE__ */ t(D, { filled: r, onClick: e })
|
|
196
196
|
}
|
|
197
197
|
)
|
|
198
198
|
}
|
|
199
199
|
);
|
|
200
200
|
}
|
|
201
|
-
function
|
|
202
|
-
const { navigateToProduct:
|
|
201
|
+
function mt({ product: o, hideFavoriteAction: e = !1 }) {
|
|
202
|
+
const { navigateToProduct: r } = O(), { saveProduct: i, unsaveProduct: l } = M(), {
|
|
203
203
|
id: a,
|
|
204
|
-
title:
|
|
205
|
-
featuredImage:
|
|
206
|
-
reviewAnalytics:
|
|
204
|
+
title: u,
|
|
205
|
+
featuredImage: P,
|
|
206
|
+
reviewAnalytics: C,
|
|
207
207
|
price: m,
|
|
208
208
|
compareAtPrice: p,
|
|
209
|
-
isFavorited:
|
|
209
|
+
isFavorited: T,
|
|
210
210
|
selectedVariant: f,
|
|
211
211
|
defaultVariantId: h,
|
|
212
212
|
shop: g
|
|
213
|
-
} = o, [x,
|
|
214
|
-
|
|
213
|
+
} = o, [x, N] = w.useState(T), y = C?.averageRating, I = C?.reviewCount, v = m?.amount ? A(m?.amount, m?.currencyCode) : void 0, L = P?.url, R = P?.altText || u, k = p?.amount ? A(p?.amount, p?.currencyCode) : void 0, b = k && k !== v, S = w.useCallback(() => {
|
|
214
|
+
r({
|
|
215
215
|
productId: a
|
|
216
216
|
});
|
|
217
|
-
}, [
|
|
218
|
-
const
|
|
219
|
-
|
|
217
|
+
}, [r, a]), V = w.useCallback(async () => {
|
|
218
|
+
const s = x;
|
|
219
|
+
N(!s);
|
|
220
220
|
try {
|
|
221
|
-
|
|
221
|
+
s ? await l({
|
|
222
222
|
productId: a,
|
|
223
223
|
shopId: g.id,
|
|
224
224
|
productVariantId: f?.id || h
|
|
225
|
-
}) : await
|
|
225
|
+
}) : await i({
|
|
226
226
|
productId: a,
|
|
227
227
|
shopId: g.id,
|
|
228
228
|
productVariantId: f?.id || h
|
|
229
229
|
});
|
|
230
230
|
} catch {
|
|
231
|
-
|
|
231
|
+
N(s);
|
|
232
232
|
}
|
|
233
233
|
}, [
|
|
234
234
|
x,
|
|
@@ -236,29 +236,29 @@ function ut({ product: o }) {
|
|
|
236
236
|
g.id,
|
|
237
237
|
f?.id,
|
|
238
238
|
h,
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
i,
|
|
240
|
+
l
|
|
241
241
|
]);
|
|
242
242
|
return /* @__PURE__ */ c(
|
|
243
|
-
|
|
243
|
+
H,
|
|
244
244
|
{
|
|
245
245
|
layout: "horizontal",
|
|
246
|
-
discount:
|
|
247
|
-
onPress:
|
|
246
|
+
discount: b ? "small" : "none",
|
|
247
|
+
onPress: S,
|
|
248
248
|
children: [
|
|
249
|
-
/* @__PURE__ */ t(
|
|
249
|
+
/* @__PURE__ */ t(J, { layout: "horizontal", children: L ? /* @__PURE__ */ t(
|
|
250
250
|
"img",
|
|
251
251
|
{
|
|
252
|
-
src:
|
|
253
|
-
alt:
|
|
252
|
+
src: L,
|
|
253
|
+
alt: R,
|
|
254
254
|
className: "h-full w-full object-cover"
|
|
255
255
|
}
|
|
256
256
|
) : /* @__PURE__ */ t("div", { className: "h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs", children: "No Image" }) }),
|
|
257
|
-
/* @__PURE__ */ c(
|
|
258
|
-
/* @__PURE__ */ t(
|
|
259
|
-
|
|
260
|
-
Array.from({ length: 5 }, (
|
|
261
|
-
|
|
257
|
+
/* @__PURE__ */ c(K, { layout: "horizontal", children: [
|
|
258
|
+
/* @__PURE__ */ t(Q, { children: u }),
|
|
259
|
+
I && y && /* @__PURE__ */ t($, { children: /* @__PURE__ */ c("div", { className: "flex items-center gap-1", children: [
|
|
260
|
+
Array.from({ length: 5 }, (s, d) => /* @__PURE__ */ t(
|
|
261
|
+
_,
|
|
262
262
|
{
|
|
263
263
|
fill: d < Math.floor(y) ? "currentColor" : "none",
|
|
264
264
|
className: n(
|
|
@@ -270,20 +270,20 @@ function ut({ product: o }) {
|
|
|
270
270
|
)),
|
|
271
271
|
/* @__PURE__ */ c("span", { className: "text-xs text-gray-600 ml-1", children: [
|
|
272
272
|
"(",
|
|
273
|
-
|
|
273
|
+
I,
|
|
274
274
|
")"
|
|
275
275
|
] })
|
|
276
276
|
] }) }),
|
|
277
|
-
/* @__PURE__ */ t(
|
|
278
|
-
/* @__PURE__ */ t(
|
|
279
|
-
/* @__PURE__ */ t(
|
|
280
|
-
] }) : /* @__PURE__ */ t(
|
|
277
|
+
/* @__PURE__ */ t(W, { children: b ? /* @__PURE__ */ c(F, { children: [
|
|
278
|
+
/* @__PURE__ */ t(Z, { children: v }),
|
|
279
|
+
/* @__PURE__ */ t(Y, { children: k })
|
|
280
|
+
] }) : /* @__PURE__ */ t(X, { children: v }) })
|
|
281
281
|
] }),
|
|
282
|
-
/* @__PURE__ */ t(
|
|
283
|
-
|
|
282
|
+
e ? null : /* @__PURE__ */ t(
|
|
283
|
+
tt,
|
|
284
284
|
{
|
|
285
285
|
filled: x,
|
|
286
|
-
onPress:
|
|
286
|
+
onPress: V
|
|
287
287
|
}
|
|
288
288
|
)
|
|
289
289
|
]
|
|
@@ -291,6 +291,6 @@ function ut({ product: o }) {
|
|
|
291
291
|
);
|
|
292
292
|
}
|
|
293
293
|
export {
|
|
294
|
-
|
|
294
|
+
mt as ProductLink
|
|
295
295
|
};
|
|
296
296
|
//# sourceMappingURL=product-link.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Star} from 'lucide-react'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {FavoriteButton} from '../atoms/favorite-button'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn(\n 'flex items-center gap-1 text-xs text-muted-foreground',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n onPress,\n filled = false,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n onPress?: () => void\n filled?: boolean\n}) {\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <FavoriteButton filled={filled} onClick={onPress} />\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkProps {\n product: Product\n}\n\n// Composed ProductLink component\nfunction ProductLink({product}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n }, [navigateToProduct, id])\n\n const handleActionPress = React.useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n isFavoritedLocal,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n unsaveProduct,\n ])\n\n return (\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {reviewCount && averageRating && (\n <ProductLinkRating>\n <div className=\"flex items-center gap-1\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n fill={\n i < Math.floor(averageRating!) ? 'currentColor' : 'none'\n }\n className={cn(\n 'h-3 w-3',\n i < Math.floor(averageRating!)\n ? 'text-primary'\n : 'text-gray-300'\n )}\n />\n ))}\n <span className=\"text-xs text-gray-600 ml-1\">\n ({reviewCount})\n </span>\n </div>\n </ProductLinkRating>\n )}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n />\n </ProductLinkRoot>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","filled","CardAction","FavoriteButton","ProductLink","product","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","_","i","Star","Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Star} from 'lucide-react'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {FavoriteButton} from '../atoms/favorite-button'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn(\n 'flex items-center gap-1 text-xs text-muted-foreground',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n onPress,\n filled = false,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n onPress?: () => void\n filled?: boolean\n}) {\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <FavoriteButton filled={filled} onClick={onPress} />\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkProps {\n product: Product\n hideFavoriteAction?: boolean\n}\n\n// Composed ProductLink component\nfunction ProductLink({product, hideFavoriteAction = false}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n }, [navigateToProduct, id])\n\n const handleActionPress = React.useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n isFavoritedLocal,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n unsaveProduct,\n ])\n\n return (\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {reviewCount && averageRating && (\n <ProductLinkRating>\n <div className=\"flex items-center gap-1\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n fill={\n i < Math.floor(averageRating!) ? 'currentColor' : 'none'\n }\n className={cn(\n 'h-3 w-3',\n i < Math.floor(averageRating!)\n ? 'text-primary'\n : 'text-gray-300'\n )}\n />\n ))}\n <span className=\"text-xs text-gray-600 ml-1\">\n ({reviewCount})\n </span>\n </div>\n </ProductLinkRating>\n )}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n {hideFavoriteAction ? null : (\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n />\n )}\n </ProductLinkRoot>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","filled","CardAction","FavoriteButton","ProductLink","product","hideFavoriteAction","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","_","i","Star","Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { jsx as e, jsxs as g } from "react/jsx-runtime";
|
|
2
|
+
import { useState as P, useCallback as y, createContext as k, useContext as L } from "react";
|
|
3
|
+
import { useProductSearch as T } from "../../hooks/product/useProductSearch.js";
|
|
4
|
+
import { cn as p } from "../../lib/utils.js";
|
|
5
|
+
import { IconButton as E } from "../atoms/icon-button.js";
|
|
6
|
+
import { List as _ } from "../atoms/list.js";
|
|
7
|
+
import { Input as j } from "../ui/input.js";
|
|
8
|
+
import { ProductLink as v } from "./product-link.js";
|
|
9
|
+
import { ProductLinkSkeleton as u } from "./product-link-skeleton.js";
|
|
10
|
+
import z from "../../shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/search.js";
|
|
11
|
+
import M from "../../shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/x.js";
|
|
12
|
+
const Q = 100, S = k(null);
|
|
13
|
+
function N() {
|
|
14
|
+
const t = L(S);
|
|
15
|
+
if (!t)
|
|
16
|
+
throw new Error("useSearchContext must be used within a SearchProvider");
|
|
17
|
+
return t;
|
|
18
|
+
}
|
|
19
|
+
function q({ initialQuery: t = "", children: i }) {
|
|
20
|
+
const [r, a] = P(t), { products: o, loading: s, error: n, fetchMore: c, hasNextPage: l, isTyping: d } = T({
|
|
21
|
+
query: r,
|
|
22
|
+
fetchPolicy: "network-only"
|
|
23
|
+
}), m = y((f) => {
|
|
24
|
+
a(f);
|
|
25
|
+
}, []), h = {
|
|
26
|
+
query: r,
|
|
27
|
+
setQuery: m,
|
|
28
|
+
products: o,
|
|
29
|
+
loading: s,
|
|
30
|
+
error: n,
|
|
31
|
+
fetchMore: c,
|
|
32
|
+
hasNextPage: l,
|
|
33
|
+
isTyping: d
|
|
34
|
+
};
|
|
35
|
+
return /* @__PURE__ */ e(S.Provider, { value: h, children: i });
|
|
36
|
+
}
|
|
37
|
+
function A({
|
|
38
|
+
placeholder: t = "Search products...",
|
|
39
|
+
className: i,
|
|
40
|
+
inputProps: r
|
|
41
|
+
}) {
|
|
42
|
+
const { query: a, setQuery: o } = N(), s = y(
|
|
43
|
+
(n) => {
|
|
44
|
+
o(n.target.value), r?.onChange?.(n);
|
|
45
|
+
},
|
|
46
|
+
[r, o]
|
|
47
|
+
);
|
|
48
|
+
return /* @__PURE__ */ g("div", { className: "relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100", children: [
|
|
49
|
+
/* @__PURE__ */ e("div", { className: "relative flex items-center", children: /* @__PURE__ */ e(
|
|
50
|
+
z,
|
|
51
|
+
{
|
|
52
|
+
size: 18,
|
|
53
|
+
className: p("text-accent-foreground opacity-60")
|
|
54
|
+
}
|
|
55
|
+
) }),
|
|
56
|
+
/* @__PURE__ */ e("div", { className: "relative flex-1 flex items-center mx-2", children: /* @__PURE__ */ e(
|
|
57
|
+
j,
|
|
58
|
+
{
|
|
59
|
+
name: "search",
|
|
60
|
+
onChange: s,
|
|
61
|
+
placeholder: t,
|
|
62
|
+
type: "search",
|
|
63
|
+
role: "searchbox",
|
|
64
|
+
autoComplete: "off",
|
|
65
|
+
value: a,
|
|
66
|
+
"data-testid": "search-input",
|
|
67
|
+
...r,
|
|
68
|
+
className: p(
|
|
69
|
+
"w-full flex overflow-hidden rounded-radius-28 border-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60",
|
|
70
|
+
i
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
) }),
|
|
74
|
+
/* @__PURE__ */ e("div", { className: "relative flex items-center", children: a === "" ? null : /* @__PURE__ */ e(
|
|
75
|
+
E,
|
|
76
|
+
{
|
|
77
|
+
Icon: M,
|
|
78
|
+
size: "sm",
|
|
79
|
+
filled: !1,
|
|
80
|
+
iconStyles: "",
|
|
81
|
+
onClick: () => o(""),
|
|
82
|
+
buttonStyles: "flex items-center rounded-radius-max bg-[var(--grayscale-l20)]"
|
|
83
|
+
}
|
|
84
|
+
) })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
function F({
|
|
88
|
+
height: t = window.innerHeight,
|
|
89
|
+
renderItem: i,
|
|
90
|
+
itemHeight: r = Q,
|
|
91
|
+
initialStateComponent: a,
|
|
92
|
+
showScrollbar: o,
|
|
93
|
+
overscanCount: s = 5
|
|
94
|
+
}) {
|
|
95
|
+
const { query: n, products: c, loading: l, fetchMore: d, hasNextPage: m, isTyping: h } = N(), f = (x, I) => i ? i(x, I) : /* @__PURE__ */ e("div", { className: "p-2", children: /* @__PURE__ */ e(v, { product: x, hideFavoriteAction: !0 }, x.id) }), b = n.trim().length === 0, w = (!c || c.length === 0) && (l || h), C = (!c || c.length === 0) && !l;
|
|
96
|
+
return b ? a || /* @__PURE__ */ e("div", { className: "flex items-center justify-center h-32 text-gray-500", children: "Start typing to search for products" }) : w ? /* @__PURE__ */ g("div", { className: "flex flex-col px-4 py-4", children: [
|
|
97
|
+
/* @__PURE__ */ e(u, { className: "mb-4" }),
|
|
98
|
+
/* @__PURE__ */ e(u, { className: "mb-4" }),
|
|
99
|
+
/* @__PURE__ */ e(u, { className: "mb-4" }),
|
|
100
|
+
/* @__PURE__ */ e(u, { className: "mb-4" })
|
|
101
|
+
] }) : C ? /* @__PURE__ */ e("div", { className: "flex items-center justify-center h-32 text-gray-500", children: `No products found for "${n}"` }) : /* @__PURE__ */ e(
|
|
102
|
+
_,
|
|
103
|
+
{
|
|
104
|
+
items: c || [],
|
|
105
|
+
height: t,
|
|
106
|
+
renderItem: f,
|
|
107
|
+
itemSizeForRow: () => r,
|
|
108
|
+
fetchMore: m ? d : void 0,
|
|
109
|
+
showScrollbar: o,
|
|
110
|
+
overscanCount: s
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function J({
|
|
115
|
+
initialQuery: t,
|
|
116
|
+
placeholder: i,
|
|
117
|
+
inputProps: r,
|
|
118
|
+
height: a,
|
|
119
|
+
className: o,
|
|
120
|
+
renderItem: s,
|
|
121
|
+
itemHeight: n
|
|
122
|
+
}) {
|
|
123
|
+
const c = (l, d) => s ? s(l, d) : /* @__PURE__ */ e("div", { className: "p-2", children: /* @__PURE__ */ e(v, { product: l, hideFavoriteAction: !0 }, l.id) });
|
|
124
|
+
return /* @__PURE__ */ e(q, { initialQuery: t, children: /* @__PURE__ */ g("div", { className: p("flex flex-col ", o), children: [
|
|
125
|
+
/* @__PURE__ */ e("div", { className: "fixed top-0 left-0 right-0 p-4 w-full z-20 bg-background", children: /* @__PURE__ */ e(A, { placeholder: i, inputProps: r }) }),
|
|
126
|
+
/* @__PURE__ */ e("div", { className: "h-14" }),
|
|
127
|
+
/* @__PURE__ */ e(
|
|
128
|
+
F,
|
|
129
|
+
{
|
|
130
|
+
height: a,
|
|
131
|
+
renderItem: c,
|
|
132
|
+
itemHeight: n,
|
|
133
|
+
showScrollbar: !0
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
] }) });
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
J as Search,
|
|
140
|
+
A as SearchInput,
|
|
141
|
+
q as SearchProvider,
|
|
142
|
+
F as SearchResultsList
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sources":["../../../src/components/commerce/search.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useContext, useState, useCallback} from 'react'\n\nimport {SearchIcon, X} from 'lucide-react'\n\nimport {useProductSearch} from '../../hooks/product/useProductSearch'\nimport {cn} from '../../lib/utils'\nimport {type Product} from '../../types'\nimport {IconButton} from '../atoms/icon-button'\nimport {List} from '../atoms/list'\nimport {Input} from '../ui/input'\n\nimport {ProductLink} from './product-link'\nimport {ProductLinkSkeleton} from './product-link-skeleton'\n\nconst ESTIMATED_PRODUCT_LINK_HEIGHT = 100\n\ninterface SearchContextValue {\n query: string\n setQuery: (query: string) => void\n products: Product[] | null\n loading: boolean\n error: Error | null\n fetchMore?: () => void\n hasNextPage: boolean\n isTyping: boolean\n}\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\nfunction useSearchContext() {\n const context = useContext(SearchContext)\n if (!context) {\n throw new Error('useSearchContext must be used within a SearchProvider')\n }\n return context\n}\n\nexport interface SearchProviderProps {\n initialQuery?: string\n children: React.ReactNode\n}\n\nfunction SearchProvider({initialQuery = '', children}: SearchProviderProps) {\n const [query, setQueryState] = useState(initialQuery)\n\n const {products, loading, error, fetchMore, hasNextPage, isTyping} =\n useProductSearch({\n query,\n fetchPolicy: 'network-only',\n })\n\n const setQuery = useCallback((newQuery: string) => {\n setQueryState(newQuery)\n }, [])\n\n const contextValue: SearchContextValue = {\n query,\n setQuery,\n products,\n loading,\n error,\n fetchMore,\n hasNextPage,\n isTyping,\n }\n\n return (\n <SearchContext.Provider value={contextValue}>\n {children}\n </SearchContext.Provider>\n )\n}\n\nexport interface SearchInputProps {\n placeholder?: string\n className?: string\n inputProps?: React.ComponentProps<'input'>\n}\n\nfunction SearchInput({\n placeholder = 'Search products...',\n className,\n inputProps,\n}: SearchInputProps) {\n const {query, setQuery} = useSearchContext()\n\n const handleQueryChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n setQuery(event.target.value)\n inputProps?.onChange?.(event)\n },\n [inputProps, setQuery]\n )\n\n return (\n <div className=\"relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100\">\n <div className=\"relative flex items-center\">\n <SearchIcon\n size={18}\n className={cn('text-accent-foreground opacity-60')}\n />\n </div>\n <div className=\"relative flex-1 flex items-center mx-2\">\n <Input\n name=\"search\"\n onChange={handleQueryChange}\n placeholder={placeholder}\n type=\"search\"\n role=\"searchbox\"\n autoComplete=\"off\"\n value={query}\n data-testid=\"search-input\"\n {...inputProps}\n className={cn(\n `w-full flex overflow-hidden rounded-radius-28 border-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60`,\n className\n )}\n />\n </div>\n <div className=\"relative flex items-center\">\n {query === '' ? null : (\n <IconButton\n Icon={X}\n size=\"sm\"\n filled={false}\n iconStyles=\"\"\n onClick={() => setQuery('')}\n buttonStyles=\"flex items-center rounded-radius-max bg-[var(--grayscale-l20)]\"\n />\n )}\n </div>\n </div>\n )\n}\n\nexport interface SearchResultsListProps {\n renderItem?: (product: Product, index: number) => React.ReactNode\n height?: number\n itemHeight?: number\n initialStateComponent?: React.JSX.Element\n showScrollbar?: boolean\n overscanCount?: number\n}\n\nfunction SearchResultsList({\n height = window.innerHeight,\n renderItem,\n itemHeight = ESTIMATED_PRODUCT_LINK_HEIGHT,\n initialStateComponent,\n showScrollbar,\n overscanCount = 5,\n}: SearchResultsListProps) {\n const {query, products, loading, fetchMore, hasNextPage, isTyping} =\n useSearchContext()\n\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n const shouldShowStartingState = query.trim().length === 0\n const shouldShowLoading =\n (!products || products.length === 0) && (loading || isTyping)\n const shouldShowEmptyState = (!products || products.length === 0) && !loading\n\n if (shouldShowStartingState) {\n return (\n initialStateComponent || (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n Start typing to search for products\n </div>\n )\n )\n }\n\n if (shouldShowLoading) {\n return (\n <div className=\"flex flex-col px-4 py-4\">\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n </div>\n )\n }\n\n if (shouldShowEmptyState) {\n return (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n {`No products found for \"${query}\"`}\n </div>\n )\n }\n\n return (\n <List\n items={products || []}\n height={height}\n renderItem={_renderItem}\n itemSizeForRow={() => itemHeight}\n fetchMore={hasNextPage ? fetchMore : undefined}\n showScrollbar={showScrollbar}\n overscanCount={overscanCount}\n />\n )\n}\n\ninterface SearchProviderPropsWithoutChildren\n extends Omit<SearchProviderProps, 'children'> {}\nexport interface SearchResultsProps\n extends SearchProviderPropsWithoutChildren,\n SearchInputProps,\n SearchResultsListProps {\n showSearchInput?: boolean\n}\n\nfunction Search({\n initialQuery,\n placeholder,\n inputProps,\n height,\n className,\n renderItem,\n itemHeight,\n}: SearchResultsProps) {\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n return (\n <SearchProvider initialQuery={initialQuery}>\n <div className={cn('flex flex-col ', className)}>\n <div className=\"fixed top-0 left-0 right-0 p-4 w-full z-20 bg-background\">\n <SearchInput placeholder={placeholder} inputProps={inputProps} />\n </div>\n <div className=\"h-14\" />\n <SearchResultsList\n height={height}\n renderItem={_renderItem}\n itemHeight={itemHeight}\n showScrollbar\n />\n </div>\n </SearchProvider>\n )\n}\n\nexport {SearchProvider, SearchInput, SearchResultsList, Search}\n"],"names":["ESTIMATED_PRODUCT_LINK_HEIGHT","SearchContext","createContext","useSearchContext","context","useContext","SearchProvider","initialQuery","children","query","setQueryState","useState","products","loading","error","fetchMore","hasNextPage","isTyping","useProductSearch","setQuery","useCallback","newQuery","contextValue","SearchInput","placeholder","className","inputProps","handleQueryChange","event","jsxs","jsx","SearchIcon","cn","Input","IconButton","X","SearchResultsList","height","renderItem","itemHeight","initialStateComponent","showScrollbar","overscanCount","_renderItem","product","index","ProductLink","shouldShowStartingState","shouldShowLoading","shouldShowEmptyState","ProductLinkSkeleton","List","Search"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as t } from "react/jsx-runtime";
|
|
2
2
|
import { cn as n } from "../../lib/utils.js";
|
|
3
|
-
function
|
|
3
|
+
function l({ className: e, type: i, ...r }) {
|
|
4
4
|
return /* @__PURE__ */ t(
|
|
5
5
|
"input",
|
|
6
6
|
{
|
|
@@ -8,7 +8,7 @@ function a({ className: e, type: i, ...r }) {
|
|
|
8
8
|
"data-slot": "input",
|
|
9
9
|
className: n(
|
|
10
10
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
11
|
-
"focus-
|
|
11
|
+
"focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:outline-none",
|
|
12
12
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
13
13
|
e
|
|
14
14
|
),
|
|
@@ -17,6 +17,6 @@ function a({ className: e, type: i, ...r }) {
|
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
export {
|
|
20
|
-
|
|
20
|
+
l as Input
|
|
21
21
|
};
|
|
22
22
|
//# sourceMappingURL=input.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","sources":["../../../src/components/ui/input.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {cn} from '../../lib/utils'\n\nfunction Input({className, type, ...props}: React.ComponentProps<'input'>) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n 'focus-
|
|
1
|
+
{"version":3,"file":"input.js","sources":["../../../src/components/ui/input.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {cn} from '../../lib/utils'\n\nfunction Input({className, type, ...props}: React.ComponentProps<'input'>) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n 'focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:outline-none',\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n className\n )}\n {...props}\n />\n )\n}\n\nexport {Input}\n"],"names":["Input","className","type","props","jsx","cn"],"mappings":";;AAIA,SAASA,EAAM,EAAC,WAAAC,GAAW,MAAAC,GAAM,GAAGC,KAAuC;AAEvE,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAAF;AAAA,MACA,aAAU;AAAA,MACV,WAAWG;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACAJ;AAAA,MACF;AAAA,MACC,GAAGE;AAAA,IAAA;AAAA,EACN;AAEJ;"}
|