@thorprovider/blocks 0.1.3 → 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 (99) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +73 -29
  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/product-categories/category-card-grid.test.tsx +122 -0
  7. package/src/blocks/e-commerce/product-categories/category-card.test.tsx +68 -0
  8. package/src/blocks/e-commerce/product-categories/category-card.tsx +172 -0
  9. package/src/blocks/e-commerce/product-categories/category-grid.test.tsx +67 -0
  10. package/src/blocks/e-commerce/product-categories/category-shared.tsx +109 -0
  11. package/src/blocks/e-commerce/product-categories/product-category-grid.tsx +170 -0
  12. package/src/blocks/e-commerce/product-categories/product-category.test.tsx +266 -0
  13. package/src/blocks/e-commerce/product-categories/product-category.tsx +462 -0
  14. package/src/blocks/e-commerce/product-list/product-card.test.tsx +263 -0
  15. package/src/blocks/e-commerce/product-list/product-card.tsx +424 -0
  16. package/src/blocks/e-commerce/product-list/product-grid.test.tsx +162 -0
  17. package/src/blocks/e-commerce/product-list/product-grid.tsx +114 -0
  18. package/src/blocks/e-commerce/product-overview/product-overview-gallery.tsx +71 -0
  19. package/src/blocks/e-commerce/product-overview/{product-overview-5-lightbox-tab.tsx → product-overview-lightbox-tab.tsx} +35 -23
  20. package/src/blocks/e-commerce/product-overview/{product-overview-5-reviews-tab.tsx → product-overview-reviews-tab.tsx} +40 -17
  21. package/src/blocks/e-commerce/product-overview/{product-overview-5-tabs.tsx → product-overview-tabs.tsx} +8 -7
  22. package/src/blocks/e-commerce/product-overview/product-overview.test.tsx +283 -0
  23. package/src/blocks/e-commerce/product-overview/product-overview.tsx +1186 -0
  24. package/src/blocks/e-commerce/reviews/review-rating-2.tsx +5 -6
  25. package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +399 -0
  26. package/src/blocks/e-commerce/shopping-carts/{shopping-cart-4.tsx → shopping-cart.tsx} +119 -85
  27. package/src/blocks/e-commerce/storefront-hero/storefront-hero.test.tsx +412 -0
  28. package/src/blocks/e-commerce/storefront-hero/storefront-hero.tsx +1283 -0
  29. package/src/blocks/marketing/features/feature.test.tsx +153 -0
  30. package/src/blocks/marketing/features/feature.tsx +195 -0
  31. package/src/blocks/marketing/navbars/navbar-4-account-menu.test.tsx +27 -9
  32. package/src/blocks/marketing/navbars/navbar-4.test.tsx +33 -2
  33. package/src/blocks/marketing/navbars/navbar-4.tsx +29 -11
  34. package/src/index.ts +77 -48
  35. package/src/lib/contracts-curated.test.ts +42 -24
  36. package/src/lib/contracts.test.ts +143 -2
  37. package/src/lib/contracts.ts +346 -14
  38. package/src/ui/button-group.tsx +1 -1
  39. package/src/ui/carousel.tsx +1 -1
  40. package/src/ui/dialog.tsx +1 -1
  41. package/src/ui/field.tsx +2 -2
  42. package/src/ui/input-group.tsx +3 -3
  43. package/src/ui/pagination.tsx +1 -1
  44. package/src/ui/sidebar.tsx +7 -7
  45. package/src/ui/toggle-group.tsx +1 -1
  46. package/tsconfig.build.json +12 -0
  47. package/tsup.config.ts +11 -7
  48. package/.turbo/turbo-build.log +0 -24
  49. package/.turbo/turbo-test.log +0 -47
  50. package/.turbo/turbo-type-check.log +0 -0
  51. package/dist/.tsbuildinfo +0 -1
  52. package/dist/contracts.d.mts +0 -1732
  53. package/dist/contracts.d.ts +0 -1732
  54. package/dist/contracts.js +0 -423
  55. package/dist/contracts.js.map +0 -1
  56. package/dist/contracts.mjs +0 -365
  57. package/dist/contracts.mjs.map +0 -1
  58. package/dist/index.d.mts +0 -3068
  59. package/dist/index.d.ts +0 -3068
  60. package/dist/index.js +0 -17834
  61. package/dist/index.js.map +0 -1
  62. package/dist/index.mjs +0 -17740
  63. package/dist/index.mjs.map +0 -1
  64. package/src/blocks/e-commerce/product-categories/avatar-group.tsx +0 -86
  65. package/src/blocks/e-commerce/product-categories/product-category-1.tsx +0 -126
  66. package/src/blocks/e-commerce/product-categories/product-category-2.test.tsx +0 -51
  67. package/src/blocks/e-commerce/product-categories/product-category-2.tsx +0 -120
  68. package/src/blocks/e-commerce/product-categories/product-category-4.tsx +0 -129
  69. package/src/blocks/e-commerce/product-categories/product-category-5.tsx +0 -218
  70. package/src/blocks/e-commerce/product-list/product-card-1.test.tsx +0 -88
  71. package/src/blocks/e-commerce/product-list/product-card-1.tsx +0 -192
  72. package/src/blocks/e-commerce/product-list/product-card-2.tsx +0 -71
  73. package/src/blocks/e-commerce/product-list/product-card-3-reaction-toggle.tsx +0 -166
  74. package/src/blocks/e-commerce/product-list/product-card-3.tsx +0 -215
  75. package/src/blocks/e-commerce/product-list/product-card-5-checkbox-check.tsx +0 -98
  76. package/src/blocks/e-commerce/product-list/product-card-5-reaction-toggle.tsx +0 -171
  77. package/src/blocks/e-commerce/product-list/product-card-5.tsx +0 -367
  78. package/src/blocks/e-commerce/product-overview/product-overview-1.test.tsx +0 -182
  79. package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +0 -370
  80. package/src/blocks/e-commerce/product-overview/product-overview-2.tsx +0 -272
  81. package/src/blocks/e-commerce/product-overview/product-overview-5-gallery.tsx +0 -59
  82. package/src/blocks/e-commerce/product-overview/product-overview-5.tsx +0 -274
  83. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1-spinning-counter.tsx +0 -142
  84. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1.tsx +0 -291
  85. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-item-row.tsx +0 -110
  86. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-recommendations.tsx +0 -51
  87. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-spinning-counter.tsx +0 -146
  88. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2.tsx +0 -295
  89. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3-spinning-counter.tsx +0 -142
  90. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3.tsx +0 -342
  91. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4-spinning-counter.tsx +0 -146
  92. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.test.tsx +0 -229
  93. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.test.tsx +0 -72
  94. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +0 -263
  95. package/src/blocks/e-commerce/storefront-hero/storefront-hero-2.tsx +0 -283
  96. package/src/blocks/e-commerce/storefront-hero/storefront-hero-5.tsx +0 -318
  97. package/src/blocks/e-commerce/storefront-hero/storefront-hero-6.tsx +0 -205
  98. package/src/blocks/marketing/features/feature-1.tsx +0 -97
  99. package/src/blocks/marketing/features/feature-2.tsx +0 -82
