@shopify/shop-minis-react 0.4.12 → 0.4.15

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 (31) hide show
  1. package/dist/components/atoms/product-variant-price.js +1 -1
  2. package/dist/components/atoms/product-variant-price.js.map +1 -1
  3. package/dist/components/commerce/product-card.js +1 -1
  4. package/dist/components/commerce/product-card.js.map +1 -1
  5. package/dist/components/commerce/product-link.js +1 -1
  6. package/dist/components/commerce/product-link.js.map +1 -1
  7. package/dist/hooks/storage/useImageUpload.js +32 -24
  8. package/dist/hooks/storage/useImageUpload.js.map +1 -1
  9. package/dist/index.js +48 -46
  10. package/dist/index.js.map +1 -1
  11. package/dist/mocks.js +70 -50
  12. package/dist/mocks.js.map +1 -1
  13. package/dist/utils/formatMoney.js.map +1 -0
  14. package/eslint/config.cjs +2 -0
  15. package/eslint/index.cjs +4 -0
  16. package/eslint/rules/asset-path-patterns.cjs +30 -0
  17. package/eslint/rules/no-dynamic-asset-paths.cjs +78 -0
  18. package/eslint/rules/no-hardcoded-asset-paths.cjs +124 -0
  19. package/package.json +2 -2
  20. package/src/components/atoms/product-variant-price.test.tsx +1 -1
  21. package/src/components/atoms/product-variant-price.tsx +1 -1
  22. package/src/components/commerce/product-card.test.tsx +1 -1
  23. package/src/components/commerce/product-card.tsx +1 -1
  24. package/src/components/commerce/product-link.test.tsx +1 -1
  25. package/src/components/commerce/product-link.tsx +1 -1
  26. package/src/hooks/storage/useImageUpload.ts +13 -0
  27. package/src/mocks.ts +48 -19
  28. package/src/utils/index.ts +1 -0
  29. package/dist/lib/formatMoney.js.map +0 -1
  30. /package/dist/{lib → utils}/formatMoney.js +0 -0
  31. /package/src/{lib → utils}/formatMoney.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  import { jsx as n, jsxs as h, Fragment as d } from "react/jsx-runtime";
2
- import { formatMoney as a } from "../../lib/formatMoney.js";
3
2
  import { cn as i } from "../../lib/utils.js";
