@hanzo/ui 4.6.0 → 4.8.2

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 (184) hide show
  1. package/assets/general.tsx +1 -1
  2. package/assets/hanzo-logo.tsx +3 -1
  3. package/assets/index.ts +119 -5
  4. package/blocks/auth/index.ts +6 -0
  5. package/blocks/auth/login-2fa.tsx +165 -0
  6. package/blocks/auth/login-basic.tsx +94 -0
  7. package/blocks/auth/login-social.tsx +148 -0
  8. package/blocks/auth/magic-link.tsx +129 -0
  9. package/blocks/auth/password-reset.tsx +97 -0
  10. package/blocks/auth/signup.tsx +157 -0
  11. package/blocks/data-display/activity-feed.tsx +242 -0
  12. package/blocks/data-display/data-table.tsx +235 -0
  13. package/blocks/data-display/stats-grid.tsx +194 -0
  14. package/blocks/ecommerce/checkout.tsx +242 -0
  15. package/blocks/ecommerce/index.ts +7 -0
  16. package/blocks/ecommerce/product-detail.tsx +257 -0
  17. package/blocks/ecommerce/product-grid.tsx +148 -0
  18. package/blocks/ecommerce/shopping-cart.tsx +181 -0
  19. package/blocks/marketing/cta-section.tsx +207 -0
  20. package/blocks/marketing/faq.tsx +159 -0
  21. package/blocks/marketing/features-grid.tsx +156 -0
  22. package/blocks/marketing/hero-section.tsx +192 -0
  23. package/blocks/marketing/index.ts +6 -0
  24. package/blocks/marketing/pricing-table.tsx +121 -0
  25. package/blocks/marketing/testimonials.tsx +196 -0
  26. package/components/index.ts +4 -51
  27. package/dist/index.js +9351 -0
  28. package/dist/index.mjs +9340 -0
  29. package/dist/lib/utils.js +47 -0
  30. package/dist/lib/utils.mjs +28 -0
  31. package/dist/src/utils.js +47 -0
  32. package/dist/src/utils.mjs +28 -0
  33. package/dist/tailwind/index.js +2050 -0
  34. package/dist/tailwind/index.mjs +2019 -0
  35. package/dist/types/index.js +79 -0
  36. package/dist/types/index.mjs +56 -0
  37. package/dist/util/format-text.js +51 -0
  38. package/dist/util/format-text.mjs +32 -0
  39. package/dist/util/index.js +384 -0
  40. package/dist/util/index.mjs +363 -0
  41. package/frameworks/core/index.ts +6 -0
  42. package/frameworks/core/utils/index.ts +64 -0
  43. package/frameworks/react/components/button.tsx +26 -0
  44. package/frameworks/react/components/index.ts +5 -0
  45. package/frameworks/react/hooks/index.ts +5 -0
  46. package/frameworks/react/index.ts +9 -0
  47. package/frameworks/react/package.json +8 -0
  48. package/frameworks/react/utils/index.ts +2 -0
  49. package/frameworks/react-native/index.ts +9 -0
  50. package/frameworks/react-native/package.json +8 -0
  51. package/frameworks/registry.json +371 -0
  52. package/frameworks/setup.sh +69 -0
  53. package/frameworks/svelte/index.ts +9 -0
  54. package/frameworks/svelte/package.json +8 -0
  55. package/frameworks/tracker.json +1854 -0
  56. package/frameworks/vue/index.ts +9 -0
  57. package/frameworks/vue/package.json +8 -0
  58. package/package.json +192 -28
  59. package/primitives/accordion.tsx +1 -1
  60. package/primitives/alert-dialog.tsx +1 -1
  61. package/primitives/alert.tsx +1 -1
  62. package/primitives/avatar.tsx +1 -1
  63. package/primitives/badge.tsx +2 -1
  64. package/primitives/breadcrumb.tsx +1 -1
  65. package/primitives/button.tsx +37 -47
  66. package/primitives/card.tsx +1 -1
  67. package/primitives/carousel.tsx +3 -2
  68. package/primitives/chat/chat-input-area.tsx +5 -4
  69. package/primitives/chat/chat-input.tsx +2 -2
  70. package/primitives/chat/files-preview.tsx +5 -4
  71. package/primitives/chat/message-list.tsx +2 -1
  72. package/primitives/chat/sqlite-preview.tsx +8 -8
  73. package/primitives/checkbox.tsx +2 -1
  74. package/primitives/command.tsx +3 -1
  75. package/primitives/context-menu.tsx +1 -1
  76. package/primitives/dialog.tsx +6 -1
  77. package/primitives/drawer.tsx +4 -1
  78. package/primitives/dropdown-menu.tsx +1 -1
  79. package/primitives/file-uploader.tsx +4 -2
  80. package/primitives/form.tsx +1 -1
  81. package/primitives/hover-card.tsx +1 -1
  82. package/primitives/icons/github.tsx +2 -2
  83. package/primitives/icons/youtube-logo.tsx +1 -1
  84. package/primitives/index-common.ts +7 -6
  85. package/primitives/input-otp.tsx +1 -1
  86. package/primitives/input.tsx +2 -1
  87. package/primitives/label.tsx +2 -1
  88. package/primitives/markdown-preview.tsx +3 -0
  89. package/primitives/mermaid.tsx +13 -18
  90. package/primitives/next/image.tsx +2 -1
  91. package/primitives/next/inline-icon.tsx +14 -14
  92. package/primitives/next/media-stack.tsx +2 -19
  93. package/primitives/pagination.tsx +1 -1
  94. package/primitives/popover.tsx +4 -2
  95. package/primitives/progress.tsx +2 -1
  96. package/primitives/prompt-textarea.tsx +1 -1
  97. package/primitives/radio-group.tsx +1 -1
  98. package/primitives/scroll-area.tsx +1 -1
  99. package/primitives/search-input.tsx +1 -1
  100. package/primitives/select.tsx +1 -1
  101. package/primitives/separator.tsx +2 -1
  102. package/primitives/sheet.tsx +1 -1
  103. package/primitives/skeleton.tsx +1 -0
  104. package/primitives/slider.tsx +2 -1
  105. package/primitives/stepper.tsx +1 -1
  106. package/primitives/switch.tsx +2 -1
  107. package/primitives/table.tsx +1 -1
  108. package/primitives/tabs.tsx +1 -1
  109. package/primitives/textarea.tsx +2 -1
  110. package/primitives/textfield.tsx +1 -0
  111. package/primitives/toggle-group.tsx +1 -1
  112. package/primitives/toggle.tsx +1 -1
  113. package/primitives/tooltip.tsx +1 -1
  114. package/src/hooks/use-copy-clipboard.ts +1 -1
  115. package/src/index-lean.ts +87 -0
  116. package/src/index.ts +54 -0
  117. package/src/registry/api.ts +1 -1
  118. package/src/utils.ts +19 -1
  119. package/tailwind/tailwind.config.hanzo-preset.js +7 -7
  120. package/tailwind/typo-plugin/index.js +1 -1
  121. package/types/animation-def.ts +1 -1
  122. package/types/index.ts +2 -1
  123. package/util/blob.ts +9 -4
  124. package/util/date.ts +2 -1
  125. package/util/format-text.ts +2 -1
  126. package/util/index.ts +103 -0
  127. package/util/spread-to-transform.ts +9 -8
  128. package/MCP-INSTRUCTIONS.md +0 -73
  129. package/README-MCP.md +0 -175
  130. package/dist/button.d.ts +0 -1
  131. package/dist/button.js +0 -1
  132. package/dist/hooks/index.d.ts +0 -7
  133. package/dist/hooks/index.js +0 -7
  134. package/dist/hooks/use-click-away.d.ts +0 -2
  135. package/dist/hooks/use-click-away.js +0 -23
  136. package/dist/hooks/use-combined-refs.d.ts +0 -3
  137. package/dist/hooks/use-combined-refs.js +0 -18
  138. package/dist/hooks/use-copy-clipboard.d.ts +0 -9
  139. package/dist/hooks/use-copy-clipboard.js +0 -21
  140. package/dist/hooks/use-debounce.d.ts +0 -1
  141. package/dist/hooks/use-debounce.js +0 -13
  142. package/dist/hooks/use-fill-ids.d.ts +0 -8
  143. package/dist/hooks/use-fill-ids.js +0 -20
  144. package/dist/hooks/use-map.d.ts +0 -1
  145. package/dist/hooks/use-map.js +0 -20
  146. package/dist/hooks/use-measure.d.ts +0 -8
  147. package/dist/hooks/use-measure.js +0 -25
  148. package/dist/hooks/use-reverse-video-playback.d.ts +0 -1
  149. package/dist/hooks/use-reverse-video-playback.js +0 -41
  150. package/dist/hooks/use-scroll-restoration.d.ts +0 -8
  151. package/dist/hooks/use-scroll-restoration.js +0 -36
  152. package/dist/mcp/enhanced-server.d.ts +0 -29
  153. package/dist/mcp/enhanced-server.js +0 -1128
  154. package/dist/mcp/index.d.ts +0 -28
  155. package/dist/mcp/index.js +0 -436
  156. package/dist/registry/api.d.ts +0 -37
  157. package/dist/registry/api.js +0 -129
  158. package/dist/registry/index.d.ts +0 -353
  159. package/dist/registry/index.js +0 -45
  160. package/dist/utils.d.ts +0 -1
  161. package/dist/utils.js +0 -1
  162. package/environment.d.ts +0 -6
  163. package/public/r/accordion.json +0 -11
  164. package/public/r/alert.json +0 -11
  165. package/public/r/avatar.json +0 -11
  166. package/public/r/badge.json +0 -11
  167. package/public/r/button.json +0 -11
  168. package/public/r/card.json +0 -11
  169. package/public/r/checkbox.json +0 -11
  170. package/public/r/default.json +0 -6
  171. package/public/r/dialog.json +0 -11
  172. package/public/r/input.json +0 -11
  173. package/public/r/label.json +0 -11
  174. package/public/r/new-york.json +0 -6
  175. package/public/r/popover.json +0 -11
  176. package/public/r/select.json +0 -11
  177. package/public/r/table.json +0 -11
  178. package/public/r/tabs.json +0 -11
  179. package/public/r/toast.json +0 -11
  180. package/registry.json +0 -184
  181. package/test/test-registry.js +0 -73
  182. package/test-imports.mjs +0 -19
  183. package/tsconfig.json +0 -22
  184. package/utils.ts +0 -9
