@authdog/react-elements 0.0.49 → 0.2.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.
- package/dist/components/ui/alert.d.mts +12 -0
- package/dist/components/ui/alert.d.ts +12 -0
- package/dist/components/ui/avatar.d.mts +8 -0
- package/dist/components/ui/avatar.d.ts +8 -0
- package/dist/components/ui/badge.d.mts +12 -0
- package/dist/components/ui/badge.d.ts +12 -0
- package/dist/components/ui/button.d.mts +14 -0
- package/dist/components/ui/button.d.ts +14 -0
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/button.mjs.map +1 -1
- package/dist/components/ui/card.d.mts +11 -0
- package/dist/components/ui/card.d.ts +11 -0
- package/dist/components/ui/dropdown-menu.d.mts +27 -0
- package/dist/components/ui/dropdown-menu.d.ts +27 -0
- package/dist/components/ui/input.d.mts +5 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/label.d.mts +6 -0
- package/dist/components/ui/label.d.ts +6 -0
- package/dist/components/ui/separator.d.mts +6 -0
- package/dist/components/ui/separator.d.ts +6 -0
- package/dist/components/ui/sheet.d.mts +15 -0
- package/dist/components/ui/sheet.d.ts +15 -0
- package/dist/components/ui/theme-toggle.d.mts +5 -0
- package/dist/components/ui/theme-toggle.d.ts +5 -0
- package/dist/components/ui/theme-toggle.js.map +1 -1
- package/dist/components/ui/theme-toggle.mjs.map +1 -1
- package/dist/index.d.mts +12 -21
- package/dist/index.d.ts +12 -21
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/lib/utils.d.mts +5 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/styles.css +1 -4
- package/package.json +40 -25
- package/.eslintrc.js +0 -9
- package/.storybook/main.ts +0 -21
- package/.storybook/preview.ts +0 -17
- package/.storybook/vitest.setup.ts +0 -7
- package/.turbo/turbo-build.log +0 -77
- package/CHANGELOG.md +0 -286
- package/components.json +0 -20
- package/postcss.config.mjs +0 -11
- package/src/components/core/client-only.tsx +0 -15
- package/src/components/core/navbar.tsx +0 -307
- package/src/components/core/placeholder-alert.tsx +0 -23
- package/src/components/core/user-dropdown.tsx +0 -160
- package/src/components/core/user-profile.tsx +0 -521
- package/src/components/flow/login.tsx +0 -167
- package/src/components/flow/totp-validator.tsx +0 -252
- package/src/components/icons.tsx +0 -30
- package/src/components/ui/alert.tsx +0 -66
- package/src/components/ui/avatar.tsx +0 -53
- package/src/components/ui/badge.tsx +0 -46
- package/src/components/ui/button.tsx +0 -56
- package/src/components/ui/card.tsx +0 -92
- package/src/components/ui/dropdown-menu.tsx +0 -265
- package/src/components/ui/input.tsx +0 -21
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/sheet.tsx +0 -142
- package/src/components/ui/theme-toggle.tsx +0 -56
- package/src/global.css +0 -81
- package/src/index.ts +0 -8
- package/src/lib/utils.ts +0 -6
- package/src/stories/core/Navbar.stories.tsx +0 -51
- package/src/stories/core/PlaceholderAlert.stories.tsx +0 -23
- package/src/stories/core/UserDropdown.stories.tsx +0 -56
- package/src/stories/core/UserProfile.stories.tsx +0 -47
- package/src/stories/flow/LoginForm.stories.tsx +0 -20
- package/src/stories/flow/TotpValidator.stories.tsx +0 -23
- package/src/stories/showcase/Landing.stories.tsx +0 -376
- package/src/stories/ui/Button.stories.tsx +0 -45
- package/src/types.ts +0 -0
- package/tailwind.config.ts +0 -82
- package/tsconfig.json +0 -11
- package/tsup.config.ts +0 -31
- package/vitest.config.ts +0 -39
- package/vitest.shims.d.ts +0 -1
- package/wrangler.prod.toml +0 -4
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type React from "react";
|
|
4
|
-
|
|
5
|
-
import { useState, useRef } from "react";
|
|
6
|
-
import { Button } from "../../components/ui/button";
|
|
7
|
-
import { Card, CardContent } from "../../components/ui/card";
|
|
8
|
-
import { Alert, AlertDescription } from "../../components/ui/alert";
|
|
9
|
-
import { Shield, CheckCircle, AlertCircle } from "lucide-react";
|
|
10
|
-
|
|
11
|
-
interface TOTPValidatorProps {
|
|
12
|
-
onValidate: (code: string) => Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const TOTPValidator = ({ onValidate }: TOTPValidatorProps) => {
|
|
16
|
-
const [code, setCode] = useState(["", "", "", "", "", ""]);
|
|
17
|
-
const [loading, setLoading] = useState(false);
|
|
18
|
-
const [error, setError] = useState("");
|
|
19
|
-
const [success, setSuccess] = useState(false);
|
|
20
|
-
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
|
|
21
|
-
|
|
22
|
-
const handleInputChange = (index: number, value: string) => {
|
|
23
|
-
// Only allow digits
|
|
24
|
-
if (!/^\d*$/.test(value)) return;
|
|
25
|
-
|
|
26
|
-
const newCode = [...code];
|
|
27
|
-
newCode[index] = value.slice(-1); // Only take the last character
|
|
28
|
-
|
|
29
|
-
setCode(newCode);
|
|
30
|
-
setError("");
|
|
31
|
-
setSuccess(false);
|
|
32
|
-
|
|
33
|
-
// Auto-focus next input
|
|
34
|
-
if (value && index < 5) {
|
|
35
|
-
inputRefs.current[index + 1]?.focus();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleKeyDown = (index: number, e: React.KeyboardEvent) => {
|
|
40
|
-
// Handle backspace
|
|
41
|
-
if (e.key === "Backspace" && !code[index] && index > 0) {
|
|
42
|
-
inputRefs.current[index - 1]?.focus();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Handle paste
|
|
46
|
-
if (e.key === "v" && (e.ctrlKey || e.metaKey)) {
|
|
47
|
-
e.preventDefault();
|
|
48
|
-
navigator.clipboard.readText().then((text) => {
|
|
49
|
-
const digits = text.replace(/\D/g, "").slice(0, 6).split("");
|
|
50
|
-
const newCode = [...code];
|
|
51
|
-
digits.forEach((digit, i) => {
|
|
52
|
-
if (i < 6) newCode[i] = digit;
|
|
53
|
-
});
|
|
54
|
-
setCode(newCode);
|
|
55
|
-
|
|
56
|
-
// Focus the next empty input or the last one
|
|
57
|
-
const nextIndex = Math.min(digits.length, 5);
|
|
58
|
-
inputRefs.current[nextIndex]?.focus();
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const validateTOTP = async () => {
|
|
64
|
-
const totpCode = code.join("");
|
|
65
|
-
|
|
66
|
-
if (totpCode.length !== 6) {
|
|
67
|
-
setError("Please enter all 6 digits");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
setLoading(true);
|
|
72
|
-
setError("");
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
await onValidate(totpCode);
|
|
76
|
-
setSuccess(true);
|
|
77
|
-
} catch (error) {
|
|
78
|
-
setError("Invalid TOTP code. Please try again.");
|
|
79
|
-
} finally {
|
|
80
|
-
setLoading(false);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// try {
|
|
84
|
-
// Call your TOTP validation endpoint
|
|
85
|
-
// const response = await fetch("/api/validate-totp", {
|
|
86
|
-
// method: "POST",
|
|
87
|
-
// headers: {
|
|
88
|
-
// "Content-Type": "application/json",
|
|
89
|
-
// },
|
|
90
|
-
// body: JSON.stringify({ code: totpCode }),
|
|
91
|
-
// })
|
|
92
|
-
|
|
93
|
-
// // Check if response is JSON
|
|
94
|
-
// const contentType = response.headers.get("content-type")
|
|
95
|
-
// if (!contentType || !contentType.includes("application/json")) {
|
|
96
|
-
// throw new Error("Server returned non-JSON response")
|
|
97
|
-
// }
|
|
98
|
-
|
|
99
|
-
// const result = await response.json()
|
|
100
|
-
|
|
101
|
-
// if (response.ok && result.valid) {
|
|
102
|
-
// setSuccess(true)
|
|
103
|
-
// setError("")
|
|
104
|
-
// } else {
|
|
105
|
-
// setError(result.message || "Invalid TOTP code. Please try again.")
|
|
106
|
-
// // Clear the code on error
|
|
107
|
-
// setCode(["", "", "", "", "", ""])
|
|
108
|
-
// inputRefs.current[0]?.focus()
|
|
109
|
-
// }
|
|
110
|
-
// } catch (err) {
|
|
111
|
-
// console.error("TOTP validation error:", err)
|
|
112
|
-
// if (err instanceof Error && err.message.includes("non-JSON")) {
|
|
113
|
-
// setError("Server error. Please try again later.")
|
|
114
|
-
// } else {
|
|
115
|
-
// setError("Network error. Please try again.")
|
|
116
|
-
// }
|
|
117
|
-
// // Clear the code on error
|
|
118
|
-
// setCode(["", "", "", "", "", ""])
|
|
119
|
-
// inputRefs.current[0]?.focus()
|
|
120
|
-
// } finally {
|
|
121
|
-
// setLoading(false)
|
|
122
|
-
// }
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const handleSubmit = (e: React.FormEvent) => {
|
|
126
|
-
e.preventDefault();
|
|
127
|
-
validateTOTP();
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const clearCode = () => {
|
|
131
|
-
setCode(["", "", "", "", "", ""]);
|
|
132
|
-
setError("");
|
|
133
|
-
setSuccess(false);
|
|
134
|
-
setLoading(false);
|
|
135
|
-
inputRefs.current[0]?.focus();
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
if (success) {
|
|
139
|
-
return (
|
|
140
|
-
<div className="max-w-md mx-auto">
|
|
141
|
-
<Card className="border-green-200 bg-green-50">
|
|
142
|
-
<CardContent className="pt-6">
|
|
143
|
-
<div className="text-center space-y-4">
|
|
144
|
-
<div className="mx-auto w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
|
145
|
-
<CheckCircle className="w-8 h-8 text-green-600" />
|
|
146
|
-
</div>
|
|
147
|
-
<div>
|
|
148
|
-
<h3 className="text-lg font-semibold text-green-900">
|
|
149
|
-
Verification Successful!
|
|
150
|
-
</h3>
|
|
151
|
-
<p className="text-sm text-green-700 mt-1">
|
|
152
|
-
Your TOTP code has been validated.
|
|
153
|
-
</p>
|
|
154
|
-
</div>
|
|
155
|
-
<Button
|
|
156
|
-
onClick={clearCode}
|
|
157
|
-
variant="outline"
|
|
158
|
-
className="bg-white"
|
|
159
|
-
>
|
|
160
|
-
Verify Another Code
|
|
161
|
-
</Button>
|
|
162
|
-
</div>
|
|
163
|
-
</CardContent>
|
|
164
|
-
</Card>
|
|
165
|
-
</div>
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<div className="max-w-md mx-auto">
|
|
171
|
-
<Card>
|
|
172
|
-
<CardContent className="pt-6">
|
|
173
|
-
<div className="text-center space-y-6">
|
|
174
|
-
<div>
|
|
175
|
-
<div className="mx-auto w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-4">
|
|
176
|
-
<Shield className="w-6 h-6 text-blue-600" />
|
|
177
|
-
</div>
|
|
178
|
-
<h3 className="text-lg font-semibold">Enter Verification Code</h3>
|
|
179
|
-
<p className="text-sm text-muted-foreground mt-1">
|
|
180
|
-
Enter the 6-digit code from your authenticator app
|
|
181
|
-
</p>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
<form onSubmit={handleSubmit} className="space-y-6">
|
|
185
|
-
<div className="flex justify-center gap-2">
|
|
186
|
-
{code.map((digit, index) => (
|
|
187
|
-
<Card
|
|
188
|
-
key={index}
|
|
189
|
-
className="w-12 h-14 border-2 focus-within:border-blue-500 transition-colors"
|
|
190
|
-
>
|
|
191
|
-
<CardContent className="p-0 h-full flex items-center justify-center">
|
|
192
|
-
<input
|
|
193
|
-
ref={(el) => {
|
|
194
|
-
inputRefs.current[index] = el;
|
|
195
|
-
}}
|
|
196
|
-
type="tel"
|
|
197
|
-
inputMode="numeric"
|
|
198
|
-
maxLength={1}
|
|
199
|
-
value={digit}
|
|
200
|
-
onChange={(e) =>
|
|
201
|
-
handleInputChange(index, e.target.value)
|
|
202
|
-
}
|
|
203
|
-
onKeyDown={(e) => handleKeyDown(index, e)}
|
|
204
|
-
className="w-full h-full text-center text-2xl font-bold border-none outline-none bg-transparent"
|
|
205
|
-
autoComplete="one-time-code"
|
|
206
|
-
disabled={loading}
|
|
207
|
-
style={{
|
|
208
|
-
height: "auto",
|
|
209
|
-
}}
|
|
210
|
-
/>
|
|
211
|
-
</CardContent>
|
|
212
|
-
</Card>
|
|
213
|
-
))}
|
|
214
|
-
</div>
|
|
215
|
-
|
|
216
|
-
{error && (
|
|
217
|
-
<Alert variant="destructive">
|
|
218
|
-
<AlertCircle className="h-4 w-4" />
|
|
219
|
-
<AlertDescription>{error}</AlertDescription>
|
|
220
|
-
</Alert>
|
|
221
|
-
)}
|
|
222
|
-
|
|
223
|
-
<div className="space-y-3">
|
|
224
|
-
<Button
|
|
225
|
-
type="submit"
|
|
226
|
-
className="w-full"
|
|
227
|
-
disabled={loading || code.some((digit) => digit === "")}
|
|
228
|
-
>
|
|
229
|
-
{loading ? "Verifying..." : "Verify Code"}
|
|
230
|
-
</Button>
|
|
231
|
-
|
|
232
|
-
<Button
|
|
233
|
-
type="button"
|
|
234
|
-
variant="ghost"
|
|
235
|
-
size="sm"
|
|
236
|
-
onClick={clearCode}
|
|
237
|
-
className="w-full text-xs"
|
|
238
|
-
>
|
|
239
|
-
Clear Code
|
|
240
|
-
</Button>
|
|
241
|
-
</div>
|
|
242
|
-
</form>
|
|
243
|
-
|
|
244
|
-
<p className="text-xs text-muted-foreground">
|
|
245
|
-
Codes refresh every 30 seconds
|
|
246
|
-
</p>
|
|
247
|
-
</div>
|
|
248
|
-
</CardContent>
|
|
249
|
-
</Card>
|
|
250
|
-
</div>
|
|
251
|
-
);
|
|
252
|
-
};
|
package/src/components/icons.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { LucideProps } from "lucide-react";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
|
|
4
|
-
const iconProps: LucideProps = {
|
|
5
|
-
className: "mr-2 h-4 w-4 text-current",
|
|
6
|
-
"aria-hidden": "true",
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const IconWrapper = ({ Icon, withMargin = true }: { Icon: any; withMargin?: boolean }) => {
|
|
10
|
-
const [isMounted, setIsMounted] = useState(false);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
setIsMounted(true);
|
|
14
|
-
}, []);
|
|
15
|
-
|
|
16
|
-
const iconClassWithMargin: LucideProps = withMargin ? iconProps : {
|
|
17
|
-
className: "h-4 w-4 text-current",
|
|
18
|
-
"aria-hidden": "true",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<span className="inline-flex items-center justify-center">
|
|
23
|
-
{!isMounted ? (
|
|
24
|
-
<span className={withMargin ? "mr-2 h-4 w-4" : "h-4 w-4"} aria-hidden="true" />
|
|
25
|
-
) : (
|
|
26
|
-
<Icon {...iconClassWithMargin} />
|
|
27
|
-
)}
|
|
28
|
-
</span>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../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 };
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
|
|
8
|
-
function Avatar({
|
|
9
|
-
className,
|
|
10
|
-
...props
|
|
11
|
-
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
|
12
|
-
return (
|
|
13
|
-
<AvatarPrimitive.Root
|
|
14
|
-
data-slot="avatar"
|
|
15
|
-
className={cn(
|
|
16
|
-
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
17
|
-
className,
|
|
18
|
-
)}
|
|
19
|
-
{...props}
|
|
20
|
-
/>
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function AvatarImage({
|
|
25
|
-
className,
|
|
26
|
-
...props
|
|
27
|
-
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
28
|
-
return (
|
|
29
|
-
<AvatarPrimitive.Image
|
|
30
|
-
data-slot="avatar-image"
|
|
31
|
-
className={cn("aspect-square size-full", className)}
|
|
32
|
-
{...props}
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function AvatarFallback({
|
|
38
|
-
className,
|
|
39
|
-
...props
|
|
40
|
-
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
41
|
-
return (
|
|
42
|
-
<AvatarPrimitive.Fallback
|
|
43
|
-
data-slot="avatar-fallback"
|
|
44
|
-
className={cn(
|
|
45
|
-
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
46
|
-
className,
|
|
47
|
-
)}
|
|
48
|
-
{...props}
|
|
49
|
-
/>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { Avatar, AvatarImage, AvatarFallback };
|
|
@@ -1,46 +0,0 @@
|
|
|
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 "../../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 };
|
|
@@ -1,56 +0,0 @@
|
|
|
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 "../../lib/utils";
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
-
destructive:
|
|
14
|
-
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
15
|
-
outline:
|
|
16
|
-
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
17
|
-
secondary:
|
|
18
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
20
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
-
},
|
|
22
|
-
size: {
|
|
23
|
-
default: "h-10 px-4 py-2",
|
|
24
|
-
sm: "h-9 rounded-md px-3",
|
|
25
|
-
lg: "h-11 rounded-md px-8",
|
|
26
|
-
icon: "h-10 w-10",
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
defaultVariants: {
|
|
30
|
-
variant: "default",
|
|
31
|
-
size: "default",
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
export interface ButtonProps
|
|
37
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
38
|
-
VariantProps<typeof buttonVariants> {
|
|
39
|
-
asChild?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
43
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
44
|
-
const Comp = asChild ? Slot : "button";
|
|
45
|
-
return (
|
|
46
|
-
<Comp
|
|
47
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
48
|
-
ref={ref}
|
|
49
|
-
{...props}
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
Button.displayName = "Button";
|
|
55
|
-
|
|
56
|
-
export { Button, buttonVariants };
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../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
|
-
};
|