@shopify/shop-minis-react 0.0.25 → 0.0.26

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.
Files changed (42) hide show
  1. package/dist/_virtual/index10.js +2 -2
  2. package/dist/_virtual/index4.js +2 -3
  3. package/dist/_virtual/index4.js.map +1 -1
  4. package/dist/_virtual/index7.js +3 -2
  5. package/dist/_virtual/index7.js.map +1 -1
  6. package/dist/_virtual/index9.js +2 -2
  7. package/dist/components/atoms/product-variant-price.js +61 -0
  8. package/dist/components/atoms/product-variant-price.js.map +1 -0
  9. package/dist/components/commerce/product-card.js +120 -153
  10. package/dist/components/commerce/product-card.js.map +1 -1
  11. package/dist/components/commerce/product-link.js +12 -16
  12. package/dist/components/commerce/product-link.js.map +1 -1
  13. package/dist/components/content/content-monitor.js +17 -0
  14. package/dist/components/content/content-monitor.js.map +1 -0
  15. package/dist/components/content/content-wrapper.js +17 -0
  16. package/dist/components/content/content-wrapper.js.map +1 -0
  17. package/dist/hooks/content/useContent.js +24 -0
  18. package/dist/hooks/content/useContent.js.map +1 -0
  19. package/dist/hooks/content/useCreateImageContent.js +21 -18
  20. package/dist/hooks/content/useCreateImageContent.js.map +1 -1
  21. package/dist/index.js +227 -223
  22. package/dist/index.js.map +1 -1
  23. package/dist/mocks.js +21 -6
  24. package/dist/mocks.js.map +1 -1
  25. package/dist/shop-minis-platform/src/types/content.js +5 -0
  26. package/dist/shop-minis-platform/src/types/content.js.map +1 -0
  27. 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
  28. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
  29. package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  30. 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
  31. package/package.json +5 -4
  32. package/src/components/atoms/product-variant-price.tsx +74 -0
  33. package/src/components/commerce/product-card.tsx +7 -56
  34. package/src/components/commerce/product-link.tsx +0 -2
  35. package/src/components/content/content-monitor.tsx +23 -0
  36. package/src/components/content/content-wrapper.tsx +56 -0
  37. package/src/components/index.ts +2 -0
  38. package/src/hooks/content/useContent.ts +50 -0
  39. package/src/hooks/content/useCreateImageContent.ts +20 -5
  40. package/src/mocks.ts +15 -0
  41. package/src/stories/ProductVariantPrice.stories.tsx +73 -0
  42. package/src/stories/Toaster.stories.tsx +2 -2
