@thorprovider/blocks 0.1.2 → 1.0.0

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 (111) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +108 -23
  3. package/docs/standards/data-contracts.md +13 -2
  4. package/package.json +26 -10
  5. package/scripts/fix-esm-extensions.mjs +40 -0
  6. package/src/blocks/e-commerce/navigation/back-to-top.test.tsx +27 -0
  7. package/src/blocks/e-commerce/navigation/back-to-top.tsx +71 -0
  8. package/src/blocks/e-commerce/navigation/breadcrumb.test.tsx +174 -0
  9. package/src/blocks/e-commerce/navigation/breadcrumb.tsx +130 -0
  10. package/src/blocks/e-commerce/navigation/mobile-bottom-nav.test.tsx +74 -0
  11. package/src/blocks/e-commerce/navigation/mobile-bottom-nav.tsx +78 -0
  12. package/src/blocks/e-commerce/navigation/skip-to-content.test.tsx +35 -0
  13. package/src/blocks/e-commerce/navigation/skip-to-content.tsx +37 -0
  14. package/src/blocks/e-commerce/product-categories/category-card-grid.test.tsx +122 -0
  15. package/src/blocks/e-commerce/product-categories/category-card.test.tsx +68 -0
  16. package/src/blocks/e-commerce/product-categories/category-card.tsx +172 -0
  17. package/src/blocks/e-commerce/product-categories/category-grid.test.tsx +67 -0
  18. package/src/blocks/e-commerce/product-categories/category-shared.tsx +109 -0
  19. package/src/blocks/e-commerce/product-categories/product-category-grid.tsx +170 -0
  20. package/src/blocks/e-commerce/product-categories/product-category.test.tsx +266 -0
  21. package/src/blocks/e-commerce/product-categories/product-category.tsx +462 -0
  22. package/src/blocks/e-commerce/product-list/product-card.test.tsx +263 -0
  23. package/src/blocks/e-commerce/product-list/product-card.tsx +424 -0
  24. package/src/blocks/e-commerce/product-list/product-grid.test.tsx +162 -0
  25. package/src/blocks/e-commerce/product-list/product-grid.tsx +114 -0
  26. package/src/blocks/e-commerce/product-list/product-showcase-1.test.tsx +78 -0
  27. package/src/blocks/e-commerce/product-list/product-showcase-1.tsx +277 -0
  28. package/src/blocks/e-commerce/product-overview/product-overview-gallery.tsx +71 -0
  29. package/src/blocks/e-commerce/product-overview/{product-overview-5-lightbox-tab.tsx → product-overview-lightbox-tab.tsx} +35 -23
  30. package/src/blocks/e-commerce/product-overview/{product-overview-5-reviews-tab.tsx → product-overview-reviews-tab.tsx} +40 -17
  31. package/src/blocks/e-commerce/product-overview/{product-overview-5-tabs.tsx → product-overview-tabs.tsx} +8 -7
  32. package/src/blocks/e-commerce/product-overview/product-overview.test.tsx +283 -0
  33. package/src/blocks/e-commerce/product-overview/product-overview.tsx +1186 -0
  34. package/src/blocks/e-commerce/reviews/review-rating-2.tsx +5 -6
  35. package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +399 -0
  36. package/src/blocks/e-commerce/shopping-carts/{shopping-cart-4.tsx → shopping-cart.tsx} +204 -109
  37. package/src/blocks/e-commerce/storefront-hero/storefront-hero.test.tsx +412 -0
  38. package/src/blocks/e-commerce/storefront-hero/storefront-hero.tsx +1283 -0
  39. package/src/blocks/marketing/features/feature.test.tsx +153 -0
  40. package/src/blocks/marketing/features/feature.tsx +195 -0
  41. package/src/blocks/marketing/navbars/navbar-4-account-menu.test.tsx +56 -0
  42. package/src/blocks/marketing/navbars/navbar-4-account-menu.tsx +30 -14
  43. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.test.tsx +150 -0
  44. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.tsx +102 -9
  45. package/src/blocks/marketing/navbars/navbar-4.test.tsx +154 -2
  46. package/src/blocks/marketing/navbars/navbar-4.tsx +152 -32
  47. package/src/index.ts +93 -46
  48. package/src/lib/contracts-curated.test.ts +42 -24
  49. package/src/lib/contracts.test.ts +143 -2
  50. package/src/lib/contracts.ts +395 -13
  51. package/src/ui/button-group.tsx +1 -1
  52. package/src/ui/carousel.tsx +1 -1
  53. package/src/ui/dialog.tsx +1 -1
  54. package/src/ui/field.tsx +2 -2
  55. package/src/ui/input-group.tsx +3 -3
  56. package/src/ui/pagination.tsx +1 -1
  57. package/src/ui/sidebar.tsx +7 -7
  58. package/src/ui/toggle-group.tsx +1 -1
  59. package/tsconfig.build.json +12 -0
  60. package/tsup.config.ts +11 -7
  61. package/.turbo/turbo-build.log +0 -24
  62. package/.turbo/turbo-test.log +0 -37
  63. package/.turbo/turbo-type-check.log +0 -0
  64. package/dist/.tsbuildinfo +0 -1
  65. package/dist/contracts.d.mts +0 -1584
  66. package/dist/contracts.d.ts +0 -1584
  67. package/dist/contracts.js +0 -390
  68. package/dist/contracts.js.map +0 -1
  69. package/dist/contracts.mjs +0 -334
  70. package/dist/contracts.mjs.map +0 -1
  71. package/dist/index.d.mts +0 -2786
  72. package/dist/index.d.ts +0 -2786
  73. package/dist/index.js +0 -17139
  74. package/dist/index.js.map +0 -1
  75. package/dist/index.mjs +0 -17059
  76. package/dist/index.mjs.map +0 -1
  77. package/src/blocks/e-commerce/product-categories/avatar-group.tsx +0 -86
  78. package/src/blocks/e-commerce/product-categories/product-category-1.tsx +0 -126
  79. package/src/blocks/e-commerce/product-categories/product-category-2.test.tsx +0 -51
  80. package/src/blocks/e-commerce/product-categories/product-category-2.tsx +0 -120
  81. package/src/blocks/e-commerce/product-categories/product-category-4.tsx +0 -129
  82. package/src/blocks/e-commerce/product-categories/product-category-5.tsx +0 -218
  83. package/src/blocks/e-commerce/product-list/product-card-1.tsx +0 -131
  84. package/src/blocks/e-commerce/product-list/product-card-2.tsx +0 -71
  85. package/src/blocks/e-commerce/product-list/product-card-3-reaction-toggle.tsx +0 -166
  86. package/src/blocks/e-commerce/product-list/product-card-3.tsx +0 -215
  87. package/src/blocks/e-commerce/product-list/product-card-5-checkbox-check.tsx +0 -98
  88. package/src/blocks/e-commerce/product-list/product-card-5-reaction-toggle.tsx +0 -171
  89. package/src/blocks/e-commerce/product-list/product-card-5.tsx +0 -367
  90. package/src/blocks/e-commerce/product-overview/product-overview-1.test.tsx +0 -182
  91. package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +0 -370
  92. package/src/blocks/e-commerce/product-overview/product-overview-2.tsx +0 -272
  93. package/src/blocks/e-commerce/product-overview/product-overview-5-gallery.tsx +0 -59
  94. package/src/blocks/e-commerce/product-overview/product-overview-5.tsx +0 -274
  95. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1-spinning-counter.tsx +0 -142
  96. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1.tsx +0 -291
  97. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-item-row.tsx +0 -110
  98. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-recommendations.tsx +0 -51
  99. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-spinning-counter.tsx +0 -146
  100. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2.tsx +0 -295
  101. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3-spinning-counter.tsx +0 -142
  102. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3.tsx +0 -342
  103. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4-spinning-counter.tsx +0 -146
  104. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.test.tsx +0 -137
  105. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.test.tsx +0 -65
  106. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +0 -263
  107. package/src/blocks/e-commerce/storefront-hero/storefront-hero-2.tsx +0 -283
  108. package/src/blocks/e-commerce/storefront-hero/storefront-hero-5.tsx +0 -318
  109. package/src/blocks/e-commerce/storefront-hero/storefront-hero-6.tsx +0 -205
  110. package/src/blocks/marketing/features/feature-1.tsx +0 -97
  111. package/src/blocks/marketing/features/feature-2.tsx +0 -82
