@authdog/react-elements 0.0.13 → 0.0.14

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 (54) hide show
  1. package/.turbo/turbo-build.log +46 -22
  2. package/CHANGELOG.md +6 -0
  3. package/dist/components/ui/alert.js +3 -0
  4. package/dist/components/ui/alert.js.map +1 -0
  5. package/dist/components/ui/alert.mjs +3 -0
  6. package/dist/components/ui/alert.mjs.map +1 -0
  7. package/dist/components/ui/badge.js +3 -0
  8. package/dist/components/ui/badge.js.map +1 -0
  9. package/dist/components/ui/badge.mjs +3 -0
  10. package/dist/components/ui/badge.mjs.map +1 -0
  11. package/dist/components/ui/card.js +3 -0
  12. package/dist/components/ui/card.js.map +1 -0
  13. package/dist/components/ui/card.mjs +3 -0
  14. package/dist/components/ui/card.mjs.map +1 -0
  15. package/dist/components/ui/input.js +3 -0
  16. package/dist/components/ui/input.js.map +1 -0
  17. package/dist/components/ui/input.mjs +3 -0
  18. package/dist/components/ui/input.mjs.map +1 -0
  19. package/dist/components/ui/label.js +3 -0
  20. package/dist/components/ui/label.js.map +1 -0
  21. package/dist/components/ui/label.mjs +3 -0
  22. package/dist/components/ui/label.mjs.map +1 -0
  23. package/dist/components/ui/separator.js +3 -0
  24. package/dist/components/ui/separator.js.map +1 -0
  25. package/dist/components/ui/separator.mjs +3 -0
  26. package/dist/components/ui/separator.mjs.map +1 -0
  27. package/dist/index.d.mts +18 -1
  28. package/dist/index.d.ts +18 -1
  29. package/dist/index.js +1 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +1 -1
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/postcss.config.mjs +2 -0
  34. package/dist/styles.css +406 -0
  35. package/dist/tailwind.config.ts +5 -5
  36. package/ladle.config.mjs +21 -0
  37. package/package.json +16 -4
  38. package/postcss.config.mjs +2 -0
  39. package/src/components/core/user-profile.tsx +245 -0
  40. package/src/components/flow/login.tsx +158 -0
  41. package/src/components/ui/alert.tsx +66 -0
  42. package/src/components/ui/badge.tsx +46 -0
  43. package/src/components/ui/card.tsx +92 -0
  44. package/src/components/ui/input.tsx +21 -0
  45. package/src/components/ui/label.tsx +24 -0
  46. package/src/components/ui/separator.tsx +28 -0
  47. package/src/index.ts +3 -2
  48. package/src/main.tsx +11 -0
  49. package/src/preview.tsx +10 -0
  50. package/src/stories/Button.stories.tsx +24 -0
  51. package/src/stories/LoginForm.stories.tsx +29 -0
  52. package/src/stories/Navbar.stories.tsx +22 -0
  53. package/src/stories/UserProfile.stories.tsx +56 -0
  54. package/tailwind.config.ts +5 -5
package/package.json CHANGED
@@ -1,21 +1,29 @@
1
1
  {
2
2
  "name": "@authdog/react-elements",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
7
7
  "peerDependencies": {
8
- "react": "^18.3.1"
8
+ "react": "^19.1.0",
9
+ "react-dom": "^19.1.0"
9
10
  },
10
11
  "devDependencies": {
12
+ "@ladle/react": "^2.0.0",
11
13
  "@types/node": "^20",
12
- "@types/react": "^18.3.11",
14
+ "@types/react": "^19.1.0",
15
+ "@vitejs/plugin-react": "^4.4.1",
13
16
  "autoprefixer": "^10",
17
+ "css-loader": "^6.8.1",
14
18
  "postcss": "^8",
15
19
  "postcss-cli": "^11.0.1",
20
+ "postcss-import": "^16.1.0",
16
21
  "postcss-load-config": "^6",
22
+ "style-loader": "^3.3.3",
17
23
  "tailwindcss": "3.4.17",
24
+ "ts-loader": "^9.5.1",
18
25
  "typescript": "^5.6.3",
26
+ "webpack": "^5.89.0",
19
27
  "@authdog/eslint-config": "0.0.0",
20
28
  "@authdog/typescript-config": "0.0.0"
21
29
  },
@@ -23,6 +31,8 @@
23
31
  "@radix-ui/react-avatar": "^1.1.9",
24
32
  "@radix-ui/react-dialog": "^1.1.13",
25
33
  "@radix-ui/react-dropdown-menu": "^2.1.14",
34
+ "@radix-ui/react-label": "^2.1.6",
35
+ "@radix-ui/react-separator": "^1.1.6",
26
36
  "@radix-ui/react-slot": "^1.2.2",
27
37
  "class-variance-authority": "^0.7.0",
28
38
  "clsx": "^2.1.1",
@@ -60,6 +70,8 @@
60
70
  "ui": "pnpm dlx shadcn@latest",
61
71
  "lint": "eslint .",
62
72
  "build:styles": "postcss src/global.css -o dist/styles.css",
63
- "build": "pnpm tsup && pnpm build:styles"
73
+ "build": "pnpm tsup && pnpm build:styles",
74
+ "ladle": "ladle serve",
75
+ "ladle:build": "ladle build"
64
76
  }
