@shopify/shop-minis-react 0.4.15 → 0.4.17

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 (50) hide show
  1. package/dist/components/atoms/alert-dialog.js.map +1 -1
  2. package/dist/components/atoms/button.js.map +1 -1
  3. package/dist/components/atoms/icon-button.js.map +1 -1
  4. package/dist/components/atoms/image.js +65 -51
  5. package/dist/components/atoms/image.js.map +1 -1
  6. package/dist/components/atoms/list.js.map +1 -1
  7. package/dist/components/atoms/text-input.js.map +1 -1
  8. package/dist/components/atoms/touchable.js.map +1 -1
  9. package/dist/components/atoms/video-player.js +1 -1
  10. package/dist/components/atoms/video-player.js.map +1 -1
  11. package/dist/components/commerce/add-to-cart.js.map +1 -1
  12. package/dist/components/commerce/buy-now.js.map +1 -1
  13. package/dist/components/commerce/favorite-button.js +1 -4
  14. package/dist/components/commerce/favorite-button.js.map +1 -1
  15. package/dist/components/commerce/merchant-card.js.map +1 -1
  16. package/dist/components/commerce/product-link.js.map +1 -1
  17. package/dist/components/commerce/quantity-selector.js.map +1 -1
  18. package/dist/components/content/image-content-wrapper.js.map +1 -1
  19. package/dist/components/navigation/minis-router.js.map +1 -1
  20. package/dist/components/navigation/transition-link.js.map +1 -1
  21. package/dist/components/ui/alert.js.map +1 -1
  22. package/dist/components/ui/badge.js.map +1 -1
  23. package/dist/components/ui/input.js.map +1 -1
  24. package/dist/index.js +75 -73
  25. package/dist/utils/image.js +44 -24
  26. package/dist/utils/image.js.map +1 -1
  27. package/eslint/rules/validate-manifest.cjs +91 -41
  28. package/package.json +1 -1
  29. package/src/components/atoms/alert-dialog.tsx +3 -3
  30. package/src/components/atoms/button.tsx +22 -0
  31. package/src/components/atoms/icon-button.tsx +16 -8
  32. package/src/components/atoms/image.test.tsx +27 -13
  33. package/src/components/atoms/image.tsx +41 -8
  34. package/src/components/atoms/list.tsx +25 -2
  35. package/src/components/atoms/text-input.tsx +3 -1
  36. package/src/components/atoms/touchable.tsx +15 -4
  37. package/src/components/atoms/video-player.tsx +16 -6
  38. package/src/components/commerce/add-to-cart.tsx +7 -11
  39. package/src/components/commerce/buy-now.tsx +7 -10
  40. package/src/components/commerce/favorite-button.tsx +6 -5
  41. package/src/components/commerce/merchant-card.tsx +4 -0
  42. package/src/components/commerce/product-link.tsx +15 -0
  43. package/src/components/commerce/quantity-selector.tsx +6 -1
  44. package/src/components/content/image-content-wrapper.tsx +16 -1
  45. package/src/components/navigation/minis-router.tsx +2 -2
  46. package/src/components/navigation/transition-link.tsx +11 -1
  47. package/src/components/ui/alert.tsx +7 -0
  48. package/src/components/ui/badge.tsx +9 -0
  49. package/src/components/ui/input.tsx +15 -0
  50. package/src/utils/image.ts +38 -0
@@ -10,22 +10,18 @@ import {useShopCartActions} from '../../internal/useShopCartActions'
10
10
  import {cn} from '../../lib/utils'
11
11
  import {Button} from '../atoms/button'
12
12
 
13
- interface AddToCartButtonProps {
13
+ export interface AddToCartButtonProps {
14
+ /** Whether the button is disabled */
14
15
  disabled?: boolean
16
+ /** CSS class name */
15
17
  className?: string
18
+ /** Button size variant */
16
19
  size?: 'default' | 'sm' | 'lg'
17
- /**
18
- * The discount codes to apply to the cart.
19
- */
20
+ /** The discount codes to apply to the cart */
20
21
  discountCodes?: string[]
21
- /**
22
- * The GID of the product variant. E.g. `gid://shopify/ProductVariant/456`.
23
- */
22
+ /** The GID of the product variant. E.g. `gid://shopify/ProductVariant/456` */
24
23
  productVariantId: string
25
-
26
- /**
27
- * The product to add to the cart.
28
- */
24
+ /** The product to add to the cart */
29
25
  product?: Product
30
26
  }
31
27
 
@@ -8,21 +8,18 @@ import {useShopCartActions} from '../../internal/useShopCartActions'
8
8
  import {cn} from '../../lib/utils'
9
9
  import {Button} from '../atoms/button'
10
10
 
11
- interface BuyNowButtonProps {
11
+ export interface BuyNowButtonProps {
12
+ /** Whether the button is disabled */
12
13
  disabled?: boolean
14
+ /** CSS class name */
13
15
  className?: string
16
+ /** Button size variant */
14
17
  size?: 'default' | 'sm' | 'lg'
15
- /**
16
- * The discount code to apply to the purchase.
17
- */
18
+ /** The discount code to apply to the purchase */
18
19
  discountCode?: string
19
- /**
20
- * The GID of the product variant. E.g. `gid://shopify/ProductVariant/456`.
21
- */
20
+ /** The GID of the product variant. E.g. `gid://shopify/ProductVariant/456` */
22
21
  productVariantId: string
23
- /**
24
- * The product to buy now.
25
- */
22
+ /** The product to buy now */
26
23
  product?: Product
27
24
  }
28
25
 
@@ -2,13 +2,14 @@ import {Heart} from 'lucide-react'
2
2
 
3
3
  import {IconButton} from '../atoms/icon-button'
4
4
 
5
- export function FavoriteButton({
6
- onClick,
7
- filled = false,
8
- }: {
5
+ export interface FavoriteButtonProps {
6
+ /** Click handler for toggling favorite state */
9
7
  onClick?: () => void
8
+ /** Whether the product is currently favorited */
10
9
  filled?: boolean
11
- }) {
10
+ }
11
+
12
+ export function FavoriteButton({onClick, filled = false}: FavoriteButtonProps) {
12
13
  return (
13
14
  <IconButton
14
15
  Icon={Heart}
@@ -365,9 +365,13 @@ function MerchantCardHeader({
365
365
  }
366
366
 
367
367
  export interface MerchantCardProps {
368
+ /** The shop/merchant to display */
368
369
  shop: Shop
370
+ /** Whether the card is tappable to navigate to shop (default: true) */
369
371
  touchable?: boolean
372
+ /** Maximum number of featured product images to show (default: 4) */
370
373
  featuredImagesLimit?: number
374
+ /** Custom content to render inside the card */
371
375
  children?: React.ReactNode
372
376
  }
373
377
 
@@ -226,6 +226,21 @@ function ProductLinkActions({
226
226
  )
227
227
  }
228
228
 
229
+ export interface ProductLinkDocProps {
230
+ /** The product to display */
231
+ product: Product
232
+ /** Hide the favorite/save button */
233
+ hideFavoriteAction?: boolean
234
+ /** Callback when the product link is clicked */
235
+ onClick?: (product: Product) => void
236
+ /** Hide the review stars */
237
+ reviewsDisabled?: boolean
238
+ /** Custom action element to replace the favorite button. Must be provided with `onCustomActionClick`. */
239
+ customAction?: React.ReactNode
240
+ /** Callback when the custom action is clicked. Must be provided with `customAction`. */
241
+ onCustomActionClick?: () => void
242
+ }
243
+
229
244
  export type ProductLinkProps = {
230
245
  product: Product
231
246
  hideFavoriteAction?: boolean
@@ -5,11 +5,16 @@ import {Minus, Plus} from 'lucide-react'
5
5
  import {cn} from '../../lib/utils'
6
6
  import {IconButton} from '../atoms/icon-button'
7
7
 
8
- interface QuantitySelectorProps {
8
+ export interface QuantitySelectorProps {
9
+ /** Current quantity value */
9
10
  quantity: number
11
+ /** Callback when quantity changes */
10
12
  onQuantityChange: (quantity: number) => void
13
+ /** Maximum allowed quantity */
11
14
  maxQuantity: number
15
+ /** Minimum allowed quantity (default: 1) */
12
16
  minQuantity?: number
17
+ /** Whether the selector is disabled */
13
18
  disabled?: boolean
14
19
  }
15
20
 
@@ -1,7 +1,22 @@
1
1
  import {ContentWrapper} from '../atoms/content-wrapper'
2
2
  import {Image} from '../atoms/image'
3
3
 
4
- type ImageContentWrapperProps = (
4
+ export interface ImageContentWrapperDocProps {
5
+ /** The public ID of the uploaded image (use this OR externalId) */
6
+ publicId?: string
7
+ /** The external ID of the uploaded image (use this OR publicId) */
8
+ externalId?: string
9
+ /** Callback when the image loads */
10
+ onLoad?: () => void
11
+ /** Image width */
12
+ width?: number
13
+ /** Image height */
14
+ height?: number
15
+ /** Loading placeholder */
16
+ Loader?: React.ReactNode | string
17
+ }
18
+
19
+ export type ImageContentWrapperProps = (
5
20
  | {publicId: string; externalId?: never}
6
21
  | {externalId: string; publicId?: never}
7
22
  ) & {
@@ -2,7 +2,7 @@ import {BrowserRouter, BrowserRouterProps} from 'react-router'
2
2
 
3
3
  import {TransitionContainer} from './transition-container'
4
4
 
5
- type ShopMinisRouterProps = BrowserRouterProps & {
5
+ export interface MinisRouterProps extends BrowserRouterProps {
6
6
  viewTransitions?: boolean
7
7
  }
8
8
 
@@ -10,7 +10,7 @@ export function MinisRouter({
10
10
  children,
11
11
  viewTransitions = false,
12
12
  ...props
13
- }: ShopMinisRouterProps) {
13
+ }: MinisRouterProps) {
14
14
  if (viewTransitions) {
15
15
  return (
16
16
  <BrowserRouter {...props}>
@@ -4,7 +4,17 @@ import {useHref} from 'react-router'
4
4
 
5
5
  import {useNavigateWithTransition} from '../../hooks/navigation/useNavigateWithTransition'
6
6
 
7
- type TransitionLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
7
+ export interface TransitionLinkDocProps {
8
+ /** The target path to navigate to */
9
+ to: string
10
+ /** Click handler called before navigation */
11
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>
12
+ /** Content to render inside the link */
13
+ children?: React.ReactNode
14
+ }
15
+
16
+ export interface TransitionLinkProps
17
+ extends AnchorHTMLAttributes<HTMLAnchorElement> {
8
18
  to: string
9
19
  }
10
20
 
@@ -4,6 +4,13 @@ import {cva, type VariantProps} from 'class-variance-authority'
4
4
 
5
5
  import {cn} from '../../lib/utils'
6
6
 
7
+ export interface AlertDocProps {
8
+ /** Visual style variant */
9
+ variant?: 'default' | 'destructive'
10
+ /** Content to render inside */
11
+ children?: React.ReactNode
12
+ }
13
+
7
14
  const alertVariants = cva(
8
15
  'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
9
16
  {
@@ -5,6 +5,15 @@ import {Slot as SlotPrimitive} from 'radix-ui'
5
5
 
6
6
  import {cn} from '../../lib/utils'
7
7
 
8
+ export interface BadgeDocProps {
9
+ /** Visual style variant */
10
+ variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'
11
+ /** Render as child element instead of span */
12
+ asChild?: boolean
13
+ /** Content to render inside */
14
+ children?: React.ReactNode
15
+ }
16
+
8
17
  const badgeVariants = cva(
9
18
  'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none transition-[color,box-shadow] overflow-hidden',
10
19
  {
@@ -2,6 +2,21 @@ import * as React from 'react'
2
2
 
3
3
  import {cn} from '../../lib/utils'
4
4
 
5
+ export interface InputDocProps {
6
+ /** Ref to the input element (use instead of ref) */
7
+ innerRef?: React.Ref<HTMLInputElement>
8
+ /** Input type (text, email, password, etc.) */
9
+ type?: string
10
+ /** Placeholder text */
11
+ placeholder?: string
12
+ /** Current value */
13
+ value?: string
14
+ /** Change handler */
15
+ onChange?: React.ChangeEventHandler<HTMLInputElement>
16
+ /** Whether the input is disabled */
17
+ disabled?: boolean
18
+ }
19
+
5
20
  // using the default ref doesn't seem to set the parent's ref object when mounted
6
21
  // Since this is a shadCN component, we need to make sure to add back the innerRef prop,
7
22
  // whenever the component is updated.
@@ -18,6 +18,44 @@ export function getThumbhashDataURL(thumbhash?: string): string | undefined {
18
18
  }
19
19
  }
20
20
 
21
+ /**
22
+ * Converts a data URL to a Blob
23
+ * @param dataURL The data URL to convert
24
+ * @returns A Blob object
25
+ */
26
+ export function dataURLToBlob(dataURL: string): Blob {
27
+ const [header, base64Data] = dataURL.split(',')
28
+ const mimeMatch = header.match(/:(.*?);/)
29
+ const mime = mimeMatch ? mimeMatch[1] : 'image/png'
30
+ const binary = atob(base64Data)
31
+ const array = new Uint8Array(binary.length)
32
+ for (let i = 0; i < binary.length; i++) {
33
+ array[i] = binary.charCodeAt(i)
34
+ }
35
+ return new Blob([array], {type: mime})
36
+ }
37
+
38
+ /**
39
+ * Converts a thumbhash string to a blob URL for use as an image placeholder
40
+ * This is useful when CSP restrictions prevent data URLs
41
+ * @param thumbhash Base64 encoded thumbhash string
42
+ * @returns Blob URL that can be used as image source or undefined if conversion fails
43
+ */
44
+ export function getThumbhashBlobURL(thumbhash?: string): string | undefined {
45
+ if (!thumbhash) return
46
+
47
+ try {
48
+ const dataURL = getThumbhashDataURL(thumbhash)
49
+ if (!dataURL) return
50
+
51
+ const blob = dataURLToBlob(dataURL)
52
+ return URL.createObjectURL(blob)
53
+ } catch (error) {
54
+ console.warn('Failed to create thumbhash blob URL', error)
55
+ return undefined
56
+ }
57
+ }
58
+
21
59
  /** Converts a file to a data URI
22
60
  * @param file The file to convert
23
61
  * @returns A promise that resolves to the data URI string