@@ -1,295 +0,0 @@
1
- 'use client'
2
-
3
- import { useState } from 'react'
4
- import { ArrowLeft, Shield, ShoppingBag, Tag, Truck } from 'lucide-react'
5
- import { Button } from '../../../ui/button'
6
- import { Card, CardContent, CardHeader, CardTitle } from '../../../ui/card'
7
- import { Input } from '../../../ui/input'
8
- import { Separator } from '../../../ui/separator'
9
- import { ShoppingCart2ItemRow } from './shopping-cart-2-item-row'
10
- import { ShoppingCart2Recommendations } from './shopping-cart-2-recommendations'
11
- import { ShoppingCart2SpinningCounter } from './shopping-cart-2-spinning-counter'
12
-
13
- export interface ShoppingCart2Item {
14
- id: string
15
- name: string
16
- brand?: string
17
- price: number
18
- originalPrice?: number
19
- quantity: number
20
- image: string
21
- color?: string
22
- size?: string
23
- }
24
-
25
- export interface ShoppingCart2Recommendation {
26
- id: string
27
- name: string
28
- price: number
29
- image: string
30
- }
31
-
32
- export interface ShoppingCart2Props {
33
- items: ShoppingCart2Item[]
34
- recommendations?: ShoppingCart2Recommendation[]
35
- taxRate?: number
36
- shippingCost?: number
37
- freeShippingThreshold?: number
38
- validatePromo?: (code: string) => number | null
39
- titleLabel?: string
40
- itemsCountLabel?: string
41
- cartItemsLabel?: string
42
- orderSummaryLabel?: string
43
- subtotalLabel?: string
44
- savingsLabel?: string
45
- shippingLabel?: string
46
- freeShippingLabel?: string
47
- taxLabel?: string
48
- totalLabel?: string
49
- promoPlaceholder?: string
50
- applyPromoLabel?: string
51
- promoAppliedLabel?: string
52
- continueShoppingLabel?: string
53
- emptyCartTitleLabel?: string
54
- emptyCartDescriptionLabel?: string
55
- checkoutLabel?: string
56
- freeShippingNoteLabel?: string
57
- freeShippingThresholdNoteLabel?: string
58
- secureCheckoutLabel?: string
59
- recommendationsLabel?: string
60
- onQuantityChange?: (id: string, quantity: number) => void
61
- onRemove?: (id: string) => void
62
- onAddToWishlist?: (item: ShoppingCart2Item) => void
63
- onCheckout?: () => void
64
- onContinueShopping?: () => void
65
- onRecommendationClick?: (product: ShoppingCart2Recommendation) => void
66
- }
67
-
68
- export function ShoppingCart2({
69
- items,
70
- recommendations,
71
- taxRate = 0.08,
72
- shippingCost = 9.99,
73
- freeShippingThreshold = 75,
74
- validatePromo,
75
- titleLabel = 'Carrito de compras',
76
- itemsCountLabel = 'artículos en tu carrito',
77
- cartItemsLabel = 'Artículos',
78
- orderSummaryLabel = 'Resumen del pedido',
79
- subtotalLabel = 'Subtotal',
80
- savingsLabel = 'Ahorro',
81
- shippingLabel = 'Envío',
82
- freeShippingLabel = 'Gratis',
83
- taxLabel = 'Impuestos',
84
- totalLabel = 'Total',
85
- promoPlaceholder = 'Código de descuento',
86
- applyPromoLabel = 'Aplicar código',
87
- promoAppliedLabel = 'Código "{code}" aplicado',
88
- continueShoppingLabel = 'Seguir comprando',
89
- emptyCartTitleLabel = 'Tu carrito está vacío',
90
- emptyCartDescriptionLabel = 'Añade algunos artículos para empezar',
91
- checkoutLabel = 'Finalizar compra',
92
- freeShippingNoteLabel = 'Envío gratis en este pedido',
93
- freeShippingThresholdNoteLabel = 'Envío gratis en pedidos superiores a ${threshold}',
94
- secureCheckoutLabel = 'Pago seguro garantizado',
95
- recommendationsLabel = 'También te puede gustar',
96
- onQuantityChange,
97
- onRemove,
98
- onAddToWishlist,
99
- onCheckout,
100
- onContinueShopping,
101
- onRecommendationClick,
102
- }: ShoppingCart2Props) {
103
- const [promoCode, setPromoCode] = useState('')
104
- const [appliedPromo, setAppliedPromo] = useState<string | null>(null)
105
-
106
- const applyPromoCode = () => {
107
- if (!validatePromo) return
108
- const code = promoCode.trim()
109
- const discount = validatePromo(code)
110
- if (discount !== null && discount > 0) {
111
- setAppliedPromo(code)
112
- setPromoCode('')
113
- }
114
- }
115
-
116
- const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0)
117
- const itemCount = items.reduce((sum, item) => sum + item.quantity, 0)
118
- const savings = items.reduce((sum, item) => {
119
- const originalPrice = item.originalPrice ?? item.price
120
- return sum + (originalPrice - item.price) * item.quantity
121
- }, 0)
122
- const promoDiscount = appliedPromo && validatePromo ? subtotal * ((validatePromo(appliedPromo) ?? 0) / 100) : 0
123
- const shipping = subtotal > freeShippingThreshold ? 0 : shippingCost
124
- const tax = (subtotal - promoDiscount) * taxRate
125
- const total = subtotal - promoDiscount + shipping + tax
126
-
127
- const formatPrice = (value: number) => `$${value.toFixed(2)}`
128
-
129
- return (
130
- <section className='py-8'>
131
- <div className='mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8'>
132
- {items.length === 0 ? (
133
- <Card>
134
- <CardContent className='py-12 text-center'>
135
- <ShoppingBag className='text-muted-foreground mx-auto mb-4 size-16' />
136
- <h2 className='mb-2 text-2xl font-semibold'>{emptyCartTitleLabel}</h2>
137
- <p className='text-muted-foreground mb-6'>{emptyCartDescriptionLabel}</p>
138
- <Button size='lg' className='h-10 px-8 cursor-pointer' onClick={onContinueShopping}>
139
- {continueShoppingLabel}
140
- </Button>
141
- </CardContent>
142
- </Card>
143
- ) : (
144
- <>
145
- <div className='mb-8'>
146
- <Button
147
- variant='ghost'
148
- size='sm'
149
- className='mb-4 h-8 px-3 text-xs cursor-pointer'
150
- onClick={onContinueShopping}
151
- >
152
- <ArrowLeft />
153
- {continueShoppingLabel}
154
- </Button>
155
- <h1 className='text-3xl font-bold tracking-tight text-balance'>{titleLabel}</h1>
156
- <p className='text-muted-foreground mt-2'>
157
- {items.length} {itemsCountLabel}
158
- </p>
159
- </div>
160
-
161
- <div className='grid gap-8 lg:grid-cols-3'>
162
- <div className='lg:col-span-2'>
163
- <Card>
164
- <CardHeader>
165
- <CardTitle>{cartItemsLabel}</CardTitle>
166
- </CardHeader>
167
- <CardContent className='flex flex-col gap-6'>
168
- {items.map((item, index) => (
169
- <ShoppingCart2ItemRow
170
- key={item.id}
171
- item={item}
172
- isLast={index === items.length - 1}
173
- onRemove={onRemove}
174
- onQuantityChange={onQuantityChange}
175
- onAddToWishlist={onAddToWishlist}
176
- />
177
- ))}
178
- </CardContent>
179
- </Card>
180
- </div>
181
-
182
- <div className='lg:col-span-1'>
183
- <Card className='lg:sticky lg:top-6'>
184
- <CardHeader>
185
- <CardTitle>{orderSummaryLabel}</CardTitle>
186
- </CardHeader>
187
- <CardContent className='flex flex-col gap-4'>
188
- <div className='flex flex-col gap-3 text-sm'>
189
- <div className='flex justify-between gap-4 flex-wrap'>
190
- <span className='flex-1'>
191
- {subtotalLabel} ({itemCount} {cartItemsLabel})
192
- </span>
193
- <span className='font-medium shrink-0'>{formatPrice(subtotal)}</span>
194
- </div>
195
-
196
- {savings > 0 ? (
197
- <div className='flex justify-between gap-4 text-success flex-wrap'>
198
- <span className='flex-1'>{savingsLabel}</span>
199
- <span className='font-medium shrink-0'>-{formatPrice(savings)}</span>
200
- </div>
201
- ) : null}
202
-
203
- {appliedPromo ? (
204
- <div className='flex justify-between gap-4 text-success flex-wrap'>
205
- <span className='flex-1'>{promoAppliedLabel.replace('{code}', appliedPromo)}</span>
206
- <span className='font-medium shrink-0'>-{formatPrice(promoDiscount)}</span>
207
- </div>
208
- ) : null}
209
-
210
- <div className='flex justify-between gap-4 flex-wrap'>
211
- <span className='flex-1'>{shippingLabel}</span>
212
- <span className='font-medium shrink-0'>
213
- {shipping === 0 ? freeShippingLabel : formatPrice(shipping)}
214
- </span>
215
- </div>
216
-
217
- <div className='flex justify-between gap-4 flex-wrap'>
218
- <span className='flex-1'>{taxLabel}</span>
219
- <span className='font-medium shrink-0'>{formatPrice(tax)}</span>
220
- </div>
221
- </div>
222
-
223
- <Separator />
224
-
225
- <div className='flex justify-between gap-4 text-lg font-semibold flex-wrap'>
226
- <span>{totalLabel}</span>
227
- <span className='shrink-0'>
228
- <ShoppingCart2SpinningCounter value={total} decimals={2} prefix='$' />
229
- </span>
230
- </div>
231
-
232
- <div className='flex flex-col gap-3'>
233
- <Input
234
- placeholder={promoPlaceholder}
235
- value={promoCode}
236
- onChange={e => setPromoCode(e.target.value)}
237
- aria-label={promoPlaceholder}
238
- className='h-9 flex-1'
239
- />
240
- <Button
241
- variant='outline'
242
- onClick={applyPromoCode}
243
- className='h-9 cursor-pointer px-4 py-2'
244
- disabled={!promoCode || !validatePromo}
245
- aria-label={applyPromoLabel}
246
- >
247
- <Tag />
248
- </Button>
249
- </div>
250
-
251
- {appliedPromo ? (
252
- <div className='flex items-center gap-2 text-sm text-success'>
253
- <Tag className='size-4' />
254
- {promoAppliedLabel.replace('{code}', appliedPromo)}
255
- </div>
256
- ) : null}
257
-
258
- <Button size='lg' className='h-10 w-full px-8 cursor-pointer' onClick={onCheckout}>
259
- {checkoutLabel}
260
- </Button>
261
-
262
- <div className='flex flex-col gap-3 text-sm'>
263
- <div className='flex items-center gap-2 text-success'>
264
- <Truck className='size-4' />
265
- <span>
266
- {shipping === 0
267
- ? freeShippingNoteLabel
268
- : freeShippingThresholdNoteLabel.replace('${threshold}', String(freeShippingThreshold))}
269
- </span>
270
- </div>
271
- <div className='text-muted-foreground flex items-center gap-2'>
272
- <Shield className='size-4' />
273
- <span>{secureCheckoutLabel}</span>
274
- </div>
275
- </div>
276
- </CardContent>
277
- </Card>
278
- </div>
279
- </div>
280
-
281
- {recommendations && recommendations.length > 0 ? (
282
- <ShoppingCart2Recommendations
283
- recommendations={recommendations}
284
- title={recommendationsLabel}
285
- onRecommendationClick={onRecommendationClick}
286
- />
287
- ) : null}
288
- </>
289
- )}
290
- </div>
291
- </section>
292
- )
293
- }
294
-
295
- export default ShoppingCart2
@@ -1,142 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useRef } from 'react'
4
-
5
- import { cn } from '../../../lib/utils'
6
-
7
- const CELL_SIZE = 20
8
- const DURATION = 450
9
- const STAGGER = 60
10
- const SPINS = 1
11
- const SPIN_BLUR = 2
12
-
13
- const REEL_DIGITS = Array.from({ length: (SPINS + 1) * 10 }, (_, i) => i % 10)
14
-
15
- type ShoppingCart3SpinningCounterProps = {
16
- value: number
17
- decimals?: number
18
- prefix?: string
19
- className?: string
20
- digitClassName?: string
21
- }
22
-
23
- export function ShoppingCart3SpinningCounter({
24
- value,
25
- decimals = 0,
26
- prefix,
27
- className,
28
- digitClassName,
29
- }: ShoppingCart3SpinningCounterProps) {
30
- const safeValue = Math.max(0, value)
31
- const formatted = safeValue.toFixed(decimals)
32
- const chars = formatted.split('')
33
- const containerRef = useRef<HTMLSpanElement>(null)
34
- const isFirstRender = useRef(true)
35
-
36
- useEffect(() => {
37
- const container = containerRef.current
38
- if (!container) return
39
-
40
- const strips = Array.from(container.querySelectorAll<HTMLElement>('[data-reel-strip]'))
41
- const timeouts: number[] = []
42
-
43
- const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
44
-
45
- strips.forEach((strip, index) => {
46
- const digit = Number(strip.dataset.digit)
47
- const delay = (strips.length - 1 - index) * STAGGER
48
-
49
- if (isFirstRender.current || !strip.dataset.initialized || prefersReducedMotion) {
50
- strip.style.transition = 'none'
51
- strip.style.transform = `translateY(-${digit * CELL_SIZE}px)`
52
- strip.style.filter = 'blur(0px)'
53
- strip.dataset.initialized = 'true'
54
- void strip.offsetHeight
55
- return
56
- }
57
-
58
- const landingIndex = SPINS * 10 + digit
59
- strip.style.transition = `transform ${DURATION}ms cubic-bezier(0.16, 1, 0.3, 1) ${delay}ms, filter ${DURATION}ms ease-out ${delay}ms`
60
- strip.style.transform = `translateY(-${landingIndex * CELL_SIZE}px)`
61
- strip.style.filter = `blur(${SPIN_BLUR}px)`
62
-
63
- const settleTimeout = window.setTimeout(() => {
64
- strip.style.filter = 'blur(0px)'
65
- }, delay + 30)
66
-
67
- const resetTimeout = window.setTimeout(() => {
68
- strip.style.transition = 'none'
69
- strip.style.transform = `translateY(-${digit * CELL_SIZE}px)`
70
- void strip.offsetHeight
71
- }, delay + DURATION + 30)
72
-
73
- timeouts.push(settleTimeout, resetTimeout)
74
- })
75
-
76
- isFirstRender.current = false
77
-
78
- return () => {
79
- timeouts.forEach(window.clearTimeout)
80
- }
81
- }, [formatted])
82
-
83
- return (
84
- <span className='relative inline-flex items-center'>
85
- <span className='sr-only'>{prefix}{formatted}</span>
86
- <span
87
- ref={containerRef}
88
- className={cn('inline-flex items-center font-[inherit] tabular-nums', className)}
89
- style={{ height: CELL_SIZE }}
90
- aria-hidden='true'
91
- >
92
- {prefix ? (
93
- <span
94
- className={digitClassName}
95
- style={{ display: 'inline-flex', alignItems: 'center', height: CELL_SIZE }}
96
- >
97
- {prefix}
98
- </span>
99
- ) : null}
100
- {chars.map((char, index) => {
101
- if (!/\d/.test(char)) {
102
- return (
103
- <span
104
- key={index}
105
- className={digitClassName}
106
- style={{ display: 'inline-flex', alignItems: 'center', height: CELL_SIZE }}
107
- >
108
- {char}
109
- </span>
110
- )
111
- }
112
-
113
- return (
114
- <span
115
- key={index}
116
- className='relative inline-block overflow-hidden'
117
- style={{
118
- height: CELL_SIZE,
119
- maskImage: 'linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%)',
120
- WebkitMaskImage: 'linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%)',
121
- }}
122
- >
123
- <span data-reel-strip data-digit={char} className='flex flex-col will-change-[transform,filter]'>
124
- {REEL_DIGITS.map((cellDigit, cellIndex) => (
125
- <span
126
- key={cellIndex}
127
- className={cn('flex items-center justify-center', digitClassName)}
128
- style={{ height: CELL_SIZE }}
129
- >
130
- {cellDigit}
131
- </span>
132
- ))}
133
- </span>
134
- </span>
135
- )
136
- })}
137
- </span>
138
- </span>
139
- )
140
- }
141
-
142
- export default ShoppingCart3SpinningCounter