@shopify/shop-minis-react 0.0.33 → 0.0.34
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/index2.js +4 -4
- package/dist/_virtual/index3.js +4 -4
- package/dist/_virtual/index4.js +2 -2
- package/dist/_virtual/index5.js +3 -2
- package/dist/_virtual/index5.js.map +1 -1
- package/dist/_virtual/index6.js +2 -2
- package/dist/_virtual/index7.js +2 -3
- package/dist/_virtual/index7.js.map +1 -1
- package/dist/_virtual/index8.js +2 -2
- package/dist/_virtual/index9.js +2 -2
- package/dist/components/atoms/image.js +52 -0
- package/dist/components/atoms/image.js.map +1 -0
- package/dist/components/commerce/merchant-card.js +1 -1
- package/dist/components/commerce/merchant-card.js.map +1 -1
- package/dist/components/commerce/product-card.js +11 -11
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/content/image-content-wrapper.js +29 -22
- package/dist/components/content/image-content-wrapper.js.map +1 -1
- package/dist/hooks/content/useCreateImageContent.js +16 -22
- package/dist/hooks/content/useCreateImageContent.js.map +1 -1
- package/dist/hooks/storage/useImageUpload.js +36 -37
- package/dist/hooks/storage/useImageUpload.js.map +1 -1
- package/dist/index.js +56 -54
- package/dist/shop-minis-platform/src/types/content.js.map +1 -1
- 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/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/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/color-string@1.9.1/node_modules/color-string/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
- 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/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/dist/utils/image.js +45 -9
- package/dist/utils/image.js.map +1 -1
- package/package.json +2 -2
- package/src/components/atoms/{thumbhash-image.tsx → image.tsx} +14 -14
- package/src/components/commerce/merchant-card.tsx +2 -2
- package/src/components/commerce/product-card.tsx +2 -2
- package/src/components/content/image-content-wrapper.tsx +9 -2
- package/src/components/index.ts +1 -1
- package/src/hooks/content/useCreateImageContent.ts +1 -7
- package/src/hooks/storage/useImageUpload.ts +22 -20
- package/src/utils/image.ts +72 -0
- package/src/utils/index.ts +1 -1
- package/dist/components/atoms/thumbhash-image.js +0 -54
- package/dist/components/atoms/thumbhash-image.js.map +0 -1
- package/dist/utils/imageToDataUri.js +0 -10
- package/dist/utils/imageToDataUri.js.map +0 -1
- package/src/utils/imageToDataUri.ts +0 -8
package/dist/_virtual/index10.js
CHANGED
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
|
package/dist/_virtual/index4.js
CHANGED
package/dist/_virtual/index5.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index5.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index5.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/_virtual/index6.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { __require as r } from "../shop-minis-react/node_modules/.pnpm
|
|
1
|
+
import { __require as r } from "../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";
|
|
2
2
|
var i = r();
|
|
3
3
|
export {
|
|
4
|
-
i as
|
|
4
|
+
i as s
|
|
5
5
|
};
|
|
6
6
|
//# sourceMappingURL=index6.js.map
|
package/dist/_virtual/index7.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
var i = r();
|
|
1
|
+
var r = {};
|
|
3
2
|
export {
|
|
4
|
-
|
|
3
|
+
r as __exports
|
|
5
4
|
};
|
|
6
5
|
//# sourceMappingURL=index7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
package/dist/_virtual/index8.js
CHANGED
package/dist/_virtual/index9.js
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { memo as f, useState as L, useMemo as r, useCallback as v } from "react";
|
|
3
|
+
import { cn as c } from "../../lib/utils.js";
|
|
4
|
+
import { getThumbhashDataURL as I, getResizedImageUrl as k } from "../../utils/image.js";
|
|
5
|
+
const U = f(function(m) {
|
|
6
|
+
const {
|
|
7
|
+
src: e,
|
|
8
|
+
thumbhash: o,
|
|
9
|
+
onLoad: a,
|
|
10
|
+
className: i,
|
|
11
|
+
style: n,
|
|
12
|
+
aspectRatio: d = "auto",
|
|
13
|
+
...l
|
|
14
|
+
} = m, [u, g] = L(!1), t = r(
|
|
15
|
+
() => I(o ?? void 0),
|
|
16
|
+
[o]
|
|
17
|
+
), h = v(
|
|
18
|
+
(p) => {
|
|
19
|
+
g(!0), a?.(p);
|
|
20
|
+
},
|
|
21
|
+
[a]
|
|
22
|
+
), b = r(() => k(e), [e]);
|
|
23
|
+
return /* @__PURE__ */ s(
|
|
24
|
+
"div",
|
|
25
|
+
{
|
|
26
|
+
className: c("relative w-full ", i),
|
|
27
|
+
style: {
|
|
28
|
+
...n,
|
|
29
|
+
aspectRatio: d,
|
|
30
|
+
backgroundImage: t ? `url(${t})` : void 0,
|
|
31
|
+
backgroundSize: "cover",
|
|
32
|
+
backgroundPosition: "center"
|
|
33
|
+
},
|
|
34
|
+
children: /* @__PURE__ */ s(
|
|
35
|
+
"img",
|
|
36
|
+
{
|
|
37
|
+
className: c(
|
|
38
|
+
"absolute inset-0 opacity-0 object-cover",
|
|
39
|
+
u && "opacity-100"
|
|
40
|
+
),
|
|
41
|
+
src: b,
|
|
42
|
+
onLoad: h,
|
|
43
|
+
...l
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
export {
|
|
50
|
+
U as Image
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +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;"}
|
|
@@ -3,7 +3,7 @@ import { useCallback as w, useMemo as f, createContext as N, useContext as y } f
|
|
|
3
3
|
import { useShopNavigation as T } from "../../hooks/navigation/useShopNavigation.js";
|
|
4
4
|
import { cn as c } from "../../lib/utils.js";
|
|
5
5
|
import { isDarkColor as b } from "../../utils/colors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { Image as k } from "../atoms/image.js";
|
|
7
7
|
import { Touchable as I } from "../atoms/touchable.js";
|
|
8
8
|
import { extractBrandTheme as M, normalizeRating as z, formatReviewCount as j, getFeaturedImages as D } from "../../utils/merchant-card.js";
|
|
9
9
|
import S 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";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merchant-card.js","sources":["../../../src/components/commerce/merchant-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useCallback, useContext, useMemo} from 'react'\n\nimport {type Shop} from '@shopify/shop-minis-platform'\nimport {Star} from 'lucide-react'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {cn} from '../../lib/utils'\nimport {\n type ExtractedBrandTheme,\n extractBrandTheme,\n formatReviewCount,\n getFeaturedImages,\n normalizeRating,\n} from '../../utils'\nimport {isDarkColor} from '../../utils/colors'\nimport {ThumbhashImage} from '../atoms/thumbhash-image'\nimport {Touchable} from '../atoms/touchable'\n\ninterface MerchantCardContextValue {\n // Core data\n shop: Shop\n\n // Derived data\n cardTheme: ExtractedBrandTheme\n\n // UI configuration\n touchable: boolean\n featuredImagesLimit: number\n\n // Actions\n onClick: () => void\n}\n\nconst MerchantCardContext = createContext<MerchantCardContextValue | undefined>(\n undefined\n)\n\nfunction useMerchantCardContext() {\n const context = useContext(MerchantCardContext)\n if (!context) {\n throw new Error(\n 'useMerchantCardContext must be used within a MerchantCardProvider'\n )\n }\n return context\n}\n\nfunction MerchantCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, cardTheme, onClick} = useMerchantCardContext()\n\n const content = (\n <div\n style={{\n backgroundColor: cardTheme.backgroundColor,\n }}\n className={cn(\n 'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square',\n\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 MerchantCardImage({\n className,\n src,\n alt,\n thumbhash,\n ...props\n}: React.ComponentProps<'img'> & {\n src?: string\n alt?: string\n thumbhash?: string\n}) {\n if (!src) {\n return <div className=\"w-full h-full bg-gray-100\" />\n }\n\n if (thumbhash) {\n return (\n <ThumbhashImage\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n thumbhash={thumbhash}\n className={cn(className)}\n {...props}\n />\n )\n }\n\n return (\n <img\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n}\n\nfunction MerchantCardLogo({className, ...props}: React.ComponentProps<'div'>) {\n const {shop} = useMerchantCardContext()\n const {name, visualTheme} = shop\n\n const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage\n const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant\n const logoColor = logoAverageColor || logoDominantColor\n\n const logoBackgroundClassName = useMemo(\n () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),\n [logoColor]\n )\n\n const logoUrl = visualTheme?.logoImage?.url\n const altText = `${name} logo`\n\n return (\n <div\n data-slot=\"merchant-card-logo\"\n className={cn(\n 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',\n 'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',\n 'flex items-center justify-center overflow-hidden',\n logoBackgroundClassName,\n className\n )}\n {...props}\n >\n {logoUrl ? (\n <img\n src={logoUrl}\n alt={altText}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"w-full h-full bg-gray-200 flex items-center justify-center\">\n <span className=\"text-gray-600 font-semibold text-lg\">\n {name?.slice(0, 1)}\n </span>\n </div>\n )}\n </div>\n )\n}\n\nfunction MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {cardTheme} = useMerchantCardContext()\n\n const isDarkTheme = useMemo(() => {\n return (\n cardTheme.backgroundColor !== 'white' &&\n isDarkColor(cardTheme.backgroundColor)\n )\n }, [cardTheme.backgroundColor])\n\n const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'\n\n return (\n <div\n data-slot=\"merchant-card-info\"\n className={cn(\n 'p-3 flex-shrink-0 flex flex-col min-w-0',\n textColor,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MerchantCardName({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {shop} = useMerchantCardContext()\n const {name} = shop\n const nameContent = children ?? name\n\n return (\n <h3\n data-slot=\"merchant-card-name\"\n className={cn('text-sm font-medium truncate', className)}\n {...props}\n >\n {nameContent}\n </h3>\n )\n}\n\nfunction MerchantCardRating({\n className,\n\n ...props\n}: React.ComponentProps<'div'> & {\n rating?: number | null\n reviewCount?: number\n}) {\n const {shop} = useMerchantCardContext()\n\n const {\n reviewAnalytics: {averageRating, reviewCount},\n } = shop\n\n if (!averageRating || !reviewCount) return null\n\n return (\n <div\n data-slot=\"merchant-card-rating\"\n className={cn('flex items-center gap-1 text-xs', className)}\n {...props}\n >\n <Star className=\"h-3 w-3 fill-current\" />\n <span className=\"text-xs\">\n {normalizeRating(averageRating)} ({formatReviewCount(reviewCount)})\n </span>\n </div>\n )\n}\n\nfunction MerchantCardDefaultHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme, featuredImagesLimit} = useMerchantCardContext()\n const {visualTheme} = shop\n\n const featuredImages = useMemo(\n () => getFeaturedImages(visualTheme, featuredImagesLimit),\n [visualTheme, featuredImagesLimit]\n )\n\n const numberOfFeaturedImages = featuredImages?.length ?? 0\n\n const displayDefaultCover = () => {\n if (numberOfFeaturedImages > 0) {\n const heightClass = numberOfFeaturedImages === 2 ? 'h-full' : 'h-1/2'\n return featuredImages?.map((image, index) => (\n <div className={`z-0 w-1/2 ${heightClass}`} key={image.url || index}>\n <MerchantCardImage\n src={image.url}\n alt={image.altText ?? undefined}\n thumbhash={image.thumbhash ?? undefined}\n className=\"aspect-square\"\n />\n </div>\n ))\n } else if (cardTheme.type === 'coverImage') {\n return (\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n thumbhash={cardTheme.coverImageThumbhash}\n />\n )\n }\n }\n\n return (\n <div className=\"w-full h-full bg-muted relative flex flex-wrap overflow-hidden\">\n {withLogo && (\n <div className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10\">\n <MerchantCardLogo />\n </div>\n )}\n\n {displayDefaultCover()}\n </div>\n )\n}\n\nfunction MerchantCardBrandedHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme} = useMerchantCardContext()\n const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark\n\n return (\n <div className=\"size-full relative\">\n {cardTheme.type === 'coverImage' && (\n <>\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n alt={shop.name}\n thumbhash={cardTheme.coverImageThumbhash ?? undefined}\n />\n\n <div className=\"absolute inset-0 z-[1] bg-black/20\" />\n\n <div\n className=\"absolute bottom-0 z-[1] size-full\"\n style={{\n background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,\n }}\n />\n </>\n )}\n\n {withLogo && (\n <div className=\"absolute inset-0 z-[1] flex items-center justify-center\">\n {wordmarkImage ? (\n <img\n src={wordmarkImage.url}\n alt={wordmarkImage.altText || shop.name}\n className=\"max-h-16 min-h-10 max-w-28 object-contain\"\n data-testid=\"store-data-wordmark\"\n />\n ) : (\n <MerchantCardLogo />\n )}\n </div>\n )}\n </div>\n )\n}\n\ninterface MerchantCardHeaderProps {\n isDefault?: boolean\n withLogo?: boolean\n}\n\nfunction MerchantCardHeader({\n isDefault,\n withLogo,\n className,\n ...props\n}: React.ComponentProps<'div'> & MerchantCardHeaderProps) {\n const {cardTheme} = useMerchantCardContext()\n\n const isBranded =\n cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'\n\n return (\n <div\n className={cn('relative overflow-hidden flex-1 flex-wrap', className)}\n {...props}\n >\n {isBranded && !isDefault ? (\n <MerchantCardBrandedHeader withLogo={withLogo} />\n ) : (\n <MerchantCardDefaultHeader withLogo={withLogo} />\n )}\n </div>\n )\n}\n\nexport interface MerchantCardProps {\n shop: Shop\n touchable?: boolean\n featuredImagesLimit?: number\n children?: React.ReactNode\n}\n\nfunction MerchantCard({\n shop,\n touchable = true,\n featuredImagesLimit = 4,\n children,\n}: MerchantCardProps) {\n const {navigateToShop} = useShopNavigation()\n\n const {id, visualTheme} = shop\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n navigateToShop({shopId: id})\n }, [navigateToShop, id, touchable])\n\n const cardTheme = useMemo(\n () => extractBrandTheme(visualTheme?.brandSettings),\n [visualTheme?.brandSettings]\n )\n\n const contextValue = useMemo<MerchantCardContextValue>(\n () => ({\n shop,\n cardTheme,\n touchable,\n featuredImagesLimit,\n onClick: handleClick,\n }),\n [shop, cardTheme, touchable, featuredImagesLimit, handleClick]\n )\n\n return (\n <MerchantCardContext.Provider value={contextValue}>\n {children ?? (\n <MerchantCardContainer>\n <MerchantCardHeader withLogo />\n <MerchantCardInfo>\n <MerchantCardName />\n <MerchantCardRating />\n </MerchantCardInfo>\n </MerchantCardContainer>\n )}\n </MerchantCardContext.Provider>\n )\n}\n\nexport {\n MerchantCard,\n MerchantCardContainer,\n MerchantCardHeader,\n MerchantCardInfo,\n MerchantCardName,\n MerchantCardRating,\n}\n"],"names":["MerchantCardContext","createContext","useMerchantCardContext","context","useContext","MerchantCardContainer","className","props","touchable","cardTheme","onClick","content","jsx","cn","Touchable","MerchantCardImage","src","alt","thumbhash","ThumbhashImage","MerchantCardLogo","shop","name","visualTheme","logoAverageColor","logoDominantColor","logoColor","logoBackgroundClassName","useMemo","isDarkColor","logoUrl","altText","MerchantCardInfo","textColor","MerchantCardName","children","nameContent","MerchantCardRating","averageRating","reviewCount","jsxs","Star","normalizeRating","formatReviewCount","MerchantCardDefaultHeader","withLogo","featuredImagesLimit","featuredImages","getFeaturedImages","numberOfFeaturedImages","heightClass","image","index","MerchantCardBrandedHeader","wordmarkImage","Fragment","MerchantCardHeader","isDefault","isBranded","MerchantCard","navigateToShop","useShopNavigation","id","handleClick","useCallback","extractBrandTheme","contextValue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merchant-card.js","sources":["../../../src/components/commerce/merchant-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useCallback, useContext, useMemo} from 'react'\n\nimport {type Shop} from '@shopify/shop-minis-platform'\nimport {Star} from 'lucide-react'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {cn} from '../../lib/utils'\nimport {\n type ExtractedBrandTheme,\n extractBrandTheme,\n formatReviewCount,\n getFeaturedImages,\n normalizeRating,\n} from '../../utils'\nimport {isDarkColor} from '../../utils/colors'\nimport {Image} from '../atoms/image'\nimport {Touchable} from '../atoms/touchable'\n\ninterface MerchantCardContextValue {\n // Core data\n shop: Shop\n\n // Derived data\n cardTheme: ExtractedBrandTheme\n\n // UI configuration\n touchable: boolean\n featuredImagesLimit: number\n\n // Actions\n onClick: () => void\n}\n\nconst MerchantCardContext = createContext<MerchantCardContextValue | undefined>(\n undefined\n)\n\nfunction useMerchantCardContext() {\n const context = useContext(MerchantCardContext)\n if (!context) {\n throw new Error(\n 'useMerchantCardContext must be used within a MerchantCardProvider'\n )\n }\n return context\n}\n\nfunction MerchantCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, cardTheme, onClick} = useMerchantCardContext()\n\n const content = (\n <div\n style={{\n backgroundColor: cardTheme.backgroundColor,\n }}\n className={cn(\n 'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square',\n\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 MerchantCardImage({\n className,\n src,\n alt,\n thumbhash,\n ...props\n}: React.ComponentProps<'img'> & {\n src?: string\n alt?: string\n thumbhash?: string\n}) {\n if (!src) {\n return <div className=\"w-full h-full bg-gray-100\" />\n }\n\n if (thumbhash) {\n return (\n <Image\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n thumbhash={thumbhash}\n className={cn(className)}\n {...props}\n />\n )\n }\n\n return (\n <img\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n}\n\nfunction MerchantCardLogo({className, ...props}: React.ComponentProps<'div'>) {\n const {shop} = useMerchantCardContext()\n const {name, visualTheme} = shop\n\n const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage\n const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant\n const logoColor = logoAverageColor || logoDominantColor\n\n const logoBackgroundClassName = useMemo(\n () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),\n [logoColor]\n )\n\n const logoUrl = visualTheme?.logoImage?.url\n const altText = `${name} logo`\n\n return (\n <div\n data-slot=\"merchant-card-logo\"\n className={cn(\n 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',\n 'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',\n 'flex items-center justify-center overflow-hidden',\n logoBackgroundClassName,\n className\n )}\n {...props}\n >\n {logoUrl ? (\n <img\n src={logoUrl}\n alt={altText}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"w-full h-full bg-gray-200 flex items-center justify-center\">\n <span className=\"text-gray-600 font-semibold text-lg\">\n {name?.slice(0, 1)}\n </span>\n </div>\n )}\n </div>\n )\n}\n\nfunction MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {cardTheme} = useMerchantCardContext()\n\n const isDarkTheme = useMemo(() => {\n return (\n cardTheme.backgroundColor !== 'white' &&\n isDarkColor(cardTheme.backgroundColor)\n )\n }, [cardTheme.backgroundColor])\n\n const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'\n\n return (\n <div\n data-slot=\"merchant-card-info\"\n className={cn(\n 'p-3 flex-shrink-0 flex flex-col min-w-0',\n textColor,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MerchantCardName({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {shop} = useMerchantCardContext()\n const {name} = shop\n const nameContent = children ?? name\n\n return (\n <h3\n data-slot=\"merchant-card-name\"\n className={cn('text-sm font-medium truncate', className)}\n {...props}\n >\n {nameContent}\n </h3>\n )\n}\n\nfunction MerchantCardRating({\n className,\n\n ...props\n}: React.ComponentProps<'div'> & {\n rating?: number | null\n reviewCount?: number\n}) {\n const {shop} = useMerchantCardContext()\n\n const {\n reviewAnalytics: {averageRating, reviewCount},\n } = shop\n\n if (!averageRating || !reviewCount) return null\n\n return (\n <div\n data-slot=\"merchant-card-rating\"\n className={cn('flex items-center gap-1 text-xs', className)}\n {...props}\n >\n <Star className=\"h-3 w-3 fill-current\" />\n <span className=\"text-xs\">\n {normalizeRating(averageRating)} ({formatReviewCount(reviewCount)})\n </span>\n </div>\n )\n}\n\nfunction MerchantCardDefaultHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme, featuredImagesLimit} = useMerchantCardContext()\n const {visualTheme} = shop\n\n const featuredImages = useMemo(\n () => getFeaturedImages(visualTheme, featuredImagesLimit),\n [visualTheme, featuredImagesLimit]\n )\n\n const numberOfFeaturedImages = featuredImages?.length ?? 0\n\n const displayDefaultCover = () => {\n if (numberOfFeaturedImages > 0) {\n const heightClass = numberOfFeaturedImages === 2 ? 'h-full' : 'h-1/2'\n return featuredImages?.map((image, index) => (\n <div className={`z-0 w-1/2 ${heightClass}`} key={image.url || index}>\n <MerchantCardImage\n src={image.url}\n alt={image.altText ?? undefined}\n thumbhash={image.thumbhash ?? undefined}\n className=\"aspect-square\"\n />\n </div>\n ))\n } else if (cardTheme.type === 'coverImage') {\n return (\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n thumbhash={cardTheme.coverImageThumbhash}\n />\n )\n }\n }\n\n return (\n <div className=\"w-full h-full bg-muted relative flex flex-wrap overflow-hidden\">\n {withLogo && (\n <div className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10\">\n <MerchantCardLogo />\n </div>\n )}\n\n {displayDefaultCover()}\n </div>\n )\n}\n\nfunction MerchantCardBrandedHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme} = useMerchantCardContext()\n const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark\n\n return (\n <div className=\"size-full relative\">\n {cardTheme.type === 'coverImage' && (\n <>\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n alt={shop.name}\n thumbhash={cardTheme.coverImageThumbhash ?? undefined}\n />\n\n <div className=\"absolute inset-0 z-[1] bg-black/20\" />\n\n <div\n className=\"absolute bottom-0 z-[1] size-full\"\n style={{\n background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,\n }}\n />\n </>\n )}\n\n {withLogo && (\n <div className=\"absolute inset-0 z-[1] flex items-center justify-center\">\n {wordmarkImage ? (\n <img\n src={wordmarkImage.url}\n alt={wordmarkImage.altText || shop.name}\n className=\"max-h-16 min-h-10 max-w-28 object-contain\"\n data-testid=\"store-data-wordmark\"\n />\n ) : (\n <MerchantCardLogo />\n )}\n </div>\n )}\n </div>\n )\n}\n\ninterface MerchantCardHeaderProps {\n isDefault?: boolean\n withLogo?: boolean\n}\n\nfunction MerchantCardHeader({\n isDefault,\n withLogo,\n className,\n ...props\n}: React.ComponentProps<'div'> & MerchantCardHeaderProps) {\n const {cardTheme} = useMerchantCardContext()\n\n const isBranded =\n cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'\n\n return (\n <div\n className={cn('relative overflow-hidden flex-1 flex-wrap', className)}\n {...props}\n >\n {isBranded && !isDefault ? (\n <MerchantCardBrandedHeader withLogo={withLogo} />\n ) : (\n <MerchantCardDefaultHeader withLogo={withLogo} />\n )}\n </div>\n )\n}\n\nexport interface MerchantCardProps {\n shop: Shop\n touchable?: boolean\n featuredImagesLimit?: number\n children?: React.ReactNode\n}\n\nfunction MerchantCard({\n shop,\n touchable = true,\n featuredImagesLimit = 4,\n children,\n}: MerchantCardProps) {\n const {navigateToShop} = useShopNavigation()\n\n const {id, visualTheme} = shop\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n navigateToShop({shopId: id})\n }, [navigateToShop, id, touchable])\n\n const cardTheme = useMemo(\n () => extractBrandTheme(visualTheme?.brandSettings),\n [visualTheme?.brandSettings]\n )\n\n const contextValue = useMemo<MerchantCardContextValue>(\n () => ({\n shop,\n cardTheme,\n touchable,\n featuredImagesLimit,\n onClick: handleClick,\n }),\n [shop, cardTheme, touchable, featuredImagesLimit, handleClick]\n )\n\n return (\n <MerchantCardContext.Provider value={contextValue}>\n {children ?? (\n <MerchantCardContainer>\n <MerchantCardHeader withLogo />\n <MerchantCardInfo>\n <MerchantCardName />\n <MerchantCardRating />\n </MerchantCardInfo>\n </MerchantCardContainer>\n )}\n </MerchantCardContext.Provider>\n )\n}\n\nexport {\n MerchantCard,\n MerchantCardContainer,\n MerchantCardHeader,\n MerchantCardInfo,\n MerchantCardName,\n MerchantCardRating,\n}\n"],"names":["MerchantCardContext","createContext","useMerchantCardContext","context","useContext","MerchantCardContainer","className","props","touchable","cardTheme","onClick","content","jsx","cn","Touchable","MerchantCardImage","src","alt","thumbhash","Image","MerchantCardLogo","shop","name","visualTheme","logoAverageColor","logoDominantColor","logoColor","logoBackgroundClassName","useMemo","isDarkColor","logoUrl","altText","MerchantCardInfo","textColor","MerchantCardName","children","nameContent","MerchantCardRating","averageRating","reviewCount","jsxs","Star","normalizeRating","formatReviewCount","MerchantCardDefaultHeader","withLogo","featuredImagesLimit","featuredImages","getFeaturedImages","numberOfFeaturedImages","heightClass","image","index","MerchantCardBrandedHeader","wordmarkImage","Fragment","MerchantCardHeader","isDefault","isBranded","MerchantCard","navigateToShop","useShopNavigation","id","handleClick","useCallback","extractBrandTheme","contextValue"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as o, jsxs as v } from "react/jsx-runtime";
|
|
2
2
|
import * as N from "react";
|
|
3
3
|
import { useState as F, useCallback as C, useMemo as A, useContext as k } from "react";
|
|
4
|
-
import { useShopNavigation as
|
|
5
|
-
import { useSavedProductsActions as
|
|
6
|
-
import { formatMoney as
|
|
4
|
+
import { useShopNavigation as V } from "../../hooks/navigation/useShopNavigation.js";
|
|
5
|
+
import { useSavedProductsActions as j } from "../../hooks/user/useSavedProductsActions.js";
|
|
6
|
+
import { formatMoney as B } from "../../lib/formatMoney.js";
|
|
7
7
|
import { cn as s } from "../../lib/utils.js";
|
|
8
|
-
import { FavoriteButton as
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { FavoriteButton as T } from "../atoms/favorite-button.js";
|
|
9
|
+
import { Image as E } from "../atoms/image.js";
|
|
10
|
+
import { ProductVariantPrice as O } from "../atoms/product-variant-price.js";
|
|
11
11
|
import { Touchable as S } from "../atoms/touchable.js";
|
|
12
12
|
import { Badge as z } from "../ui/badge.js";
|
|
13
13
|
const I = N.createContext(void 0);
|
|
@@ -69,7 +69,7 @@ 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
|
+
E,
|
|
73
73
|
{
|
|
74
74
|
"data-slot": "product-card-image",
|
|
75
75
|
src: l,
|
|
@@ -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(T, { 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
|
+
O,
|
|
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(w, { position: "top-left", children:
|
|
182
|
+
return !n || !i ? null : /* @__PURE__ */ o(w, { position: "top-left", children: B(i, n) });
|
|
183
183
|
}
|
|
184
184
|
function ot({
|
|
185
185
|
product: t,
|
|
@@ -192,7 +192,7 @@ function ot({
|
|
|
192
192
|
onFavoriteToggled: c,
|
|
193
193
|
children: p
|
|
194
194
|
}) {
|
|
195
|
-
const { navigateToProduct: l } =
|
|
195
|
+
const { navigateToProduct: l } = V(), { saveProduct: h, unsaveProduct: g } = j(), [f, P] = F(t.isFavorited), y = C(() => {
|
|
196
196
|
a && (d?.(), l({
|
|
197
197
|
productId: t.id
|
|
198
198
|
}));
|
|
@@ -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 {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {ThumbhashImage} from '../atoms/thumbhash-image'\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 <ThumbhashImage\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","ThumbhashImage","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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"}
|
|
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,27 +1,34 @@
|
|
|
1
|
-
import { jsx as r, Fragment as
|
|
2
|
-
import { ContentWrapper as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { jsx as r, Fragment as o } from "react/jsx-runtime";
|
|
2
|
+
import { ContentWrapper as s } from "../atoms/content-wrapper.js";
|
|
3
|
+
import { Image as f } from "../atoms/image.js";
|
|
4
|
+
function C({
|
|
5
|
+
onLoad: e,
|
|
6
|
+
width: h,
|
|
7
|
+
height: t,
|
|
8
|
+
className: g,
|
|
9
|
+
publicId: p,
|
|
10
|
+
externalId: m,
|
|
11
|
+
Loader: a
|
|
11
12
|
}) {
|
|
12
|
-
return /* @__PURE__ */ r(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
return /* @__PURE__ */ r(s, { ...m ? { externalId: m } : { publicId: p }, children: ({ content: i, loading: u }) => {
|
|
14
|
+
if (u) return a ? /* @__PURE__ */ r(o, { children: a }) : null;
|
|
15
|
+
const l = i?.image?.width && i?.image?.height ? i.image.width / i.image.height : void 0;
|
|
16
|
+
return /* @__PURE__ */ r(
|
|
17
|
+
f,
|
|
18
|
+
{
|
|
19
|
+
src: i?.image?.url,
|
|
20
|
+
thumbhash: i?.image?.thumbhash,
|
|
21
|
+
width: h,
|
|
22
|
+
height: t,
|
|
23
|
+
alt: i?.title,
|
|
24
|
+
onLoad: e,
|
|
25
|
+
className: g,
|
|
26
|
+
aspectRatio: l
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
} });
|
|
23
30
|
}
|
|
24
31
|
export {
|
|
25
|
-
|
|
32
|
+
C as ImageContentWrapper
|
|
26
33
|
};
|
|
27
34
|
//# sourceMappingURL=image-content-wrapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-content-wrapper.js","sources":["../../../src/components/content/image-content-wrapper.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"image-content-wrapper.js","sources":["../../../src/components/content/image-content-wrapper.tsx"],"sourcesContent":["import {ContentWrapper} from '../atoms/content-wrapper'\nimport {Image} from '../atoms/image'\n\ntype ImageContentWrapperProps = (\n | {publicId: string; externalId?: never}\n | {externalId: string; publicId?: never}\n) & {\n onLoad?: () => void\n width?: number\n height?: number\n className?: string\n Loader?: React.ReactNode | string\n}\n\nexport function ImageContentWrapper({\n onLoad,\n width,\n height,\n className,\n publicId,\n externalId,\n Loader,\n}: ImageContentWrapperProps) {\n return (\n <ContentWrapper {...(externalId ? {externalId} : {publicId: publicId!})}>\n {({content, loading}) => {\n if (loading) return Loader ? <>{Loader}</> : null\n\n const aspectRatio =\n content?.image?.width && content?.image?.height\n ? content.image.width / content.image.height\n : undefined\n\n return (\n <Image\n src={content?.image?.url}\n thumbhash={content?.image?.thumbhash}\n width={width}\n height={height}\n alt={content?.title}\n onLoad={onLoad}\n className={className}\n aspectRatio={aspectRatio}\n />\n )\n }}\n </ContentWrapper>\n )\n}\n"],"names":["ImageContentWrapper","onLoad","width","height","className","publicId","externalId","Loader","jsx","ContentWrapper","content","loading","Fragment","aspectRatio","Image"],"mappings":";;;AAcO,SAASA,EAAoB;AAAA,EAClC,QAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,QAAAC;AACF,GAA6B;AAC3B,SACG,gBAAAC,EAAAC,GAAA,EAAgB,GAAIH,IAAa,EAAC,YAAAA,EAAU,IAAI,EAAC,UAAAD,EAC/C,GAAA,UAAA,CAAC,EAAC,SAAAK,GAAS,SAAAC,QAAa;AACvB,QAAIA,EAAS,QAAOJ,IAAS,gBAAAC,EAAAI,GAAA,EAAG,YAAO,CAAA,IAAM;AAE7C,UAAMC,IACJH,GAAS,OAAO,SAASA,GAAS,OAAO,SACrCA,EAAQ,MAAM,QAAQA,EAAQ,MAAM,SACpC;AAGJ,WAAA,gBAAAF;AAAA,MAACM;AAAA,MAAA;AAAA,QACC,KAAKJ,GAAS,OAAO;AAAA,QACrB,WAAWA,GAAS,OAAO;AAAA,QAC3B,OAAAR;AAAA,QACA,QAAAC;AAAA,QACA,KAAKO,GAAS;AAAA,QACd,QAAAT;AAAA,QACA,WAAAG;AAAA,QACA,aAAAS;AAAA,MAAA;AAAA,IACF;AAAA,EAAA,GAGN;AAEJ;"}
|
|
@@ -1,40 +1,34 @@
|
|
|
1
|
-
import { useState as
|
|
1
|
+
import { useState as g, useCallback as p } from "react";
|
|
2
2
|
import { useHandleAction as f } from "../../internal/useHandleAction.js";
|
|
3
3
|
import { useShopActions as d } from "../../internal/useShopActions.js";
|
|
4
|
-
import { useImageUpload as
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
async (m) => {
|
|
4
|
+
import { useImageUpload as I } from "../storage/useImageUpload.js";
|
|
5
|
+
const U = () => {
|
|
6
|
+
const { createContent: t } = d(), { uploadImage: a } = I(), [r, o] = g(!1), i = p(
|
|
7
|
+
async (s) => {
|
|
9
8
|
o(!0);
|
|
10
|
-
const { image: e, contentTitle:
|
|
9
|
+
const { image: e, contentTitle: l, visibility: m } = s;
|
|
11
10
|
if (!e.type)
|
|
12
11
|
throw new Error("Unable to determine file type");
|
|
13
12
|
if (!e.type.startsWith("image/"))
|
|
14
13
|
throw new Error("Invalid file type: must be an image");
|
|
15
|
-
const [c] = await a(
|
|
16
|
-
|
|
17
|
-
mimeType: e.type,
|
|
18
|
-
uri: await I(e)
|
|
19
|
-
}
|
|
20
|
-
]), r = c.imageUrl;
|
|
21
|
-
if (!r)
|
|
14
|
+
const [c] = await a(e), n = c.imageUrl;
|
|
15
|
+
if (!n)
|
|
22
16
|
throw new Error("Image upload failed");
|
|
23
|
-
const
|
|
24
|
-
title:
|
|
25
|
-
imageUrl:
|
|
26
|
-
visibility:
|
|
17
|
+
const u = await t({
|
|
18
|
+
title: l,
|
|
19
|
+
imageUrl: n,
|
|
20
|
+
visibility: m
|
|
27
21
|
});
|
|
28
|
-
return o(!1),
|
|
22
|
+
return o(!1), u;
|
|
29
23
|
},
|
|
30
24
|
[t, a]
|
|
31
25
|
);
|
|
32
26
|
return {
|
|
33
|
-
createImageContent: f(
|
|
34
|
-
loading:
|
|
27
|
+
createImageContent: f(i),
|
|
28
|
+
loading: r
|
|
35
29
|
};
|
|
36
30
|
};
|
|
37
31
|
export {
|
|
38
|
-
|
|
32
|
+
U as useCreateImageContent
|
|
39
33
|
};
|
|
40
34
|
//# sourceMappingURL=useCreateImageContent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateImageContent.js","sources":["../../../src/hooks/content/useCreateImageContent.ts"],"sourcesContent":["import {useCallback, useState} from 'react'\n\nimport {\n ContentVisibility,\n Content,\n ContentCreateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\nimport {
|
|
1
|
+
{"version":3,"file":"useCreateImageContent.js","sources":["../../../src/hooks/content/useCreateImageContent.ts"],"sourcesContent":["import {useCallback, useState} from 'react'\n\nimport {\n ContentVisibility,\n Content,\n ContentCreateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\nimport {useImageUpload} from '../storage/useImageUpload'\n\ninterface CreateImageContentParams {\n image: File\n contentTitle: string\n visibility?: ContentVisibility[] | null\n}\n\ninterface UseCreateImageContentReturns {\n /**\n * Upload an image and create content.\n */\n createImageContent: (\n params: CreateImageContentParams\n ) => Promise<{data: Content; userErrors?: ContentCreateUserErrors[]}>\n /**\n * Whether the content is being created.\n */\n loading: boolean\n}\n\nexport const useCreateImageContent = (): UseCreateImageContentReturns => {\n const {createContent} = useShopActions()\n const {uploadImage} = useImageUpload()\n const [loading, setLoading] = useState(false)\n\n const createImageContent = useCallback(\n async (params: CreateImageContentParams) => {\n setLoading(true)\n\n const {image, contentTitle, visibility} = params\n\n if (!image.type) {\n throw new Error('Unable to determine file type')\n }\n if (!image.type.startsWith('image/')) {\n throw new Error('Invalid file type: must be an image')\n }\n\n const [uploadImageResult] = await uploadImage(image)\n const uploadImageUrl = uploadImageResult.imageUrl\n\n if (!uploadImageUrl) {\n throw new Error('Image upload failed')\n }\n\n const createContentResult = await createContent({\n title: contentTitle,\n imageUrl: uploadImageUrl,\n visibility,\n })\n\n setLoading(false)\n\n return createContentResult\n },\n [createContent, uploadImage]\n )\n\n return {\n createImageContent: useHandleAction(createImageContent),\n loading,\n }\n}\n"],"names":["useCreateImageContent","createContent","useShopActions","uploadImage","useImageUpload","loading","setLoading","useState","createImageContent","useCallback","params","image","contentTitle","visibility","uploadImageResult","uploadImageUrl","createContentResult","useHandleAction"],"mappings":";;;;AA+BO,MAAMA,IAAwB,MAAoC;AACjE,QAAA,EAAC,eAAAC,EAAa,IAAIC,EAAe,GACjC,EAAC,aAAAC,EAAW,IAAIC,EAAe,GAC/B,CAACC,GAASC,CAAU,IAAIC,EAAS,EAAK,GAEtCC,IAAqBC;AAAA,IACzB,OAAOC,MAAqC;AAC1C,MAAAJ,EAAW,EAAI;AAEf,YAAM,EAAC,OAAAK,GAAO,cAAAC,GAAc,YAAAC,EAAc,IAAAH;AAEtC,UAAA,CAACC,EAAM;AACH,cAAA,IAAI,MAAM,+BAA+B;AAEjD,UAAI,CAACA,EAAM,KAAK,WAAW,QAAQ;AAC3B,cAAA,IAAI,MAAM,qCAAqC;AAGvD,YAAM,CAACG,CAAiB,IAAI,MAAMX,EAAYQ,CAAK,GAC7CI,IAAiBD,EAAkB;AAEzC,UAAI,CAACC;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGjC,YAAAC,IAAsB,MAAMf,EAAc;AAAA,QAC9C,OAAOW;AAAA,QACP,UAAUG;AAAA,QACV,YAAAF;AAAA,MAAA,CACD;AAED,aAAAP,EAAW,EAAK,GAETU;AAAA,IACT;AAAA,IACA,CAACf,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBc,EAAgBT,CAAkB;AAAA,IACtD,SAAAH;AAAA,EACF;AACF;"}
|