@@ -1,205 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
- import { ArrowRight, Sparkles } from 'lucide-react'
5
-
6
- import { Badge } from '../../../ui/badge'
7
- import { Button } from '../../../ui/button'
8
- import { Card, CardContent } from '../../../ui/card'
9
-
10
- export interface HeroDeal {
11
- /** Unique identifier for the deal. */
12
- id: string
13
- /** Deal name shown in the badge, e.g. "Summer Sale". */
14
- title: string
15
- /** Discount label, e.g. "50% OFF". */
16
- discount: string
17
- /** Countdown text, e.g. "2d : 15h : 30m". */
18
- endTime: string
19
- }
20
-
21
- export interface HeroCategory {
22
- /** Unique identifier for the category. */
23
- id: string
24
- /** Category name. */
25
- name: string
26
- /** Product count label, e.g. "2.5k+". */
27
- count: string
28
- /** Image URL for the category card. */
29
- image: string
30
- /** Accessible image description (falls back to `name`). */
31
- imageAlt?: string
32
- /** Optional link destination; renders the card arrow as a link when present. */
33
- href?: string
34
- }
35
-
36
- export interface StorefrontHero6Props {
37
- /** Small pill shown above the headline. */
38
- badge?: string
39
- /** Main headline. */
40
- title: string
41
- /** Accent line rendered on a new line after the headline. */
42
- subtitle?: string
43
- /** Supporting paragraph under the headline. */
44
- description: string
45
- /** Deals rendered in the sticky auto-rotating top bar. */
46
- deals: HeroDeal[]
47
- /** Product categories rendered as a card grid. */
48
- categories: HeroCategory[]
49
- /** Milliseconds between auto-rotating deals (default 3000). */
50
- rotationInterval?: number
51
- /** Label shown before the deal countdown, e.g. "Ends in". */
52
- endsInLabel?: string
53
- /** Label of the shop-now button in the deals bar. */
54
- shopNowLabel?: string
55
- /** Unit appended to each category product count. */
56
- productsLabel?: string
57
- /** Prefix for the category arrow accessible label. */
58
- viewLabel?: string
59
- /** Called when the shop-now button in the deals bar is clicked. */
60
- onShopNow?: (deal: HeroDeal) => void
61
- /** Called when a category arrow is clicked (categories without `href`). */
62
- onCategoryClick?: (category: HeroCategory) => void
63
- }
64
-
65
- function StorefrontHero6({
66
- badge,
67
- title,
68
- subtitle,
69
- description,
70
- deals,
71
- categories,
72
- rotationInterval = 3000,
73
- endsInLabel = 'Termina en',
74
- shopNowLabel = 'Comprar ahora',
75
- productsLabel = 'productos',
76
- viewLabel = 'Ver',
77
- onShopNow,
78
- onCategoryClick,
79
- }: StorefrontHero6Props) {
80
- const [currentDeal, setCurrentDeal] = useState(0)
81
-
82
- useEffect(() => {
83
- if (deals.length <= 1) return
84
-
85
- const interval = setInterval(() => {
86
- setCurrentDeal(prev => (prev + 1) % deals.length)
87
- }, rotationInterval)
88
-
89
- return () => clearInterval(interval)
90
- }, [deals.length, rotationInterval])
91
-
92
- const deal = deals[currentDeal]
93
-
94
- return (
95
- <div className='relative'>
96
- {deal && (
97
- <header className='bg-primary text-primary-foreground sticky top-0 z-50 shadow-md'>
98
- <div className='mx-auto w-full max-w-7xl px-4 py-2 sm:px-6 lg:px-8'>
99
- <div className='flex items-center justify-center'>
100
- <div className='flex w-full max-w-3xl items-center justify-center gap-4'>
101
- <div className='flex items-center gap-2'>
102
- <Badge
103
- variant='secondary'
104
- className='bg-primary-foreground text-primary px-2.5 py-0.5 font-semibold'
105
- >
106
- {deal.title}
107
- </Badge>
108
- <span className='font-semibold'>{deal.discount}</span>
109
- </div>
110
- <span className='hidden sm:inline'>
111
- {endsInLabel} {deal.endTime}
112
- </span>
113
- <Button
114
- variant='link'
115
- size='sm'
116
- className='text-primary-foreground hover:text-primary-foreground/80 ml-auto h-8 cursor-pointer px-3 text-xs'
117
- onClick={onShopNow ? () => onShopNow(deal) : undefined}
118
- >
119
- {shopNowLabel} <ArrowRight />
120
- </Button>
121
- </div>
122
- </div>
123
- </div>
124
- </header>
125
- )}
126
-
127
- <main className='mx-auto w-full max-w-7xl px-4 py-8 sm:px-6 lg:px-8'>
128
- <div className='mb-12 text-center'>
129
- {badge && (
130
- <div className='relative inline-block'>
131
- <Badge
132
- variant='outline'
133
- className='mb-4 flex h-auto items-center gap-2 rounded-full px-3.5 py-1.5 text-sm font-normal'
134
- >
135
- <Sparkles className='size-4' aria-hidden='true' />
136
- {badge}
137
- </Badge>
138
- </div>
139
- )}
140
- <h1 className='text-4xl font-bold tracking-tight text-balance sm:text-5xl md:text-6xl'>
141
- {title}
142
- {subtitle && (
143
- <>
144
- <br />
145
- {subtitle}
146
- </>
147
- )}
148
- </h1>
149
- <p className='text-muted-foreground mx-auto mt-4 max-w-2xl text-lg'>{description}</p>
150
- </div>
151
-
152
- <div className='grid gap-4 sm:grid-cols-2 lg:grid-cols-4'>
153
- {categories.map(category => (
154
- <Card key={category.id} className='group relative overflow-hidden py-0 transition-all duration-500'>
155
- <img
156
- src={category.image || undefined}
157
- alt={category.imageAlt ?? category.name}
158
- className='aspect-4/5 size-full object-cover grayscale transition-all duration-500 hover:grayscale-0'
159
- width={400}
160
- height={500}
161
- loading='lazy'
162
- />
163
- <CardContent className='absolute inset-x-0 bottom-0 p-4'>
164
- <div className='mt-1 flex items-center justify-between'>
165
- <div>
166
- <h2 className='text-background dark:text-foreground text-lg font-medium'>{category.name}</h2>
167
- <span className='text-background/80 dark:text-foreground/80 text-sm'>
168
- {category.count} {productsLabel}
169
- </span>
170
- </div>
171
- {category.href ? (
172
- <Button
173
- variant='secondary'
174
- size='icon'
175
- className='h-9 w-9 cursor-pointer rounded-full backdrop-blur-xs'
176
- aria-label={`${viewLabel} ${category.name}`}
177
- asChild
178
- >
179
- <a href={category.href}>
180
- <ArrowRight />
181
- </a>
182
- </Button>
183
- ) : (
184
- <Button
185
- variant='secondary'
186
- size='icon'
187
- className='h-9 w-9 cursor-pointer rounded-full backdrop-blur-xs'
188
- aria-label={`${viewLabel} ${category.name}`}
189
- onClick={onCategoryClick ? () => onCategoryClick(category) : undefined}
190
- >
191
- <ArrowRight />
192
- </Button>
193
- )}
194
- </div>
195
- </CardContent>
196
- </Card>
197
- ))}
198
- </div>
199
- </main>
200
- </div>
201
- )
202
- }
203
-
204
- export { StorefrontHero6 }
205
- export default StorefrontHero6
@@ -1,97 +0,0 @@
1
- import { Badge } from '../../../ui/badge'
2
- import { Button } from '../../../ui/button'
3
- import { Card, CardContent } from '../../../ui/card'
4
- import { ArrowRight } from 'lucide-react'
5
- import type { LucideIcon } from 'lucide-react'
6
-
7
- export interface Feature1Item {
8
- id: string
9
- title: string
10
- description: string
11
- icon: LucideIcon
12
- }
13
-
14
- export interface Feature1Props {
15
- items: Feature1Item[]
16
- badge?: string
17
- title?: string
18
- description?: string
19
- learnMoreLabel?: string
20
- ctaLabel?: string
21
- onCtaClick?: () => void
22
- onLearnMore?: (item: Feature1Item) => void
23
- }
24
-
25
- export function Feature1({
26
- items,
27
- badge,
28
- title,
29
- description,
30
- learnMoreLabel,
31
- ctaLabel,
32
- onCtaClick,
33
- onLearnMore,
34
- }: Feature1Props) {
35
- return (
36
- <section className='mx-auto w-full max-w-7xl px-4 py-12 sm:px-6 md:py-16 lg:px-8'>
37
- {(badge || title || description) && (
38
- <header className='mx-auto mb-12 max-w-2xl text-center'>
39
- {badge && (
40
- <Badge variant='secondary' className='h-auto mb-4 rounded-full px-4 py-1 font-medium'>
41
- {badge}
42
- </Badge>
43
- )}
44
- {title && (
45
- <h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>{title}</h2>
46
- )}
47
- {description && (
48
- <p className='text-muted-foreground text-pretty'>{description}</p>
49
- )}
50
- </header>
51
- )}
52
-
53
- <div className='grid gap-4 sm:grid-cols-2 sm:gap-6 md:grid-cols-3'>
54
- {items.map(feature => (
55
- <article key={feature.id} className='group'>
56
- <Card className='relative h-full overflow-hidden transition-all hover:shadow-md'>
57
- <CardContent className='px-6'>
58
- <div className='bg-secondary text-secondary-foreground mb-4 inline-flex size-12 items-center justify-center rounded-xl'>
59
- <feature.icon className='size-5' aria-hidden='true' />
60
- </div>
61
- <h3 className='mb-2 text-lg font-semibold'>{feature.title}</h3>
62
- <p className='text-muted-foreground mb-4 text-sm'>{feature.description}</p>
63
-
64
- {learnMoreLabel && (
65
- <Button
66
- variant='link'
67
- size='sm'
68
- className='h-8 px-3 text-xs text-muted-foreground hover:text-foreground h-auto cursor-pointer !p-0 text-sm'
69
- onClick={onLearnMore ? () => onLearnMore(feature) : undefined}
70
- >
71
- {learnMoreLabel}
72
- <ArrowRight />
73
- </Button>
74
- )}
75
- </CardContent>
76
- </Card>
77
- </article>
78
- ))}
79
- </div>
80
-
81
- {ctaLabel && (
82
- <div className='mt-12 text-center'>
83
- <Button
84
- size='lg'
85
- className='h-10 cursor-pointer rounded-lg !px-8'
86
- onClick={onCtaClick}
87
- >
88
- {ctaLabel}
89
- <ArrowRight />
90
- </Button>
91
- </div>
92
- )}
93
- </section>
94
- )
95
- }
96
-
97
- export default Feature1
@@ -1,82 +0,0 @@
1
- import { cn } from '../../../lib/utils'
2
- import type { LucideIcon } from 'lucide-react'
3
-
4
- export interface Feature2Item {
5
- id: string
6
- title: string
7
- description: string
8
- icon: LucideIcon
9
- }
10
-
11
- export interface Feature2Props {
12
- items: Feature2Item[]
13
- title?: string
14
- description?: string
15
- image?: { src: string; alt: string }
16
- className?: string
17
- }
18
-
19
- export function Feature2({
20
- items,
21
- title,
22
- description,
23
- image,
24
- className,
25
- }: Feature2Props) {
26
- return (
27
- <section
28
- className={cn('mx-auto w-full max-w-7xl px-4 py-12 sm:px-6 md:py-16 lg:px-8', className)}
29
- >
30
- <div className='grid gap-12 lg:grid-cols-2 lg:gap-16'>
31
- <div className='flex flex-col gap-10'>
32
- {(title || description) && (
33
- <header className='flex flex-col gap-4'>
34
- {title && (
35
- <h2 className='text-3xl font-bold tracking-tight text-balance sm:text-4xl lg:text-5xl'>
36
- {title}
37
- </h2>
38
- )}
39
- {description && (
40
- <p className='text-muted-foreground text-base text-pretty md:text-lg'>
41
- {description}
42
- </p>
43
- )}
44
- </header>
45
- )}
46
-
47
- <div className='grid gap-6 sm:grid-cols-2 sm:gap-8'>
48
- {items.map(item => (
49
- <div key={item.id} className='group flex flex-col items-start gap-3 rounded-lg'>
50
- <div className='bg-secondary text-secondary-foreground inline-flex size-10 items-center justify-center rounded-full'>
51
- <item.icon className='size-5' aria-hidden='true' />
52
- </div>
53
- <div className='flex flex-col gap-1.5'>
54
- <h3 className='text-foreground text-base font-semibold md:text-lg'>{item.title}</h3>
55
- <p className='text-muted-foreground text-sm text-balance'>{item.description}</p>
56
- </div>
57
- </div>
58
- ))}
59
- </div>
60
- </div>
61
-
62
- {image && (
63
- <div className='relative'>
64
- <div className='bg-muted relative z-10 overflow-hidden rounded-lg shadow-sm transition-all duration-300 hover:shadow-md'>
65
- <div className='aspect-[4/3] w-full'>
66
- <img
67
- src={image.src || undefined}
68
- alt={image.alt}
69
- className='size-full object-cover grayscale'
70
- loading='lazy'
71
- decoding='async'
72
- />
73
- </div>
74
- </div>
75
- </div>
76
- )}
77
- </div>
78
- </section>
79
- )
80
- }
81
-
82
- export default Feature2