@@ -202,27 +202,23 @@ function ut({ product: o }) {
202
202
  const { navigateToProduct: e } = j(), { saveProduct: r, unsaveProduct: i } = O(), {
203
203
  id: a,
204
204
  title: s,
205
- featuredImage: p,
205
+ featuredImage: u,
206
206
  reviewAnalytics: P,
207
- price: l,
208
- compareAtPrice: d,
207
+ price: m,
208
+ compareAtPrice: p,
209
209
  isFavorited: z,
210
210
  selectedVariant: f,
211
211
  defaultVariantId: h,
212
212
  shop: g
213
- } = o, [x, C] = w.useState(z), y = P?.averageRating, N = P?.reviewCount;
214
- l?.currencyCode;
215
- const v = l?.amount ? b(l?.amount, l?.currencyCode) : void 0, I = p?.url, T = p?.altText || s, k = d?.amount ? b(d?.amount, d?.currencyCode) : void 0;
216
- d?.currencyCode;
217
- const L = k && k !== v, R = w.useCallback(() => {
213
+ } = o, [x, C] = w.useState(z), y = P?.averageRating, N = P?.reviewCount, v = m?.amount ? b(m?.amount, m?.currencyCode) : void 0, I = u?.url, T = u?.altText || s, k = p?.amount ? b(p?.amount, p?.currencyCode) : void 0, L = k && k !== v, R = w.useCallback(() => {
218
214
  e({
219
215
  productId: a
220
216
  });
221
217
  }, [e, a]), S = w.useCallback(async () => {
222
- const u = x;
223
- C(!u);
218
+ const l = x;
219
+ C(!l);
224
220
  try {
225
- u ? await i({
221
+ l ? await i({
226
222
  productId: a,
227
223
  shopId: g.id,
228
224
  productVariantId: f?.id || h
@@ -232,7 +228,7 @@ function ut({ product: o }) {
232
228
  productVariantId: f?.id || h
233
229
  });
234
230
  } catch {
235
- C(u);
231
+ C(l);
236
232
  }
237
233
  }, [
238
234
  x,
@@ -261,16 +257,16 @@ function ut({ product: o }) {
261
257
  /* @__PURE__ */ c(J, { layout: "horizontal", children: [
262
258
  /* @__PURE__ */ t(K, { children: s }),
263
259
  N && y && /* @__PURE__ */ t(Z, { children: /* @__PURE__ */ c("div", { className: "flex items-center gap-1", children: [
264
- Array.from({ length: 5 }, (u, m) => /* @__PURE__ */ t(
260
+ Array.from({ length: 5 }, (l, d) => /* @__PURE__ */ t(
265
261
  U,
266
262
  {
267
- fill: m < Math.floor(y) ? "currentColor" : "none",
263
+ fill: d < Math.floor(y) ? "currentColor" : "none",
268
264
  className: n(
269
265
  "h-3 w-3",
270
- m < Math.floor(y) ? "text-primary" : "text-gray-300"
266
+ d < Math.floor(y) ? "text-primary" : "text-gray-300"
271
267
  )
272
268
  },
273
- m
269
+ d
274
270
  )),
275
271
  /* @__PURE__ */ c("span", { className: "text-xs text-gray-600 ml-1", children: [
276
272
  "(",
@@ -1 +1 @@
1
- {"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Star} from 'lucide-react'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {FavoriteButton} from '../atoms/favorite-button'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn(\n 'flex items-center gap-1 text-xs text-muted-foreground',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n onPress,\n filled = false,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n onPress?: () => void\n filled?: boolean\n}) {\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <FavoriteButton filled={filled} onClick={onPress} />\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkProps {\n product: Product\n}\n\n// Composed ProductLink component\nfunction ProductLink({product}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const currencyCode = price?.currencyCode\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const compareAtPriceCurrencyCode = compareAtPrice?.currencyCode\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n }, [navigateToProduct, id])\n\n const handleActionPress = React.useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n isFavoritedLocal,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n unsaveProduct,\n ])\n\n return (\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {reviewCount && averageRating && (\n <ProductLinkRating>\n <div className=\"flex items-center gap-1\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n fill={\n i < Math.floor(averageRating!) ? 'currentColor' : 'none'\n }\n className={cn(\n 'h-3 w-3',\n i < Math.floor(averageRating!)\n ? 'text-primary'\n : 'text-gray-300'\n )}\n />\n ))}\n <span className=\"text-xs text-gray-600 ml-1\">\n ({reviewCount})\n </span>\n </div>\n </ProductLinkRating>\n )}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n />\n </ProductLinkRoot>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","filled","CardAction","FavoriteButton","ProductLink","product","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","_","i","Star","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Star} from 'lucide-react'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {FavoriteButton} from '../atoms/favorite-button'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn(\n 'flex items-center gap-1 text-xs text-muted-foreground',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n onPress,\n filled = false,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n onPress?: () => void\n filled?: boolean\n}) {\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <FavoriteButton filled={filled} onClick={onPress} />\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkProps {\n product: Product\n}\n\n// Composed ProductLink component\nfunction ProductLink({product}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n }, [navigateToProduct, id])\n\n const handleActionPress = React.useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n isFavoritedLocal,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n unsaveProduct,\n ])\n\n return (\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {reviewCount && averageRating && (\n <ProductLinkRating>\n <div className=\"flex items-center gap-1\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n fill={\n i < Math.floor(averageRating!) ? 'currentColor' : 'none'\n }\n className={cn(\n 'h-3 w-3',\n i < Math.floor(averageRating!)\n ? 'text-primary'\n : 'text-gray-300'\n )}\n />\n ))}\n <span className=\"text-xs text-gray-600 ml-1\">\n ({reviewCount})\n </span>\n </div>\n </ProductLinkRating>\n )}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n />\n </ProductLinkRoot>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","filled","CardAction","FavoriteButton","ProductLink","product","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","_","i","Star","Fragment"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { jsx as r } from "react/jsx-runtime";
2
+ import { Touchable as t } from "../atoms/touchable.js";
3
+ function i({
4
+ // publicId,
5
+ children: o
6
+ }) {
7
+ return /* @__PURE__ */ r(
8
+ t,
9
+ {
10
+ children: o
11
+ }
12
+ );
13
+ }
14
+ export {
15
+ i as ContentMonitor
16
+ };
17
+ //# sourceMappingURL=content-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-monitor.js","sources":["../../../src/components/content/content-monitor.tsx"],"sourcesContent":["// import {useShopActions} from '../../internal/useShopActions'\nimport {Touchable} from '../atoms/touchable'\n\nexport function ContentMonitor({\n // publicId,\n children,\n}: {\n publicId: string\n children: React.ReactNode\n}) {\n // const {showFeedbackSheet} = useShopActions()\n\n return (\n <Touchable\n // TODO: Add long press support to Touchable\n // onLongPress={() => {\n // showFeedbackSheet({publicId})\n // }}\n >\n {children}\n </Touchable>\n )\n}\n"],"names":["ContentMonitor","children","jsx","Touchable"],"mappings":";;AAGO,SAASA,EAAe;AAAA;AAAA,EAE7B,UAAAC;AACF,GAGG;AAIC,SAAA,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MAME,UAAAF;AAAA,IAAA;AAAA,EACH;AAEJ;"}
@@ -0,0 +1,17 @@
1
+ import { jsx as c } from "react/jsx-runtime";
2
+ import { useContent as p } from "../../hooks/content/useContent.js";
3
+ import { ContentMonitor as m } from "./content-monitor.js";
4
+ function d({
5
+ publicId: r,
6
+ externalId: e,
7
+ children: o
8
+ }) {
9
+ const { content: i, loading: t } = p({
10
+ identifiers: [{ publicId: r, externalId: e }]
11
+ }), n = i?.[0];
12
+ return t || !n ? o({ loading: t }) : /* @__PURE__ */ c(m, { publicId: n.publicId, children: o({ content: n, loading: t }) });
13
+ }
14
+ export {
15
+ d as ContentWrapper
16
+ };
17
+ //# sourceMappingURL=content-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-wrapper.js","sources":["../../../src/components/content/content-wrapper.tsx"],"sourcesContent":["import {useContent} from '../../hooks/content/useContent'\nimport {Content} from '../../types'\n\nimport {ContentMonitor} from './content-monitor'\n\ninterface BaseContentWrapperProps {\n children: ({\n content,\n loading,\n }: {\n content?: Content\n loading: boolean\n }) => JSX.Element | null\n}\n\ninterface PublicIdContentWrapperProps extends BaseContentWrapperProps {\n publicId: string\n externalId?: never\n}\n\ninterface ExternalIdContentWrapperProps extends BaseContentWrapperProps {\n externalId: string\n publicId?: never\n}\n\ntype ContentWrapperProps =\n | PublicIdContentWrapperProps\n | ExternalIdContentWrapperProps\n\n// It's too messy in the docs to show the complete types here so we show a simplified version\nexport interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {\n publicId?: string\n externalId?: string\n}\n\nexport function ContentWrapper({\n publicId,\n externalId,\n children,\n}: ContentWrapperProps) {\n const {content, loading} = useContent({\n identifiers: [{publicId, externalId}],\n })\n\n const contentItem = content?.[0]\n\n if (loading || !contentItem) {\n return children({loading})\n }\n\n return (\n <ContentMonitor publicId={contentItem.publicId}>\n {children({content: contentItem, loading})}\n </ContentMonitor>\n )\n}\n"],"names":["ContentWrapper","publicId","externalId","children","content","loading","useContent","contentItem","jsx","ContentMonitor"],"mappings":";;;AAmCO,SAASA,EAAe;AAAA,EAC7B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AACF,GAAwB;AACtB,QAAM,EAAC,SAAAC,GAAS,SAAAC,EAAO,IAAIC,EAAW;AAAA,IACpC,aAAa,CAAC,EAAC,UAAAL,GAAU,YAAAC,EAAW,CAAA;AAAA,EAAA,CACrC,GAEKK,IAAcH,IAAU,CAAC;AAE3B,SAAAC,KAAW,CAACE,IACPJ,EAAS,EAAC,SAAAE,GAAQ,IAIzB,gBAAAG,EAACC,GAAe,EAAA,UAAUF,EAAY,UACnC,UAASJ,EAAA,EAAC,SAASI,GAAa,SAAAF,EAAO,CAAC,EAC3C,CAAA;AAEJ;"}
@@ -0,0 +1,24 @@
1
+ import { useMemo as m } from "react";
2
+ import { useShopActions as u } from "../../internal/useShopActions.js";
3
+ import { useShopActionsDataFetching as a } from "../../internal/useShopActionsDataFetching.js";
4
+ const l = (o) => {
5
+ const { getContent: e } = u(), { identifiers: n, skip: s = !1, ...r } = o, { data: t, ...i } = a(
6
+ e,
7
+ {
8
+ identifiers: n,
9
+ ...r
10
+ },
11
+ {
12
+ skip: s,
13
+ hook: "useContent"
14
+ }
15
+ ), c = m(() => t ?? null, [t]);
16
+ return {
17
+ ...i,
18
+ content: c
19
+ };
20
+ };
21
+ export {
22
+ l as useContent
23
+ };
24
+ //# sourceMappingURL=useContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useContent.js","sources":["../../../src/hooks/content/useContent.ts"],"sourcesContent":["import {useMemo} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\nimport {useShopActionsDataFetching} from '../../internal/useShopActionsDataFetching'\nimport {\n Content,\n ContentIdentifierInput,\n DataHookOptionsBase,\n DataHookReturnsBase,\n} from '../../types'\n\nexport interface UseContentParams extends DataHookOptionsBase {\n /**\n * The identifiers of the content to fetch.\n */\n identifiers: ContentIdentifierInput | ContentIdentifierInput[]\n}\n\ninterface UseContentReturns extends DataHookReturnsBase {\n /**\n * The content returned from the query.\n */\n content: Content[] | null\n}\n\nexport const useContent = (params: UseContentParams): UseContentReturns => {\n const {getContent} = useShopActions()\n const {identifiers, skip = false, ...restParams} = params\n\n const {data, ...rest} = useShopActionsDataFetching(\n getContent,\n {\n identifiers,\n ...restParams,\n },\n {\n skip,\n hook: 'useContent',\n }\n )\n\n const content = useMemo(() => {\n return data ?? null\n }, [data])\n\n return {\n ...rest,\n content,\n }\n}\n"],"names":["useContent","params","getContent","useShopActions","identifiers","skip","restParams","data","rest","useShopActionsDataFetching","content","useMemo"],"mappings":";;;AAyBa,MAAAA,IAAa,CAACC,MAAgD;AACnE,QAAA,EAAC,YAAAC,EAAU,IAAIC,EAAe,GAC9B,EAAC,aAAAC,GAAa,MAAAC,IAAO,IAAO,GAAGC,EAAc,IAAAL,GAE7C,EAAC,MAAAM,GAAM,GAAGC,EAAA,IAAQC;AAAA,IACtBP;AAAA,IACA;AAAA,MACE,aAAAE;AAAA,MACA,GAAGE;AAAA,IACL;AAAA,IACA;AAAA,MACE,MAAAD;AAAA,MACA,MAAM;AAAA,IAAA;AAAA,EAEV,GAEMK,IAAUC,EAAQ,MACfJ,KAAQ,MACd,CAACA,CAAI,CAAC;AAEF,SAAA;AAAA,IACL,GAAGC;AAAA,IACH,SAAAE;AAAA,EACF;AACF;"}
@@ -1,37 +1,40 @@
1
- import { useCallback as s } from "react";
2
- import { useHandleAction as p } from "../../internal/useHandleAction.js";
3
- import { useShopActions as c } from "../../internal/useShopActions.js";
4
- import { useImageUpload as u } from "../storage/useImageUpload.js";
5
- import { fileToDataUri as g } from "../../utils/imageToDataUri.js";
6
- const C = () => {
7
- const { createContent: t } = c(), { uploadImage: o } = u(), a = s(
8
- async (i) => {
9
- const { image: e, contentTitle: n, visibility: m } = i;
1
+ import { useState as u, useCallback as g } from "react";
2
+ import { useHandleAction as f } from "../../internal/useHandleAction.js";
3
+ import { useShopActions as d } from "../../internal/useShopActions.js";
4
+ import { useImageUpload as w } from "../storage/useImageUpload.js";
5
+ import { fileToDataUri as I } from "../../utils/imageToDataUri.js";
6
+ const E = () => {
7
+ const { createContent: t } = d(), { uploadImage: a } = w(), [i, o] = u(!1), n = g(
8
+ async (m) => {
9
+ o(!0);
10
+ const { image: e, contentTitle: s, visibility: l } = m;
10
11
  if (!e.type)
11
12
  throw new Error("Unable to determine file type");
12
13
  if (!e.type.startsWith("image/"))
13
14
  throw new Error("Invalid file type: must be an image");
14
- const [l] = await o([
15
+ const [c] = await a([
15
16
  {
16
17
  mimeType: e.type,
17
- uri: await g(e)
18
+ uri: await I(e)
18
19
  }
19
- ]), r = l.imageUrl;
20
+ ]), r = c.imageUrl;
20
21
  if (!r)
21
22
  throw new Error("Image upload failed");
22
- return t({
23
- title: n,
23
+ const p = await t({
24
+ title: s,
24
25
  imageUrl: r,
25
- visibility: m
26
+ visibility: l
26
27
  });
28
+ return o(!1), p;
27
29
  },
28
- [t, o]
30
+ [t, a]
29
31
  );
30
32
  return {
31
- createImageContent: p(a)
33
+ createImageContent: f(n),
34
+ loading: i
32
35
  };
33
36
  };
34
37
  export {
35
- C as useCreateImageContent
38
+ E as useCreateImageContent
36
39
  };
37
40
  //# sourceMappingURL=useCreateImageContent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCreateImageContent.js","sources":["../../../src/hooks/content/useCreateImageContent.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {ContentVisibility} from '@shopify/shop-minis-platform'\nimport {CreateContentResponse} from '@shopify/shop-minis-platform/actions'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\nimport {fileToDataUri} from '../../utils'\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<CreateContentResponse>\n}\n\nexport const useCreateImageContent = (): UseCreateImageContentReturns => {\n const {createContent} = useShopActions()\n const {uploadImage} = useImageUpload()\n\n const createImageContent = useCallback(\n async (params: CreateImageContentParams) => {\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([\n {\n mimeType: image.type,\n uri: await fileToDataUri(image),\n },\n ])\n const uploadImageUrl = uploadImageResult.imageUrl\n\n if (!uploadImageUrl) {\n throw new Error('Image upload failed')\n }\n\n return createContent({\n title: contentTitle,\n imageUrl: uploadImageUrl,\n visibility,\n })\n },\n [createContent, uploadImage]\n )\n\n return {\n createImageContent: useHandleAction(createImageContent),\n }\n}\n"],"names":["useCreateImageContent","createContent","useShopActions","uploadImage","useImageUpload","createImageContent","useCallback","params","image","contentTitle","visibility","uploadImageResult","fileToDataUri","uploadImageUrl","useHandleAction"],"mappings":";;;;;AAyBO,MAAMA,IAAwB,MAAoC;AACjE,QAAA,EAAC,eAAAC,EAAa,IAAIC,EAAe,GACjC,EAAC,aAAAC,EAAW,IAAIC,EAAe,GAE/BC,IAAqBC;AAAA,IACzB,OAAOC,MAAqC;AAC1C,YAAM,EAAC,OAAAC,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,MAAMR,EAAY;AAAA,QAC5C;AAAA,UACE,UAAUK,EAAM;AAAA,UAChB,KAAK,MAAMI,EAAcJ,CAAK;AAAA,QAAA;AAAA,MAChC,CACD,GACKK,IAAiBF,EAAkB;AAEzC,UAAI,CAACE;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGvC,aAAOZ,EAAc;AAAA,QACnB,OAAOQ;AAAA,QACP,UAAUI;AAAA,QACV,YAAAH;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,CAACT,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBW,EAAgBT,CAAkB;AAAA,EACxD;AACF;"}
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 {fileToDataUri} from '../../utils'\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([\n {\n mimeType: image.type,\n uri: await fileToDataUri(image),\n },\n ])\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","fileToDataUri","uploadImageUrl","createContentResult","useHandleAction"],"mappings":";;;;;AAgCO,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,EAAY;AAAA,QAC5C;AAAA,UACE,UAAUQ,EAAM;AAAA,UAChB,KAAK,MAAMI,EAAcJ,CAAK;AAAA,QAAA;AAAA,MAChC,CACD,GACKK,IAAiBF,EAAkB;AAEzC,UAAI,CAACE;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGjC,YAAAC,IAAsB,MAAMhB,EAAc;AAAA,QAC9C,OAAOW;AAAA,QACP,UAAUI;AAAA,QACV,YAAAH;AAAA,MAAA,CACD;AAED,aAAAP,EAAW,EAAK,GAETW;AAAA,IACT;AAAA,IACA,CAAChB,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBe,EAAgBV,CAAkB;AAAA,IACtD,SAAAH;AAAA,EACF;AACF;"}