@shopify/shop-minis-react 0.0.36 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index2.js +4 -4
- package/dist/_virtual/index3.js +4 -4
- package/dist/components/atoms/image.js +5 -5
- package/dist/components/atoms/image.js.map +1 -1
- package/dist/components/commerce/product-card.js +20 -20
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/hooks/events/useOnAppStateChange.js +14 -0
- package/dist/hooks/events/useOnAppStateChange.js.map +1 -0
- package/dist/hooks/events/useOnMiniBlur.js +14 -0
- package/dist/hooks/events/useOnMiniBlur.js.map +1 -0
- package/dist/hooks/events/useOnMiniClose.js +14 -0
- package/dist/hooks/events/useOnMiniClose.js.map +1 -0
- package/dist/hooks/events/useOnMiniFocus.js +14 -0
- package/dist/hooks/events/useOnMiniFocus.js.map +1 -0
- package/dist/hooks/navigation/useShopNavigation.js +12 -10
- package/dist/hooks/navigation/useShopNavigation.js.map +1 -1
- package/dist/hooks/user/useGenerateUserToken.js +28 -6
- package/dist/hooks/user/useGenerateUserToken.js.map +1 -1
- package/dist/index.js +114 -106
- package/dist/index.js.map +1 -1
- package/dist/mocks.js +11 -10
- package/dist/mocks.js.map +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
- package/dist/utils/colors.js +1 -1
- package/package.json +2 -2
- package/src/components/atoms/image.tsx +1 -1
- package/src/components/commerce/product-card.tsx +3 -3
- package/src/hooks/events/useOnAppStateChange.ts +20 -0
- package/src/hooks/events/useOnMiniBlur.ts +16 -0
- package/src/hooks/events/useOnMiniClose.ts +16 -0
- package/src/hooks/events/useOnMiniFocus.ts +16 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/navigation/useShopNavigation.ts +6 -0
- package/src/hooks/user/useGenerateUserToken.test.ts +340 -0
- package/src/hooks/user/useGenerateUserToken.ts +72 -2
- package/src/mocks.ts +1 -0
- package/src/stories/ImageContentWrapper.stories.tsx +1 -4
package/dist/_virtual/index2.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
|
|
2
|
-
import { __require as o } from "../shop-minis-react/node_modules/.pnpm
|
|
3
|
-
var t = o();
|
|
4
|
-
const
|
|
2
|
+
import { __require as o } from "../shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js";
|
|
3
|
+
var t = /* @__PURE__ */ o();
|
|
4
|
+
const l = /* @__PURE__ */ r(t);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
l as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=index2.js.map
|
package/dist/_virtual/index3.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
|
|
2
|
-
import { __require as o } from "../shop-minis-react/node_modules/.pnpm
|
|
3
|
-
var t =
|
|
4
|
-
const
|
|
2
|
+
import { __require as o } from "../shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js";
|
|
3
|
+
var t = o();
|
|
4
|
+
const a = /* @__PURE__ */ r(t);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
a as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=index3.js.map
|
|
@@ -9,8 +9,8 @@ const U = f(function(m) {
|
|
|
9
9
|
onLoad: a,
|
|
10
10
|
className: i,
|
|
11
11
|
style: n,
|
|
12
|
-
aspectRatio:
|
|
13
|
-
...
|
|
12
|
+
aspectRatio: l = "auto",
|
|
13
|
+
...d
|
|
14
14
|
} = m, [u, g] = L(!1), t = r(
|
|
15
15
|
() => I(o ?? void 0),
|
|
16
16
|
[o]
|
|
@@ -26,7 +26,7 @@ const U = f(function(m) {
|
|
|
26
26
|
className: c("relative w-full ", i),
|
|
27
27
|
style: {
|
|
28
28
|
...n,
|
|
29
|
-
aspectRatio:
|
|
29
|
+
aspectRatio: l,
|
|
30
30
|
backgroundImage: t ? `url(${t})` : void 0,
|
|
31
31
|
backgroundSize: "cover",
|
|
32
32
|
backgroundPosition: "center"
|
|
@@ -35,12 +35,12 @@ const U = f(function(m) {
|
|
|
35
35
|
"img",
|
|
36
36
|
{
|
|
37
37
|
className: c(
|
|
38
|
-
"absolute inset-0 opacity-0 object-cover",
|
|
38
|
+
"absolute inset-0 opacity-0 size-full object-cover",
|
|
39
39
|
u && "opacity-100"
|
|
40
40
|
),
|
|
41
41
|
src: b,
|
|
42
42
|
onLoad: h,
|
|
43
|
-
...
|
|
43
|
+
...d
|
|
44
44
|
}
|
|
45
45
|
)
|
|
46
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","sources":["../../../src/components/atoms/image.tsx"],"sourcesContent":["/* eslint-disable jsx-a11y/alt-text */\n/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */\nimport {ImgHTMLAttributes, useCallback, useMemo, memo, useState} from 'react'\n\nimport {cn} from '../../lib/utils'\nimport {getThumbhashDataURL, getResizedImageUrl} from '../../utils'\n\ntype ImageProps = ImgHTMLAttributes<HTMLImageElement> & {\n src?: string\n thumbhash?: string | null\n aspectRatio?: number | string\n}\n\nexport const Image = memo(function Image(props: ImageProps) {\n const {\n src,\n thumbhash,\n onLoad,\n className,\n style,\n aspectRatio = 'auto',\n ...restProps\n } = props\n\n const [isLoaded, setIsLoaded] = useState(false)\n\n const thumbhashDataURL = useMemo(\n () => getThumbhashDataURL(thumbhash ?? undefined),\n [thumbhash]\n )\n\n const handleLoad = useCallback(\n (event: React.SyntheticEvent<HTMLImageElement, Event>) => {\n setIsLoaded(true)\n onLoad?.(event)\n },\n [onLoad]\n )\n\n const resizedImageSrc = useMemo(() => getResizedImageUrl(src), [src])\n\n return (\n <div\n className={cn('relative w-full ', className)}\n style={{\n ...style,\n aspectRatio,\n backgroundImage: thumbhashDataURL\n ? `url(${thumbhashDataURL})`\n : undefined,\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n }}\n >\n <img\n className={cn(\n 'absolute inset-0 opacity-0 object-cover',\n isLoaded && 'opacity-100'\n )}\n src={resizedImageSrc}\n onLoad={handleLoad}\n {...restProps}\n />\n </div>\n )\n})\n"],"names":["Image","memo","props","src","thumbhash","onLoad","className","style","aspectRatio","restProps","isLoaded","setIsLoaded","useState","thumbhashDataURL","useMemo","getThumbhashDataURL","handleLoad","useCallback","event","resizedImageSrc","getResizedImageUrl","jsx","cn"],"mappings":";;;;AAaO,MAAMA,IAAQC,EAAK,SAAeC,GAAmB;AACpD,QAAA;AAAA,IACJ,KAAAC;AAAA,IACA,WAAAC;AAAA,IACA,QAAAC;AAAA,IACA,WAAAC;AAAA,IACA,OAAAC;AAAA,IACA,aAAAC,IAAc;AAAA,IACd,GAAGC;AAAA,EAAA,IACDP,GAEE,CAACQ,GAAUC,CAAW,IAAIC,EAAS,EAAK,GAExCC,IAAmBC;AAAA,IACvB,MAAMC,EAAoBX,KAAa,MAAS;AAAA,IAChD,CAACA,CAAS;AAAA,EACZ,GAEMY,IAAaC;AAAA,IACjB,CAACC,MAAyD;AACxD,MAAAP,EAAY,EAAI,GAChBN,IAASa,CAAK;AAAA,IAChB;AAAA,IACA,CAACb,CAAM;AAAA,EACT,GAEMc,IAAkBL,EAAQ,MAAMM,EAAmBjB,CAAG,GAAG,CAACA,CAAG,CAAC;AAGlE,SAAA,gBAAAkB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,EAAG,oBAAoBhB,CAAS;AAAA,MAC3C,OAAO;AAAA,QACL,GAAGC;AAAA,QACH,aAAAC;AAAA,QACA,iBAAiBK,IACb,OAAOA,CAAgB,MACvB;AAAA,QACJ,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,MACtB;AAAA,MAEA,UAAA,gBAAAQ;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWC;AAAA,YACT;AAAA,YACAZ,KAAY;AAAA,UACd;AAAA,UACA,KAAKS;AAAA,UACL,QAAQH;AAAA,UACP,GAAGP;AAAA,QAAA;AAAA,MAAA;AAAA,IACN;AAAA,EACF;AAEJ,CAAC;"}
|
|
1
|
+
{"version":3,"file":"image.js","sources":["../../../src/components/atoms/image.tsx"],"sourcesContent":["/* eslint-disable jsx-a11y/alt-text */\n/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */\nimport {ImgHTMLAttributes, useCallback, useMemo, memo, useState} from 'react'\n\nimport {cn} from '../../lib/utils'\nimport {getThumbhashDataURL, getResizedImageUrl} from '../../utils'\n\ntype ImageProps = ImgHTMLAttributes<HTMLImageElement> & {\n src?: string\n thumbhash?: string | null\n aspectRatio?: number | string\n}\n\nexport const Image = memo(function Image(props: ImageProps) {\n const {\n src,\n thumbhash,\n onLoad,\n className,\n style,\n aspectRatio = 'auto',\n ...restProps\n } = props\n\n const [isLoaded, setIsLoaded] = useState(false)\n\n const thumbhashDataURL = useMemo(\n () => getThumbhashDataURL(thumbhash ?? undefined),\n [thumbhash]\n )\n\n const handleLoad = useCallback(\n (event: React.SyntheticEvent<HTMLImageElement, Event>) => {\n setIsLoaded(true)\n onLoad?.(event)\n },\n [onLoad]\n )\n\n const resizedImageSrc = useMemo(() => getResizedImageUrl(src), [src])\n\n return (\n <div\n className={cn('relative w-full ', className)}\n style={{\n ...style,\n aspectRatio,\n backgroundImage: thumbhashDataURL\n ? `url(${thumbhashDataURL})`\n : undefined,\n backgroundSize: 'cover',\n backgroundPosition: 'center',\n }}\n >\n <img\n className={cn(\n 'absolute inset-0 opacity-0 size-full object-cover',\n isLoaded && 'opacity-100'\n )}\n src={resizedImageSrc}\n onLoad={handleLoad}\n {...restProps}\n />\n </div>\n )\n})\n"],"names":["Image","memo","props","src","thumbhash","onLoad","className","style","aspectRatio","restProps","isLoaded","setIsLoaded","useState","thumbhashDataURL","useMemo","getThumbhashDataURL","handleLoad","useCallback","event","resizedImageSrc","getResizedImageUrl","jsx","cn"],"mappings":";;;;AAaO,MAAMA,IAAQC,EAAK,SAAeC,GAAmB;AACpD,QAAA;AAAA,IACJ,KAAAC;AAAA,IACA,WAAAC;AAAA,IACA,QAAAC;AAAA,IACA,WAAAC;AAAA,IACA,OAAAC;AAAA,IACA,aAAAC,IAAc;AAAA,IACd,GAAGC;AAAA,EAAA,IACDP,GAEE,CAACQ,GAAUC,CAAW,IAAIC,EAAS,EAAK,GAExCC,IAAmBC;AAAA,IACvB,MAAMC,EAAoBX,KAAa,MAAS;AAAA,IAChD,CAACA,CAAS;AAAA,EACZ,GAEMY,IAAaC;AAAA,IACjB,CAACC,MAAyD;AACxD,MAAAP,EAAY,EAAI,GAChBN,IAASa,CAAK;AAAA,IAChB;AAAA,IACA,CAACb,CAAM;AAAA,EACT,GAEMc,IAAkBL,EAAQ,MAAMM,EAAmBjB,CAAG,GAAG,CAACA,CAAG,CAAC;AAGlE,SAAA,gBAAAkB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,EAAG,oBAAoBhB,CAAS;AAAA,MAC3C,OAAO;AAAA,QACL,GAAGC;AAAA,QACH,aAAAC;AAAA,QACA,iBAAiBK,IACb,OAAOA,CAAgB,MACvB;AAAA,QACJ,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,MACtB;AAAA,MAEA,UAAA,gBAAAQ;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWC;AAAA,YACT;AAAA,YACAZ,KAAY;AAAA,UACd;AAAA,UACA,KAAKS;AAAA,UACL,QAAQH;AAAA,UACP,GAAGP;AAAA,QAAA;AAAA,MAAA;AAAA,IACN;AAAA,EACF;AAEJ,CAAC;"}
|
|
@@ -3,13 +3,13 @@ import * as N from "react";
|
|
|
3
3
|
import { useState as F, useCallback as C, useMemo as A, useContext as k } from "react";
|
|
4
4
|
import { useShopNavigation as V } from "../../hooks/navigation/useShopNavigation.js";
|
|
5
5
|
import { useSavedProductsActions as j } from "../../hooks/user/useSavedProductsActions.js";
|
|
6
|
-
import { formatMoney as
|
|
6
|
+
import { formatMoney as z } from "../../lib/formatMoney.js";
|
|
7
7
|
import { cn as s } from "../../lib/utils.js";
|
|
8
|
-
import { FavoriteButton as
|
|
9
|
-
import { Image as
|
|
10
|
-
import { ProductVariantPrice as
|
|
11
|
-
import { Touchable as
|
|
12
|
-
import { Badge as
|
|
8
|
+
import { FavoriteButton as B } from "../atoms/favorite-button.js";
|
|
9
|
+
import { Image as T } from "../atoms/image.js";
|
|
10
|
+
import { ProductVariantPrice as E } from "../atoms/product-variant-price.js";
|
|
11
|
+
import { Touchable as O } from "../atoms/touchable.js";
|
|
12
|
+
import { Badge as S } from "../ui/badge.js";
|
|
13
13
|
const I = N.createContext(void 0);
|
|
14
14
|
function u() {
|
|
15
15
|
const t = k(I);
|
|
@@ -34,7 +34,7 @@ function L({
|
|
|
34
34
|
}
|
|
35
35
|
);
|
|
36
36
|
return r && a ? /* @__PURE__ */ o(
|
|
37
|
-
|
|
37
|
+
O,
|
|
38
38
|
{
|
|
39
39
|
onClick: a,
|
|
40
40
|
whileTap: { opacity: 0.7 },
|
|
@@ -69,14 +69,14 @@ function M({
|
|
|
69
69
|
function R({ className: t, ...e }) {
|
|
70
70
|
const { product: r, selectedProductVariant: a } = u(), n = a?.image || r.featuredImage, i = n?.url, d = n?.altText || r.title, c = r.featuredImage?.thumbhash, p = C(
|
|
71
71
|
(l) => c ? /* @__PURE__ */ o(
|
|
72
|
-
|
|
72
|
+
T,
|
|
73
73
|
{
|
|
74
74
|
"data-slot": "product-card-image",
|
|
75
75
|
src: l,
|
|
76
76
|
alt: d,
|
|
77
77
|
aspectRatio: 1,
|
|
78
78
|
thumbhash: c,
|
|
79
|
-
className: s("
|
|
79
|
+
className: s("size-full object-cover", t),
|
|
80
80
|
...e
|
|
81
81
|
}
|
|
82
82
|
) : /* @__PURE__ */ o(
|
|
@@ -85,15 +85,15 @@ function R({ className: t, ...e }) {
|
|
|
85
85
|
"data-slot": "product-card-image",
|
|
86
86
|
src: l,
|
|
87
87
|
alt: d,
|
|
88
|
-
className: s("
|
|
88
|
+
className: s("size-full", t),
|
|
89
89
|
...e
|
|
90
90
|
}
|
|
91
91
|
),
|
|
92
92
|
[d, t, e, c]
|
|
93
93
|
);
|
|
94
|
-
return /* @__PURE__ */ o("div", { className: "bg-gray-100 flex items-center justify-center
|
|
94
|
+
return /* @__PURE__ */ o("div", { className: "bg-gray-100 flex items-center justify-center size-full", children: i ? p(i) : /* @__PURE__ */ o("div", { className: "text-gray-400 text-sm w-full text-center", children: "No Image" }) });
|
|
95
95
|
}
|
|
96
|
-
function
|
|
96
|
+
function b({
|
|
97
97
|
className: t,
|
|
98
98
|
position: e = "bottom-left",
|
|
99
99
|
variant: r,
|
|
@@ -109,7 +109,7 @@ function w({
|
|
|
109
109
|
e === "top-left" ? "top-3 left-3" : "bottom-2 left-2"
|
|
110
110
|
),
|
|
111
111
|
children: /* @__PURE__ */ o(
|
|
112
|
-
|
|
112
|
+
S,
|
|
113
113
|
{
|
|
114
114
|
variant: r ?? d ?? "none",
|
|
115
115
|
className: s(
|
|
@@ -129,7 +129,7 @@ function q({
|
|
|
129
129
|
...e
|
|
130
130
|
}) {
|
|
131
131
|
const { isFavorited: r, onFavoriteToggle: a } = u();
|
|
132
|
-
return /* @__PURE__ */ o("div", { className: s("absolute bottom-3 right-3 z-10", t), ...e, children: /* @__PURE__ */ o(
|
|
132
|
+
return /* @__PURE__ */ o("div", { className: s("absolute bottom-3 right-3 z-10", t), ...e, children: /* @__PURE__ */ o(B, { onClick: a, filled: r }) });
|
|
133
133
|
}
|
|
134
134
|
function D({ className: t, ...e }) {
|
|
135
135
|
const { variant: r } = u();
|
|
@@ -165,7 +165,7 @@ function G({
|
|
|
165
165
|
function H({ className: t }) {
|
|
166
166
|
const { product: e, selectedProductVariant: r } = u(), a = r?.price || e?.price, n = r?.compareAtPrice || e?.compareAtPrice;
|
|
167
167
|
return /* @__PURE__ */ o(
|
|
168
|
-
|
|
168
|
+
E,
|
|
169
169
|
{
|
|
170
170
|
amount: a?.amount || "",
|
|
171
171
|
currencyCode: a?.currencyCode || "",
|
|
@@ -179,7 +179,7 @@ function J() {
|
|
|
179
179
|
const { product: t, selectedProductVariant: e, variant: r } = u();
|
|
180
180
|
if (r !== "priceOverlay") return null;
|
|
181
181
|
const a = e?.price || t.price, n = a?.currencyCode, i = a?.amount;
|
|
182
|
-
return !n || !i ? null : /* @__PURE__ */ o(
|
|
182
|
+
return !n || !i ? null : /* @__PURE__ */ o(b, { position: "top-left", children: z(i, n) });
|
|
183
183
|
}
|
|
184
184
|
function ot({
|
|
185
185
|
product: t,
|
|
@@ -221,7 +221,7 @@ function ot({
|
|
|
221
221
|
h,
|
|
222
222
|
g,
|
|
223
223
|
c
|
|
224
|
-
]),
|
|
224
|
+
]), w = A(
|
|
225
225
|
() => ({
|
|
226
226
|
// Core data
|
|
227
227
|
product: t,
|
|
@@ -249,11 +249,11 @@ function ot({
|
|
|
249
249
|
x
|
|
250
250
|
]
|
|
251
251
|
);
|
|
252
|
-
return /* @__PURE__ */ o(I.Provider, { value:
|
|
252
|
+
return /* @__PURE__ */ o(I.Provider, { value: w, children: p ?? /* @__PURE__ */ v(L, { children: [
|
|
253
253
|
/* @__PURE__ */ v(M, { children: [
|
|
254
254
|
/* @__PURE__ */ o(R, {}),
|
|
255
255
|
r === "priceOverlay" && /* @__PURE__ */ o(J, {}),
|
|
256
|
-
/* @__PURE__ */ o(
|
|
256
|
+
/* @__PURE__ */ o(b, {}),
|
|
257
257
|
/* @__PURE__ */ o(q, {})
|
|
258
258
|
] }),
|
|
259
259
|
r === "default" && /* @__PURE__ */ v(D, { children: [
|
|
@@ -264,7 +264,7 @@ function ot({
|
|
|
264
264
|
}
|
|
265
265
|
export {
|
|
266
266
|
ot as ProductCard,
|
|
267
|
-
|
|
267
|
+
b as ProductCardBadge,
|
|
268
268
|
L as ProductCardContainer,
|
|
269
269
|
q as ProductCardFavoriteButton,
|
|
270
270
|
R as ProductCardImage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\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 {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n\n // State\n isFavorited: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative w-full overflow-hidden rounded-xl border-0',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n // Ensure the product image is stretched to the full size of the container (can't use width/height: 100% because of flex)\n 'flex justify-stretch items-stretch',\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'w-full aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n className={cn('w-full h-full object-cover', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('w-full h-full', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center w-full h-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, onFavoriteToggle} = useProductCardContext()\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n\n // State\n isFavorited: isFavoritedLocal,\n\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":";;;;;;;;;;;;AAmCA,MAAMA,IAAqBC,EAAM,cAE/B,MAAS;AAEX,SAASC,IAAwB;AACzB,QAAAC,IAAUC,EAAWJ,CAAkB;AAC7C,MAAI,CAACG;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAEK,SAAAA;AACT;AAGA,SAASE,EAAqB;AAAA,EAC5B,WAAAC;AAAA,EACA,GAAGC;AACL,GAAgC;AAC9B,QAAM,EAAC,WAAAC,GAAW,SAAAC,EAAO,IAAIP,EAAsB,GAE7CQ,IACJ,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAN;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,IAAA;AAAA,EACN;AAGF,SAAIC,KAAaC,IAEb,gBAAAE;AAAA,IAACE;AAAA,IAAA;AAAA,MACC,SAAAJ;AAAA,MACA,UAAU,EAAC,SAAS,IAAG;AAAA,MACvB,YAAY;AAAA,QACV,SAAS,EAAC,MAAM,SAAS,UAAU,MAAM,MAAM,YAAW;AAAA,MAC5D;AAAA,MAEC,UAAAC;AAAA,IAAA;AAAA,EACH,IAIGA;AACT;AAEA,SAASI,EAA0B;AAAA,EACjC,WAAAR;AAAA,EACA,GAAGC;AACL,GAAgC;AACxB,QAAA,EAAC,SAAAQ,EAAO,IAAIb,EAAsB;AAGtC,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC;AAAA;AAAA,QAET;AAAA,QACA;AAAA,QACA;AAAA,QACAG,MAAY,YAAY,kBAAkB;AAAA,QAC1CT;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASS,EAAiB,EAAC,WAAAV,GAAW,GAAGC,KAAqC;AAC5E,QAAM,EAAC,SAAAU,GAAS,wBAAAC,EAAsB,IAAIhB,EAAsB,GAG1DiB,IAAeD,GAAwB,SAASD,EAAQ,eACxDG,IAAMD,GAAc,KACpBE,IAAMF,GAAc,WAAWF,EAAQ,OACvCK,IAAYL,EAAQ,eAAe,WAEnCM,IAAqBC;AAAA,IACzB,CAACJ,MACsBE,IACnB,gBAAAX;AAAA,MAACc;AAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,KAAKL;AAAAA,QACL,KAAAC;AAAA,QACA,aAAa;AAAA,QACb,WAAAC;AAAA,QACA,WAAWV,EAAG,8BAA8BN,CAAS;AAAA,QACpD,GAAGC;AAAA,MAAA;AAAA,IAAA,IAGN,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,KAAKS;AAAAA,QACL,KAAAC;AAAA,QACA,WAAWT,EAAG,iBAAiBN,CAAS;AAAA,QACvC,GAAGC;AAAA,MAAA;AAAA,IACN;AAAA,IAKJ,CAACc,GAAKf,GAAWC,GAAOe,CAAS;AAAA,EACnC;AAEA,SACG,gBAAAX,EAAA,OAAA,EAAI,WAAU,8DACZ,UACCS,IAAAG,EAAmBH,CAAG,IAErB,gBAAAT,EAAA,OAAA,EAAI,WAAU,4CAA2C,qBAAQ,CAAA,GAEtE;AAEJ;AAEA,SAASe,EAAiB;AAAA,EACxB,WAAApB;AAAA,EACA,UAAAqB,IAAW;AAAA,EACX,SAAAZ;AAAA,EACA,UAAAa;AAAA,EACA,GAAGrB;AACL,GAEG;AACD,QAAM,EAAC,WAAAsB,GAAW,cAAAC,EAAY,IAAI5B,EAAsB,GAElDQ,IAAUkB,KAAYC;AAExB,SAACnB,IAGH,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAe,MAAa,aAAa,iBAAiB;AAAA,MAC7C;AAAA,MAEA,UAAA,gBAAAhB;AAAA,QAACoB;AAAA,QAAA;AAAA,UACC,SAAShB,KAAWe,KAAgB;AAAA,UACpC,WAAWlB;AAAA,YACT,CAACkB,KACC,CAACf,KACD;AAAA,YACF;AAAA,YACAT;AAAA,UACF;AAAA,UACC,GAAGC;AAAA,UAEH,UAAAG;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EACF,IAtBmB;AAwBvB;AAEA,SAASsB,EAA0B;AAAA,EACjC,WAAA1B;AAAA,EACA,GAAGC;AACL,GAAgC;AAC9B,QAAM,EAAC,aAAA0B,GAAa,kBAAAC,EAAgB,IAAIhC,EAAsB;AAC9D,SACG,gBAAAS,EAAA,OAAA,EAAI,WAAWC,EAAG,kCAAkCN,CAAS,GAAI,GAAGC,GACnE,4BAAC4B,GAAe,EAAA,SAASD,GAAkB,QAAQD,EAAa,CAAA,GAClE;AAEJ;AAEA,SAASG,EAAgB,EAAC,WAAA9B,GAAW,GAAGC,KAAqC;AACrE,QAAA,EAAC,SAAAQ,EAAO,IAAIb,EAAsB;AACxC,SAAIa,MAAY,YACP,OAIP,gBAAAJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC,EAAG,4BAA4BN,CAAS;AAAA,MAClD,GAAGC;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAAS8B,EAAiB;AAAA,EACxB,WAAA/B;AAAA,EACA,UAAAsB;AAAA,EACA,GAAGrB;AACL,GAA+B;AACvB,QAAA,EAAC,SAAAU,EAAO,IAAIf,EAAsB;AAEtC,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACAN;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,MAEH,eAAYU,EAAQ;AAAA,IAAA;AAAA,EACvB;AAEJ;AAEA,SAASqB,EAAiB,EAAC,WAAAhC,KAAkC;AAC3D,QAAM,EAAC,SAAAW,GAAS,wBAAAC,EAAsB,IAAIhB,EAAsB,GAG1DqC,IAAerB,GAAwB,SAASD,GAAS,OACzDuB,IACJtB,GAAwB,kBAAkBD,GAAS;AAGnD,SAAA,gBAAAN;AAAA,IAAC8B;AAAA,IAAA;AAAA,MACC,QAAQF,GAAc,UAAU;AAAA,MAChC,cAAcA,GAAc,gBAAgB;AAAA,MAC5C,sBAAsBC,GAAuB;AAAA,MAC7C,4BAA4BA,GAAuB;AAAA,MACnD,WAAAlC;AAAA,IAAA;AAAA,EACF;AAEJ;AAGA,SAASoC,IAA+B;AACtC,QAAM,EAAC,SAAAzB,GAAS,wBAAAC,GAAwB,SAAAH,EAAA,IAAWb,EAAsB;AACrE,MAAAa,MAAY,eAAuB,QAAA;AACjC,QAAAwB,IAAerB,GAAwB,SAASD,EAAQ,OACxD0B,IAAeJ,GAAc,cAC7BK,IAASL,GAAc;AAE7B,SAAI,CAACI,KAAgB,CAACC,IAAe,yBAElClB,GAAiB,EAAA,UAAS,YACxB,UAAYmB,EAAAD,GAAQD,CAAY,GACnC;AAEJ;AAuBA,SAASG,GAAY;AAAA,EACnB,SAAA7B;AAAA,EACA,wBAAAC;AAAA,EACA,SAAAH,IAAU;AAAA,EACV,WAAAP,IAAY;AAAA,EACZ,WAAAqB;AAAA,EACA,cAAAC;AAAA,EACA,gBAAAiB;AAAA,EACA,mBAAAC;AAAA,EACA,UAAApB;AACF,GAAqB;AACb,QAAA,EAAC,mBAAAqB,EAAiB,IAAIC,EAAkB,GACxC,EAAC,aAAAC,GAAa,eAAAC,EAAa,IAAIC,EAAwB,GAGvD,CAACC,GAAkBC,CAAmB,IAAIC,EAASvC,EAAQ,WAAW,GAEtEwC,IAAcjC,EAAY,MAAM;AACpC,IAAKhB,MAEYuC,IAAA,GAECE,EAAA;AAAA,MAChB,WAAWhC,EAAQ;AAAA,IAAA,CACpB;AAAA,EAAA,GACA,CAACgC,GAAmBhC,EAAQ,IAAIT,GAAWuC,CAAc,CAAC,GAEvDW,IAAsBlC,EAAY,YAAY;AAClD,UAAMmC,IAAgBL;AAGtB,IAAAC,EAAoB,CAACI,CAAa,GAClCX,IAAoB,CAACW,CAAa;AAE9B,QAAA;AACF,MAAIA,IACF,MAAMP,EAAc;AAAA,QAClB,WAAWnC,EAAQ;AAAA,QACnB,QAAQA,EAAQ,KAAK;AAAA,QACrB,kBACEC,GAAwB,MAAMD,EAAQ;AAAA,MAAA,CACzC,IAED,MAAMkC,EAAY;AAAA,QAChB,WAAWlC,EAAQ;AAAA,QACnB,QAAQA,EAAQ,KAAK;AAAA,QACrB,kBACEC,GAAwB,MAAMD,EAAQ;AAAA,MAAA,CACzC;AAAA,YAEW;AAEd,MAAAsC,EAAoBI,CAAa,GACjCX,IAAoBW,CAAa;AAAA,IAAA;AAAA,EACnC,GACC;AAAA,IACDL;AAAA,IACArC,EAAQ;AAAA,IACRA,EAAQ,KAAK;AAAA,IACbA,EAAQ;AAAA,IACRC,GAAwB;AAAA,IACxBiC;AAAA,IACAC;AAAA,IACAJ;AAAA,EAAA,CACD,GAEKY,IAAeC;AAAA,IACnB,OAAO;AAAA;AAAA,MAEL,SAAA5C;AAAA,MACA,wBAAAC;AAAA;AAAA,MAGA,SAAAH;AAAA,MACA,WAAAP;AAAA,MACA,WAAAqB;AAAA,MACA,cAAAC;AAAA;AAAA,MAGA,aAAawB;AAAA;AAAA,MAGb,SAASG;AAAA,MACT,kBAAkBC;AAAA,IAAA;AAAA,IAEpB;AAAA,MACEzC;AAAA,MACAC;AAAA,MACAH;AAAA,MACAP;AAAA,MACAqB;AAAA,MACAC;AAAA,MACAwB;AAAA,MACAG;AAAA,MACAC;AAAA,IAAA;AAAA,EAEJ;AAGE,SAAA,gBAAA/C,EAACX,EAAmB,UAAnB,EAA4B,OAAO4D,GACjC,UAAAhC,uBACEvB,GACC,EAAA,UAAA;AAAA,IAAA,gBAAAyD,EAAChD,GACC,EAAA,UAAA;AAAA,MAAA,gBAAAH,EAACK,GAAiB,EAAA;AAAA,MACjBD,MAAY,kBAAkB,gBAAAJ,EAAC+B,GAA6B,CAAA,CAAA;AAAA,wBAC5DhB,GAAiB,EAAA;AAAA,wBACjBM,GAA0B,CAAA,CAAA;AAAA,IAAA,GAC7B;AAAA,IACCjB,MAAY,aACX,gBAAA+C,EAAC1B,GACC,EAAA,UAAA;AAAA,MAAA,gBAAAzB,EAAC0B,GAAiB,EAAA;AAAA,wBACjBC,GAAiB,CAAA,CAAA;AAAA,IAAA,EACpB,CAAA;AAAA,EAAA,EAAA,CAEJ,EAEJ,CAAA;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\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 {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n\n // State\n isFavorited: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative w-full overflow-hidden rounded-xl border-0',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n // Ensure the product image is stretched to the full size of the container (can't use width/height: 100% because of flex)\n 'flex justify-stretch items-stretch',\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'w-full aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, onFavoriteToggle} = useProductCardContext()\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n\n // State\n isFavorited: isFavoritedLocal,\n\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef as r, useEffect as s } from "react";
|
|
2
|
+
function f(n) {
|
|
3
|
+
const e = r(n);
|
|
4
|
+
e.current = n, s(() => {
|
|
5
|
+
const t = window.minisEvents.on("APP_STATE_CHANGE", (o) => {
|
|
6
|
+
e.current(o);
|
|
7
|
+
});
|
|
8
|
+
return () => window.minisEvents.off(t);
|
|
9
|
+
}, []);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
f as useOnAppStateChange
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useOnAppStateChange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnAppStateChange.js","sources":["../../../src/hooks/events/useOnAppStateChange.ts"],"sourcesContent":["import {useEffect, useRef} from 'react'\n\nimport {type AppStateChangePayload} from '@shopify/shop-minis-platform/events'\n\nexport function useOnAppStateChange(\n callback: (payload: AppStateChangePayload) => void\n) {\n // Using a ref allows the callback to be updated without triggering a re-render\n // This makes the hook nicer to use because developers don't need useCallback\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n const listenerId = window.minisEvents.on('APP_STATE_CHANGE', payload => {\n callbackRef.current(payload)\n })\n\n return () => window.minisEvents.off(listenerId)\n }, [])\n}\n"],"names":["useOnAppStateChange","callback","callbackRef","useRef","useEffect","listenerId","payload"],"mappings":";AAIO,SAASA,EACdC,GACA;AAGM,QAAAC,IAAcC,EAAOF,CAAQ;AACnC,EAAAC,EAAY,UAAUD,GAEtBG,EAAU,MAAM;AACd,UAAMC,IAAa,OAAO,YAAY,GAAG,oBAAoB,CAAWC,MAAA;AACtE,MAAAJ,EAAY,QAAQI,CAAO;AAAA,IAAA,CAC5B;AAED,WAAO,MAAM,OAAO,YAAY,IAAID,CAAU;AAAA,EAChD,GAAG,EAAE;AACP;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef as i, useEffect as r } from "react";
|
|
2
|
+
function s(n) {
|
|
3
|
+
const e = i(n);
|
|
4
|
+
e.current = n, r(() => {
|
|
5
|
+
const t = window.minisEvents.on("MINI_APP_BLUR", () => {
|
|
6
|
+
e.current();
|
|
7
|
+
});
|
|
8
|
+
return () => window.minisEvents.off(t);
|
|
9
|
+
}, []);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
s as useOnMiniBlur
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useOnMiniBlur.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnMiniBlur.js","sources":["../../../src/hooks/events/useOnMiniBlur.ts"],"sourcesContent":["import {useEffect, useRef} from 'react'\n\nexport function useOnMiniBlur(callback: () => void) {\n // Using a ref allows the callback to be updated without triggering a re-render\n // This makes the hook nicer to use because developers don't need useCallback\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n const listenerId = window.minisEvents.on('MINI_APP_BLUR', () => {\n callbackRef.current()\n })\n\n return () => window.minisEvents.off(listenerId)\n }, [])\n}\n"],"names":["useOnMiniBlur","callback","callbackRef","useRef","useEffect","listenerId"],"mappings":";AAEO,SAASA,EAAcC,GAAsB;AAG5C,QAAAC,IAAcC,EAAOF,CAAQ;AACnC,EAAAC,EAAY,UAAUD,GAEtBG,EAAU,MAAM;AACd,UAAMC,IAAa,OAAO,YAAY,GAAG,iBAAiB,MAAM;AAC9D,MAAAH,EAAY,QAAQ;AAAA,IAAA,CACrB;AAED,WAAO,MAAM,OAAO,YAAY,IAAIG,CAAU;AAAA,EAChD,GAAG,EAAE;AACP;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef as i, useEffect as o } from "react";
|
|
2
|
+
function r(n) {
|
|
3
|
+
const e = i(n);
|
|
4
|
+
e.current = n, o(() => {
|
|
5
|
+
const t = window.minisEvents.on("MINI_APP_CLOSE", () => {
|
|
6
|
+
e.current();
|
|
7
|
+
});
|
|
8
|
+
return () => window.minisEvents.off(t);
|
|
9
|
+
}, []);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
r as useOnMiniClose
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useOnMiniClose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnMiniClose.js","sources":["../../../src/hooks/events/useOnMiniClose.ts"],"sourcesContent":["import {useEffect, useRef} from 'react'\n\nexport function useOnMiniClose(callback: () => void) {\n // Using a ref allows the callback to be updated without triggering a re-render\n // This makes the hook nicer to use because developers don't need useCallback\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n const listenerId = window.minisEvents.on('MINI_APP_CLOSE', () => {\n callbackRef.current()\n })\n\n return () => window.minisEvents.off(listenerId)\n }, [])\n}\n"],"names":["useOnMiniClose","callback","callbackRef","useRef","useEffect","listenerId"],"mappings":";AAEO,SAASA,EAAeC,GAAsB;AAG7C,QAAAC,IAAcC,EAAOF,CAAQ;AACnC,EAAAC,EAAY,UAAUD,GAEtBG,EAAU,MAAM;AACd,UAAMC,IAAa,OAAO,YAAY,GAAG,kBAAkB,MAAM;AAC/D,MAAAH,EAAY,QAAQ;AAAA,IAAA,CACrB;AAED,WAAO,MAAM,OAAO,YAAY,IAAIG,CAAU;AAAA,EAChD,GAAG,EAAE;AACP;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef as i, useEffect as o } from "react";
|
|
2
|
+
function r(n) {
|
|
3
|
+
const e = i(n);
|
|
4
|
+
e.current = n, o(() => {
|
|
5
|
+
const t = window.minisEvents.on("MINI_APP_FOCUS", () => {
|
|
6
|
+
e.current();
|
|
7
|
+
});
|
|
8
|
+
return () => window.minisEvents.off(t);
|
|
9
|
+
}, []);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
r as useOnMiniFocus
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useOnMiniFocus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnMiniFocus.js","sources":["../../../src/hooks/events/useOnMiniFocus.ts"],"sourcesContent":["import {useEffect, useRef} from 'react'\n\nexport function useOnMiniFocus(callback: () => void) {\n // Using a ref allows the callback to be updated without triggering a re-render\n // This makes the hook nicer to use because developers don't need useCallback\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n const listenerId = window.minisEvents.on('MINI_APP_FOCUS', () => {\n callbackRef.current()\n })\n\n return () => window.minisEvents.off(listenerId)\n }, [])\n}\n"],"names":["useOnMiniFocus","callback","callbackRef","useRef","useEffect","listenerId"],"mappings":";AAEO,SAASA,EAAeC,GAAsB;AAG7C,QAAAC,IAAcC,EAAOF,CAAQ;AACnC,EAAAC,EAAY,UAAUD,GAEtBG,EAAU,MAAM;AACd,UAAMC,IAAa,OAAO,YAAY,GAAG,kBAAkB,MAAM;AAC/D,MAAAH,EAAY,QAAQ;AAAA,IAAA,CACrB;AAED,WAAO,MAAM,OAAO,YAAY,IAAIG,CAAU;AAAA,EAChD,GAAG,EAAE;AACP;"}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { useHandleAction as o } from "../../internal/useHandleAction.js";
|
|
2
|
-
import { useShopActions as
|
|
3
|
-
const
|
|
2
|
+
import { useShopActions as r } from "../../internal/useShopActions.js";
|
|
3
|
+
const T = () => {
|
|
4
4
|
const {
|
|
5
|
-
navigateToProduct:
|
|
6
|
-
navigateToShop:
|
|
5
|
+
navigateToProduct: a,
|
|
6
|
+
navigateToShop: t,
|
|
7
7
|
navigateToOrder: e,
|
|
8
|
-
navigateToCheckout: n
|
|
9
|
-
|
|
8
|
+
navigateToCheckout: n,
|
|
9
|
+
navigateToCart: i
|
|
10
|
+
} = r();
|
|
10
11
|
return {
|
|
11
|
-
navigateToProduct: o(
|
|
12
|
-
navigateToShop: o(
|
|
12
|
+
navigateToProduct: o(a),
|
|
13
|
+
navigateToShop: o(t),
|
|
13
14
|
navigateToOrder: o(e),
|
|
14
|
-
navigateToCheckout: o(n)
|
|
15
|
+
navigateToCheckout: o(n),
|
|
16
|
+
navigateToCart: o(i)
|
|
15
17
|
};
|
|
16
18
|
};
|
|
17
19
|
export {
|
|
18
|
-
|
|
20
|
+
T as useShopNavigation
|
|
19
21
|
};
|
|
20
22
|
//# sourceMappingURL=useShopNavigation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useShopNavigation.js","sources":["../../../src/hooks/navigation/useShopNavigation.ts"],"sourcesContent":["import {\n NavigateToCheckoutParams,\n NavigateToOrderParams,\n NavigateToProductParams,\n NavigateToShopParams,\n} from '@shopify/shop-minis-platform/actions'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseShopNavigationReturns {\n /**\n * Navigates to a product.\n */\n navigateToProduct: (params: NavigateToProductParams) => Promise<void>\n /**\n * Navigates to a shop.\n */\n navigateToShop: (params: NavigateToShopParams) => Promise<void>\n /**\n * Navigates to an order.\n */\n navigateToOrder: (params: NavigateToOrderParams) => Promise<void>\n /**\n * Navigates to a checkout.\n */\n navigateToCheckout: (params: NavigateToCheckoutParams) => Promise<void>\n}\n\nexport const useShopNavigation = (): UseShopNavigationReturns => {\n const {\n navigateToProduct,\n navigateToShop,\n navigateToOrder,\n navigateToCheckout,\n } = useShopActions()\n\n return {\n navigateToProduct: useHandleAction(navigateToProduct),\n navigateToShop: useHandleAction(navigateToShop),\n navigateToOrder: useHandleAction(navigateToOrder),\n navigateToCheckout: useHandleAction(navigateToCheckout),\n }\n}\n"],"names":["useShopNavigation","navigateToProduct","navigateToShop","navigateToOrder","navigateToCheckout","useShopActions","useHandleAction"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"useShopNavigation.js","sources":["../../../src/hooks/navigation/useShopNavigation.ts"],"sourcesContent":["import {\n NavigateToCheckoutParams,\n NavigateToOrderParams,\n NavigateToProductParams,\n NavigateToShopParams,\n} from '@shopify/shop-minis-platform/actions'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseShopNavigationReturns {\n /**\n * Navigates to a product.\n */\n navigateToProduct: (params: NavigateToProductParams) => Promise<void>\n /**\n * Navigates to a shop.\n */\n navigateToShop: (params: NavigateToShopParams) => Promise<void>\n /**\n * Navigates to an order.\n */\n navigateToOrder: (params: NavigateToOrderParams) => Promise<void>\n /**\n * Navigates to a checkout.\n */\n navigateToCheckout: (params: NavigateToCheckoutParams) => Promise<void>\n /**\n * Navigates to the cart screen.\n */\n navigateToCart: () => Promise<void>\n}\n\nexport const useShopNavigation = (): UseShopNavigationReturns => {\n const {\n navigateToProduct,\n navigateToShop,\n navigateToOrder,\n navigateToCheckout,\n navigateToCart,\n } = useShopActions()\n\n return {\n navigateToProduct: useHandleAction(navigateToProduct),\n navigateToShop: useHandleAction(navigateToShop),\n navigateToOrder: useHandleAction(navigateToOrder),\n navigateToCheckout: useHandleAction(navigateToCheckout),\n navigateToCart: useHandleAction(navigateToCart),\n }\n}\n"],"names":["useShopNavigation","navigateToProduct","navigateToShop","navigateToOrder","navigateToCheckout","navigateToCart","useShopActions","useHandleAction"],"mappings":";;AAiCO,MAAMA,IAAoB,MAAgC;AACzD,QAAA;AAAA,IACJ,mBAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,iBAAAC;AAAA,IACA,oBAAAC;AAAA,IACA,gBAAAC;AAAA,MACEC,EAAe;AAEZ,SAAA;AAAA,IACL,mBAAmBC,EAAgBN,CAAiB;AAAA,IACpD,gBAAgBM,EAAgBL,CAAc;AAAA,IAC9C,iBAAiBK,EAAgBJ,CAAe;AAAA,IAChD,oBAAoBI,EAAgBH,CAAkB;AAAA,IACtD,gBAAgBG,EAAgBF,CAAc;AAAA,EAChD;AACF;"}
|
|
@@ -1,12 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { useRef as a, useCallback as c } from "react";
|
|
2
|
+
import { useHandleAction as f } from "../../internal/useHandleAction.js";
|
|
3
|
+
import { useShopActions as p } from "../../internal/useShopActions.js";
|
|
4
|
+
function g() {
|
|
5
|
+
const { generateUserToken: s } = p(), n = f(s), r = a(null), t = a(null), o = c(
|
|
6
|
+
(e) => {
|
|
7
|
+
if (!e?.data?.token || !e.data.expiresAt)
|
|
8
|
+
return !1;
|
|
9
|
+
try {
|
|
10
|
+
const u = new Date(e.data.expiresAt).getTime(), i = Date.now(), l = 5 * 60 * 1e3;
|
|
11
|
+
return u - l > i;
|
|
12
|
+
} catch {
|
|
13
|
+
return !1;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
[]
|
|
17
|
+
);
|
|
5
18
|
return {
|
|
6
|
-
generateUserToken:
|
|
19
|
+
generateUserToken: c(async () => r.current && o(r.current) ? r.current : (t.current || (t.current = (async () => {
|
|
20
|
+
try {
|
|
21
|
+
const e = await n();
|
|
22
|
+
return e.data?.token && e.data?.expiresAt && (r.current = e), e;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw r.current = null, e;
|
|
25
|
+
} finally {
|
|
26
|
+
t.current = null;
|
|
27
|
+
}
|
|
28
|
+
})()), t.current), [n, o])
|
|
7
29
|
};
|
|
8
30
|
}
|
|
9
31
|
export {
|
|
10
|
-
|
|
32
|
+
g as useGenerateUserToken
|
|
11
33
|
};
|
|
12
34
|
//# sourceMappingURL=useGenerateUserToken.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGenerateUserToken.js","sources":["../../../src/hooks/user/useGenerateUserToken.ts"],"sourcesContent":["import {\n GeneratedTokenData,\n UserTokenGenerateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseGenerateUserTokenReturns {\n /**\n * Generates a temporary token for the user.\n */\n generateUserToken: () => Promise<{\n data: GeneratedTokenData\n userErrors?: UserTokenGenerateUserErrors[]\n }>\n}\n\nexport function useGenerateUserToken(): UseGenerateUserTokenReturns {\n const {generateUserToken} = useShopActions()\n\n return {\n generateUserToken:
|
|
1
|
+
{"version":3,"file":"useGenerateUserToken.js","sources":["../../../src/hooks/user/useGenerateUserToken.ts"],"sourcesContent":["import {useCallback, useRef} from 'react'\n\nimport {\n GeneratedTokenData,\n UserTokenGenerateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseGenerateUserTokenReturns {\n /**\n * Generates a temporary token for the user.\n * Tokens are cached in memory and reused if still valid (with a 5-minute expiry buffer).\n * A new token is automatically generated when the cached token is expired or missing.\n */\n generateUserToken: () => Promise<{\n data: GeneratedTokenData\n userErrors?: UserTokenGenerateUserErrors[]\n }>\n}\n\ninterface CachedTokenResponse {\n data: GeneratedTokenData\n userErrors?: UserTokenGenerateUserErrors[]\n}\n\nexport function useGenerateUserToken(): UseGenerateUserTokenReturns {\n const {generateUserToken: generateUserTokenAction} = useShopActions()\n const wrappedGenerateToken = useHandleAction(generateUserTokenAction)\n\n const cachedResponse = useRef<CachedTokenResponse | null>(null)\n const pendingRequest = useRef<Promise<CachedTokenResponse> | null>(null)\n\n const isTokenValid = useCallback(\n (response: CachedTokenResponse | null): boolean => {\n if (!response?.data?.token || !response.data.expiresAt) {\n return false\n }\n\n try {\n const expiryTime = new Date(response.data.expiresAt).getTime()\n const now = Date.now()\n // 5 minutes buffer to ensure token doesn't expire mid-request\n const bufferTime = 5 * 60 * 1000\n\n return expiryTime - bufferTime > now\n } catch {\n // If date parsing fails, consider token invalid\n return false\n }\n },\n []\n )\n\n const generateUserToken =\n useCallback(async (): Promise<CachedTokenResponse> => {\n // Check if cached token exists and is still valid\n if (cachedResponse.current && isTokenValid(cachedResponse.current)) {\n return cachedResponse.current\n }\n\n // If there's already a pending request, return the same promise\n if (pendingRequest.current) {\n return pendingRequest.current\n }\n\n // Create new request and store the promise\n pendingRequest.current = (async () => {\n try {\n const response = await wrappedGenerateToken()\n\n // Only cache if we got a valid token\n if (response.data?.token && response.data?.expiresAt) {\n cachedResponse.current = response\n }\n\n return response\n } catch (error) {\n // Clear cache on error to ensure fresh token on next attempt\n cachedResponse.current = null\n throw error\n } finally {\n // Clear pending request after completion (success or failure)\n pendingRequest.current = null\n }\n })()\n\n return pendingRequest.current\n }, [wrappedGenerateToken, isTokenValid])\n\n return {\n generateUserToken,\n }\n}\n"],"names":["useGenerateUserToken","generateUserTokenAction","useShopActions","wrappedGenerateToken","useHandleAction","cachedResponse","useRef","pendingRequest","isTokenValid","useCallback","response","expiryTime","now","bufferTime","error"],"mappings":";;;AA2BO,SAASA,IAAoD;AAClE,QAAM,EAAC,mBAAmBC,EAAuB,IAAIC,EAAe,GAC9DC,IAAuBC,EAAgBH,CAAuB,GAE9DI,IAAiBC,EAAmC,IAAI,GACxDC,IAAiBD,EAA4C,IAAI,GAEjEE,IAAeC;AAAA,IACnB,CAACC,MAAkD;AACjD,UAAI,CAACA,GAAU,MAAM,SAAS,CAACA,EAAS,KAAK;AACpC,eAAA;AAGL,UAAA;AACF,cAAMC,IAAa,IAAI,KAAKD,EAAS,KAAK,SAAS,EAAE,QAAQ,GACvDE,IAAM,KAAK,IAAI,GAEfC,IAAa,IAAI,KAAK;AAE5B,eAAOF,IAAaE,IAAaD;AAAA,MAAA,QAC3B;AAEC,eAAA;AAAA,MAAA;AAAA,IAEX;AAAA,IACA,CAAA;AAAA,EACF;AAsCO,SAAA;AAAA,IACL,mBApCAH,EAAY,YAENJ,EAAe,WAAWG,EAAaH,EAAe,OAAO,IACxDA,EAAe,WAIpBE,EAAe,YAKnBA,EAAe,WAAW,YAAY;AAChC,UAAA;AACI,cAAAG,IAAW,MAAMP,EAAqB;AAG5C,eAAIO,EAAS,MAAM,SAASA,EAAS,MAAM,cACzCL,EAAe,UAAUK,IAGpBA;AAAA,eACAI,GAAO;AAEd,cAAAT,EAAe,UAAU,MACnBS;AAAA,MAAA,UACN;AAEA,QAAAP,EAAe,UAAU;AAAA,MAAA;AAAA,IAC3B,GACC,IAEIA,EAAe,UACrB,CAACJ,GAAsBK,CAAY,CAAC;AAAA,EAIzC;AACF;"}
|