3
+ import { formatMoney as a } from "../../utils/formatMoney.js";
4
4
  function N({
5
5
  amount: r,
6
6
  currencyCode: t,
@@ -1 +1 @@
1
- {"version":3,"file":"product-variant-price.js","sources":["../../../src/components/atoms/product-variant-price.tsx"],"sourcesContent":["import {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\n\nexport interface ProductVariantPriceProps {\n amount: number | string\n currencyCode?: string\n compareAtPriceAmount?: number | string\n compareAtPriceCurrencyCode?: string\n currentPriceClassName?: string\n originalPriceClassName?: string\n className?: string\n}\n\nexport function ProductVariantPrice({\n amount,\n currencyCode,\n compareAtPriceAmount,\n compareAtPriceCurrencyCode,\n currentPriceClassName,\n originalPriceClassName,\n className,\n}: ProductVariantPriceProps) {\n if (!amount || !currencyCode) {\n return null\n }\n\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const amountStr = String(amount)\n const compareAtPriceAmountStr = compareAtPriceAmount\n ? String(compareAtPriceAmount)\n : undefined\n\n return (\n <div className={cn('flex items-center gap-2', className)}>\n {hasDiscount ? (\n <>\n <span\n className={cn(\n 'text-sm font-semibold text-gray-900',\n currentPriceClassName\n )}\n >\n {formatMoney(amountStr, currencyCode)}\n </span>\n <span\n className={cn(\n 'text-sm text-gray-500 line-through',\n originalPriceClassName\n )}\n >\n {formatMoney(\n compareAtPriceAmountStr!,\n compareAtPriceCurrencyCode || currencyCode\n )}\n </span>\n </>\n ) : (\n <span\n className={cn(\n 'text-sm font-semibold text-gray-900',\n currentPriceClassName\n )}\n >\n {formatMoney(amountStr, currencyCode)}\n </span>\n )}\n </div>\n )\n}\n"],"names":["ProductVariantPrice","amount","currencyCode","compareAtPriceAmount","compareAtPriceCurrencyCode","currentPriceClassName","originalPriceClassName","className","hasDiscount","amountStr","compareAtPriceAmountStr","jsx","cn","jsxs","Fragment","formatMoney"],"mappings":";;;AAaO,SAASA,EAAoB;AAAA,EAClC,QAAAC;AAAA,EACA,cAAAC;AAAA,EACA,sBAAAC;AAAA,EACA,4BAAAC;AAAA,EACA,uBAAAC;AAAA,EACA,wBAAAC;AAAA,EACA,WAAAC;AACF,GAA6B;AACvB,MAAA,CAACN,KAAU,CAACC;AACP,WAAA;AAGH,QAAAM,IAAcL,KAAwBA,MAAyBF,GAE/DQ,IAAY,OAAOR,CAAM,GACzBS,IAA0BP,IAC5B,OAAOA,CAAoB,IAC3B;AAGF,SAAA,gBAAAQ,EAAC,SAAI,WAAWC,EAAG,2BAA2BL,CAAS,GACpD,cAEG,gBAAAM,EAAAC,GAAA,EAAA,UAAA;AAAA,IAAA,gBAAAH;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC;AAAA,UACT;AAAA,UACAP;AAAA,QACF;AAAA,QAEC,UAAAU,EAAYN,GAAWP,CAAY;AAAA,MAAA;AAAA,IACtC;AAAA,IACA,gBAAAS;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC;AAAA,UACT;AAAA,UACAN;AAAA,QACF;AAAA,QAEC,UAAAS;AAAA,UACCL;AAAA,UACAN,KAA8BF;AAAA,QAAA;AAAA,MAChC;AAAA,IAAA;AAAA,EACF,EAAA,CACF,IAEA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAP;AAAA,MACF;AAAA,MAEC,UAAAU,EAAYN,GAAWP,CAAY;AAAA,IAAA;AAAA,EAAA,GAG1C;AAEJ;"}
1
+ {"version":3,"file":"product-variant-price.js","sources":["../../../src/components/atoms/product-variant-price.tsx"],"sourcesContent":["import {cn} from '../../lib/utils'\nimport {formatMoney} from '../../utils/formatMoney'\n\nexport interface ProductVariantPriceProps {\n amount: number | string\n currencyCode?: string\n compareAtPriceAmount?: number | string\n compareAtPriceCurrencyCode?: string\n currentPriceClassName?: string\n originalPriceClassName?: string\n className?: string\n}\n\nexport function ProductVariantPrice({\n amount,\n currencyCode,\n compareAtPriceAmount,\n compareAtPriceCurrencyCode,\n currentPriceClassName,\n originalPriceClassName,\n className,\n}: ProductVariantPriceProps) {\n if (!amount || !currencyCode) {\n return null\n }\n\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const amountStr = String(amount)\n const compareAtPriceAmountStr = compareAtPriceAmount\n ? String(compareAtPriceAmount)\n : undefined\n\n return (\n <div className={cn('flex items-center gap-2', className)}>\n {hasDiscount ? (\n <>\n <span\n className={cn(\n 'text-sm font-semibold text-gray-900',\n currentPriceClassName\n )}\n >\n {formatMoney(amountStr, currencyCode)}\n </span>\n <span\n className={cn(\n 'text-sm text-gray-500 line-through',\n originalPriceClassName\n )}\n >\n {formatMoney(\n compareAtPriceAmountStr!,\n compareAtPriceCurrencyCode || currencyCode\n )}\n </span>\n </>\n ) : (\n <span\n className={cn(\n 'text-sm font-semibold text-gray-900',\n currentPriceClassName\n )}\n >\n {formatMoney(amountStr, currencyCode)}\n </span>\n )}\n </div>\n )\n}\n"],"names":["ProductVariantPrice","amount","currencyCode","compareAtPriceAmount","compareAtPriceCurrencyCode","currentPriceClassName","originalPriceClassName","className","hasDiscount","amountStr","compareAtPriceAmountStr","jsx","cn","jsxs","Fragment","formatMoney"],"mappings":";;;AAaO,SAASA,EAAoB;AAAA,EAClC,QAAAC;AAAA,EACA,cAAAC;AAAA,EACA,sBAAAC;AAAA,EACA,4BAAAC;AAAA,EACA,uBAAAC;AAAA,EACA,wBAAAC;AAAA,EACA,WAAAC;AACF,GAA6B;AACvB,MAAA,CAACN,KAAU,CAACC;AACP,WAAA;AAGH,QAAAM,IAAcL,KAAwBA,MAAyBF,GAE/DQ,IAAY,OAAOR,CAAM,GACzBS,IAA0BP,IAC5B,OAAOA,CAAoB,IAC3B;AAGF,SAAA,gBAAAQ,EAAC,SAAI,WAAWC,EAAG,2BAA2BL,CAAS,GACpD,cAEG,gBAAAM,EAAAC,GAAA,EAAA,UAAA;AAAA,IAAA,gBAAAH;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC;AAAA,UACT;AAAA,UACAP;AAAA,QACF;AAAA,QAEC,UAAAU,EAAYN,GAAWP,CAAY;AAAA,MAAA;AAAA,IACtC;AAAA,IACA,gBAAAS;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC;AAAA,UACT;AAAA,UACAN;AAAA,QACF;AAAA,QAEC,UAAAS;AAAA,UACCL;AAAA,UACAN,KAA8BF;AAAA,QAAA;AAAA,MAChC;AAAA,IAAA;AAAA,EACF,EAAA,CACF,IAEA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAP;AAAA,MACF;AAAA,MAEC,UAAAU,EAAYN,GAAWP,CAAY;AAAA,IAAA;AAAA,EAAA,GAG1C;AAEJ;"}
@@ -4,8 +4,8 @@ import { useState as R, useCallback as C, useMemo as k, useContext as z } from "
4
4
  import { useShopNavigation as S } from "../../hooks/navigation/useShopNavigation.js";
5
5
  import { useSavedProductsActions as V } from "../../hooks/user/useSavedProductsActions.js";
6
6
  import { ProductReviewStars as j } from "../../internal/components/product-review-stars.js";
7
- import { formatMoney as B } from "../../lib/formatMoney.js";
8
7
  import { cn as d } from "../../lib/utils.js";
8
+ import { formatMoney as B } from "../../utils/formatMoney.js";
9
9
  import { Image as T } from "../atoms/image.js";
10
10
  import { ProductVariantPrice as E } from "../atoms/product-variant-price.js";
11
11
  import { Touchable as O } from "../atoms/touchable.js";
@@ -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 {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\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 favoriteButtonDisabled: boolean\n reviewsDisabled: boolean\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 size-full overflow-hidden rounded-xl border-0 isolate',\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 'relative overflow-hidden rounded-xl border border-gray-200',\n '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 objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (favoriteButtonDisabled) return null\n\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-testid=\"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 ProductCardReviewStars({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {product, reviewsDisabled} = useProductCardContext()\n const reviewAnalytics = product.reviewAnalytics\n\n if (reviewsDisabled || !reviewAnalytics?.averageRating) {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-review-stars\"\n className={cn('', className)}\n {...props}\n >\n <ProductReviewStars\n averageRating={reviewAnalytics.averageRating}\n reviewCount={reviewAnalytics.reviewCount}\n />\n </div>\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 /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n /** Whether review stars are disabled */\n reviewsDisabled?: boolean\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 favoriteButtonDisabled = false,\n reviewsDisabled = false,\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 favoriteButtonDisabled,\n reviewsDisabled,\n\n // State\n isFavorited: isFavoritedLocal,\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 favoriteButtonDisabled,\n reviewsDisabled,\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 <ProductCardReviewStars />\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 ProductCardReviewStars,\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","favoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardReviewStars","reviewsDisabled","reviewAnalytics","ProductReviewStars","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 {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {cn} from '../../lib/utils'\nimport {formatMoney} from '../../utils/formatMoney'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\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 favoriteButtonDisabled: boolean\n reviewsDisabled: boolean\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 size-full overflow-hidden rounded-xl border-0 isolate',\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 'relative overflow-hidden rounded-xl border border-gray-200',\n '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 objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (favoriteButtonDisabled) return null\n\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-testid=\"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 ProductCardReviewStars({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {product, reviewsDisabled} = useProductCardContext()\n const reviewAnalytics = product.reviewAnalytics\n\n if (reviewsDisabled || !reviewAnalytics?.averageRating) {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-review-stars\"\n className={cn('', className)}\n {...props}\n >\n <ProductReviewStars\n averageRating={reviewAnalytics.averageRating}\n reviewCount={reviewAnalytics.reviewCount}\n />\n </div>\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 /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n /** Whether review stars are disabled */\n reviewsDisabled?: boolean\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 favoriteButtonDisabled = false,\n reviewsDisabled = false,\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 favoriteButtonDisabled,\n reviewsDisabled,\n\n // State\n isFavorited: isFavoritedLocal,\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 favoriteButtonDisabled,\n reviewsDisabled,\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 <ProductCardReviewStars />\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 ProductCardReviewStars,\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","favoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardReviewStars","reviewsDisabled","reviewAnalytics","ProductReviewStars","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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"}
@@ -4,8 +4,8 @@ import { cva as q } from "../../shop-minis-react/node_modules/.pnpm/class-varian
4
4
  import { useShopNavigation as B } from "../../hooks/navigation/useShopNavigation.js";
5
5
  import { useSavedProductsActions as M } from "../../hooks/user/useSavedProductsActions.js";
6
6
  import { ProductReviewStars as U } from "../../internal/components/product-review-stars.js";
7
- import { formatMoney as T } from "../../lib/formatMoney.js";
8
7
  import { cn as n } from "../../lib/utils.js";
8
+ import { formatMoney as T } from "../../utils/formatMoney.js";
9
9
  import { Touchable as V } from "../atoms/touchable.js";
10
10
  import { Card as E, CardContent as G, CardAction as H } from "../ui/card.js";
11
11
  import { FavoriteButton as J } from "./favorite-button.js";
@@ -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 {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nimport {FavoriteButton} from './favorite-button'\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('', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n hideFavoriteAction = false,\n onPress,\n filled = false,\n customAction,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n hideFavoriteAction?: boolean\n onPress?: () => void\n filled?: boolean\n customAction?: React.ReactNode\n}) {\n const favoriteAction = hideFavoriteAction ? null : (\n <FavoriteButton filled={filled} onClick={onPress} />\n )\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 {customAction ? <>{customAction}</> : favoriteAction}\n </Touchable>\n </CardAction>\n )\n}\n\nexport type ProductLinkProps = {\n product: Product\n hideFavoriteAction?: boolean\n onClick?: (product: Product) => void\n reviewsDisabled?: boolean\n} & (\n | {\n customAction?: never\n onCustomActionClick?: never\n }\n | {\n customAction: React.ReactNode\n onCustomActionClick: () => void\n }\n)\n\n// Composed ProductLink component\nfunction ProductLink({\n product,\n hideFavoriteAction = false,\n onClick,\n customAction,\n onCustomActionClick,\n reviewsDisabled = false,\n}: 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 onClick?.(product)\n }, [navigateToProduct, id, onClick, product])\n\n const handleActionPress = React.useCallback(async () => {\n if (customAction || onCustomActionClick) {\n onCustomActionClick?.()\n return\n }\n\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 customAction,\n onCustomActionClick,\n isFavoritedLocal,\n unsaveProduct,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\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 {averageRating && !reviewsDisabled ? (\n <ProductLinkRating>\n <ProductReviewStars\n averageRating={averageRating}\n reviewCount={reviewCount}\n />\n </ProductLinkRating>\n ) : null}\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 customAction={customAction}\n hideFavoriteAction={hideFavoriteAction}\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","hideFavoriteAction","filled","customAction","favoriteAction","FavoriteButton","CardAction","Fragment","ProductLink","product","onClick","onCustomActionClick","reviewsDisabled","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","ProductReviewStars"],"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 {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {cn} from '../../lib/utils'\nimport {formatMoney} from '../../utils/formatMoney'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nimport {FavoriteButton} from './favorite-button'\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('', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n hideFavoriteAction = false,\n onPress,\n filled = false,\n customAction,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n hideFavoriteAction?: boolean\n onPress?: () => void\n filled?: boolean\n customAction?: React.ReactNode\n}) {\n const favoriteAction = hideFavoriteAction ? null : (\n <FavoriteButton filled={filled} onClick={onPress} />\n )\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 {customAction ? <>{customAction}</> : favoriteAction}\n </Touchable>\n </CardAction>\n )\n}\n\nexport type ProductLinkProps = {\n product: Product\n hideFavoriteAction?: boolean\n onClick?: (product: Product) => void\n reviewsDisabled?: boolean\n} & (\n | {\n customAction?: never\n onCustomActionClick?: never\n }\n | {\n customAction: React.ReactNode\n onCustomActionClick: () => void\n }\n)\n\n// Composed ProductLink component\nfunction ProductLink({\n product,\n hideFavoriteAction = false,\n onClick,\n customAction,\n onCustomActionClick,\n reviewsDisabled = false,\n}: 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 onClick?.(product)\n }, [navigateToProduct, id, onClick, product])\n\n const handleActionPress = React.useCallback(async () => {\n if (customAction || onCustomActionClick) {\n onCustomActionClick?.()\n return\n }\n\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 customAction,\n onCustomActionClick,\n isFavoritedLocal,\n unsaveProduct,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\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 {averageRating && !reviewsDisabled ? (\n <ProductLinkRating>\n <ProductReviewStars\n averageRating={averageRating}\n reviewCount={reviewCount}\n />\n </ProductLinkRating>\n ) : null}\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 customAction={customAction}\n hideFavoriteAction={hideFavoriteAction}\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","hideFavoriteAction","filled","customAction","favoriteAction","FavoriteButton","CardAction","Fragment","ProductLink","product","onClick","onCustomActionClick","reviewsDisabled","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","ProductReviewStars"],"mappings":"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"}
@@ -1,31 +1,31 @@
1
- import { useCallback as m } from "react";
2
- import { useShopActions as c } from "../../internal/useShopActions.js";
3
- import { fileToDataUri as u } from "../../utils/image.js";
4
- const d = async (e) => {
5
- const r = await u(e), o = await (await fetch(r)).blob();
1
+ import { useCallback as c } from "react";
2
+ import { useShopActions as m } from "../../internal/useShopActions.js";
3
+ import { fileToDataUri as d } from "../../utils/image.js";
4
+ const u = async (e) => {
5
+ const o = await d(e), a = await (await fetch(o)).blob();
6
6
  return {
7
7
  mimeType: e.type,
8
- fileSize: e.size ?? o.size,
9
- fileBlob: o
8
+ fileSize: e.size ?? a.size,
9
+ fileBlob: a
10
10
  };
11
- }, f = async (e, r) => {
11
+ }, f = async (e, o) => {
12
12
  const t = new FormData();
13
- r.parameters.forEach(({ name: s, value: a }) => {
14
- t.append(s, a);
13
+ o.parameters.forEach(({ name: s, value: r }) => {
14
+ t.append(s, r);
15
15
  }), t.append("file", e.fileBlob);
16
- const o = await fetch(r.url, {
16
+ const a = await fetch(o.url, {
17
17
  method: "POST",
18
18
  body: t
19
19
  });
20
- return o.ok ? {} : (console.error("Failed to upload image", {
21
- response: await o.text()
20
+ return a.ok ? {} : (console.error("Failed to upload image", {
21
+ response: await a.text()
22
22
  }), { error: "Failed to upload image" });
23
23
  }, U = () => {
24
- const { createImageUploadLink: e, completeImageUpload: r } = c();
24
+ const { createImageUploadLink: e, completeImageUpload: o } = m();
25
25
  return {
26
- uploadImage: m(
27
- async (o) => {
28
- const s = await d(o), a = await e({
26
+ uploadImage: c(
27
+ async (a) => {
28
+ const s = await u(a), r = await e({
29
29
  input: [
30
30
  {
31
31
  mimeType: s.mimeType,
@@ -33,18 +33,26 @@ const d = async (e) => {
33
33
  }
34
34
  ]
35
35
  });
36
- if (!a.ok)
37
- throw new Error(a.error.message);
36
+ if (!r.ok)
37
+ throw new Error(r.error.message);
38
+ if (r.mocked)
39
+ return [
40
+ {
41
+ id: "uploaded-image-id",
42
+ imageUrl: "https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175",
43
+ resourceUrl: "https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175"
44
+ }
45
+ ];
38
46
  const { error: n } = await f(
39
47
  s,
40
- a?.data?.targets?.[0]
48
+ r?.data?.targets?.[0]
41
49
  );
42
50
  if (n)
43
51
  throw new Error(n);
44
52
  let p = 0;
45
53
  for (; p < 30; ) {
46
- const i = await r({
47
- resourceUrls: a?.data?.targets?.map((l) => l.resourceUrl) || []
54
+ const i = await o({
55
+ resourceUrls: r?.data?.targets?.map((l) => l.resourceUrl) || []
48
56
  });
49
57
  if (!i.ok)
50
58
  throw new Error(i.error.message);
@@ -53,14 +61,14 @@ const d = async (e) => {
53
61
  {
54
62
  id: i.data.files[0].id,
55
63
  imageUrl: i.data.files[0].image?.url,
56
- resourceUrl: a?.data?.targets?.[0]?.resourceUrl
64
+ resourceUrl: r?.data?.targets?.[0]?.resourceUrl
57
65
  }
58
66
  ];
59
67
  await new Promise((l) => setTimeout(l, 1e3)), p++;
60
68
  }
61
69
  throw new Error("Image upload completion timed out");
62
70
  },
63
- [e, r]
71
+ [e, o]
64
72
  )
65
73
  };
66
74
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\nimport {fileToDataUri} from '../../utils'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The file to upload.\n */\n image: File\n}\n\ninterface ProcessedImage {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize: number\n /**\n * The file blob of the image.\n */\n fileBlob: Blob\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image which will be attached to the current user.\n */\n uploadImage: (image: File) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: File): Promise<ProcessedImage> => {\n const uri = await fileToDataUri(image)\n\n const response = await fetch(uri)\n const blob = await response.blob()\n\n return {\n mimeType: image.type,\n fileSize: image.size ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (image: ProcessedImage, target: UploadTarget) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (image: File) => {\n const processedImageParams = await processFileData(image)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","uri","fileToDataUri","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;;AAqDA,MAAMA,IAAkB,OAAOC,MAAyC;AAChE,QAAAC,IAAM,MAAMC,EAAcF,CAAK,GAG/BG,IAAO,OADI,MAAM,MAAMF,CAAG,GACJ,KAAK;AAE1B,SAAA;AAAA,IACL,UAAUD,EAAM;AAAA,IAChB,UAAUA,EAAM,QAAQG,EAAK;AAAA,IAC7B,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OAAOJ,GAAuBK,MAAyB;AACvE,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQN,EAAM,QAAQ;AAEtC,QAAMS,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AA4D7D,SAAA;AAAA,IACL,aA3DkBC;AAAA,MAClB,OAAOd,MAAgB;AACf,cAAAe,IAAuB,MAAMhB,EAAgBC,CAAK,GAElDgB,IAAQ,MAAML,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUI,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAIrC,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMb;AAAA,UACjCW;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMP,EAAoB;AAAA,YACvC,cACEI,GAAO,MAAM,SAAS,IAAI,CAAUX,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACc,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACP,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
1
+ {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\nimport {fileToDataUri} from '../../utils'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The file to upload.\n */\n image: File\n}\n\ninterface ProcessedImage {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize: number\n /**\n * The file blob of the image.\n */\n fileBlob: Blob\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image which will be attached to the current user.\n */\n uploadImage: (image: File) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: File): Promise<ProcessedImage> => {\n const uri = await fileToDataUri(image)\n\n const response = await fetch(uri)\n const blob = await response.blob()\n\n return {\n mimeType: image.type,\n fileSize: image.size ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (image: ProcessedImage, target: UploadTarget) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (image: File) => {\n const processedImageParams = await processFileData(image)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n if (links.mocked) {\n // Skip upload and return mock data\n return [\n {\n id: 'uploaded-image-id',\n imageUrl:\n 'https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175',\n resourceUrl:\n 'https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175',\n },\n ]\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","uri","fileToDataUri","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;;AAqDA,MAAMA,IAAkB,OAAOC,MAAyC;AAChE,QAAAC,IAAM,MAAMC,EAAcF,CAAK,GAG/BG,IAAO,OADI,MAAM,MAAMF,CAAG,GACJ,KAAK;AAE1B,SAAA;AAAA,IACL,UAAUD,EAAM;AAAA,IAChB,UAAUA,EAAM,QAAQG,EAAK;AAAA,IAC7B,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OAAOJ,GAAuBK,MAAyB;AACvE,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQN,EAAM,QAAQ;AAEtC,QAAMS,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AAyE7D,SAAA;AAAA,IACL,aAxEkBC;AAAA,MAClB,OAAOd,MAAgB;AACf,cAAAe,IAAuB,MAAMhB,EAAgBC,CAAK,GAElDgB,IAAQ,MAAML,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUI,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAGrC,YAAIA,EAAM;AAED,iBAAA;AAAA,YACL;AAAA,cACE,IAAI;AAAA,cACJ,UACE;AAAA,cACF,aACE;AAAA,YAAA;AAAA,UAEN;AAIF,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMb;AAAA,UACjCW;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMP,EAAoB;AAAA,YACvC,cACEI,GAAO,MAAM,SAAS,IAAI,CAAUX,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACc,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACP,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
package/dist/index.js CHANGED
@@ -4,11 +4,11 @@ import { AddToCartButton as n } from "./components/commerce/add-to-cart.js";
4
4
  import { BuyNowButton as s } from "./components/commerce/buy-now.js";
5
5
  import { ProductCard as u, ProductCardBadge as f, ProductCardContainer as x, ProductCardFavoriteButton as c, ProductCardImage as d, ProductCardImageContainer as C, ProductCardInfo as g, ProductCardPrice as S, ProductCardReviewStars as A, ProductCardTitle as D } from "./components/commerce/product-card.js";
6
6
  import { ProductLink as T } from "./components/commerce/product-link.js";
7
- import { MerchantCard as I, MerchantCardContainer as E, MerchantCardHeader as R, MerchantCardInfo as w, MerchantCardName as F, MerchantCardRating as M } from "./components/commerce/merchant-card.js";
7
+ import { MerchantCard as I, MerchantCardContainer as E, MerchantCardHeader as M, MerchantCardInfo as R, MerchantCardName as w, MerchantCardRating as F } from "./components/commerce/merchant-card.js";
8
8
  import { ProductCardSkeleton as B } from "./components/commerce/product-card-skeleton.js";
9
9
  import { MerchantCardSkeleton as L } from "./components/commerce/merchant-card-skeleton.js";
10
10
  import { QuantitySelector as b } from "./components/commerce/quantity-selector.js";
11
- import { Search as _, SearchInput as G, SearchProvider as O, SearchResultsList as y } from "./components/commerce/search.js";
11
+ import { Search as _, SearchInput as y, SearchProvider as G, SearchResultsList as O } from "./components/commerce/search.js";
12
12
  import { FavoriteButton as V } from "./components/commerce/favorite-button.js";
13
13
  import { ImageContentWrapper as W } from "./components/content/image-content-wrapper.js";
14
14
  import { MinisRouter as q } from "./components/navigation/minis-router.js";
@@ -26,19 +26,19 @@ import { ContentWrapper as cr } from "./components/atoms/content-wrapper.js";
26
26
  import { ProductVariantPrice as Cr } from "./components/atoms/product-variant-price.js";
27
27
  import { Accordion as Sr, AccordionContent as Ar, AccordionItem as Dr, AccordionTrigger as Pr } from "./components/ui/accordion.js";
28
28
  import { Alert as hr, AlertDescription as Ir, AlertTitle as Er } from "./components/ui/alert.js";
29
- import { AlertDialog as wr, AlertDialogAction as Fr, AlertDialogCancel as Mr, AlertDialogContent as vr, AlertDialogDescription as Br, AlertDialogFooter as Ur, AlertDialogHeader as Lr, AlertDialogOverlay as Nr, AlertDialogPortal as br, AlertDialogTitle as kr, AlertDialogTrigger as _r } from "./components/ui/alert-dialog.js";
30
- import { Avatar as Or, AvatarFallback as yr, AvatarImage as Hr } from "./components/ui/avatar.js";
29
+ import { AlertDialog as Rr, AlertDialogAction as wr, AlertDialogCancel as Fr, AlertDialogContent as vr, AlertDialogDescription as Br, AlertDialogFooter as Ur, AlertDialogHeader as Lr, AlertDialogOverlay as Nr, AlertDialogPortal as br, AlertDialogTitle as kr, AlertDialogTrigger as _r } from "./components/ui/alert-dialog.js";
30
+ import { Avatar as Gr, AvatarFallback as Or, AvatarImage as Hr } from "./components/ui/avatar.js";
31
31
  import { Badge as zr, badgeVariants as Wr } from "./components/ui/badge.js";
32
32
  import { Card as qr, CardAction as Kr, CardContent as Qr, CardDescription as Zr, CardFooter as jr, CardHeader as Jr, CardTitle as Xr } from "./components/ui/card.js";
33
33
  import { Carousel as re, CarouselContent as ee, CarouselItem as oe, CarouselNext as te, CarouselPrevious as ae } from "./components/ui/carousel.js";
34
34
  import { Checkbox as pe } from "./components/ui/checkbox.js";
35
35
  import { Dialog as me, DialogClose as se, DialogContent as le, DialogDescription as ue, DialogFooter as fe, DialogHeader as xe, DialogOverlay as ce, DialogPortal as de, DialogTitle as Ce, DialogTrigger as ge } from "./components/ui/dialog.js";
36
- import { Drawer as Ae, DrawerClose as De, DrawerContent as Pe, DrawerDescription as Te, DrawerFooter as he, DrawerHeader as Ie, DrawerOverlay as Ee, DrawerPortal as Re, DrawerTitle as we, DrawerTrigger as Fe } from "./components/ui/drawer.js";
36
+ import { Drawer as Ae, DrawerClose as De, DrawerContent as Pe, DrawerDescription as Te, DrawerFooter as he, DrawerHeader as Ie, DrawerOverlay as Ee, DrawerPortal as Me, DrawerTitle as Re, DrawerTrigger as we } from "./components/ui/drawer.js";
37
37
  import { Input as ve } from "./components/ui/input.js";
38
38
  import { Label as Ue } from "./components/ui/label.js";
39
39
  import { Progress as Ne } from "./components/ui/progress.js";
40
40
  import { RadioGroup as ke, RadioGroupItem as _e } from "./components/ui/radio-group.js";
41
- import { ResizableHandle as Oe, ResizablePanel as ye, ResizablePanelGroup as He } from "./components/ui/resizable.js";
41
+ import { ResizableHandle as Ge, ResizablePanel as Oe, ResizablePanelGroup as He } from "./components/ui/resizable.js";
42
42
  import { ScrollArea as ze, ScrollBar as We } from "./components/ui/scroll-area.js";
43
43
  import { Select as qe, SelectContent as Ke, SelectGroup as Qe, SelectItem as Ze, SelectLabel as je, SelectScrollDownButton as Je, SelectScrollUpButton as Xe, SelectSeparator as $e, SelectTrigger as ro, SelectValue as eo } from "./components/ui/select.js";
44
44
  import { Separator as to } from "./components/ui/separator.js";
@@ -49,13 +49,13 @@ import { Skeleton as Ao } from "./components/ui/skeleton.js";
49
49
  import { useRecentProducts as Po } from "./hooks/user/useRecentProducts.js";
50
50
  import { useRecentShops as ho } from "./hooks/user/useRecentShops.js";
51
51
  import { useSavedProducts as Eo } from "./hooks/user/useSavedProducts.js";
52
- import { useSavedProductsActions as wo } from "./hooks/user/useSavedProductsActions.js";
53
- import { useFollowedShops as Mo } from "./hooks/user/useFollowedShops.js";
52
+ import { useSavedProductsActions as Ro } from "./hooks/user/useSavedProductsActions.js";
53
+ import { useFollowedShops as Fo } from "./hooks/user/useFollowedShops.js";
54
54
  import { useFollowedShopsActions as Bo } from "./hooks/user/useFollowedShopsActions.js";
55
55
  import { useCurrentUser as Lo } from "./hooks/user/useCurrentUser.js";
56
56
  import { useOrders as bo } from "./hooks/user/useOrders.js";
57
57
  import { useBuyerAttributes as _o } from "./hooks/user/useBuyerAttributes.js";
58
- import { useGenerateUserToken as Oo } from "./hooks/user/useGenerateUserToken.js";
58
+ import { useGenerateUserToken as Go } from "./hooks/user/useGenerateUserToken.js";
59
59
  import { useProductListActions as Ho } from "./hooks/product/useProductListActions.js";
60
60
  import { useProductLists as zo } from "./hooks/product/useProductLists.js";
61
61
  import { useProductList as Yo } from "./hooks/product/useProductList.js";
@@ -76,13 +76,13 @@ import { useDeeplink as At } from "./hooks/navigation/useDeeplink.js";
76
76
  import { useNavigateWithTransition as Pt } from "./hooks/navigation/useNavigateWithTransition.js";
77
77
  import { useShop as ht } from "./hooks/shop/useShop.js";
78
78
  import { useRecommendedShops as Et } from "./hooks/shop/useRecommendedShops.js";
79
- import { useCreateImageContent as wt } from "./hooks/content/useCreateImageContent.js";
80
- import { useErrorToast as Mt } from "./hooks/util/useErrorToast.js";
79
+ import { useCreateImageContent as Rt } from "./hooks/content/useCreateImageContent.js";
80
+ import { useErrorToast as Ft } from "./hooks/util/useErrorToast.js";
81
81
  import { useErrorScreen as Bt } from "./hooks/util/useErrorScreen.js";
82
82
  import { useShare as Lt } from "./hooks/util/useShare.js";
83
83
  import { useImagePicker as bt } from "./hooks/util/useImagePicker.js";
84
84
  import { useKeyboardAvoidingView as _t } from "./hooks/util/useKeyboardAvoidingView.js";
85
- import { useRequestPermissions as Ot } from "./hooks/util/useRequestPermissions.js";
85
+ import { useRequestPermissions as Gt } from "./hooks/util/useRequestPermissions.js";
86
86
  import { useOnMiniFocus as Ht } from "./hooks/events/useOnMiniFocus.js";
87
87
  import { useOnMiniBlur as zt } from "./hooks/events/useOnMiniBlur.js";
88
88
  import { useOnMiniClose as Yt } from "./hooks/events/useOnMiniClose.js";
@@ -91,10 +91,11 @@ import { MiniEntityNotFoundError as Zt, MiniError as jt, MiniNetworkError as Jt,
91
91
  import { extractBrandTheme as ra, formatReviewCount as ea, getFeaturedImages as oa, normalizeRating as ta } from "./utils/merchant-card.js";
92
92
  import { parseUrl as ia } from "./utils/parseUrl.js";
93
93
  import { fileToDataUri as na, getResizedImageUrl as ma, getThumbhashDataURL as sa } from "./utils/image.js";
94
- import { UserState as ua, UserTokenGenerateUserErrorCode as fa } from "./shop-minis-platform/src/types/user.js";
95
- import { ContentCreateUserErrorCode as ca, MinisContentStatus as da } from "./shop-minis-platform/src/types/content.js";
96
- import { Social as ga } from "./shop-minis-platform/src/types/share.js";
97
- import { DATA_FETCHING_DEFAULT_FETCH_POLICY as Aa, DATA_FETCHING_DEFAULT_PAGE_SIZE as Da } from "./shop-minis-platform/src/constants.js";
94
+ import { formatMoney as ua } from "./utils/formatMoney.js";
95
+ import { UserState as xa, UserTokenGenerateUserErrorCode as ca } from "./shop-minis-platform/src/types/user.js";
96
+ import { ContentCreateUserErrorCode as Ca, MinisContentStatus as ga } from "./shop-minis-platform/src/types/content.js";
97
+ import { Social as Aa } from "./shop-minis-platform/src/types/share.js";
98
+ import { DATA_FETCHING_DEFAULT_FETCH_POLICY as Pa, DATA_FETCHING_DEFAULT_PAGE_SIZE as Ta } from "./shop-minis-platform/src/constants.js";
98
99
  export {
99
100
  Sr as Accordion,
100
101
  Ar as AccordionContent,
@@ -103,10 +104,10 @@ export {
103
104
  n as AddToCartButton,
104
105
  hr as Alert,
105
106
  Ir as AlertDescription,
106
- wr as AlertDialog,
107
- Fr as AlertDialogAction,
107
+ Rr as AlertDialog,
108
+ wr as AlertDialogAction,
108
109
  pr as AlertDialogAtom,
109
- Mr as AlertDialogCancel,
110
+ Fr as AlertDialogCancel,
110
111
  vr as AlertDialogContent,
111
112
  Br as AlertDialogDescription,
112
113
  Ur as AlertDialogFooter,
@@ -116,8 +117,8 @@ export {
116
117
  kr as AlertDialogTitle,
117
118
  _r as AlertDialogTrigger,
118
119
  Er as AlertTitle,
119
- Or as Avatar,
120
- yr as AvatarFallback,
120
+ Gr as Avatar,
121
+ Or as AvatarFallback,
121
122
  Hr as AvatarImage,
122
123
  zr as Badge,
123
124
  j as Button,
@@ -135,10 +136,10 @@ export {
135
136
  te as CarouselNext,
136
137
  ae as CarouselPrevious,
137
138
  pe as Checkbox,
138
- ca as ContentCreateUserErrorCode,
139
+ Ca as ContentCreateUserErrorCode,
139
140
  cr as ContentWrapper,
140
- Aa as DATA_FETCHING_DEFAULT_FETCH_POLICY,
141
- Da as DATA_FETCHING_DEFAULT_PAGE_SIZE,
141
+ Pa as DATA_FETCHING_DEFAULT_FETCH_POLICY,
142
+ Ta as DATA_FETCHING_DEFAULT_PAGE_SIZE,
142
143
  o as DATA_NAVIGATION_TYPE_ATTRIBUTE,
143
144
  me as Dialog,
144
145
  se as DialogClose,
@@ -157,9 +158,9 @@ export {
157
158
  he as DrawerFooter,
158
159
  Ie as DrawerHeader,
159
160
  Ee as DrawerOverlay,
160
- Re as DrawerPortal,
161
- we as DrawerTitle,
162
- Fe as DrawerTrigger,
161
+ Me as DrawerPortal,
162
+ Re as DrawerTitle,
163
+ we as DrawerTrigger,
163
164
  V as FavoriteButton,
164
165
  X as IconButton,
165
166
  rr as Image,
@@ -170,16 +171,16 @@ export {
170
171
  ar as LongPressDetector,
171
172
  I as MerchantCard,
172
173
  E as MerchantCardContainer,
173
- R as MerchantCardHeader,
174
- w as MerchantCardInfo,
175
- F as MerchantCardName,
176
- M as MerchantCardRating,
174
+ M as MerchantCardHeader,
175
+ R as MerchantCardInfo,
176
+ w as MerchantCardName,
177
+ F as MerchantCardRating,
177
178
  L as MerchantCardSkeleton,
178
179
  Zt as MiniEntityNotFoundError,
179
180
  jt as MiniError,
180
181
  Jt as MiniNetworkError,
181
182
  i as MinisContainer,
182
- da as MinisContentStatus,
183
+ ga as MinisContentStatus,
183
184
  q as MinisRouter,
184
185
  t as NAVIGATION_TYPES,
185
186
  u as ProductCard,
@@ -199,15 +200,15 @@ export {
199
200
  b as QuantitySelector,
200
201
  ke as RadioGroup,
201
202
  _e as RadioGroupItem,
202
- Oe as ResizableHandle,
203
- ye as ResizablePanel,
203
+ Ge as ResizableHandle,
204
+ Oe as ResizablePanel,
204
205
  He as ResizablePanelGroup,
205
206
  ze as ScrollArea,
206
207
  We as ScrollBar,
207
208
  _ as Search,
208
- G as SearchInput,
209
- O as SearchProvider,
210
- y as SearchResultsList,
209
+ y as SearchInput,
210
+ G as SearchProvider,
211
+ O as SearchResultsList,
211
212
  qe as Select,
212
213
  Ke as SelectContent,
213
214
  Qe as SelectGroup,
@@ -228,18 +229,19 @@ export {
228
229
  uo as SheetTitle,
229
230
  fo as SheetTrigger,
230
231
  Ao as Skeleton,
231
- ga as Social,
232
+ Aa as Social,
232
233
  fr as TextInput,
233
234
  co as Toaster,
234
235
  or as Touchable,
235
236
  Q as TransitionLink,
236
- ua as UserState,
237
- fa as UserTokenGenerateUserErrorCode,
237
+ xa as UserState,
238
+ ca as UserTokenGenerateUserErrorCode,
238
239
  lr as VideoPlayer,
239
240
  Wr as badgeVariants,
240
241
  ra as extractBrandTheme,
241
242
  na as fileToDataUri,
242
243
  Xt as formatError,
244
+ ua as formatMoney,
243
245
  ea as formatReviewCount,
244
246
  oa as getFeaturedImages,
245
247
  ma as getResizedImageUrl,
@@ -250,15 +252,15 @@ export {
250
252
  st as useAsyncStorage,
251
253
  _o as useBuyerAttributes,
252
254
  gt as useCloseMini,
253
- wt as useCreateImageContent,
255
+ Rt as useCreateImageContent,
254
256
  nt as useCuratedProducts,
255
257
  Lo as useCurrentUser,
256
258
  At as useDeeplink,
257
259
  Bt as useErrorScreen,
258
- Mt as useErrorToast,
259
- Mo as useFollowedShops,
260
+ Ft as useErrorToast,
261
+ Fo as useFollowedShops,
260
262
  Bo as useFollowedShopsActions,
261
- Oo as useGenerateUserToken,
263
+ Go as useGenerateUserToken,
262
264
  bt as useImagePicker,
263
265
  xt as useImageUpload,
264
266
  _t as useKeyboardAvoidingView,
@@ -281,9 +283,9 @@ export {
281
283
  ho as useRecentShops,
282
284
  tt as useRecommendedProducts,
283
285
  Et as useRecommendedShops,
284
- Ot as useRequestPermissions,
286
+ Gt as useRequestPermissions,
285
287
  Eo as useSavedProducts,
286
- wo as useSavedProductsActions,
288
+ Ro as useSavedProductsActions,
287
289
  ut as useSecureStorage,
288
290
  Lt as useShare,
289
291
  ht as useShop,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}