@shopify/shop-minis-react 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index10.js +2 -2
- package/dist/_virtual/index8.js +2 -2
- package/dist/_virtual/index9.js +2 -2
- package/dist/components/commerce/merchant-card.js +188 -245
- package/dist/components/commerce/merchant-card.js.map +1 -1
- package/dist/components/commerce/product-card.js +195 -199
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/ui/badge.js +21 -19
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/index.js +258 -246
- package/dist/mocks.js +2 -2
- package/dist/mocks.js.map +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
- package/package.json +1 -1
- package/src/components/commerce/merchant-card.tsx +222 -223
- package/src/components/commerce/product-card.tsx +211 -182
- package/src/components/ui/badge.tsx +7 -9
- package/src/mocks.ts +3 -3
- package/src/stories/MerchantCard.stories.tsx +0 -3
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
-
import {useCallback} from 'react'
|
|
2
|
+
import {useCallback, useContext, useMemo, useState} from 'react'
|
|
3
3
|
|
|
4
4
|
import {type Product, type ProductVariant} from '@shopify/shop-minis-platform'
|
|
5
|
-
import {cva, type VariantProps} from 'class-variance-authority'
|
|
6
|
-
import {Slot as SlotPrimitive} from 'radix-ui'
|
|
7
5
|
|
|
8
6
|
import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
|
|
9
7
|
import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
|
|
@@ -15,62 +13,61 @@ import {ThumbhashImage} from '../atoms/thumbhash-image'
|
|
|
15
13
|
import {Touchable} from '../atoms/touchable'
|
|
16
14
|
import {Badge} from '../ui/badge'
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
default: '',
|
|
24
|
-
priceOverlay: '',
|
|
25
|
-
compact: '',
|
|
26
|
-
},
|
|
27
|
-
touchable: {
|
|
28
|
-
true: 'cursor-pointer',
|
|
29
|
-
false: '',
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
defaultVariants: {
|
|
33
|
-
variant: 'default',
|
|
34
|
-
touchable: true,
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
)
|
|
16
|
+
// Context definition
|
|
17
|
+
interface ProductCardContextValue {
|
|
18
|
+
// Core data
|
|
19
|
+
product: Product
|
|
20
|
+
selectedProductVariant?: ProductVariant
|
|
38
21
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
22
|
+
// UI configuration
|
|
23
|
+
variant: 'default' | 'priceOverlay' | 'compact'
|
|
24
|
+
touchable: boolean
|
|
25
|
+
badgeText?: string
|
|
26
|
+
badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'
|
|
27
|
+
|
|
28
|
+
// State
|
|
29
|
+
isFavorited: boolean
|
|
30
|
+
|
|
31
|
+
// Actions
|
|
32
|
+
onClick: () => void
|
|
33
|
+
onFavoriteToggle: () => void
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
|
|
36
|
+
const ProductCardContext = React.createContext<
|
|
37
|
+
ProductCardContextValue | undefined
|
|
38
|
+
>(undefined)
|
|
39
|
+
|
|
40
|
+
function useProductCardContext() {
|
|
41
|
+
const context = useContext(ProductCardContext)
|
|
42
|
+
if (!context) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
'ProductCard components must be used within a ProductCard provider'
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
return context
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Primitive components (building blocks)
|
|
51
|
+
function ProductCardContainer({
|
|
50
52
|
className,
|
|
51
|
-
variant,
|
|
52
|
-
touchable = true,
|
|
53
|
-
asChild = false,
|
|
54
|
-
onPress,
|
|
55
53
|
...props
|
|
56
|
-
}:
|
|
57
|
-
const
|
|
54
|
+
}: React.ComponentProps<'div'>) {
|
|
55
|
+
const {touchable, onClick} = useProductCardContext()
|
|
58
56
|
|
|
59
57
|
const content = (
|
|
60
|
-
<
|
|
58
|
+
<div
|
|
61
59
|
className={cn(
|
|
62
|
-
|
|
63
|
-
'border-0',
|
|
60
|
+
'relative w-full overflow-hidden rounded-xl border-0',
|
|
64
61
|
className
|
|
65
62
|
)}
|
|
66
63
|
{...props}
|
|
67
64
|
/>
|
|
68
65
|
)
|
|
69
66
|
|
|
70
|
-
if (touchable &&
|
|
67
|
+
if (touchable && onClick) {
|
|
71
68
|
return (
|
|
72
69
|
<Touchable
|
|
73
|
-
onClick={
|
|
70
|
+
onClick={onClick}
|
|
74
71
|
whileTap={{opacity: 0.7}}
|
|
75
72
|
transition={{
|
|
76
73
|
opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
|
|
@@ -86,11 +83,10 @@ function ProductCardRoot({
|
|
|
86
83
|
|
|
87
84
|
function ProductCardImageContainer({
|
|
88
85
|
className,
|
|
89
|
-
variant = 'default',
|
|
90
86
|
...props
|
|
91
|
-
}: React.ComponentProps<'div'>
|
|
92
|
-
variant
|
|
93
|
-
|
|
87
|
+
}: React.ComponentProps<'div'>) {
|
|
88
|
+
const {variant} = useProductCardContext()
|
|
89
|
+
|
|
94
90
|
return (
|
|
95
91
|
<div
|
|
96
92
|
data-slot="product-card-image-container"
|
|
@@ -107,36 +103,28 @@ function ProductCardImageContainer({
|
|
|
107
103
|
)
|
|
108
104
|
}
|
|
109
105
|
|
|
110
|
-
function ProductCardImage({
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
alt?: string
|
|
120
|
-
aspectRatio?: number
|
|
121
|
-
thumbhash?: string
|
|
122
|
-
}) {
|
|
106
|
+
function ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {
|
|
107
|
+
const {product, selectedProductVariant} = useProductCardContext()
|
|
108
|
+
|
|
109
|
+
// Derive display image locally
|
|
110
|
+
const displayImage = selectedProductVariant?.image || product.featuredImage
|
|
111
|
+
const src = displayImage?.url
|
|
112
|
+
const alt = displayImage?.altText || product.title
|
|
113
|
+
const thumbhash = product.featuredImage?.thumbhash
|
|
114
|
+
|
|
123
115
|
const renderImageElement = useCallback(
|
|
124
116
|
(src: string) => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return (
|
|
117
|
+
const imageElement = thumbhash ? (
|
|
118
|
+
<ThumbhashImage
|
|
119
|
+
data-slot="product-card-image"
|
|
120
|
+
src={src}
|
|
121
|
+
alt={alt}
|
|
122
|
+
aspectRatio={1}
|
|
123
|
+
thumbhash={thumbhash}
|
|
124
|
+
className={cn('w-full h-full object-cover', className)}
|
|
125
|
+
{...props}
|
|
126
|
+
/>
|
|
127
|
+
) : (
|
|
140
128
|
<img
|
|
141
129
|
data-slot="product-card-image"
|
|
142
130
|
src={src}
|
|
@@ -145,8 +133,10 @@ function ProductCardImage({
|
|
|
145
133
|
{...props}
|
|
146
134
|
/>
|
|
147
135
|
)
|
|
136
|
+
|
|
137
|
+
return imageElement
|
|
148
138
|
},
|
|
149
|
-
[alt,
|
|
139
|
+
[alt, className, props, thumbhash]
|
|
150
140
|
)
|
|
151
141
|
|
|
152
142
|
return (
|
|
@@ -163,11 +153,18 @@ function ProductCardImage({
|
|
|
163
153
|
function ProductCardBadge({
|
|
164
154
|
className,
|
|
165
155
|
position = 'bottom-left',
|
|
156
|
+
variant,
|
|
166
157
|
children,
|
|
167
158
|
...props
|
|
168
159
|
}: React.ComponentProps<typeof Badge> & {
|
|
169
160
|
position?: 'top-left' | 'bottom-left'
|
|
170
161
|
}) {
|
|
162
|
+
const {badgeText, badgeVariant} = useProductCardContext()
|
|
163
|
+
// If no children provided, use badgeText from context
|
|
164
|
+
const content = children || badgeText
|
|
165
|
+
|
|
166
|
+
if (!content) return null
|
|
167
|
+
|
|
171
168
|
return (
|
|
172
169
|
<div
|
|
173
170
|
className={cn(
|
|
@@ -176,10 +173,17 @@ function ProductCardBadge({
|
|
|
176
173
|
)}
|
|
177
174
|
>
|
|
178
175
|
<Badge
|
|
179
|
-
|
|
176
|
+
variant={variant ?? badgeVariant ?? 'none'}
|
|
177
|
+
className={cn(
|
|
178
|
+
!badgeVariant &&
|
|
179
|
+
!variant &&
|
|
180
|
+
'bg-black/50 text-white border-transparent',
|
|
181
|
+
'rounded',
|
|
182
|
+
className
|
|
183
|
+
)}
|
|
180
184
|
{...props}
|
|
181
185
|
>
|
|
182
|
-
{
|
|
186
|
+
{content}
|
|
183
187
|
</Badge>
|
|
184
188
|
</div>
|
|
185
189
|
)
|
|
@@ -187,27 +191,18 @@ function ProductCardBadge({
|
|
|
187
191
|
|
|
188
192
|
function ProductCardFavoriteButton({
|
|
189
193
|
className,
|
|
190
|
-
onPress,
|
|
191
|
-
filled = false,
|
|
192
194
|
...props
|
|
193
|
-
}: React.ComponentProps<'div'>
|
|
194
|
-
|
|
195
|
-
filled?: boolean
|
|
196
|
-
}) {
|
|
195
|
+
}: React.ComponentProps<'div'>) {
|
|
196
|
+
const {isFavorited, onFavoriteToggle} = useProductCardContext()
|
|
197
197
|
return (
|
|
198
198
|
<div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>
|
|
199
|
-
<FavoriteButton onClick={
|
|
199
|
+
<FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />
|
|
200
200
|
</div>
|
|
201
201
|
)
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
function ProductCardInfo({
|
|
205
|
-
|
|
206
|
-
variant = 'default',
|
|
207
|
-
...props
|
|
208
|
-
}: React.ComponentProps<'div'> & {
|
|
209
|
-
variant?: 'default' | 'priceOverlay' | 'compact'
|
|
210
|
-
}) {
|
|
204
|
+
function ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {
|
|
205
|
+
const {variant} = useProductCardContext()
|
|
211
206
|
if (variant !== 'default') {
|
|
212
207
|
return null
|
|
213
208
|
}
|
|
@@ -226,6 +221,7 @@ function ProductCardTitle({
|
|
|
226
221
|
children,
|
|
227
222
|
...props
|
|
228
223
|
}: React.ComponentProps<'h3'>) {
|
|
224
|
+
const {product} = useProductCardContext()
|
|
229
225
|
return (
|
|
230
226
|
<h3
|
|
231
227
|
data-slot="product-card-title"
|
|
@@ -236,11 +232,46 @@ function ProductCardTitle({
|
|
|
236
232
|
)}
|
|
237
233
|
{...props}
|
|
238
234
|
>
|
|
239
|
-
{children}
|
|
235
|
+
{children || product.title}
|
|
240
236
|
</h3>
|
|
241
237
|
)
|
|
242
238
|
}
|
|
243
239
|
|
|
240
|
+
function ProductCardPrice({className}: {className?: string}) {
|
|
241
|
+
const {product, selectedProductVariant} = useProductCardContext()
|
|
242
|
+
|
|
243
|
+
// Derive price data locally
|
|
244
|
+
const displayPrice = selectedProductVariant?.price || product?.price
|
|
245
|
+
const displayCompareAtPrice =
|
|
246
|
+
selectedProductVariant?.compareAtPrice || product?.compareAtPrice
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<ProductVariantPrice
|
|
250
|
+
amount={displayPrice?.amount || ''}
|
|
251
|
+
currencyCode={displayPrice?.currencyCode || ''}
|
|
252
|
+
compareAtPriceAmount={displayCompareAtPrice?.amount}
|
|
253
|
+
compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}
|
|
254
|
+
className={className}
|
|
255
|
+
/>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Special PriceOverlayBadge for price overlay variant
|
|
260
|
+
function ProductCardPriceOverlayBadge() {
|
|
261
|
+
const {product, selectedProductVariant, variant} = useProductCardContext()
|
|
262
|
+
if (variant !== 'priceOverlay') return null
|
|
263
|
+
const displayPrice = selectedProductVariant?.price || product.price
|
|
264
|
+
const currencyCode = displayPrice?.currencyCode
|
|
265
|
+
const amount = displayPrice?.amount
|
|
266
|
+
|
|
267
|
+
if (!currencyCode || !amount) return null
|
|
268
|
+
return (
|
|
269
|
+
<ProductCardBadge position="top-left">
|
|
270
|
+
{formatMoney(amount, currencyCode)}
|
|
271
|
+
</ProductCardBadge>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
244
275
|
export interface ProductCardProps {
|
|
245
276
|
/** The product to display in the card */
|
|
246
277
|
product: Product
|
|
@@ -253,61 +284,43 @@ export interface ProductCardProps {
|
|
|
253
284
|
/** Optional text to display in a badge on the card */
|
|
254
285
|
badgeText?: string
|
|
255
286
|
/** Visual style variant for the badge */
|
|
256
|
-
badgeVariant?: '
|
|
287
|
+
badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'
|
|
288
|
+
/** Callback fired when the product is clicked */
|
|
289
|
+
onProductClick?: () => void
|
|
257
290
|
/** Callback fired when the favorite button is toggled */
|
|
258
291
|
onFavoriteToggled?: (isFavorited: boolean) => void
|
|
259
|
-
/**
|
|
260
|
-
|
|
292
|
+
/** Custom layout via children */
|
|
293
|
+
children?: React.ReactNode
|
|
261
294
|
}
|
|
262
295
|
|
|
263
|
-
// Composed ProductCard component
|
|
264
296
|
function ProductCard({
|
|
265
297
|
product,
|
|
266
298
|
selectedProductVariant,
|
|
267
299
|
variant = 'default',
|
|
268
300
|
touchable = true,
|
|
269
301
|
badgeText,
|
|
270
|
-
badgeVariant
|
|
302
|
+
badgeVariant,
|
|
303
|
+
onProductClick,
|
|
271
304
|
onFavoriteToggled,
|
|
305
|
+
children,
|
|
272
306
|
}: ProductCardProps) {
|
|
273
307
|
const {navigateToProduct} = useShopNavigation()
|
|
274
308
|
const {saveProduct, unsaveProduct} = useSavedProductsActions()
|
|
275
309
|
|
|
276
|
-
const {
|
|
277
|
-
id,
|
|
278
|
-
title,
|
|
279
|
-
featuredImage,
|
|
280
|
-
price,
|
|
281
|
-
compareAtPrice,
|
|
282
|
-
isFavorited,
|
|
283
|
-
defaultVariantId,
|
|
284
|
-
shop,
|
|
285
|
-
} = product
|
|
286
|
-
|
|
287
|
-
// Use selected variant data if available
|
|
288
|
-
const displayImage = selectedProductVariant?.image || featuredImage
|
|
289
|
-
const displayPrice = selectedProductVariant?.price || price
|
|
290
|
-
const displayCompareAtPrice =
|
|
291
|
-
selectedProductVariant?.compareAtPrice || compareAtPrice
|
|
292
|
-
|
|
293
310
|
// Local state for optimistic UI updates
|
|
294
|
-
const [isFavoritedLocal, setIsFavoritedLocal] =
|
|
295
|
-
|
|
296
|
-
const currencyCode = displayPrice?.currencyCode
|
|
297
|
-
const amount = displayPrice?.amount
|
|
298
|
-
const imageUrl = displayImage?.url
|
|
299
|
-
const imageAltText = displayImage?.altText || title
|
|
300
|
-
const compareAtPriceAmount = displayCompareAtPrice?.amount
|
|
311
|
+
const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)
|
|
301
312
|
|
|
302
|
-
const
|
|
313
|
+
const handleClick = useCallback(() => {
|
|
303
314
|
if (!touchable) return
|
|
304
315
|
|
|
316
|
+
onProductClick?.()
|
|
317
|
+
|
|
305
318
|
navigateToProduct({
|
|
306
|
-
productId: id,
|
|
319
|
+
productId: product.id,
|
|
307
320
|
})
|
|
308
|
-
}, [navigateToProduct, id, touchable])
|
|
321
|
+
}, [navigateToProduct, product.id, touchable, onProductClick])
|
|
309
322
|
|
|
310
|
-
const
|
|
323
|
+
const handleFavoriteClick = useCallback(async () => {
|
|
311
324
|
const previousState = isFavoritedLocal
|
|
312
325
|
|
|
313
326
|
// Optimistic update
|
|
@@ -317,15 +330,17 @@ function ProductCard({
|
|
|
317
330
|
try {
|
|
318
331
|
if (previousState) {
|
|
319
332
|
await unsaveProduct({
|
|
320
|
-
productId: id,
|
|
321
|
-
shopId: shop.id,
|
|
322
|
-
productVariantId:
|
|
333
|
+
productId: product.id,
|
|
334
|
+
shopId: product.shop.id,
|
|
335
|
+
productVariantId:
|
|
336
|
+
selectedProductVariant?.id || product.defaultVariantId,
|
|
323
337
|
})
|
|
324
338
|
} else {
|
|
325
339
|
await saveProduct({
|
|
326
|
-
productId: id,
|
|
327
|
-
shopId: shop.id,
|
|
328
|
-
productVariantId:
|
|
340
|
+
productId: product.id,
|
|
341
|
+
shopId: product.shop.id,
|
|
342
|
+
productVariantId:
|
|
343
|
+
selectedProductVariant?.id || product.defaultVariantId,
|
|
329
344
|
})
|
|
330
345
|
}
|
|
331
346
|
} catch (error) {
|
|
@@ -335,63 +350,77 @@ function ProductCard({
|
|
|
335
350
|
}
|
|
336
351
|
}, [
|
|
337
352
|
isFavoritedLocal,
|
|
338
|
-
id,
|
|
339
|
-
shop.id,
|
|
353
|
+
product.id,
|
|
354
|
+
product.shop.id,
|
|
355
|
+
product.defaultVariantId,
|
|
340
356
|
selectedProductVariant?.id,
|
|
341
|
-
defaultVariantId,
|
|
342
357
|
saveProduct,
|
|
343
358
|
unsaveProduct,
|
|
344
359
|
onFavoriteToggled,
|
|
345
360
|
])
|
|
346
361
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
onPress={handleFavoritePress}
|
|
379
|
-
/>
|
|
380
|
-
</ProductCardImageContainer>
|
|
381
|
-
|
|
382
|
-
{/* Product info for default variant */}
|
|
383
|
-
<ProductCardInfo variant={variant}>
|
|
384
|
-
<ProductCardTitle>{title}</ProductCardTitle>
|
|
362
|
+
const contextValue = useMemo<ProductCardContextValue>(
|
|
363
|
+
() => ({
|
|
364
|
+
// Core data
|
|
365
|
+
product,
|
|
366
|
+
selectedProductVariant,
|
|
367
|
+
|
|
368
|
+
// UI configuration
|
|
369
|
+
variant,
|
|
370
|
+
touchable,
|
|
371
|
+
badgeText,
|
|
372
|
+
badgeVariant,
|
|
373
|
+
|
|
374
|
+
// State
|
|
375
|
+
isFavorited: isFavoritedLocal,
|
|
376
|
+
|
|
377
|
+
// Actions
|
|
378
|
+
onClick: handleClick,
|
|
379
|
+
onFavoriteToggle: handleFavoriteClick,
|
|
380
|
+
}),
|
|
381
|
+
[
|
|
382
|
+
product,
|
|
383
|
+
selectedProductVariant,
|
|
384
|
+
variant,
|
|
385
|
+
touchable,
|
|
386
|
+
badgeText,
|
|
387
|
+
badgeVariant,
|
|
388
|
+
isFavoritedLocal,
|
|
389
|
+
handleClick,
|
|
390
|
+
handleFavoriteClick,
|
|
391
|
+
]
|
|
392
|
+
)
|
|
385
393
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
+
return (
|
|
395
|
+
<ProductCardContext.Provider value={contextValue}>
|
|
396
|
+
{children ?? (
|
|
397
|
+
<ProductCardContainer>
|
|
398
|
+
<ProductCardImageContainer>
|
|
399
|
+
<ProductCardImage />
|
|
400
|
+
{variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}
|
|
401
|
+
<ProductCardBadge />
|
|
402
|
+
<ProductCardFavoriteButton />
|
|
403
|
+
</ProductCardImageContainer>
|
|
404
|
+
{variant === 'default' && (
|
|
405
|
+
<ProductCardInfo>
|
|
406
|
+
<ProductCardTitle />
|
|
407
|
+
<ProductCardPrice />
|
|
408
|
+
</ProductCardInfo>
|
|
409
|
+
)}
|
|
410
|
+
</ProductCardContainer>
|
|
411
|
+
)}
|
|
412
|
+
</ProductCardContext.Provider>
|
|
394
413
|
)
|
|
395
414
|
}
|
|
396
415
|
|
|
397
|
-
export {
|
|
416
|
+
export {
|
|
417
|
+
ProductCard,
|
|
418
|
+
ProductCardContainer,
|
|
419
|
+
ProductCardImageContainer,
|
|
420
|
+
ProductCardImage,
|
|
421
|
+
ProductCardBadge,
|
|
422
|
+
ProductCardFavoriteButton,
|
|
423
|
+
ProductCardInfo,
|
|
424
|
+
ProductCardTitle,
|
|
425
|
+
ProductCardPrice,
|
|
426
|
+
}
|
|
@@ -6,22 +6,20 @@ import {Slot as SlotPrimitive} from 'radix-ui'
|
|
|
6
6
|
import {cn} from '../../lib/utils'
|
|
7
7
|
|
|
8
8
|
const badgeVariants = cva(
|
|
9
|
-
'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
|
|
9
|
+
'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
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
secondary:
|
|
16
|
-
'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
|
13
|
+
primary: 'border-transparent bg-primary text-primary-foreground',
|
|
14
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground',
|
|
17
15
|
destructive:
|
|
18
|
-
'border-transparent bg-destructive text-white
|
|
19
|
-
outline:
|
|
20
|
-
|
|
16
|
+
'border-transparent bg-destructive text-white dark:bg-destructive/60',
|
|
17
|
+
outline: 'bg-white text-foreground',
|
|
18
|
+
none: '', // Allows custom classes
|
|
21
19
|
},
|
|
22
20
|
},
|
|
23
21
|
defaultVariants: {
|
|
24
|
-
variant: '
|
|
22
|
+
variant: 'primary',
|
|
25
23
|
},
|
|
26
24
|
}
|
|
27
25
|
)
|
package/src/mocks.ts
CHANGED
|
@@ -468,9 +468,9 @@ const isMobile = (): boolean => {
|
|
|
468
468
|
return isIOS || isAndroid
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
export const injectMocks = () => {
|
|
472
|
-
// Only inject mocks if we aren't on a mobile device
|
|
473
|
-
if (isMobile()) {
|
|
471
|
+
export const injectMocks = ({force}: {force?: boolean} = {}) => {
|
|
472
|
+
// Only inject mocks if we aren't on a mobile device or we force it
|
|
473
|
+
if (isMobile() && !force) {
|
|
474
474
|
return
|
|
475
475
|
}
|
|
476
476
|
|