@hanzo/ui 4.7.0 → 4.8.3

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 (278) hide show
  1. package/assets/ai-icons.tsx +207 -0
  2. package/assets/crypto.tsx +33 -0
  3. package/assets/file-type-icon.tsx +66 -0
  4. package/assets/file.tsx +45 -0
  5. package/assets/general.tsx +2318 -0
  6. package/assets/hanzo-logo.svg +9 -0
  7. package/assets/hanzo-logo.tsx +17 -0
  8. package/assets/index.ts +122 -0
  9. package/assets/index.tsx +4 -0
  10. package/assets/llm-provider.tsx +1094 -0
  11. package/blocks/auth/index.ts +6 -0
  12. package/blocks/auth/login-2fa.tsx +165 -0
  13. package/blocks/auth/login-basic.tsx +94 -0
  14. package/blocks/auth/login-social.tsx +148 -0
  15. package/blocks/auth/magic-link.tsx +129 -0
  16. package/blocks/auth/password-reset.tsx +97 -0
  17. package/blocks/auth/signup.tsx +157 -0
  18. package/blocks/components/accordian-block.tsx +48 -0
  19. package/blocks/components/block-component-props.ts +11 -0
  20. package/blocks/components/bullet-cards-block.tsx +46 -0
  21. package/blocks/components/card-block/index.tsx +171 -0
  22. package/blocks/components/card-block/link-out-button.tsx +20 -0
  23. package/blocks/components/card-block/util.ts +28 -0
  24. package/blocks/components/carte-blanche-block/index.tsx +127 -0
  25. package/blocks/components/carte-blanche-block/variant-content-left.tsx +49 -0
  26. package/blocks/components/content.tsx +70 -0
  27. package/blocks/components/cta-block.tsx +115 -0
  28. package/blocks/components/enh-heading-block.tsx +204 -0
  29. package/blocks/components/grid-block/grid-block-mutator.ts +12 -0
  30. package/blocks/components/grid-block/index.tsx +83 -0
  31. package/blocks/components/grid-block/mutator-registry.ts +10 -0
  32. package/blocks/components/grid-block/table-borders.mutator.ts +47 -0
  33. package/blocks/components/group-block.tsx +83 -0
  34. package/blocks/components/heading-block.tsx +88 -0
  35. package/blocks/components/image-block.tsx +111 -0
  36. package/blocks/components/index.ts +30 -0
  37. package/blocks/components/screenful-block/content.tsx +123 -0
  38. package/blocks/components/screenful-block/index.tsx +107 -0
  39. package/blocks/components/screenful-block/poster-background.tsx +34 -0
  40. package/blocks/components/screenful-block/video-background.tsx +45 -0
  41. package/blocks/components/space-block.tsx +66 -0
  42. package/blocks/components/video-block.tsx +138 -0
  43. package/blocks/data-display/activity-feed.tsx +242 -0
  44. package/blocks/data-display/data-table.tsx +235 -0
  45. package/blocks/data-display/stats-grid.tsx +194 -0
  46. package/blocks/def/accordian-block.ts +14 -0
  47. package/blocks/def/block.ts +7 -0
  48. package/blocks/def/bullet-cards-block.ts +22 -0
  49. package/blocks/def/card-block.ts +22 -0
  50. package/blocks/def/carte-blanche-block.ts +21 -0
  51. package/blocks/def/cta-block.ts +19 -0
  52. package/blocks/def/element-block.ts +11 -0
  53. package/blocks/def/enh-heading-block.ts +44 -0
  54. package/blocks/def/grid-block.ts +16 -0
  55. package/blocks/def/group-block.ts +11 -0
  56. package/blocks/def/heading-block.ts +15 -0
  57. package/blocks/def/image-block.ts +31 -0
  58. package/blocks/def/index.ts +35 -0
  59. package/blocks/def/screenful-block.ts +54 -0
  60. package/blocks/def/space-block.ts +64 -0
  61. package/blocks/def/video-block.ts +9 -0
  62. package/blocks/ecommerce/checkout.tsx +242 -0
  63. package/blocks/ecommerce/index.ts +7 -0
  64. package/blocks/ecommerce/product-detail.tsx +257 -0
  65. package/blocks/ecommerce/product-grid.tsx +148 -0
  66. package/blocks/ecommerce/shopping-cart.tsx +181 -0
  67. package/blocks/index.ts +2 -0
  68. package/blocks/marketing/cta-section.tsx +207 -0
  69. package/blocks/marketing/faq.tsx +159 -0
  70. package/blocks/marketing/features-grid.tsx +156 -0
  71. package/blocks/marketing/hero-section.tsx +192 -0
  72. package/blocks/marketing/index.ts +6 -0
  73. package/blocks/marketing/pricing-table.tsx +121 -0
  74. package/blocks/marketing/testimonials.tsx +196 -0
  75. package/components/index.ts +9 -0
  76. package/dist/index.js +1408 -1514
  77. package/dist/index.mjs +1364 -1472
  78. package/dist/lib/utils.js +1 -0
  79. package/dist/lib/utils.mjs +1 -0
  80. package/dist/src/utils.js +1 -0
  81. package/dist/src/utils.mjs +1 -0
  82. package/dist/tailwind/index.js +4 -1
  83. package/dist/tailwind/index.mjs +4 -1
  84. package/dist/types/index.js +1 -0
  85. package/dist/types/index.mjs +2 -0
  86. package/dist/util/format-text.js +52 -0
  87. package/dist/util/format-text.mjs +33 -0
  88. package/dist/util/index.js +385 -0
  89. package/dist/util/index.mjs +364 -0
  90. package/frameworks/core/index.ts +6 -0
  91. package/frameworks/core/utils/index.ts +64 -0
  92. package/frameworks/react/components/button.tsx +26 -0
  93. package/frameworks/react/components/index.ts +5 -0
  94. package/frameworks/react/hooks/index.ts +5 -0
  95. package/frameworks/react/index.ts +9 -0
  96. package/frameworks/react/package.json +8 -0
  97. package/frameworks/react/utils/index.ts +2 -0
  98. package/frameworks/react-native/index.ts +9 -0
  99. package/frameworks/react-native/package.json +8 -0
  100. package/frameworks/registry.json +371 -0
  101. package/frameworks/setup.sh +69 -0
  102. package/frameworks/svelte/index.ts +9 -0
  103. package/frameworks/svelte/package.json +8 -0
  104. package/frameworks/tracker.json +1854 -0
  105. package/frameworks/vue/index.ts +9 -0
  106. package/frameworks/vue/package.json +8 -0
  107. package/helpers/file.ts +33 -0
  108. package/helpers/memoization.ts +40 -0
  109. package/package.json +49 -11
  110. package/primitives/accordion.tsx +74 -0
  111. package/primitives/action-button.tsx +42 -0
  112. package/primitives/alert-dialog.tsx +185 -0
  113. package/primitives/alert.tsx +74 -0
  114. package/primitives/apply-typography.tsx +55 -0
  115. package/primitives/aspect-ratio.tsx +5 -0
  116. package/primitives/avatar.tsx +57 -0
  117. package/primitives/background-beams.tsx +142 -0
  118. package/primitives/badge.tsx +45 -0
  119. package/primitives/breadcrumb.tsx +130 -0
  120. package/primitives/breakpoint-indicator.tsx +19 -0
  121. package/primitives/button.tsx +72 -0
  122. package/primitives/calendar.tsx +72 -0
  123. package/primitives/card.tsx +97 -0
  124. package/primitives/carousel.tsx +238 -0
  125. package/primitives/chat/chat-input-area.tsx +88 -0
  126. package/primitives/chat/chat-input.tsx +71 -0
  127. package/primitives/chat/files-preview.tsx +331 -0
  128. package/primitives/chat/index.ts +6 -0
  129. package/primitives/chat/json-form.tsx +8 -0
  130. package/primitives/chat/message-list.tsx +308 -0
  131. package/primitives/chat/message.tsx +569 -0
  132. package/primitives/chat/sqlite-preview.tsx +215 -0
  133. package/primitives/checkbox.tsx +32 -0
  134. package/primitives/collapsible.tsx +9 -0
  135. package/primitives/combobox.tsx +239 -0
  136. package/primitives/command.tsx +151 -0
  137. package/primitives/context-menu.tsx +206 -0
  138. package/primitives/copy-to-clipboard-icon.tsx +60 -0
  139. package/primitives/dialog-video-controller.tsx +38 -0
  140. package/primitives/dialog.tsx +128 -0
  141. package/primitives/dot-pattern.tsx +57 -0
  142. package/primitives/dots-loader.tsx +13 -0
  143. package/primitives/drawer.tsx +113 -0
  144. package/primitives/dropdown-menu.tsx +199 -0
  145. package/primitives/error-message.tsx +19 -0
  146. package/primitives/file-uploader.tsx +202 -0
  147. package/primitives/form.tsx +185 -0
  148. package/primitives/hover-card.tsx +28 -0
  149. package/primitives/icons/github.tsx +14 -0
  150. package/primitives/icons/index.ts +18 -0
  151. package/primitives/icons/youtube-logo.tsx +59 -0
  152. package/primitives/index-common.ts +304 -0
  153. package/primitives/index-next.ts +4 -0
  154. package/primitives/input-otp.tsx +65 -0
  155. package/primitives/input.tsx +128 -0
  156. package/primitives/label.tsx +21 -0
  157. package/primitives/list-adaptor.ts +12 -0
  158. package/primitives/list-box.tsx +74 -0
  159. package/primitives/loading-spinner.tsx +33 -0
  160. package/primitives/markdown-preview.tsx +612 -0
  161. package/primitives/mermaid.tsx +191 -0
  162. package/primitives/navigation-menu.tsx +147 -0
  163. package/primitives/next/image.tsx +91 -0
  164. package/primitives/next/index.ts +7 -0
  165. package/primitives/next/inline-icon.tsx +36 -0
  166. package/primitives/next/link-element.tsx +109 -0
  167. package/primitives/next/mdx-link.tsx +22 -0
  168. package/primitives/next/media-stack.tsx +52 -0
  169. package/primitives/next/nav-items.tsx +45 -0
  170. package/primitives/next/youtube-embed.tsx +83 -0
  171. package/primitives/pagination.tsx +117 -0
  172. package/primitives/popover.tsx +34 -0
  173. package/primitives/pretty-json-print.tsx +28 -0
  174. package/primitives/progress.tsx +27 -0
  175. package/primitives/prompt-textarea.tsx +72 -0
  176. package/primitives/qr-code.tsx +112 -0
  177. package/primitives/radio-group.tsx +42 -0
  178. package/primitives/resizable.tsx +47 -0
  179. package/primitives/scroll-area.tsx +57 -0
  180. package/primitives/search-input.tsx +66 -0
  181. package/primitives/select.tsx +122 -0
  182. package/primitives/separator.tsx +26 -0
  183. package/primitives/sheet.tsx +139 -0
  184. package/primitives/skeleton.tsx +18 -0
  185. package/primitives/slider.tsx +63 -0
  186. package/primitives/sonner.tsx +35 -0
  187. package/primitives/step-indicator.tsx +69 -0
  188. package/primitives/stepper.tsx +272 -0
  189. package/primitives/switch.tsx +27 -0
  190. package/primitives/table.tsx +105 -0
  191. package/primitives/tabs.tsx +50 -0
  192. package/primitives/text-area.tsx +26 -0
  193. package/primitives/text-link.tsx +25 -0
  194. package/primitives/textarea.tsx +64 -0
  195. package/primitives/textfield.tsx +78 -0
  196. package/primitives/toast.tsx +30 -0
  197. package/primitives/toggle-group.tsx +63 -0
  198. package/primitives/toggle.tsx +44 -0
  199. package/primitives/tooltip.tsx +47 -0
  200. package/primitives/video-player.tsx +23 -0
  201. package/src/button.ts +1 -0
  202. package/src/hooks/index.ts +7 -0
  203. package/src/hooks/use-click-away.ts +31 -0
  204. package/src/hooks/use-combined-refs.ts +22 -0
  205. package/src/hooks/use-copy-clipboard.ts +30 -0
  206. package/src/hooks/use-debounce.ts +17 -0
  207. package/src/hooks/use-fill-ids.ts +25 -0
  208. package/src/hooks/use-map.ts +26 -0
  209. package/src/hooks/use-measure.ts +42 -0
  210. package/src/hooks/use-reverse-video-playback.ts +43 -0
  211. package/src/hooks/use-scroll-restoration.ts +50 -0
  212. package/src/index-lean.ts +87 -0
  213. package/src/index.ts +54 -0
  214. package/src/mcp/README.md +141 -0
  215. package/src/mcp/enhanced-server.ts +1208 -0
  216. package/src/mcp/index.ts +518 -0
  217. package/src/mcp/package.json +10 -0
  218. package/src/registry/api.ts +164 -0
  219. package/src/registry/index.ts +60 -0
  220. package/src/registry/package.json +10 -0
  221. package/src/utils.ts +19 -0
  222. package/tailwind/colors.tailwind.js +53 -0
  223. package/tailwind/fontFamily.tailwind.ts +7 -0
  224. package/tailwind/fontSize.tailwind.ts +13 -0
  225. package/tailwind/index.ts +7 -0
  226. package/tailwind/safelist.tailwind.js +26 -0
  227. package/tailwind/screens.tailwind.js +8 -0
  228. package/tailwind/spacing.tailwind.js +65 -0
  229. package/tailwind/tailwind.config.hanzo-preset.d.ts +5 -0
  230. package/tailwind/tailwind.config.hanzo-preset.js +915 -0
  231. package/tailwind/tw-font-desc.ts +15 -0
  232. package/tailwind/typo-plugin/get-plugin-styles.js +679 -0
  233. package/tailwind/typo-plugin/index.d.ts +9 -0
  234. package/tailwind/typo-plugin/index.js +141 -0
  235. package/tailwind/typo-plugin/utils.js +60 -0
  236. package/tailwind/typography-test.mdx +35 -0
  237. package/tailwind/z-index.tailwind.js +71 -0
  238. package/types/animation-def.ts +3 -0
  239. package/types/breakpoints.ts +11 -0
  240. package/types/bullet-item.ts +10 -0
  241. package/types/button-def.ts +39 -0
  242. package/types/dimensions.ts +8 -0
  243. package/types/grid-def.ts +56 -0
  244. package/types/image-def.ts +32 -0
  245. package/types/index.ts +30 -0
  246. package/types/link-def.ts +56 -0
  247. package/types/media-stack-def.ts +31 -0
  248. package/types/t-shirt-size.ts +5 -0
  249. package/types/tshirt-dimensions.ts +20 -0
  250. package/types/video-def.ts +25 -0
  251. package/util/blob.ts +33 -0
  252. package/util/copy-to-clipboard.ts +17 -0
  253. package/util/create-shadow-root.ts +22 -0
  254. package/util/date.ts +84 -0
  255. package/util/debounce.ts +11 -0
  256. package/util/file.ts +15 -0
  257. package/util/format-and-abbreviate-as-currency.ts +125 -0
  258. package/util/format-text.ts +34 -0
  259. package/util/format-to-max-char.ts +68 -0
  260. package/util/index-client.ts +3 -0
  261. package/util/index.ts +112 -0
  262. package/util/number-abbreviate.ts +49 -0
  263. package/util/specifier.ts +43 -0
  264. package/util/spread-to-transform.ts +25 -0
  265. package/util/step-animation.ts +90 -0
  266. package/util/timing.ts +3 -0
  267. package/util/toasts.tsx +17 -0
  268. package/util/two-way-map.ts +19 -0
  269. package/dist/index.d.mts +0 -16
  270. package/dist/index.d.ts +0 -16
  271. package/dist/lib/utils.d.mts +0 -2
  272. package/dist/lib/utils.d.ts +0 -2
  273. package/dist/src/utils.d.mts +0 -7
  274. package/dist/src/utils.d.ts +0 -7
  275. package/dist/tailwind/index.d.mts +0 -2
  276. package/dist/tailwind/index.d.ts +0 -2
  277. package/dist/types/index.d.mts +0 -12
  278. package/dist/types/index.d.ts +0 -12