@@ -167,7 +167,7 @@ export const ChatBubbleIcon = ({ className }: { className?: string }) => (
167
167
  </svg>
168
168
  );
169
169
 
170
- export const PaperClipIcon = ({ className }: { className?: string }) => (
170
+ export const AttachmentIcon = ({ className }: { className?: string }) => (
171
171
  <svg
172
172
  className={cn('shrink-0', className)}
173
173
  fill="none"
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
 
3
- export const HanzoLogo = ({ className, ...props }: React.SVGProps<SVGSVGElement>) => (
3
+ export type HanzoLogoProps = React.SVGProps<SVGSVGElement>;
4
+
5
+ export const HanzoLogo = ({ className, ...props }: HanzoLogoProps) => (
4
6
  <svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg" className={className} {...props}>
5
7
  <path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor"/>
6
8
  <path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="currentColor" opacity="0.85"/>
package/assets/index.ts CHANGED
@@ -1,8 +1,122 @@
1
1
  // Export all asset components and icons
2
- export * from './ai-icons';
3
- export * from './file-type-icon';
2
+ // Note: Some files have overlapping exports, so we export them selectively
3
+
4
+ // File-related icons
4
5
  export * from './file';
5
- export * from './general';
6
+
7
+ // File type icons (specific exports from file-type-icon to avoid conflicts with general)
8
+ export {
9
+ FileTypeIcon,
10
+ DirectoryTypeIcon,
11
+ type FileTypeIconProps
12
+ } from './file-type-icon';
13
+
14
+ // General icons (excluding duplicates: DirectoryTypeIcon, FileTypeIcon, HanzoIcon)
15
+ export {
16
+ HanzoLogoIcon,
17
+ HanzoLogoSoloIcon,
18
+ HanzoCombinationMarkIcon,
19
+ SendIcon,
20
+ ExportIcon,
21
+ QrIcon,
22
+ JobBubbleIcon,
23
+ ChatBubbleIcon,
24
+ AttachmentIcon,
25
+ DisconnectIcon,
26
+ AgentIcon,
27
+ AddAgentIcon,
28
+ InboxIcon,
29
+ ArchiveIcon,
30
+ ArchivedIcon,
31
+ ActiveIcon,
32
+ FilesIcon,
33
+ SharedFolderIcon,
34
+ AddNewFolderIcon,
35
+ UploadVectorResourceIcon,
36
+ GenerateDocIcon,
37
+ GenerateFromWebIcon,
38
+ CreateAIIcon,
39
+ FileEmptyStateIcon,
40
+ AiTasksIcon,
41
+ AISearchContentIcon,
42
+ BrowseSubscriptionIcon,
43
+ MySubscriptionsIcon,
44
+ PromptLibraryIcon,
45
+ NotificationIcon,
46
+ SheetFileIcon,
47
+ FormulaIcon,
48
+ SheetIcon,
49
+ ShortcutsIcon,
50
+ NetworkAgentIcon,
51
+ MCPIcon,
52
+ ToolsIcon,
53
+ ToolsDisabledIcon,
54
+ StoreIcon,
55
+ ScheduledTasksIcon,
56
+ AddCryptoWalletIcon,
57
+ CryptoWalletIcon,
58
+ ReactJsIcon,
59
+ AIAgentIcon,
60
+ AisIcon,
61
+ ToolAssetsIcon,
62
+ ScheduledTasksComingSoonIcon,
63
+ EmbeddingsGeneratedIcon,
64
+ UnknownLanguageIcon,
65
+ PythonIcon,
66
+ TypeScriptIcon,
67
+ MetadataIcon,
68
+ SaveIcon,
69
+ CloudModelIcon,
70
+ LocalModelIcon,
71
+ ArrowRightIcon,
72
+ ImportIcon,
73
+ WebSearchIcon,
74
+ WebSearchDisabledIcon,
75
+ ChatSettingsIcon,
76
+ PlusIcon,
77
+ ReasoningIcon,
78
+ HomeIcon,
79
+ TracingIcon,
80
+ DownloadIcon,
81
+ CategoryIcon,
82
+ PartyIcon,
83
+ // Exclude DirectoryTypeIcon and FileTypeIcon as they're exported above
84
+ // Exclude HanzoIcon as it's exported below from hanzo-logo
85
+ } from './general';
86
+
87
+ // Crypto icons
6
88
  export * from './crypto';
7
- export * from './llm-provider';
8
- export * from './hanzo-logo';
89
+
90
+ // AI-related icons are already exported from general above
91
+ // Removed duplicate exports to avoid conflicts
92
+
93
+ // LLM provider icons (specific exports to avoid HanzoIcon conflict)
94
+ export {
95
+ MistralIcon,
96
+ GoogleIcon,
97
+ MetaIcon,
98
+ MicrosoftIcon,
99
+ OpenBMBIcon,
100
+ AnthropicIcon,
101
+ AzureIcon,
102
+ DeepSeekIcon,
103
+ GroqIcon,
104
+ LmStudioIcon,
105
+ OllamaIcon,
106
+ OpenAIIcon,
107
+ OpenRouterIcon,
108
+ PerplexityIcon,
109
+ QwenIcon,
110
+ ExoIcon,
111
+ GeminiIcon,
112
+ GrokIcon,
113
+ AyaCohereIcon,
114
+ // Exclude HanzoIcon as it's exported below from hanzo-logo
115
+ } from './llm-provider';
116
+
117
+ // Hanzo logo (primary export for HanzoIcon)
118
+ export {
119
+ HanzoLogo,
120
+ HanzoIcon, // Use this as the canonical HanzoIcon export
121
+ type HanzoLogoProps
122
+ } from './hanzo-logo';
@@ -0,0 +1,6 @@
1
+ export { LoginBasic } from './login-basic'
2
+ export { LoginSocial } from './login-social'
3
+ export { Login2FA } from './login-2fa'
4
+ export { Signup } from './signup'
5
+ export { PasswordReset } from './password-reset'
6
+ export { MagicLink } from './magic-link'
@@ -0,0 +1,165 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { cn } from '@hanzo/ui/util'
5
+ import { Button } from '@hanzo/ui/primitives'
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle,
12
+ } from '@hanzo/ui/primitives'
13
+ import { Input } from '@hanzo/ui/primitives'
14
+ import { Label } from '@hanzo/ui/primitives'
15
+ import {
16
+ InputOTP,
17
+ InputOTPGroup,
18
+ InputOTPSeparator,
19
+ InputOTPSlot,
20
+ } from '@hanzo/ui/primitives'
21
+
22
+ interface Login2FAProps extends React.ComponentPropsWithoutRef<'div'> {
23
+ onSubmit?: (email: string, password: string, code?: string) => void
24
+ onResendCode?: () => void
25
+ onForgotPassword?: () => void
26
+ onSignUp?: () => void
27
+ }
28
+
29
+ export function Login2FA({
30
+ className,
31
+ onSubmit,
32
+ onResendCode,
33
+ onForgotPassword,
34
+ onSignUp,
35
+ ...props
36
+ }: Login2FAProps) {
37
+ const [step, setStep] = useState<'credentials' | '2fa'>('credentials')
38
+ const [credentials, setCredentials] = useState({ email: '', password: '' })
39
+ const [otpValue, setOtpValue] = useState('')
40
+
41
+ const handleCredentialsSubmit = (e: React.FormEvent<HTMLFormElement>) => {
42
+ e.preventDefault()
43
+ const formData = new FormData(e.currentTarget)
44
+ const email = formData.get('email') as string
45
+ const password = formData.get('password') as string
46
+ setCredentials({ email, password })
47
+ setStep('2fa')
48
+ }
49
+
50
+ const handleOTPSubmit = (e: React.FormEvent<HTMLFormElement>) => {
51
+ e.preventDefault()
52
+ onSubmit?.(credentials.email, credentials.password, otpValue)
53
+ }
54
+
55
+ return (
56
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
57
+ <Card>
58
+ <CardHeader>
59
+ <CardTitle className="text-2xl">
60
+ {step === 'credentials' ? 'Login' : 'Two-factor authentication'}
61
+ </CardTitle>
62
+ <CardDescription>
63
+ {step === 'credentials'
64
+ ? 'Enter your email and password to login'
65
+ : 'Enter the 6-digit code from your authenticator app'}
66
+ </CardDescription>
67
+ </CardHeader>
68
+ <CardContent>
69
+ {step === 'credentials' ? (
70
+ <form onSubmit={handleCredentialsSubmit}>
71
+ <div className="flex flex-col gap-6">
72
+ <div className="grid gap-2">
73
+ <Label htmlFor="email">Email</Label>
74
+ <Input
75
+ id="email"
76
+ name="email"
77
+ type="email"
78
+ placeholder="m@example.com"
79
+ required
80
+ />
81
+ </div>
82
+ <div className="grid gap-2">
83
+ <div className="flex items-center">
84
+ <Label htmlFor="password">Password</Label>
85
+ {onForgotPassword && (
86
+ <button
87
+ type="button"
88
+ onClick={onForgotPassword}
89
+ className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
90
+ >
91
+ Forgot your password?
92
+ </button>
93
+ )}
94
+ </div>
95
+ <Input id="password" name="password" type="password" required />
96
+ </div>
97
+ <Button type="submit" className="w-full">
98
+ Continue
99
+ </Button>
100
+ </div>
101
+ {onSignUp && (
102
+ <div className="mt-4 text-center text-sm">
103
+ Don&apos;t have an account?{' '}
104
+ <button
105
+ type="button"
106
+ onClick={onSignUp}
107
+ className="underline underline-offset-4"
108
+ >
109
+ Sign up
110
+ </button>
111
+ </div>
112
+ )}
113
+ </form>
114
+ ) : (
115
+ <form onSubmit={handleOTPSubmit}>
116
+ <div className="flex flex-col gap-6">
117
+ <div className="flex justify-center">
118
+ <InputOTP
119
+ maxLength={6}
120
+ value={otpValue}
121
+ onChange={(value) => setOtpValue(value)}
122
+ >
123
+ <InputOTPGroup>
124
+ <InputOTPSlot index={0} />
125
+ <InputOTPSlot index={1} />
126
+ <InputOTPSlot index={2} />
127
+ </InputOTPGroup>
128
+ <InputOTPSeparator />
129
+ <InputOTPGroup>
130
+ <InputOTPSlot index={3} />
131
+ <InputOTPSlot index={4} />
132
+ <InputOTPSlot index={5} />
133
+ </InputOTPGroup>
134
+ </InputOTP>
135
+ </div>
136
+ <Button type="submit" className="w-full" disabled={otpValue.length !== 6}>
137
+ Verify
138
+ </Button>
139
+ <div className="flex flex-col gap-2">
140
+ <Button
141
+ type="button"
142
+ variant="outline"
143
+ className="w-full"
144
+ onClick={() => setStep('credentials')}
145
+ >
146
+ Back to login
147
+ </Button>
148
+ {onResendCode && (
149
+ <button
150
+ type="button"
151
+ onClick={onResendCode}
152
+ className="text-center text-sm underline underline-offset-4"
153
+ >
154
+ Didn&apos;t receive a code? Resend
155
+ </button>
156
+ )}
157
+ </div>
158
+ </div>
159
+ </form>
160
+ )}
161
+ </CardContent>
162
+ </Card>
163
+ </div>
164
+ )
165
+ }
@@ -0,0 +1,94 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Button } from '@hanzo/ui/primitives'
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from '@hanzo/ui/primitives'
12
+ import { Input } from '@hanzo/ui/primitives'
13
+ import { Label } from '@hanzo/ui/primitives'
14
+
15
+ interface LoginBasicProps extends React.ComponentPropsWithoutRef<'div'> {
16
+ onSubmit?: (email: string, password: string) => void
17
+ onForgotPassword?: () => void
18
+ onSignUp?: () => void
19
+ }
20
+
21
+ export function LoginBasic({
22
+ className,
23
+ onSubmit,
24
+ onForgotPassword,
25
+ onSignUp,
26
+ ...props
27
+ }: LoginBasicProps) {
28
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
29
+ e.preventDefault()
30
+ const formData = new FormData(e.currentTarget)
31
+ const email = formData.get('email') as string
32
+ const password = formData.get('password') as string
33
+ onSubmit?.(email, password)
34
+ }
35
+
36
+ return (
37
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
38
+ <Card>
39
+ <CardHeader>
40
+ <CardTitle className="text-2xl">Login</CardTitle>
41
+ <CardDescription>
42
+ Enter your email below to login to your account
43
+ </CardDescription>
44
+ </CardHeader>
45
+ <CardContent>
46
+ <form onSubmit={handleSubmit}>
47
+ <div className="flex flex-col gap-6">
48
+ <div className="grid gap-2">
49
+ <Label htmlFor="email">Email</Label>
50
+ <Input
51
+ id="email"
52
+ name="email"
53
+ type="email"
54
+ placeholder="m@example.com"
55
+ required
56
+ />
57
+ </div>
58
+ <div className="grid gap-2">
59
+ <div className="flex items-center">
60
+ <Label htmlFor="password">Password</Label>
61
+ {onForgotPassword && (
62
+ <button
63
+ type="button"
64
+ onClick={onForgotPassword}
65
+ className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
66
+ >
67
+ Forgot your password?
68
+ </button>
69
+ )}
70
+ </div>
71
+ <Input id="password" name="password" type="password" required />
72
+ </div>
73
+ <Button type="submit" className="w-full">
74
+ Login
75
+ </Button>
76
+ </div>
77
+ {onSignUp && (
78
+ <div className="mt-4 text-center text-sm">
79
+ Don&apos;t have an account?{' '}
80
+ <button
81
+ type="button"
82
+ onClick={onSignUp}
83
+ className="underline underline-offset-4"
84
+ >
85
+ Sign up
86
+ </button>
87
+ </div>
88
+ )}
89
+ </form>
90
+ </CardContent>
91
+ </Card>
92
+ </div>
93
+ )
94
+ }
@@ -0,0 +1,148 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@hanzo/ui/util'
4
+ import { Button } from '@hanzo/ui/primitives'
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from '@hanzo/ui/primitives'
12
+ import { Input } from '@hanzo/ui/primitives'
13
+ import { Label } from '@hanzo/ui/primitives'
14
+ import { Icons } from '@hanzo/ui/primitives'
15
+
16
+ interface LoginSocialProps extends React.ComponentPropsWithoutRef<'div'> {
17
+ onSubmit?: (email: string, password: string) => void
18
+ onGoogleLogin?: () => void
19
+ onGitHubLogin?: () => void
20
+ onTwitterLogin?: () => void
21
+ onForgotPassword?: () => void
22
+ onSignUp?: () => void
23
+ }
24
+
25
+ export function LoginSocial({
26
+ className,
27
+ onSubmit,
28
+ onGoogleLogin,
29
+ onGitHubLogin,
30
+ onTwitterLogin,
31
+ onForgotPassword,
32
+ onSignUp,
33
+ ...props
34
+ }: LoginSocialProps) {
35
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
36
+ e.preventDefault()
37
+ const formData = new FormData(e.currentTarget)
38
+ const email = formData.get('email') as string
39
+ const password = formData.get('password') as string
40
+ onSubmit?.(email, password)
41
+ }
42
+
43
+ return (
44
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
45
+ <Card>
46
+ <CardHeader>
47
+ <CardTitle className="text-2xl">Login</CardTitle>
48
+ <CardDescription>
49
+ Enter your email below to login to your account
50
+ </CardDescription>
51
+ </CardHeader>
52
+ <CardContent>
53
+ <form onSubmit={handleSubmit}>
54
+ <div className="flex flex-col gap-6">
55
+ <div className="grid gap-2">
56
+ <Label htmlFor="email">Email</Label>
57
+ <Input
58
+ id="email"
59
+ name="email"
60
+ type="email"
61
+ placeholder="m@example.com"
62
+ required
63
+ />
64
+ </div>
65
+ <div className="grid gap-2">
66
+ <div className="flex items-center">
67
+ <Label htmlFor="password">Password</Label>
68
+ {onForgotPassword && (
69
+ <button
70
+ type="button"
71
+ onClick={onForgotPassword}
72
+ className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
73
+ >
74
+ Forgot your password?
75
+ </button>
76
+ )}
77
+ </div>
78
+ <Input id="password" name="password" type="password" required />
79
+ </div>
80
+ <Button type="submit" className="w-full">
81
+ Login
82
+ </Button>
83
+
84
+ <div className="relative">
85
+ <div className="absolute inset-0 flex items-center">
86
+ <span className="w-full border-t" />
87
+ </div>
88
+ <div className="relative flex justify-center text-xs uppercase">
89
+ <span className="bg-background px-2 text-muted-foreground">
90
+ Or continue with
91
+ </span>
92
+ </div>
93
+ </div>
94
+
95
+ <div className="grid gap-2">
96
+ {onGoogleLogin && (
97
+ <Button
98
+ type="button"
99
+ variant="outline"
100
+ className="w-full"
101
+ onClick={onGoogleLogin}
102
+ >
103
+ <Icons.google className="mr-2 h-4 w-4" />
104
+ Google
105
+ </Button>
106
+ )}
107
+ {onGitHubLogin && (
108
+ <Button
109
+ type="button"
110
+ variant="outline"
111
+ className="w-full"
112
+ onClick={onGitHubLogin}
113
+ >
114
+ <Icons.gitHub className="mr-2 h-4 w-4" />
115
+ GitHub
116
+ </Button>
117
+ )}
118
+ {onTwitterLogin && (
119
+ <Button
120
+ type="button"
121
+ variant="outline"
122
+ className="w-full"
123
+ onClick={onTwitterLogin}
124
+ >
125
+ <Icons.twitter className="mr-2 h-4 w-4" />
126
+ Twitter
127
+ </Button>
128
+ )}
129
+ </div>
130
+ </div>
131
+ {onSignUp && (
132
+ <div className="mt-4 text-center text-sm">
133
+ Don&apos;t have an account?{' '}
134
+ <button
135
+ type="button"
136
+ onClick={onSignUp}
137
+ className="underline underline-offset-4"
138
+ >
139
+ Sign up
140
+ </button>
141
+ </div>
142
+ )}
143
+ </form>
144
+ </CardContent>
145
+ </Card>
146
+ </div>
147
+ )
148
+ }
@@ -0,0 +1,129 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { cn } from '@hanzo/ui/util'
5
+ import { Button } from '@hanzo/ui/primitives'
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle,
12
+ } from '@hanzo/ui/primitives'
13
+ import { Input } from '@hanzo/ui/primitives'
14
+ import { Label } from '@hanzo/ui/primitives'
15
+ import { Icons } from '@hanzo/ui/primitives'
16
+
17
+ interface MagicLinkProps extends React.ComponentPropsWithoutRef<'div'> {
18
+ onSubmit?: (email: string) => void
19
+ onSignUp?: () => void
20
+ onUsePassword?: () => void
21
+ }
22
+
23
+ export function MagicLink({
24
+ className,
25
+ onSubmit,
26
+ onSignUp,
27
+ onUsePassword,
28
+ ...props
29
+ }: MagicLinkProps) {
30
+ const [sent, setSent] = useState(false)
31
+ const [email, setEmail] = useState('')
32
+
33
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
34
+ e.preventDefault()
35
+ const formData = new FormData(e.currentTarget)
36
+ const emailValue = formData.get('email') as string
37
+ setEmail(emailValue)
38
+ setSent(true)
39
+ onSubmit?.(emailValue)
40
+ }
41
+
42
+ return (
43
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
44
+ <Card>
45
+ <CardHeader>
46
+ <CardTitle className="text-2xl">
47
+ {sent ? 'Check your email' : 'Sign in with email'}
48
+ </CardTitle>
49
+ <CardDescription>
50
+ {sent
51
+ ? `We've sent a magic link to ${email}`
52
+ : 'We'll send you a magic link to sign in instantly'}
53
+ </CardDescription>
54
+ </CardHeader>
55
+ <CardContent>
56
+ {sent ? (
57
+ <div className="flex flex-col gap-6">
58
+ <div className="flex justify-center">
59
+ <div className="rounded-full bg-primary/10 p-6">
60
+ <Icons.mail className="h-8 w-8 text-primary" />
61
+ </div>
62
+ </div>
63
+ <div className="text-center text-sm text-muted-foreground">
64
+ Click the link in your email to sign in. The link will expire in 10 minutes.
65
+ </div>
66
+ <div className="flex flex-col gap-2">
67
+ <Button
68
+ type="button"
69
+ variant="outline"
70
+ className="w-full"
71
+ onClick={() => setSent(false)}
72
+ >
73
+ Try a different email
74
+ </Button>
75
+ <button
76
+ type="button"
77
+ onClick={() => onSubmit?.(email)}
78
+ className="text-center text-sm underline underline-offset-4"
79
+ >
80
+ Didn&apos;t receive the email? Resend
81
+ </button>
82
+ </div>
83
+ </div>
84
+ ) : (
85
+ <form onSubmit={handleSubmit}>
86
+ <div className="flex flex-col gap-6">
87
+ <div className="grid gap-2">
88
+ <Label htmlFor="email">Email</Label>
89
+ <Input
90
+ id="email"
91
+ name="email"
92
+ type="email"
93
+ placeholder="m@example.com"
94
+ required
95
+ />
96
+ </div>
97
+ <Button type="submit" className="w-full">
98
+ Send magic link
99
+ </Button>
100
+ {onUsePassword && (
101
+ <Button
102
+ type="button"
103
+ variant="outline"
104
+ className="w-full"
105
+ onClick={onUsePassword}
106
+ >
107
+ Use password instead
108
+ </Button>
109
+ )}
110
+ </div>
111
+ {onSignUp && (
112
+ <div className="mt-4 text-center text-sm">
113
+ Don&apos;t have an account?{' '}
114
+ <button
115
+ type="button"
116
+ onClick={onSignUp}
117
+ className="underline underline-offset-4"
118
+ >
119
+ Sign up
120
+ </button>
121
+ </div>
122
+ )}
123
+ </form>
124
+ )}
125
+ </CardContent>
126
+ </Card>
127
+ </div>
128
+ )
129
+ }