65
77
  }
@@ -1,6 +1,8 @@
1
1
  /** @type {import('postcss-load-config').Config} */
2
2
  const config = {
3
3
  plugins: {
4
+ 'postcss-import': {},
5
+ 'tailwindcss/nesting': {},
4
6
  tailwindcss: {},
5
7
  autoprefixer: {},
6
8
  },
@@ -0,0 +1,245 @@
1
+ "use client"
2
+
3
+ import { useEffect, useState } from "react"
4
+ import { Avatar, AvatarFallback, AvatarImage } from "../../components/ui/avatar"
5
+ import { Button } from "../../components/ui/button"
6
+ import { Card } from "../../components/ui/card"
7
+ import { Badge } from "../../components/ui/badge"
8
+ import { PlusCircle, User, Shield, X, LucideProps } from "lucide-react"
9
+
10
+ export interface UserProfileProps {
11
+ user: {
12
+ name: string;
13
+ email: string;
14
+ image: string;
15
+ };
16
+ emails?: { address: string; isPrimary?: boolean }[];
17
+ connectedAccounts?: { provider: string; email: string }[];
18
+ }
19
+
20
+ export const UserProfile = ({
21
+ user,
22
+ emails = [],
23
+ connectedAccounts = [],
24
+ }: UserProfileProps) => {
25
+ const [isMounted, setIsMounted] = useState(false)
26
+ const [activeTab, setActiveTab] = useState<"profile" | "security">("profile");
27
+
28
+ useEffect(() => {
29
+ setIsMounted(true)
30
+ }, []);
31
+
32
+ const iconProps: LucideProps = {
33
+ className: "mr-2 h-4 w-4",
34
+ "aria-hidden": "true"
35
+ }
36
+
37
+ const renderIcon = (Icon: any) => {
38
+ if (!isMounted) return null
39
+ return <Icon {...iconProps} />
40
+ }
41
+
42
+ return (
43
+ <div className="grid grid-cols-[16rem,1fr] h-screen bg-gray-100">
44
+ <div className="h-full border-r p-6 bg-white flex flex-col min-w-0">
45
+ <div className="mb-6">
46
+ <h1 className="text-xl font-bold">Account</h1>
47
+ <p className="text-sm text-gray-500">Manage your account info.</p>
48
+ </div>
49
+
50
+ <nav className="space-y-1 flex-1">
51
+ <button
52
+ onClick={() => setActiveTab("profile")}
53
+ className={`flex items-center w-full px-3 py-2 text-sm rounded-md ${
54
+ activeTab === "profile" ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-50"
55
+ }`}
56
+ >
57
+ {renderIcon(User)}
58
+ Profile
59
+ </button>
60
+ <button
61
+ onClick={() => setActiveTab("security")}
62
+ className={`flex items-center w-full px-3 py-2 text-sm rounded-md ${
63
+ activeTab === "security" ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-50"
64
+ }`}
65
+ >
66
+ {renderIcon(Shield)}
67
+ Security
68
+ </button>
69
+ </nav>
70
+ </div>
71
+
72
+ <div className="h-full p-10 overflow-y-auto min-w-0 bg-white">
73
+ <div className="flex justify-between items-center mb-6">
74
+ <h2 className="text-xl font-semibold">
75
+ {activeTab === "profile" ? "Profile details" : "Security settings"}
76
+ </h2>
77
+ <button className="text-gray-500 hover:text-gray-700">
78
+ {renderIcon(X)}
79
+ </button>
80
+ </div>
81
+
82
+ {activeTab === "profile" ? (
83
+ <div className="space-y-8">
84
+ {/* Profile Section */}
85
+ <div>
86
+ <h3 className="text-sm font-medium mb-4">Profile</h3>
87
+ <div className="flex items-center justify-between">
88
+ <div className="flex items-center">
89
+ <Avatar className="h-12 w-12 mr-4 border">
90
+ <AvatarImage src={user.image} alt="Profile picture" />
91
+ <AvatarFallback>{user.name.split(" ").map(n => n[0]).join("")}</AvatarFallback>
92
+ </Avatar>
93
+ <span className="font-medium">{user.name}</span>
94
+ </div>
95
+ <Button variant="outline" size="sm">
96
+ Edit profile
97
+ </Button>
98
+ </div>
99
+ </div>
100
+
101
+ {/* Email Addresses Section */}
102
+ <div>
103
+ <h3 className="text-sm font-medium mb-4">Email addresses</h3>
104
+ <div className="space-y-3">
105
+ {(emails.length > 0 ? emails : [{ address: user.email, isPrimary: true }]).map((email, i) => (
106
+ <div className="flex items-center justify-between" key={email.address}>
107
+ <span>{email.address}</span>
108
+ {email.isPrimary && (
109
+ <Badge variant="outline" className="text-xs bg-gray-100 text-gray-700 hover:bg-gray-100">
110
+ Primary
111
+ </Badge>
112
+ )}
113
+ </div>
114
+ ))}
115
+ <Button variant="ghost" size="sm" className="flex items-center text-gray-700">
116
+ {renderIcon(PlusCircle)}
117
+ Add email address
118
+ </Button>
119
+ </div>
120
+ </div>
121
+
122
+ {/* Phone Number Section */}
123
+ <div>
124
+ <h3 className="text-sm font-medium mb-4">Phone number</h3>
125
+ <div className="space-y-3">
126
+ <div className="flex items-center justify-between">
127
+ <span>+1 (555) 123-4567</span>
128
+ <Badge variant="outline" className="text-xs bg-gray-100 text-gray-700 hover:bg-gray-100">
129
+ Primary
130
+ </Badge>
131
+ </div>
132
+ <Button variant="ghost" size="sm" className="flex items-center text-gray-700">
133
+ {renderIcon(PlusCircle)}
134
+ Add phone number
135
+ </Button>
136
+ </div>
137
+ </div>
138
+
139
+ {/* Connected Accounts Section */}
140
+ <div>
141
+ <h3 className="text-sm font-medium mb-4">Connected accounts</h3>
142
+ <div className="space-y-3">
143
+ {connectedAccounts.length > 0 ? connectedAccounts.map((acc, i) => (
144
+ <div className="flex items-center justify-between" key={acc.provider + acc.email}>
145
+ <div className="flex items-center">
146
+ <div className="mr-2">
147
+ <span>{acc.provider}</span>
148
+ </div>
149
+ <span>{acc.provider}</span>
150
+ </div>
151
+ <span className="text-sm text-gray-500">{acc.email}</span>
152
+ </div>
153
+ )) : (
154
+ <div className="flex items-center justify-between">
155
+ <div className="flex items-center">
156
+ <div className="mr-2">
157
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">
158
+ <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
159
+ <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
160
+ <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
161
+ <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
162
+ </svg>
163
+ </div>
164
+ <span>Google</span>
165
+ </div>
166
+ <span className="text-sm text-gray-500">{user.email}</span>
167
+ </div>
168
+ )}
169
+ <Button variant="ghost" size="sm" className="flex items-center text-gray-700">
170
+ {renderIcon(PlusCircle)}
171
+ Connect account
172
+ </Button>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ ) : (
177
+ <div className="space-y-8">
178
+ {/* Security Settings */}
179
+ <div>
180
+ <h3 className="text-sm font-medium mb-4">Two-factor authentication</h3>
181
+ <div className="space-y-3">
182
+ <div className="flex items-center justify-between">
183
+ <div>
184
+ <p className="font-medium">Two-factor authentication</p>
185
+ <p className="text-sm text-gray-500">Add an extra layer of security to your account</p>
186
+ </div>
187
+ <Button variant="outline" size="sm">
188
+ Enable
189
+ </Button>
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <div>
195
+ <h3 className="text-sm font-medium mb-4">Password</h3>
196
+ <div className="space-y-3">
197
+ <div className="flex items-center justify-between">
198
+ <div>
199
+ <p className="font-medium">Change password</p>
200
+ <p className="text-sm text-gray-500">Last changed 3 months ago</p>
201
+ </div>
202
+ <Button variant="outline" size="sm">
203
+ Change
204
+ </Button>
205
+ </div>
206
+ </div>
207
+ </div>
208
+
209
+ <div>
210
+ <h3 className="text-sm font-medium mb-4">Active sessions</h3>
211
+ <div className="space-y-3">
212
+ <div className="flex items-center justify-between">
213
+ <div>
214
+ <p className="font-medium">Current session</p>
215
+ <p className="text-sm text-gray-500">Chrome on Windows • Active now</p>
216
+ </div>
217
+ <Button variant="outline" size="sm">
218
+ Sign out
219
+ </Button>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ )}
225
+ </div>
226
+
227
+ <div className="absolute bottom-4 text-xs text-gray-500 flex items-center">
228
+ Secured by
229
+ <span className="ml-1 font-medium flex items-center">
230
+ <svg
231
+ width="14"
232
+ height="14"
233
+ viewBox="0 0 16 16"
234
+ fill="none"
235
+ xmlns="http://www.w3.org/2000/svg"
236
+ className="mr-1"
237
+ >
238
+ <path d="M8 0L14.9282 4V12L8 16L1.07179 12V4L8 0Z" fill="#6C47FF" />
239
+ </svg>
240
+ Authdog
241
+ </span>
242
+ </div>
243
+ </div>
244
+ )
245
+ }
@@ -0,0 +1,158 @@
1
+ "use client"
2
+
3
+ import type React from "react"
4
+
5
+ import { useState } from "react"
6
+ import { Button } from "../../components/ui/button"
7
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../components/ui/card"
8
+ import { Input } from "../../components/ui/input"
9
+ import { Label } from "../../components/ui/label"
10
+ import { Separator } from "../../components/ui/separator"
11
+ import { Github, AlertCircle } from "lucide-react"
12
+ import { Alert, AlertDescription } from "../../components/ui/alert"
13
+
14
+ export const LoginForm = () => {
15
+ const [email, setEmail] = useState("")
16
+ const [password, setPassword] = useState("")
17
+ const [error, setError] = useState("")
18
+ const [isLoading, setIsLoading] = useState(false)
19
+
20
+ const handleEmailLogin = async (e: React.FormEvent) => {
21
+ e.preventDefault()
22
+ setIsLoading(true)
23
+ setError("")
24
+
25
+ try {
26
+ // Simulate API call
27
+ await new Promise((resolve) => setTimeout(resolve, 1000))
28
+
29
+ // Redirect after successful login
30
+ // router.push('/dashboard')
31
+ } catch (err) {
32
+ setError("Invalid email or password")
33
+ } finally {
34
+ setIsLoading(false)
35
+ }
36
+ }
37
+
38
+ const handleOAuthLogin = async (provider: string) => {
39
+ setIsLoading(true)
40
+ setError("")
41
+
42
+ try {
43
+
44
+ await new Promise((resolve) => setTimeout(resolve, 1000))
45
+ } catch (err) {
46
+ setError(`Failed to login with ${provider}`)
47
+ } finally {
48
+ setIsLoading(false)
49
+ }
50
+ }
51
+
52
+ return (
53
+ <div className="flex min-h-screen items-center justify-center bg-gray-50 px-4 py-12 sm:px-6 lg:px-8">
54
+ <Card className="w-full max-w-md">
55
+ <CardHeader className="space-y-1">
56
+ <CardTitle className="text-2xl font-bold text-center">Login</CardTitle>
57
+ <CardDescription className="text-center">Choose your preferred login method</CardDescription>
58
+ </CardHeader>
59
+ <CardContent className="space-y-4">
60
+ {error && (
61
+ <Alert variant="destructive">
62
+ <AlertCircle className="h-4 w-4" />
63
+ <AlertDescription>{error}</AlertDescription>
64
+ </Alert>
65
+ )}
66
+
67
+ <div className="space-y-2">
68
+ <Button
69
+ variant="outline"
70
+ className="w-full"
71
+ onClick={() => handleOAuthLogin("google")}
72
+ disabled={isLoading}
73
+ >
74
+ <svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
75
+ <path
76
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
77
+ fill="#4285F4"
78
+ />
79
+ <path
80
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
81
+ fill="#34A853"
82
+ />
83
+ <path
84
+ d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
85
+ fill="#FBBC05"
86
+ />
87
+ <path
88
+ d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
89
+ fill="#EA4335"
90
+ />
91
+ <path d="M1 1h22v22H1z" fill="none" />
92
+ </svg>
93
+ Continue with Google
94
+ </Button>
95
+
96
+ <Button
97
+ variant="outline"
98
+ className="w-full"
99
+ onClick={() => handleOAuthLogin("github")}
100
+ disabled={isLoading}
101
+ >
102
+ <Github className="mr-2 h-4 w-4" />
103
+ Continue with GitHub
104
+ </Button>
105
+ </div>
106
+
107
+ <div className="flex items-center">
108
+ <Separator className="flex-1" />
109
+ <span className="mx-2 text-xs text-muted-foreground">OR</span>
110
+ <Separator className="flex-1" />
111
+ </div>
112
+
113
+ <form onSubmit={handleEmailLogin} className="space-y-4">
114
+ <div className="space-y-2">
115
+ <Label htmlFor="email">Email</Label>
116
+ <Input
117
+ id="email"
118
+ type="email"
119
+ placeholder="m@example.com"
120
+ value={email}
121
+ onChange={(e) => setEmail(e.target.value)}
122
+ required
123
+ disabled={isLoading}
124
+ />
125
+ </div>
126
+ <div className="space-y-2">
127
+ <div className="flex items-center justify-between">
128
+ <Label htmlFor="password">Password</Label>
129
+ <a href="/forgot-password" className="text-xs text-primary hover:underline">
130
+ Forgot password?
131
+ </a>
132
+ </div>
133
+ <Input
134
+ id="password"
135
+ type="password"
136
+ value={password}
137
+ onChange={(e) => setPassword(e.target.value)}
138
+ required
139
+ disabled={isLoading}
140
+ />
141
+ </div>
142
+ <Button type="submit" className="w-full" disabled={isLoading}>
143
+ {isLoading ? "Loading..." : "Sign in with Email"}
144
+ </Button>
145
+ </form>
146
+ </CardContent>
147
+ <CardFooter className="flex justify-center">
148
+ <p className="text-xs text-muted-foreground">
149
+ Don't have an account?{" "}
150
+ <a href="/signup" className="text-primary hover:underline">
151
+ Sign up
152
+ </a>
153
+ </p>
154
+ </CardFooter>
155
+ </Card>
156
+ </div>
157
+ )
158
+ }
@@ -0,0 +1,66 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@authdog/react-elements/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-card text-card-foreground",
12
+ destructive:
13
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ function Alert({
23
+ className,
24
+ variant,
25
+ ...props
26
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
27
+ return (
28
+ <div
29
+ data-slot="alert"
30
+ role="alert"
31
+ className={cn(alertVariants({ variant }), className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38
+ return (
39
+ <div
40
+ data-slot="alert-title"
41
+ className={cn(
42
+ "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
43
+ className
44
+ )}
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ function AlertDescription({
51
+ className,
52
+ ...props
53
+ }: React.ComponentProps<"div">) {
54
+ return (
55
+ <div
56
+ data-slot="alert-description"
57
+ className={cn(
58
+ "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
59
+ className
60
+ )}
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ export { Alert, AlertTitle, AlertDescription }
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@authdog/react-elements/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16
+ destructive:
17
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ outline:
19
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ }
26
+ )
27
+
28
+ function Badge({
29
+ className,
30
+ variant,
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35
+ const Comp = asChild ? Slot : "span"
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ className={cn(badgeVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ export { Badge, badgeVariants }
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@authdog/react-elements/lib/utils"
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ }