@shopify/shop-minis-react 0.0.11 → 0.0.12

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 (99) hide show
  1. package/dist/_virtual/debounce.js +8 -0
  2. package/dist/_virtual/debounce.js.map +1 -0
  3. package/dist/_virtual/index2.js +3 -2
  4. package/dist/_virtual/index2.js.map +1 -1
  5. package/dist/_virtual/index3.js +2 -3
  6. package/dist/_virtual/index3.js.map +1 -1
  7. package/dist/components/commerce/merchant-card.js +201 -0
  8. package/dist/components/commerce/merchant-card.js.map +1 -0
  9. package/dist/components/commerce/product-card.js +11 -9
  10. package/dist/components/commerce/product-card.js.map +1 -1
  11. package/dist/hooks/product/useCuratedProducts.js +20 -0
  12. package/dist/hooks/product/useCuratedProducts.js.map +1 -0
  13. package/dist/hooks/product/useProduct.js +24 -0
  14. package/dist/hooks/product/useProduct.js.map +1 -0
  15. package/dist/hooks/product/useProductMedia.js +24 -0
  16. package/dist/hooks/product/useProductMedia.js.map +1 -0
  17. package/dist/hooks/product/useProductSearch.js +44 -0
  18. package/dist/hooks/product/useProductSearch.js.map +1 -0
  19. package/dist/hooks/product/useProductVariants.js +21 -0
  20. package/dist/hooks/product/useProductVariants.js.map +1 -0
  21. package/dist/hooks/product/useProducts.js +17 -0
  22. package/dist/hooks/product/useProducts.js.map +1 -0
  23. package/dist/hooks/shop/useShop.js +17 -0
  24. package/dist/hooks/shop/useShop.js.map +1 -0
  25. package/dist/hooks/user/useFollowedShops.js +21 -0
  26. package/dist/hooks/user/useFollowedShops.js.map +1 -0
  27. package/dist/hooks/user/useRecentProducts.js +21 -0
  28. package/dist/hooks/user/useRecentProducts.js.map +1 -0
  29. package/dist/hooks/user/useRecentShops.js +21 -0
  30. package/dist/hooks/user/useRecentShops.js.map +1 -0
  31. package/dist/hooks/user/useSavedProducts.js +21 -0
  32. package/dist/hooks/user/useSavedProducts.js.map +1 -0
  33. package/dist/index.js +210 -178
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal/useShopActionsPaginatedDataFetching.js +24 -24
  36. package/dist/internal/useShopActionsPaginatedDataFetching.js.map +1 -1
  37. package/dist/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
  38. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_Symbol.js +12 -0
  39. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_Symbol.js.map +1 -0
  40. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_baseGetTag.js +17 -0
  41. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_baseGetTag.js.map +1 -0
  42. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_baseTrim.js +15 -0
  43. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_baseTrim.js.map +1 -0
  44. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_freeGlobal.js +12 -0
  45. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_freeGlobal.js.map +1 -0
  46. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_getRawTag.js +22 -0
  47. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_getRawTag.js.map +1 -0
  48. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_objectToString.js +14 -0
  49. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_objectToString.js.map +1 -0
  50. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_root.js +12 -0
  51. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_root.js.map +1 -0
  52. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_trimmedEndIndex.js +16 -0
  53. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/_trimmedEndIndex.js.map +1 -0
  54. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/debounce.js +61 -0
  55. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/debounce.js.map +1 -0
  56. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isObject.js +14 -0
  57. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isObject.js.map +1 -0
  58. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isObjectLike.js +13 -0
  59. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isObjectLike.js.map +1 -0
  60. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isSymbol.js +16 -0
  61. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/isSymbol.js.map +1 -0
  62. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/now.js +14 -0
  63. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/now.js.map +1 -0
  64. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/toNumber.js +29 -0
  65. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/toNumber.js.map +1 -0
  66. package/dist/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  67. package/dist/types/minisSDK.generated.d.js.map +1 -1
  68. package/package.json +4 -3
  69. package/src/base.css +264 -0
  70. package/src/components/commerce/merchant-card.tsx +262 -0
  71. package/src/components/commerce/product-card.tsx +3 -1
  72. package/src/components/index.ts +1 -0
  73. package/src/hooks/index.ts +11 -0
  74. package/src/hooks/product/useCuratedProducts.doc.ts +32 -0
  75. package/src/hooks/product/useCuratedProducts.example.tsx +10 -0
  76. package/src/hooks/product/useCuratedProducts.ts +41 -0
  77. package/src/hooks/product/useProduct.ts +45 -0
  78. package/src/hooks/product/useProductMedia.ts +51 -0
  79. package/src/hooks/product/useProductSearch.doc.ts +32 -0
  80. package/src/hooks/product/useProductSearch.example.tsx +13 -0
  81. package/src/hooks/product/useProductSearch.ts +95 -0
  82. package/src/hooks/product/useProductVariants.ts +48 -0
  83. package/src/hooks/product/useProducts.ts +38 -0
  84. package/src/hooks/shop/useShop.doc.ts +31 -0
  85. package/src/hooks/shop/useShop.example.tsx +7 -0
  86. package/src/hooks/shop/useShop.ts +38 -0
  87. package/src/hooks/user/useFollowedShops.ts +43 -0
  88. package/src/hooks/user/useRecentProducts.doc.ts +32 -0
  89. package/src/hooks/user/useRecentProducts.example.tsx +13 -0
  90. package/src/hooks/user/useRecentProducts.ts +46 -0
  91. package/src/hooks/user/useRecentShops.ts +43 -0
  92. package/src/hooks/user/useSavedProducts.doc.ts +32 -0
  93. package/src/hooks/user/useSavedProducts.example.tsx +13 -0
  94. package/src/hooks/user/useSavedProducts.ts +46 -0
  95. package/src/index.css +1 -221
  96. package/src/internal/useShopActionsPaginatedDataFetching.ts +8 -8
  97. package/src/types/minisSDK.generated.d.ts +239 -4
  98. package/src/dev.tsx +0 -868
  99. package/src/mockActions.ts +0 -237