@@ -0,0 +1,156 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@hanzo/ui/primitives'
5
+ import type { LucideIcon } from 'lucide-react'
6
+
7
+ interface Feature {
8
+ icon?: LucideIcon
9
+ title: string
10
+ description: string
11
+ href?: string
12
+ }
13
+
14
+ interface FeaturesGridProps extends React.ComponentPropsWithoutRef<'section'> {
15
+ title?: string
16
+ subtitle?: string
17
+ description?: string
18
+ features: Feature[]
19
+ columns?: 2 | 3 | 4
20
+ variant?: 'default' | 'cards' | 'minimal'
21
+ }
22
+
23
+ export function FeaturesGrid({
24
+ className,
25
+ title,
26
+ subtitle,
27
+ description,
28
+ features,
29
+ columns = 3,
30
+ variant = 'default',
31
+ ...props
32
+ }: FeaturesGridProps) {
33
+ const gridCols = {
34
+ 2: 'md:grid-cols-2',
35
+ 3: 'md:grid-cols-2 lg:grid-cols-3',
36
+ 4: 'md:grid-cols-2 lg:grid-cols-4',
37
+ }[columns]
38
+
39
+ return (
40
+ <section className={cn('py-24 sm:py-32', className)} {...props}>
41
+ <div className="container">
42
+ {(title || subtitle || description) && (
43
+ <div className="mx-auto max-w-3xl text-center">
44
+ {subtitle && (
45
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
46
+ {subtitle}
47
+ </p>
48
+ )}
49
+ {title && (
50
+ <h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
51
+ {title}
52
+ </h2>
53
+ )}
54
+ {description && (
55
+ <p className="mt-4 text-lg text-muted-foreground">
56
+ {description}
57
+ </p>
58
+ )}
59
+ </div>
60
+ )}
61
+
62
+ {variant === 'cards' && (
63
+ <div className={cn('mt-16 grid gap-6', gridCols)}>
64
+ {features.map((feature, i) => {
65
+ const Icon = feature.icon
66
+ return (
67
+ <Card key={i} className="relative">
68
+ {feature.href && (
69
+ <a
70
+ href={feature.href}
71
+ className="absolute inset-0 z-10"
72
+ aria-label={feature.title}
73
+ />
74
+ )}
75
+ <CardHeader>
76
+ {Icon && (
77
+ <div className="mb-2 inline-flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
78
+ <Icon className="h-5 w-5 text-primary" />
79
+ </div>
80
+ )}
81
+ <CardTitle>{feature.title}</CardTitle>
82
+ </CardHeader>
83
+ <CardContent>
84
+ <CardDescription>{feature.description}</CardDescription>
85
+ </CardContent>
86
+ </Card>
87
+ )
88
+ })}
89
+ </div>
90
+ )}
91
+
92
+ {variant === 'minimal' && (
93
+ <div className={cn('mt-16 grid gap-x-8 gap-y-10', gridCols)}>
94
+ {features.map((feature, i) => {
95
+ const Icon = feature.icon
96
+ return (
97
+ <div key={i} className="relative">
98
+ {feature.href && (
99
+ <a
100
+ href={feature.href}
101
+ className="absolute inset-0 z-10"
102
+ aria-label={feature.title}
103
+ />
104
+ )}
105
+ <div className="flex items-start gap-4">
106
+ {Icon && (
107
+ <div className="flex-shrink-0">
108
+ <div className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
109
+ <Icon className="h-5 w-5 text-primary" />
110
+ </div>
111
+ </div>
112
+ )}
113
+ <div>
114
+ <h3 className="font-semibold">{feature.title}</h3>
115
+ <p className="mt-2 text-sm text-muted-foreground">
116
+ {feature.description}
117
+ </p>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ )
122
+ })}
123
+ </div>
124
+ )}
125
+
126
+ {variant === 'default' && (
127
+ <div className={cn('mt-16 grid gap-8', gridCols)}>
128
+ {features.map((feature, i) => {
129
+ const Icon = feature.icon
130
+ return (
131
+ <div key={i} className="relative text-center">
132
+ {feature.href && (
133
+ <a
134
+ href={feature.href}
135
+ className="absolute inset-0 z-10"
136
+ aria-label={feature.title}
137
+ />
138
+ )}
139
+ {Icon && (
140
+ <div className="mx-auto mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
141
+ <Icon className="h-6 w-6 text-primary" />
142
+ </div>
143
+ )}
144
+ <h3 className="mb-2 text-lg font-semibold">{feature.title}</h3>
145
+ <p className="text-sm text-muted-foreground">
146
+ {feature.description}
147
+ </p>
148
+ </div>
149
+ )
150
+ })}
151
+ </div>
152
+ )}
153
+ </div>
154
+ </section>
155
+ )
156
+ }
@@ -0,0 +1,192 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Button } from '@hanzo/ui/primitives'
5
+
6
+ interface HeroSectionProps extends React.ComponentPropsWithoutRef<'section'> {
7
+ title: string
8
+ subtitle?: string
9
+ description?: string
10
+ primaryAction?: {
11
+ label: string
12
+ onClick: () => void
13
+ }
14
+ secondaryAction?: {
15
+ label: string
16
+ onClick: () => void
17
+ }
18
+ backgroundImage?: string
19
+ variant?: 'default' | 'centered' | 'split'
20
+ }
21
+
22
+ export function HeroSection({
23
+ className,
24
+ title,
25
+ subtitle,
26
+ description,
27
+ primaryAction,
28
+ secondaryAction,
29
+ backgroundImage,
30
+ variant = 'default',
31
+ ...props
32
+ }: HeroSectionProps) {
33
+ if (variant === 'centered') {
34
+ return (
35
+ <section
36
+ className={cn('relative py-24 sm:py-32', className)}
37
+ style={{
38
+ backgroundImage: backgroundImage ? `url(${backgroundImage})` : undefined,
39
+ }}
40
+ {...props}
41
+ >
42
+ {backgroundImage && (
43
+ <div className="absolute inset-0 bg-gradient-to-t from-background/90 to-background/20" />
44
+ )}
45
+ <div className="container relative">
46
+ <div className="mx-auto max-w-3xl text-center">
47
+ {subtitle && (
48
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
49
+ {subtitle}
50
+ </p>
51
+ )}
52
+ <h1 className="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
53
+ {title}
54
+ </h1>
55
+ {description && (
56
+ <p className="mt-6 text-lg text-muted-foreground">
57
+ {description}
58
+ </p>
59
+ )}
60
+ {(primaryAction || secondaryAction) && (
61
+ <div className="mt-10 flex flex-col gap-4 sm:flex-row sm:justify-center">
62
+ {primaryAction && (
63
+ <Button
64
+ size="lg"
65
+ onClick={primaryAction.onClick}
66
+ className="w-full sm:w-auto"
67
+ >
68
+ {primaryAction.label}
69
+ </Button>
70
+ )}
71
+ {secondaryAction && (
72
+ <Button
73
+ size="lg"
74
+ variant="outline"
75
+ onClick={secondaryAction.onClick}
76
+ className="w-full sm:w-auto"
77
+ >
78
+ {secondaryAction.label}
79
+ </Button>
80
+ )}
81
+ </div>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </section>
86
+ )
87
+ }
88
+
89
+ if (variant === 'split') {
90
+ return (
91
+ <section className={cn('py-24 sm:py-32', className)} {...props}>
92
+ <div className="container">
93
+ <div className="grid gap-12 lg:grid-cols-2 lg:gap-8">
94
+ <div className="flex flex-col justify-center">
95
+ {subtitle && (
96
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
97
+ {subtitle}
98
+ </p>
99
+ )}
100
+ <h1 className="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
101
+ {title}
102
+ </h1>
103
+ {description && (
104
+ <p className="mt-6 text-lg text-muted-foreground">
105
+ {description}
106
+ </p>
107
+ )}
108
+ {(primaryAction || secondaryAction) && (
109
+ <div className="mt-10 flex flex-col gap-4 sm:flex-row">
110
+ {primaryAction && (
111
+ <Button
112
+ size="lg"
113
+ onClick={primaryAction.onClick}
114
+ className="w-full sm:w-auto"
115
+ >
116
+ {primaryAction.label}
117
+ </Button>
118
+ )}
119
+ {secondaryAction && (
120
+ <Button
121
+ size="lg"
122
+ variant="outline"
123
+ onClick={secondaryAction.onClick}
124
+ className="w-full sm:w-auto"
125
+ >
126
+ {secondaryAction.label}
127
+ </Button>
128
+ )}
129
+ </div>
130
+ )}
131
+ </div>
132
+ {backgroundImage && (
133
+ <div className="relative aspect-[4/3] overflow-hidden rounded-lg">
134
+ <img
135
+ src={backgroundImage}
136
+ alt=""
137
+ className="h-full w-full object-cover"
138
+ />
139
+ </div>
140
+ )}
141
+ </div>
142
+ </div>
143
+ </section>
144
+ )
145
+ }
146
+
147
+ // Default variant
148
+ return (
149
+ <section className={cn('py-24 sm:py-32', className)} {...props}>
150
+ <div className="container">
151
+ <div className="max-w-3xl">
152
+ {subtitle && (
153
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
154
+ {subtitle}
155
+ </p>
156
+ )}
157
+ <h1 className="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
158
+ {title}
159
+ </h1>
160
+ {description && (
161
+ <p className="mt-6 text-lg text-muted-foreground">
162
+ {description}
163
+ </p>
164
+ )}
165
+ {(primaryAction || secondaryAction) && (
166
+ <div className="mt-10 flex flex-col gap-4 sm:flex-row">
167
+ {primaryAction && (
168
+ <Button
169
+ size="lg"
170
+ onClick={primaryAction.onClick}
171
+ className="w-full sm:w-auto"
172
+ >
173
+ {primaryAction.label}
174
+ </Button>
175
+ )}
176
+ {secondaryAction && (
177
+ <Button
178
+ size="lg"
179
+ variant="outline"
180
+ onClick={secondaryAction.onClick}
181
+ className="w-full sm:w-auto"
182
+ >
183
+ {secondaryAction.label}
184
+ </Button>
185
+ )}
186
+ </div>
187
+ )}
188
+ </div>
189
+ </div>
190
+ </section>
191
+ )
192
+ }
@@ -0,0 +1,6 @@
1
+ export { HeroSection } from './hero-section'
2
+ export { FeaturesGrid } from './features-grid'
3
+ export { PricingTable } from './pricing-table'
4
+ export { Testimonials } from './testimonials'
5
+ export { CTASection } from './cta-section'
6
+ export { FAQ } from './faq'
@@ -0,0 +1,121 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Button } from '@hanzo/ui/primitives'
5
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@hanzo/ui/primitives'
6
+ import { Check } from 'lucide-react'
7
+
8
+ interface PricingPlan {
9
+ name: string
10
+ description: string
11
+ price: string | number
12
+ currency?: string
13
+ interval?: string
14
+ features: string[]
15
+ highlighted?: boolean
16
+ badge?: string
17
+ onSelect?: () => void
18
+ buttonLabel?: string
19
+ buttonVariant?: 'default' | 'outline' | 'ghost'
20
+ }
21
+
22
+ interface PricingTableProps extends React.ComponentPropsWithoutRef<'section'> {
23
+ title?: string
24
+ subtitle?: string
25
+ description?: string
26
+ plans: PricingPlan[]
27
+ columns?: 2 | 3 | 4
28
+ }
29
+
30
+ export function PricingTable({
31
+ className,
32
+ title,
33
+ subtitle,
34
+ description,
35
+ plans,
36
+ columns = 3,
37
+ ...props
38
+ }: PricingTableProps) {
39
+ const gridCols = {
40
+ 2: 'md:grid-cols-2',
41
+ 3: 'md:grid-cols-2 lg:grid-cols-3',
42
+ 4: 'md:grid-cols-2 lg:grid-cols-4',
43
+ }[columns]
44
+
45
+ return (
46
+ <section className={cn('py-24 sm:py-32', className)} {...props}>
47
+ <div className="container">
48
+ {(title || subtitle || description) && (
49
+ <div className="mx-auto max-w-3xl text-center">
50
+ {subtitle && (
51
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
52
+ {subtitle}
53
+ </p>
54
+ )}
55
+ {title && (
56
+ <h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
57
+ {title}
58
+ </h2>
59
+ )}
60
+ {description && (
61
+ <p className="mt-4 text-lg text-muted-foreground">
62
+ {description}
63
+ </p>
64
+ )}
65
+ </div>
66
+ )}
67
+
68
+ <div className={cn('mt-16 grid gap-8', gridCols)}>
69
+ {plans.map((plan, i) => (
70
+ <Card
71
+ key={i}
72
+ className={cn(
73
+ 'relative flex flex-col',
74
+ plan.highlighted && 'border-primary shadow-lg'
75
+ )}
76
+ >
77
+ {plan.badge && (
78
+ <div className="absolute -top-3 left-0 right-0 mx-auto w-fit">
79
+ <div className="rounded-full bg-primary px-3 py-1 text-xs font-semibold text-primary-foreground">
80
+ {plan.badge}
81
+ </div>
82
+ </div>
83
+ )}
84
+ <CardHeader className={plan.badge ? 'pt-8' : ''}>
85
+ <CardTitle className="text-xl">{plan.name}</CardTitle>
86
+ <CardDescription>{plan.description}</CardDescription>
87
+ </CardHeader>
88
+ <CardContent className="flex-1">
89
+ <div className="mb-6">
90
+ <span className="text-4xl font-bold">
91
+ {plan.currency || '$'}{plan.price}
92
+ </span>
93
+ {plan.interval && (
94
+ <span className="text-muted-foreground">/{plan.interval}</span>
95
+ )}
96
+ </div>
97
+ <ul className="space-y-3">
98
+ {plan.features.map((feature, j) => (
99
+ <li key={j} className="flex items-start gap-2">
100
+ <Check className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
101
+ <span className="text-sm">{feature}</span>
102
+ </li>
103
+ ))}
104
+ </ul>
105
+ </CardContent>
106
+ <CardFooter>
107
+ <Button
108
+ className="w-full"
109
+ variant={plan.buttonVariant || (plan.highlighted ? 'default' : 'outline')}
110
+ onClick={plan.onSelect}
111
+ >
112
+ {plan.buttonLabel || 'Get started'}
113
+ </Button>
114
+ </CardFooter>
115
+ </Card>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ </section>
120
+ )
121
+ }
@@ -0,0 +1,196 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Avatar, AvatarFallback, AvatarImage } from '@hanzo/ui/primitives'
5
+ import { Card, CardContent } from '@hanzo/ui/primitives'
6
+ import { Star } from 'lucide-react'
7
+
8
+ interface Testimonial {
9
+ content: string
10
+ author: {
11
+ name: string
12
+ title?: string
13
+ company?: string
14
+ avatar?: string
15
+ }
16
+ rating?: number
17
+ }
18
+
19
+ interface TestimonialsProps extends React.ComponentPropsWithoutRef<'section'> {
20
+ title?: string
21
+ subtitle?: string
22
+ description?: string
23
+ testimonials: Testimonial[]
24
+ columns?: 1 | 2 | 3
25
+ variant?: 'default' | 'cards' | 'minimal'
26
+ }
27
+
28
+ export function Testimonials({
29
+ className,
30
+ title,
31
+ subtitle,
32
+ description,
33
+ testimonials,
34
+ columns = 3,
35
+ variant = 'default',
36
+ ...props
37
+ }: TestimonialsProps) {
38
+ const gridCols = {
39
+ 1: '',
40
+ 2: 'md:grid-cols-2',
41
+ 3: 'md:grid-cols-2 lg:grid-cols-3',
42
+ }[columns]
43
+
44
+ return (
45
+ <section className={cn('py-24 sm:py-32', className)} {...props}>
46
+ <div className="container">
47
+ {(title || subtitle || description) && (
48
+ <div className="mx-auto max-w-3xl text-center">
49
+ {subtitle && (
50
+ <p className="mb-4 text-sm font-semibold uppercase tracking-wide text-primary">
51
+ {subtitle}
52
+ </p>
53
+ )}
54
+ {title && (
55
+ <h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
56
+ {title}
57
+ </h2>
58
+ )}
59
+ {description && (
60
+ <p className="mt-4 text-lg text-muted-foreground">
61
+ {description}
62
+ </p>
63
+ )}
64
+ </div>
65
+ )}
66
+
67
+ {variant === 'cards' && (
68
+ <div className={cn('mt-16 grid gap-6', gridCols)}>
69
+ {testimonials.map((testimonial, i) => (
70
+ <Card key={i}>
71
+ <CardContent className="pt-6">
72
+ {testimonial.rating && (
73
+ <div className="mb-4 flex gap-1">
74
+ {Array.from({ length: 5 }).map((_, j) => (
75
+ <Star
76
+ key={j}
77
+ className={cn(
78
+ 'h-4 w-4',
79
+ j < testimonial.rating
80
+ ? 'fill-primary text-primary'
81
+ : 'text-muted-foreground'
82
+ )}
83
+ />
84
+ ))}
85
+ </div>
86
+ )}
87
+ <blockquote className="text-lg">
88
+ &ldquo;{testimonial.content}&rdquo;
89
+ </blockquote>
90
+ <div className="mt-6 flex items-center gap-4">
91
+ <Avatar>
92
+ <AvatarImage src={testimonial.author.avatar} />
93
+ <AvatarFallback>
94
+ {testimonial.author.name.split(' ').map(n => n[0]).join('')}
95
+ </AvatarFallback>
96
+ </Avatar>
97
+ <div>
98
+ <div className="font-semibold">{testimonial.author.name}</div>
99
+ {(testimonial.author.title || testimonial.author.company) && (
100
+ <div className="text-sm text-muted-foreground">
101
+ {testimonial.author.title}
102
+ {testimonial.author.title && testimonial.author.company && ', '}
103
+ {testimonial.author.company}
104
+ </div>
105
+ )}
106
+ </div>
107
+ </div>
108
+ </CardContent>
109
+ </Card>
110
+ ))}
111
+ </div>
112
+ )}
113
+
114
+ {variant === 'minimal' && (
115
+ <div className={cn('mt-16 space-y-12', columns > 1 && 'md:space-y-0 md:grid md:gap-8', gridCols)}>
116
+ {testimonials.map((testimonial, i) => (
117
+ <div key={i} className="mx-auto max-w-2xl">
118
+ {testimonial.rating && (
119
+ <div className="mb-4 flex gap-1">
120
+ {Array.from({ length: 5 }).map((_, j) => (
121
+ <Star
122
+ key={j}
123
+ className={cn(
124
+ 'h-4 w-4',
125
+ j < testimonial.rating
126
+ ? 'fill-primary text-primary'
127
+ : 'text-muted-foreground'
128
+ )}
129
+ />
130
+ ))}
131
+ </div>
132
+ )}
133
+ <blockquote className="text-lg text-muted-foreground">
134
+ &ldquo;{testimonial.content}&rdquo;
135
+ </blockquote>
136
+ <div className="mt-4">
137
+ <div className="font-semibold">{testimonial.author.name}</div>
138
+ {(testimonial.author.title || testimonial.author.company) && (
139
+ <div className="text-sm text-muted-foreground">
140
+ {testimonial.author.title}
141
+ {testimonial.author.title && testimonial.author.company && ', '}
142
+ {testimonial.author.company}
143
+ </div>
144
+ )}
145
+ </div>
146
+ </div>
147
+ ))}
148
+ </div>
149
+ )}
150
+
151
+ {variant === 'default' && (
152
+ <div className={cn('mt-16 grid gap-8', gridCols)}>
153
+ {testimonials.map((testimonial, i) => (
154
+ <div key={i} className="text-center">
155
+ <div className="mx-auto mb-4 h-16 w-16">
156
+ <Avatar className="h-full w-full">
157
+ <AvatarImage src={testimonial.author.avatar} />
158
+ <AvatarFallback className="text-lg">
159
+ {testimonial.author.name.split(' ').map(n => n[0]).join('')}
160
+ </AvatarFallback>
161
+ </Avatar>
162
+ </div>
163
+ {testimonial.rating && (
164
+ <div className="mb-4 flex justify-center gap-1">
165
+ {Array.from({ length: 5 }).map((_, j) => (
166
+ <Star
167
+ key={j}
168
+ className={cn(
169
+ 'h-4 w-4',
170
+ j < testimonial.rating
171
+ ? 'fill-primary text-primary'
172
+ : 'text-muted-foreground'
173
+ )}
174
+ />
175
+ ))}
176
+ </div>
177
+ )}
178
+ <blockquote className="mb-4 text-lg">
179
+ &ldquo;{testimonial.content}&rdquo;
180
+ </blockquote>
181
+ <div className="font-semibold">{testimonial.author.name}</div>
182
+ {(testimonial.author.title || testimonial.author.company) && (
183
+ <div className="text-sm text-muted-foreground">
184
+ {testimonial.author.title}
185
+ {testimonial.author.title && testimonial.author.company && ', '}
186
+ {testimonial.author.company}
187
+ </div>
188
+ )}
189
+ </div>
190
+ ))}
191
+ </div>
192
+ )}
193
+ </div>
194
+ </section>
195
+ )
196
+ }
@@ -0,0 +1,9 @@
1
+ // Component exports
2
+ // This file provides a central import point for all UI components
3
+ // Usage: import { Button, Card, cn } from '@hanzo/ui/components'
4
+
5
+ // Export commonly used utilities
6
+ export { cn, formatDate, absoluteUrl } from '../src/utils'
7
+
8
+ // Re-export all primitives as components for backward compatibility
9
+ export * from '../primitives/index-next'