@shopify/shop-minis-react 0.0.25 → 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/_virtual/index10.js +2 -2
- package/dist/_virtual/index4.js +2 -3
- package/dist/_virtual/index4.js.map +1 -1
- package/dist/_virtual/index7.js +3 -2
- package/dist/_virtual/index7.js.map +1 -1
- package/dist/_virtual/index9.js +2 -2
- package/dist/components/atoms/product-variant-price.js +61 -0
- package/dist/components/atoms/product-variant-price.js.map +1 -0
- package/dist/components/commerce/product-card.js +120 -153
- package/dist/components/commerce/product-card.js.map +1 -1
- 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 +73 -77
- 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/content/content-monitor.js +17 -0
- package/dist/components/content/content-monitor.js.map +1 -0
- package/dist/components/content/content-wrapper.js +17 -0
- package/dist/components/content/content-wrapper.js.map +1 -0
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/hooks/content/useContent.js +24 -0
- package/dist/hooks/content/useContent.js.map +1 -0
- package/dist/hooks/content/useCreateImageContent.js +21 -18
- package/dist/hooks/content/useCreateImageContent.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 +230 -221
- package/dist/index.js.map +1 -1
- package/dist/mocks.js +21 -6
- package/dist/mocks.js.map +1 -1
- package/dist/shop-minis-platform/src/types/content.js +5 -0
- package/dist/shop-minis-platform/src/types/content.js.map +1 -0
- package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +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/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
- package/dist/shop-minis-react.css +1 -1
- package/package.json +5 -4
- package/src/components/atoms/product-variant-price.tsx +74 -0
- package/src/components/commerce/product-card.tsx +7 -56
- package/src/components/commerce/product-link-skeleton.tsx +30 -0
- package/src/components/commerce/product-link.tsx +8 -7
- package/src/components/commerce/search.tsx +264 -0
- package/src/components/content/content-monitor.tsx +23 -0
- package/src/components/content/content-wrapper.tsx +56 -0
- package/src/components/index.ts +3 -0
- package/src/components/ui/input.tsx +1 -1
- package/src/hooks/content/useContent.ts +50 -0
- package/src/hooks/content/useCreateImageContent.ts +20 -5
- package/src/hooks/product/useProductSearch.ts +10 -1
- package/src/mocks.ts +15 -0
- package/src/stories/ProductVariantPrice.stories.tsx +73 -0
- package/src/stories/Toaster.stories.tsx +2 -2
- 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,47 +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:
|
|
207
|
-
price:
|
|
208
|
-
compareAtPrice:
|
|
209
|
-
isFavorited:
|
|
204
|
+
title: u,
|
|
205
|
+
featuredImage: P,
|
|
206
|
+
reviewAnalytics: C,
|
|
207
|
+
price: m,
|
|
208
|
+
compareAtPrice: p,
|
|
209
|
+
isFavorited: T,
|
|
210
210
|
selectedVariant: f,
|
|
211
211
|
defaultVariantId: h,
|
|
212
212
|
shop: g
|
|
213
|
-
} = o, [x,
|
|
214
|
-
|
|
215
|
-
const v = l?.amount ? b(l?.amount, l?.currencyCode) : void 0, I = p?.url, T = p?.altText || s, k = d?.amount ? b(d?.amount, d?.currencyCode) : void 0;
|
|
216
|
-
d?.currencyCode;
|
|
217
|
-
const L = k && k !== v, R = w.useCallback(() => {
|
|
218
|
-
e({
|
|
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({
|
|
219
215
|
productId: a
|
|
220
216
|
});
|
|
221
|
-
}, [
|
|
222
|
-
const
|
|
223
|
-
|
|
217
|
+
}, [r, a]), V = w.useCallback(async () => {
|
|
218
|
+
const s = x;
|
|
219
|
+
N(!s);
|
|
224
220
|
try {
|
|
225
|
-
|
|
221
|
+
s ? await l({
|
|
226
222
|
productId: a,
|
|
227
223
|
shopId: g.id,
|
|
228
224
|
productVariantId: f?.id || h
|
|
229
|
-
}) : await
|
|
225
|
+
}) : await i({
|
|
230
226
|
productId: a,
|
|
231
227
|
shopId: g.id,
|
|
232
228
|
productVariantId: f?.id || h
|
|
233
229
|
});
|
|
234
230
|
} catch {
|
|
235
|
-
|
|
231
|
+
N(s);
|
|
236
232
|
}
|
|
237
233
|
}, [
|
|
238
234
|
x,
|
|
@@ -240,54 +236,54 @@ function ut({ product: o }) {
|
|
|
240
236
|
g.id,
|
|
241
237
|
f?.id,
|
|
242
238
|
h,
|
|
243
|
-
|
|
244
|
-
|
|
239
|
+
i,
|
|
240
|
+
l
|
|
245
241
|
]);
|
|
246
242
|
return /* @__PURE__ */ c(
|
|
247
|
-
|
|
243
|
+
H,
|
|
248
244
|
{
|
|
249
245
|
layout: "horizontal",
|
|
250
|
-
discount:
|
|
251
|
-
onPress:
|
|
246
|
+
discount: b ? "small" : "none",
|
|
247
|
+
onPress: S,
|
|
252
248
|
children: [
|
|
253
|
-
/* @__PURE__ */ t(
|
|
249
|
+
/* @__PURE__ */ t(J, { layout: "horizontal", children: L ? /* @__PURE__ */ t(
|
|
254
250
|
"img",
|
|
255
251
|
{
|
|
256
|
-
src:
|
|
257
|
-
alt:
|
|
252
|
+
src: L,
|
|
253
|
+
alt: R,
|
|
258
254
|
className: "h-full w-full object-cover"
|
|
259
255
|
}
|
|
260
256
|
) : /* @__PURE__ */ t("div", { className: "h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs", children: "No Image" }) }),
|
|
261
|
-
/* @__PURE__ */ c(
|
|
262
|
-
/* @__PURE__ */ t(
|
|
263
|
-
|
|
264
|
-
Array.from({ length: 5 }, (
|
|
265
|
-
|
|
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
|
+
_,
|
|
266
262
|
{
|
|
267
|
-
fill:
|
|
263
|
+
fill: d < Math.floor(y) ? "currentColor" : "none",
|
|
268
264
|
className: n(
|
|
269
265
|
"h-3 w-3",
|
|
270
|
-
|
|
266
|
+
d < Math.floor(y) ? "text-primary" : "text-gray-300"
|
|
271
267
|
)
|
|
272
268
|
},
|
|
273
|
-
|
|
269
|
+
d
|
|
274
270
|
)),
|
|
275
271
|
/* @__PURE__ */ c("span", { className: "text-xs text-gray-600 ml-1", children: [
|
|
276
272
|
"(",
|
|
277
|
-
|
|
273
|
+
I,
|
|
278
274
|
")"
|
|
279
275
|
] })
|
|
280
276
|
] }) }),
|
|
281
|
-
/* @__PURE__ */ t(
|
|
282
|
-
/* @__PURE__ */ t(
|
|
283
|
-
/* @__PURE__ */ t(
|
|
284
|
-
] }) : /* @__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 }) })
|
|
285
281
|
] }),
|
|
286
|
-
/* @__PURE__ */ t(
|
|
287
|
-
|
|
282
|
+
e ? null : /* @__PURE__ */ t(
|
|
283
|
+
tt,
|
|
288
284
|
{
|
|
289
285
|
filled: x,
|
|
290
|
-
onPress:
|
|
286
|
+
onPress: V
|
|
291
287
|
}
|
|
292
288
|
)
|
|
293
289
|
]
|
|
@@ -295,6 +291,6 @@ function ut({ product: o }) {
|
|
|
295
291
|
);
|
|
296
292
|
}
|
|
297
293
|
export {
|
|
298
|
-
|
|
294
|
+
mt as ProductLink
|
|
299
295
|
};
|
|
300
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 currencyCode = price?.currencyCode\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 compareAtPriceCurrencyCode = compareAtPrice?.currencyCode\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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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { Touchable as t } from "../atoms/touchable.js";
|
|
3
|
+
function i({
|
|
4
|
+
// publicId,
|
|
5
|
+
children: o
|
|
6
|
+
}) {
|
|
7
|
+
return /* @__PURE__ */ r(
|
|
8
|
+
t,
|
|
9
|
+
{
|
|
10
|
+
children: o
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
i as ContentMonitor
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=content-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-monitor.js","sources":["../../../src/components/content/content-monitor.tsx"],"sourcesContent":["// import {useShopActions} from '../../internal/useShopActions'\nimport {Touchable} from '../atoms/touchable'\n\nexport function ContentMonitor({\n // publicId,\n children,\n}: {\n publicId: string\n children: React.ReactNode\n}) {\n // const {showFeedbackSheet} = useShopActions()\n\n return (\n <Touchable\n // TODO: Add long press support to Touchable\n // onLongPress={() => {\n // showFeedbackSheet({publicId})\n // }}\n >\n {children}\n </Touchable>\n )\n}\n"],"names":["ContentMonitor","children","jsx","Touchable"],"mappings":";;AAGO,SAASA,EAAe;AAAA;AAAA,EAE7B,UAAAC;AACF,GAGG;AAIC,SAAA,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MAME,UAAAF;AAAA,IAAA;AAAA,EACH;AAEJ;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as c } from "react/jsx-runtime";
|
|
2
|
+
import { useContent as p } from "../../hooks/content/useContent.js";
|
|
3
|
+
import { ContentMonitor as m } from "./content-monitor.js";
|
|
4
|
+
function d({
|
|
5
|
+
publicId: r,
|
|
6
|
+
externalId: e,
|
|
7
|
+
children: o
|
|
8
|
+
}) {
|
|
9
|
+
const { content: i, loading: t } = p({
|
|
10
|
+
identifiers: [{ publicId: r, externalId: e }]
|
|
11
|
+
}), n = i?.[0];
|
|
12
|
+
return t || !n ? o({ loading: t }) : /* @__PURE__ */ c(m, { publicId: n.publicId, children: o({ content: n, loading: t }) });
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
d as ContentWrapper
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=content-wrapper.js.map
|