@@ -0,0 +1,262 @@
1
+ import * as React from 'react'
2
+
3
+ import {cva, type VariantProps} from 'class-variance-authority'
4
+ import {Star} from 'lucide-react'
5
+ import {Slot as SlotPrimitive} from 'radix-ui'
6
+
7
+ import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
8
+ import {cn} from '../../lib/utils'
9
+ import {type Shop} from '../../types/minisSDK.generated.d'
10
+ import {Touchable} from '../atoms/touchable'
11
+
12
+ const merchantCardVariants = cva(
13
+ 'relative w-full aspect-square overflow-hidden rounded-xl border border-grayscale-l20 bg-grayscale-l0 flex flex-col',
14
+ {
15
+ variants: {
16
+ touchable: {
17
+ true: 'cursor-pointer',
18
+ false: '',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ touchable: true,
23
+ },
24
+ }
25
+ )
26
+
27
+ function formatReviewCount(count: number): string {
28
+ if (count >= 1000000) {
29
+ return `${Math.floor(count / 100000) / 10}M`
30
+ }
31
+ if (count >= 1000) {
32
+ return `${Math.floor(count / 1000)}K`
33
+ }
34
+ return count.toString()
35
+ }
36
+
37
+ function normalizeRating(rating: number): number {
38
+ return Math.round(rating * 10) / 10
39
+ }
40
+
41
+ export interface MerchantCardRootProps
42
+ extends React.ComponentProps<'div'>,
43
+ VariantProps<typeof merchantCardVariants> {
44
+ touchable?: boolean
45
+ asChild?: boolean
46
+ onPress?: () => void
47
+ }
48
+
49
+ function MerchantCardRoot({
50
+ className,
51
+ touchable = true,
52
+ asChild = false,
53
+ onPress,
54
+ ...props
55
+ }: MerchantCardRootProps) {
56
+ const Comp = asChild ? SlotPrimitive.Slot : 'div'
57
+
58
+ const content = (
59
+ <Comp
60
+ className={cn(merchantCardVariants({touchable}), className)}
61
+ {...props}
62
+ />
63
+ )
64
+
65
+ if (touchable && onPress) {
66
+ return (
67
+ <Touchable
68
+ onClick={onPress}
69
+ whileTap={{opacity: 0.7}}
70
+ transition={{
71
+ opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
72
+ }}
73
+ >
74
+ {content}
75
+ </Touchable>
76
+ )
77
+ }
78
+
79
+ return content
80
+ }
81
+
82
+ function MerchantCardImageContainer({
83
+ className,
84
+ ...props
85
+ }: React.ComponentProps<'div'>) {
86
+ return (
87
+ <div
88
+ data-slot="merchant-card-image-container"
89
+ className={cn('relative overflow-hidden w-full flex-grow', className)}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ function MerchantCardImage({
96
+ className,
97
+ src,
98
+ alt,
99
+ ...props
100
+ }: React.ComponentProps<'img'> & {
101
+ src?: string
102
+ alt?: string
103
+ }) {
104
+ return src ? (
105
+ <img
106
+ data-slot="merchant-card-image"
107
+ src={src}
108
+ alt={alt}
109
+ className={cn('w-full h-full object-cover', className)}
110
+ {...props}
111
+ />
112
+ ) : (
113
+ <div className="w-full h-full bg-grayscale-l10" />
114
+ )
115
+ }
116
+
117
+ function MerchantCardLogo({
118
+ className,
119
+ src,
120
+ alt,
121
+ ...props
122
+ }: React.ComponentProps<'div'> & {
123
+ src?: string
124
+ alt?: string
125
+ }) {
126
+ return (
127
+ <div
128
+ data-slot="merchant-card-logo"
129
+ className={cn(
130
+ 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
131
+ 'w-14 h-14 rounded-xl',
132
+ 'flex items-center justify-center overflow-hidden',
133
+ className
134
+ )}
135
+ {...props}
136
+ >
137
+ {src ? (
138
+ <img src={src} alt={alt} className="w-full h-full object-cover" />
139
+ ) : (
140
+ <div className="w-full h-full bg-grayscale-l20" />
141
+ )}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ function MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {
147
+ return (
148
+ <div
149
+ data-slot="merchant-card-info"
150
+ className={cn('p-3 space-y-1 flex-shrink-0', className)}
151
+ {...props}
152
+ />
153
+ )
154
+ }
155
+
156
+ function MerchantCardName({
157
+ className,
158
+ children,
159
+ ...props
160
+ }: React.ComponentProps<'h3'>) {
161
+ return (
162
+ <h3
163
+ data-slot="merchant-card-name"
164
+ className={cn(
165
+ 'text-sm font-medium text-grayscale-d100',
166
+ 'truncate overflow-hidden whitespace-nowrap text-ellipsis',
167
+ className
168
+ )}
169
+ {...props}
170
+ >
171
+ {children}
172
+ </h3>
173
+ )
174
+ }
175
+
176
+ function MerchantCardRating({
177
+ className,
178
+ rating,
179
+ reviewCount,
180
+ ...props
181
+ }: React.ComponentProps<'div'> & {
182
+ rating?: number | null
183
+ reviewCount?: number
184
+ }) {
185
+ if (!rating || !reviewCount) return null
186
+
187
+ return (
188
+ <div
189
+ data-slot="merchant-card-rating"
190
+ className={cn(
191
+ 'flex items-center gap-1 text-sm text-grayscale-d100',
192
+ className
193
+ )}
194
+ {...props}
195
+ >
196
+ <Star className="h-3.5 w-3.5 fill-current" />
197
+ <>
198
+ {normalizeRating(rating)} ({formatReviewCount(reviewCount)})
199
+ </>
200
+ </div>
201
+ )
202
+ }
203
+
204
+ export interface MerchantCardProps {
205
+ shop: Shop
206
+ touchable?: boolean
207
+ }
208
+
209
+ // Composed MerchantCard component
210
+ function MerchantCard({shop, touchable = true}: MerchantCardProps) {
211
+ const {navigateToShop} = useShopNavigation()
212
+
213
+ const {
214
+ id,
215
+ name,
216
+ logoImage,
217
+ reviewAnalytics: {averageRating, reviewCount},
218
+ } = shop
219
+
220
+ const handlePress = React.useCallback(() => {
221
+ if (!touchable) return
222
+ navigateToShop({shopId: id})
223
+ }, [navigateToShop, id, touchable])
224
+
225
+ return (
226
+ <MerchantCardRoot touchable={touchable} onPress={handlePress}>
227
+ <MerchantCardImageContainer>
228
+ {/* TODO: Add featured image */}
229
+ <MerchantCardImage src={undefined} alt={`${name} featured image`} />
230
+ <MerchantCardLogo src={logoImage?.url} alt={`${name} logo`} />
231
+ </MerchantCardImageContainer>
232
+
233
+ <MerchantCardInfo>
234
+ <MerchantCardName>{name}</MerchantCardName>
235
+ <MerchantCardRating rating={averageRating} reviewCount={reviewCount} />
236
+ </MerchantCardInfo>
237
+ </MerchantCardRoot>
238
+ )
239
+ }
240
+
241
+ // Export with Object.assign pattern
242
+ export const MerchantCardPrimitive = Object.assign(MerchantCardRoot, {
243
+ ImageContainer: MerchantCardImageContainer,
244
+ Image: MerchantCardImage,
245
+ Logo: MerchantCardLogo,
246
+ Info: MerchantCardInfo,
247
+ Name: MerchantCardName,
248
+ Rating: MerchantCardRating,
249
+ })
250
+
251
+ export {
252
+ // Composed component
253
+ MerchantCard,
254
+ // Primitive components for custom composition
255
+ MerchantCardRoot,
256
+ MerchantCardImageContainer,
257
+ MerchantCardImage,
258
+ MerchantCardLogo,
259
+ MerchantCardInfo,
260
+ MerchantCardName,
261
+ MerchantCardRating,
262
+ }
@@ -96,6 +96,8 @@ function ProductCardImageContainer({
96
96
  <div
97
97
  data-slot="product-card-image-container"
98
98
  className={cn(
99
+ // Ensure the product image is stretched to the full size of the container (can't use width/height: 100% because of flex)
100
+ 'flex justify-stretch items-stretch',
99
101
  'relative overflow-hidden rounded-xl border border-gray-200',
100
102
  'w-full aspect-square',
101
103
  variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',
@@ -116,7 +118,7 @@ function ProductCardImage({
116
118
  alt?: string
117
119
  }) {
118
120
  return (
119
- <div className="w-full h-full bg-gray-100 flex items-center justify-center">
121
+ <div className="bg-gray-100 flex items-center justify-center">
120
122
  {src ? (
121
123
  <img
122
124
  data-slot="product-card-image"
@@ -2,6 +2,7 @@ export * from './MinisContainer'
2
2
 
3
3
  export * from './commerce/product-card'
4
4
  export * from './commerce/product-link'
5
+ export * from './commerce/merchant-card'
5
6
 
6
7
  export * from './atoms/accordion'
7
8
  export * from './atoms/alert'
@@ -1,5 +1,9 @@
1
1
  // - User Hooks
2
+ export * from './user/useRecentProducts'
3
+ export * from './user/useRecentShops'
4
+ export * from './user/useSavedProducts'
2
5
  export * from './user/useSavedProductsActions'
6
+ export * from './user/useFollowedShops'
3
7
  export * from './user/useFollowedShopsActions'
4
8
  export * from './user/useCurrentUser'
5
9
  export * from './user/useOrders'
@@ -9,8 +13,14 @@ export * from './user/useBuyerAttributes'
9
13
  export * from './product/useProductListActions'
10
14
  export * from './product/useProductLists'
11
15
  export * from './product/useProductList'
16
+ export * from './product/useProduct'
17
+ export * from './product/useProducts'
18
+ export * from './product/useProductVariants'
19
+ export * from './product/useProductMedia'
20
+ export * from './product/useProductSearch'
12
21
  export * from './product/useRecommendedProducts'
13
22
  export * from './product/usePopularProducts'
23
+ export * from './product/useCuratedProducts'
14
24
 
15
25
  // - Storage Hooks
16
26
  export * from './storage/useAsyncStorage'
@@ -23,6 +33,7 @@ export * from './navigation/useCloseMini'
23
33
  export * from './navigation/useDeeplink'
24
34
 
25
35
  // - Shop Hooks
36
+ export * from './shop/useShop'
26
37
  export * from './shop/useShopCartActions'
27
38
  export * from './shop/useRecommendedShops'
28
39
 
@@ -0,0 +1,32 @@
1
+ import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'
2
+
3
+ const data: ReferenceEntityTemplateSchema = {
4
+ name: 'useCuratedProducts',
5
+ category: 'hooks',
6
+ subCategory: 'product',
7
+ isVisualComponent: false,
8
+ related: [],
9
+ description:
10
+ 'The `useCuratedProducts` hook enables fetching lists of products curated by the Shop team.',
11
+ type: 'hook',
12
+ defaultExample: {
13
+ codeblock: {
14
+ tabs: [
15
+ {
16
+ code: './useCuratedProducts.example.tsx',
17
+ language: 'tsx',
18
+ },
19
+ ],
20
+ title: 'Example code',
21
+ },
22
+ },
23
+ definitions: [
24
+ {
25
+ title: '',
26
+ type: 'UseCuratedProductsGeneratedType',
27
+ description: '',
28
+ },
29
+ ],
30
+ }
31
+
32
+ export default data
@@ -0,0 +1,10 @@
1
+ import {useCuratedProducts} from '@shopify/shop-minis-react'
2
+
3
+ export default function MyComponent() {
4
+ const {products, loading, error} = useCuratedProducts({
5
+ handle: 'ar-models',
6
+ requiredTags: ['furniture'],
7
+ })
8
+
9
+ console.log({products, loading, error})
10
+ }
@@ -0,0 +1,41 @@
1
+ import {useShopActions} from '../../internal/useShopActions'
2
+ import {useShopActionsPaginatedDataFetching} from '../../internal/useShopActionsPaginatedDataFetching'
3
+ import {
4
+ Product,
5
+ PaginatedDataHookOptionsBase,
6
+ PaginatedDataHookReturnsBase,
7
+ } from '../../types'
8
+
9
+ interface UseCuratedProductsParams extends PaginatedDataHookOptionsBase {
10
+ handle: string
11
+ requiredTags?: string[]
12
+ anyOfTags?: string[]
13
+ }
14
+
15
+ interface UseCuratedProductsReturns extends PaginatedDataHookReturnsBase {
16
+ products: Product[] | null
17
+ }
18
+
19
+ /**
20
+ * @param options - The options for the query
21
+ */
22
+ export const useCuratedProducts = (
23
+ params: UseCuratedProductsParams
24
+ ): UseCuratedProductsReturns => {
25
+ const {getCuratedProducts} = useShopActions()
26
+ const {skip, ...shopActionParams} = params ?? {}
27
+
28
+ const {data, ...rest} = useShopActionsPaginatedDataFetching(
29
+ getCuratedProducts,
30
+ shopActionParams,
31
+ {
32
+ skip,
33
+ hook: 'useCuratedProducts',
34
+ }
35
+ )
36
+
37
+ return {
38
+ ...rest,
39
+ products: data,
40
+ }
41
+ }
@@ -0,0 +1,45 @@
1
+ import {useMemo} from 'react'
2
+
3
+ import {useShopActions} from '../../internal/useShopActions'
4
+ import {useShopActionsDataFetching} from '../../internal/useShopActionsDataFetching'
5
+ import {DataHookOptionsBase, DataHookReturnsBase, Product} from '../../types'
6
+
7
+ export interface UseProductParams extends DataHookOptionsBase {
8
+ /**
9
+ * The product ID to fetch.
10
+ */
11
+ id: string
12
+ }
13
+
14
+ interface UseProductReturns extends DataHookReturnsBase {
15
+ /**
16
+ * The product returned from the query.
17
+ */
18
+ product: Product | null
19
+ }
20
+
21
+ export const useProduct = (params: UseProductParams): UseProductReturns => {
22
+ const {getProduct} = useShopActions()
23
+ const {id, skip = false, ...restParams} = params
24
+
25
+ const {data, ...rest} = useShopActionsDataFetching(
26
+ getProduct,
27
+ {
28
+ id,
29
+ ...restParams,
30
+ },
31
+ {
32
+ skip,
33
+ hook: 'useProduct',
34
+ }
35
+ )
36
+
37
+ const product = useMemo(() => {
38
+ return data ?? null
39
+ }, [data])
40
+
41
+ return {
42
+ ...rest,
43
+ product,
44
+ }
45
+ }
@@ -0,0 +1,51 @@
1
+ import {useMemo} from 'react'
2
+
3
+ import {useShopActions} from '../../internal/useShopActions'
4
+ import {useShopActionsPaginatedDataFetching} from '../../internal/useShopActionsPaginatedDataFetching'
5
+ import {
6
+ PaginatedDataHookOptionsBase,
7
+ PaginatedDataHookReturnsBase,
8
+ ProductMedia,
9
+ } from '../../types'
10
+
11
+ export interface UseProductMediaParams extends PaginatedDataHookOptionsBase {
12
+ /**
13
+ * The product ID to fetch media for.
14
+ */
15
+ id: string
16
+ }
17
+
18
+ interface UseProductMediaReturns extends PaginatedDataHookReturnsBase {
19
+ /**
20
+ * The product media returned from the query.
21
+ */
22
+ media: ProductMedia[] | null
23
+ }
24
+
25
+ export const useProductMedia = (
26
+ params: UseProductMediaParams
27
+ ): UseProductMediaReturns => {
28
+ const {getProductMedia} = useShopActions()
29
+ const {id, skip = false, ...restParams} = params
30
+
31
+ const {data, ...rest} = useShopActionsPaginatedDataFetching(
32
+ getProductMedia,
33
+ {
34
+ id,
35
+ ...restParams,
36
+ },
37
+ {
38
+ skip,
39
+ hook: 'useProductMedia',
40
+ }
41
+ )
42
+
43
+ const media = useMemo(() => {
44
+ return data ?? null
45
+ }, [data])
46
+
47
+ return {
48
+ ...rest,
49
+ media,
50
+ }
51
+ }
@@ -0,0 +1,32 @@
1
+ import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'
2
+
3
+ const data: ReferenceEntityTemplateSchema = {
4
+ name: 'useProductSearch',
5
+ category: 'hooks',
6
+ subCategory: 'product',
7
+ isVisualComponent: false,
8
+ related: [],
9
+ description:
10
+ 'The `useProductSearch` hook fetches products based on a search query.',
11
+ type: 'hook',
12
+ defaultExample: {
13
+ codeblock: {
14
+ tabs: [
15
+ {
16
+ code: './useProductSearch.example.tsx',
17
+ language: 'tsx',
18
+ },
19
+ ],
20
+ title: 'Example code',
21
+ },
22
+ },
23
+ definitions: [
24
+ {
25
+ title: '',
26
+ type: 'UseProductSearchGeneratedType',
27
+ description: '',
28
+ },
29
+ ],
30
+ }
31
+
32
+ export default data
@@ -0,0 +1,13 @@
1
+ import {useProductSearch} from '@shopify/shop-minis-react'
2
+
3
+ export default function MyComponent() {
4
+ const {products, loading} = useProductSearch({
5
+ query: 'shirt',
6
+ first: 10,
7
+ filters: {
8
+ color: ['RED'],
9
+ },
10
+ })
11
+
12
+ console.log({products, loading})
13
+ }
@@ -0,0 +1,95 @@
1
+ import {useCallback, useEffect, useMemo, useState} from 'react'
2
+
3
+ import debounce from 'lodash/debounce'
4
+
5
+ import {useShopActions} from '../../internal/useShopActions'
6
+ import {useShopActionsPaginatedDataFetching} from '../../internal/useShopActionsPaginatedDataFetching'
7
+ import {
8
+ PaginatedDataHookOptionsBase,
9
+ PaginatedDataHookReturnsBase,
10
+ Product,
11
+ ProductFilters,
12
+ ProductSearchSortBy,
13
+ } from '../../types'
14
+
15
+ export interface UseProductSearchParams extends PaginatedDataHookOptionsBase {
16
+ /**
17
+ * The search query.
18
+ */
19
+ query: string
20
+ /**
21
+ * The filters to apply to the search.
22
+ */
23
+ filters?: ProductFilters
24
+ /**
25
+ * The sort order of the results.
26
+ */
27
+ sortBy?: ProductSearchSortBy
28
+ /**
29
+ * Whether to include sensitive products.
30
+ */
31
+ includeSensitive?: boolean
32
+ }
33
+
34
+ interface UseProductSearchReturns extends PaginatedDataHookReturnsBase {
35
+ /**
36
+ * The products returned from the query.
37
+ */
38
+ products: Product[] | null
39
+ }
40
+
41
+ export const useProductSearch = (
42
+ params: UseProductSearchParams
43
+ ): UseProductSearchReturns => {
44
+ const {getProductSearch} = useShopActions()
45
+ const {
46
+ query,
47
+ filters,
48
+ sortBy,
49
+ includeSensitive = false,
50
+ skip = false,
51
+ ...restParams
52
+ } = params ?? {}
53
+
54
+ const [debouncedQuery, setDebouncedQuery] = useState<string>(query)
55
+
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ const debouncedSetQuery = useCallback(
58
+ debounce((newQuery: string) => {
59
+ setDebouncedQuery(newQuery)
60
+ }, 300),
61
+ []
62
+ )
63
+
64
+ useEffect(() => {
65
+ debouncedSetQuery(query)
66
+
67
+ return () => {
68
+ debouncedSetQuery.cancel()
69
+ }
70
+ }, [query, debouncedSetQuery])
71
+
72
+ const {data, ...rest} = useShopActionsPaginatedDataFetching(
73
+ getProductSearch,
74
+ {
75
+ query: debouncedQuery,
76
+ filters,
77
+ sortBy,
78
+ includeSensitive,
79
+ ...restParams,
80
+ },
81
+ {
82
+ skip,
83
+ hook: 'useProductSearch',
84
+ }
85
+ )
86
+
87
+ const products = useMemo(() => {
88
+ return data ?? null
89
+ }, [data])
90
+
91
+ return {
92
+ ...rest,
93
+ products,
94
+ }
95
+ }
@@ -0,0 +1,48 @@
1
+ import {useMemo} from 'react'
2
+
3
+ import {useShopActions} from '../../internal/useShopActions'
4
+ import {useShopActionsPaginatedDataFetching} from '../../internal/useShopActionsPaginatedDataFetching'
5
+ import {
6
+ PaginatedDataHookOptionsBase,
7
+ PaginatedDataHookReturnsBase,
8
+ ProductVariant,
9
+ } from '../../types'
10
+
11
+ export interface UseProductVariantsParams extends PaginatedDataHookOptionsBase {
12
+ /**
13
+ * The product ID to fetch variants for.
14
+ */
15
+ id: string
16
+ }
17
+
18
+ interface UseProductVariantsReturns extends PaginatedDataHookReturnsBase {
19
+ /**
20
+ * The product variants returned from the query.
21
+ */
22
+ variants: ProductVariant[] | null
23
+ }
24
+
25
+ export const useProductVariants = (
26
+ params: UseProductVariantsParams
27
+ ): UseProductVariantsReturns => {
28
+ const {getProductVariants} = useShopActions()
29
+ const {skip = false, ...restParams} = params
30
+
31
+ const {data, ...rest} = useShopActionsPaginatedDataFetching(
32
+ getProductVariants,
33
+ restParams,
34
+ {
35
+ skip,
36
+ hook: 'useProductVariants',
37
+ }
38
+ )
39
+
40
+ const variants = useMemo(() => {
41
+ return data ?? null
42
+ }, [data])
43
+
44
+ return {
45
+ ...rest,
46
+ variants,
47
+ }
48
+ }